From 54f2c841fa0007e5fee3b7d01a911c774f0a6cda Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 7 May 2009 17:28:59 +0200 Subject: oprofile: fix cpu buffer size The unit of oprofile_cpu_buffer_size is in samples, but was allocated in bytes. This led to the allocation of too small cpu buffers. This patch recalculates the buffer size in bytes taking also the ring_buffer_event header size into account. Reported-by: Suravee Suthikulpanit Signed-off-by: Robert Richter --- drivers/oprofile/cpu_buffer.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index f0e99d4c066b..242257b19441 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -78,16 +78,20 @@ void free_cpu_buffers(void) op_ring_buffer_write = NULL; } +#define RB_EVENT_HDR_SIZE 4 + int alloc_cpu_buffers(void) { int i; unsigned long buffer_size = oprofile_cpu_buffer_size; + unsigned long byte_size = buffer_size * (sizeof(struct op_sample) + + RB_EVENT_HDR_SIZE); - op_ring_buffer_read = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS); + op_ring_buffer_read = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS); if (!op_ring_buffer_read) goto fail; - op_ring_buffer_write = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS); + op_ring_buffer_write = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS); if (!op_ring_buffer_write) goto fail; -- cgit v1.2.3-55-g7522 From 8e7d2b2c6ecd3c21a54b877eae3d5be48292e6b5 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 8 May 2009 16:13:25 -0700 Subject: drm/i915: allocate large pointer arrays with vmalloc For awhile now, many of the GEM code paths have allocated page or object arrays with the slab allocator. This is nice and fast, but won't work well if memory is fragmented, since the slab allocator works with physically contiguous memory (i.e. order > 2 allocations are likely to fail fairly early after booting and doing some work). This patch works around the issue by falling back to vmalloc for >PAGE_SIZE allocations. This is ugly, but much less work than chaining a bunch of pages together by hand (suprisingly there's not a bunch of generic kernel helpers for this yet afaik). vmalloc space is somewhat precious on 32 bit kernels, but our allocations shouldn't be big enough to cause problems, though they're routinely more than a page. Note that this patch doesn't address the unchecked alloc-based-on-ioctl-args in GEM; that needs to be fixed in a separate patch. Also, I've deliberately ignored the DRM's "area" junk. I don't think anyone actually uses it anymore and I'm hoping it gets ripped out soon. [Updated: removed size arg to new free function. We could unify the free functions as well once the DRM mem tracking is ripped out.] fd.o bug #20152 (part 1/3) Signed-off-by: Jesse Barnes Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 38 +++++++++++++++----------------------- include/drm/drmP.h | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b189b49c7602..4a24c90fb940 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -349,7 +349,7 @@ i915_gem_shmem_pread_slow(struct drm_device *dev, struct drm_gem_object *obj, last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE; num_pages = last_data_page - first_data_page + 1; - user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + user_pages = drm_calloc_large(num_pages, sizeof(struct page *)); if (user_pages == NULL) return -ENOMEM; @@ -429,7 +429,7 @@ fail_put_user_pages: SetPageDirty(user_pages[i]); page_cache_release(user_pages[i]); } - kfree(user_pages); + drm_free_large(user_pages); return ret; } @@ -649,7 +649,7 @@ i915_gem_gtt_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE; num_pages = last_data_page - first_data_page + 1; - user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + user_pages = drm_calloc_large(num_pages, sizeof(struct page *)); if (user_pages == NULL) return -ENOMEM; @@ -719,7 +719,7 @@ out_unlock: out_unpin_pages: for (i = 0; i < pinned_pages; i++) page_cache_release(user_pages[i]); - kfree(user_pages); + drm_free_large(user_pages); return ret; } @@ -824,7 +824,7 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev, struct drm_gem_object *obj, last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE; num_pages = last_data_page - first_data_page + 1; - user_pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL); + user_pages = drm_calloc_large(num_pages, sizeof(struct page *)); if (user_pages == NULL) return -ENOMEM; @@ -902,7 +902,7 @@ fail_unlock: fail_put_user_pages: for (i = 0; i < pinned_pages; i++) page_cache_release(user_pages[i]); - kfree(user_pages); + drm_free_large(user_pages); return ret; } @@ -1408,9 +1408,7 @@ i915_gem_object_put_pages(struct drm_gem_object *obj) } obj_priv->dirty = 0; - drm_free(obj_priv->pages, - page_count * sizeof(struct page *), - DRM_MEM_DRIVER); + drm_free_large(obj_priv->pages); obj_priv->pages = NULL; } @@ -2024,8 +2022,7 @@ i915_gem_object_get_pages(struct drm_gem_object *obj) */ page_count = obj->size / PAGE_SIZE; BUG_ON(obj_priv->pages != NULL); - obj_priv->pages = drm_calloc(page_count, sizeof(struct page *), - DRM_MEM_DRIVER); + obj_priv->pages = drm_calloc_large(page_count, sizeof(struct page *)); if (obj_priv->pages == NULL) { DRM_ERROR("Faled to allocate page list\n"); obj_priv->pages_refcount--; @@ -3111,7 +3108,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list, reloc_count += exec_list[i].relocation_count; } - *relocs = drm_calloc(reloc_count, sizeof(**relocs), DRM_MEM_DRIVER); + *relocs = drm_calloc_large(reloc_count, sizeof(**relocs)); if (*relocs == NULL) return -ENOMEM; @@ -3125,8 +3122,7 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list, exec_list[i].relocation_count * sizeof(**relocs)); if (ret != 0) { - drm_free(*relocs, reloc_count * sizeof(**relocs), - DRM_MEM_DRIVER); + drm_free_large(*relocs); *relocs = NULL; return -EFAULT; } @@ -3165,7 +3161,7 @@ i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list, } err: - drm_free(relocs, reloc_count * sizeof(*relocs), DRM_MEM_DRIVER); + drm_free_large(relocs); return ret; } @@ -3198,10 +3194,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, return -EINVAL; } /* Copy in the exec list from userland */ - exec_list = drm_calloc(sizeof(*exec_list), args->buffer_count, - DRM_MEM_DRIVER); - object_list = drm_calloc(sizeof(*object_list), args->buffer_count, - DRM_MEM_DRIVER); + exec_list = drm_calloc_large(sizeof(*exec_list), args->buffer_count); + object_list = drm_calloc_large(sizeof(*object_list), args->buffer_count); if (exec_list == NULL || object_list == NULL) { DRM_ERROR("Failed to allocate exec or object list " "for %d buffers\n", @@ -3462,10 +3456,8 @@ err: } pre_mutex_err: - drm_free(object_list, sizeof(*object_list) * args->buffer_count, - DRM_MEM_DRIVER); - drm_free(exec_list, sizeof(*exec_list) * args->buffer_count, - DRM_MEM_DRIVER); + drm_free_large(object_list); + drm_free_large(exec_list); drm_free(cliprects, sizeof(*cliprects) * args->num_cliprects, DRM_MEM_DRIVER); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index c8c422151431..b84d8ae35e6f 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1519,6 +1519,30 @@ static __inline__ void *drm_calloc(size_t nmemb, size_t size, int area) { return kcalloc(nmemb, size, GFP_KERNEL); } + +static __inline__ void *drm_calloc_large(size_t nmemb, size_t size) +{ + u8 *addr; + + if (size <= PAGE_SIZE) + return kcalloc(nmemb, size, GFP_KERNEL); + + addr = vmalloc(nmemb * size); + if (!addr) + return NULL; + + memset(addr, 0, nmemb * size); + + return addr; +} + +static __inline void drm_free_large(void *ptr) +{ + if (!is_vmalloc_addr(ptr)) + return kfree(ptr); + + vfree(ptr); +} #else extern void *drm_alloc(size_t size, int area); extern void drm_free(void *pt, size_t size, int area); -- cgit v1.2.3-55-g7522 From 2b611cb6eed04062d0a9861c82248e02c844ba3f Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Fri, 27 Mar 2009 17:47:27 -0400 Subject: ath5k: fix scanning in AR2424 AR5K_PHY_PLL_40MHZ_5413 should not be ORed with AR5K_PHY_MODE_RAD_RF5112 for 5 GHz channels. The incorrect PLL value breaks scanning in the countries where 5 GHz channels are allowed. Signed-off-by: Pavel Roskin Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/reset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c index 7a17d31b2fd9..faede828c8ff 100644 --- a/drivers/net/wireless/ath5k/reset.c +++ b/drivers/net/wireless/ath5k/reset.c @@ -359,7 +359,7 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) mode |= AR5K_PHY_MODE_FREQ_5GHZ; if (ah->ah_radio == AR5K_RF5413) - clock |= AR5K_PHY_PLL_40MHZ_5413; + clock = AR5K_PHY_PLL_40MHZ_5413; else clock |= AR5K_PHY_PLL_40MHZ; -- cgit v1.2.3-55-g7522 From a54be5d43aa2d6febc5a4f8dd3b87b9429b60437 Mon Sep 17 00:00:00 2001 From: Forrest Zhang Date: Wed, 13 May 2009 11:14:39 -0400 Subject: ath5k: fix exp off-by-one when computing OFDM delta slope Commit e8f055f0c3b ("ath5k: Update reset code") subtly changed the code that computes floating point values for the PHY3_TIMING register such that the exponent is off by a decimal point, which can cause problems with OFDM channel operation. get_bitmask_order() actually returns the highest bit set plus one, whereas the previous code wanted the highest bit set. Instead, use ilog2 which is what this code is really calculating. Also check coef_scaled to handle the (invalid) case where we need log2(0). Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/reset.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/reset.c b/drivers/net/wireless/ath5k/reset.c index faede828c8ff..5f72c111c2e8 100644 --- a/drivers/net/wireless/ath5k/reset.c +++ b/drivers/net/wireless/ath5k/reset.c @@ -26,7 +26,7 @@ \*****************************/ #include /* To determine if a card is pci-e */ -#include /* For get_bitmask_order */ +#include #include "ath5k.h" #include "reg.h" #include "base.h" @@ -69,10 +69,10 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, /* Get exponent * ALGO: coef_exp = 14 - highest set bit position */ - coef_exp = get_bitmask_order(coef_scaled); + coef_exp = ilog2(coef_scaled); /* Doesn't make sense if it's zero*/ - if (!coef_exp) + if (!coef_scaled || !coef_exp) return -EINVAL; /* Note: we've shifted coef_scaled by 24 */ -- cgit v1.2.3-55-g7522 From fbc9f97bbf5e1eaee562eba93dc60faaff3f3bfa Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Fri, 15 May 2009 16:13:46 -0700 Subject: iwlwifi: do not cancel delayed work inside spin_lock_irqsave Calling cancel_delayed_work() from inside spin_lock_irqsave, introduces a potential deadlock. As explained by Johannes Berg A - lock T - timer phase CPU 1 CPU 2 --------------------------------------------- some place that calls cancel_timer_sync() (which is the | code) lock-irq(A) | "lock-irq"(T) | "unlock"(T) | wait(T) unlock(A) timer softirq "lock"(T) run(T) "unlock"(T) irq handler lock(A) unlock(A) Now all that again, interleaved, leading to deadlock: lock-irq(A) "lock"(T) run(T) IRQ during or maybe before run(T) --> lock(A) "lock-irq"(T) wait(T) We fix this by moving the call to cancel_delayed_work() into workqueue. There are cases where the work may not actually be queued or running at the time we are trying to cancel it, but cancel_delayed_work() is able to deal with this. Also cleanup iwl_set_mode related to this call. This function (iwl_set_mode) is only called when bringing interface up and there will thus not be any scanning done. No need to try to cancel scanning. Fixes http://bugzilla.kernel.org/show_bug.cgi?id=13224, which was also reported at http://marc.info/?l=linux-wireless&m=124081921903223&w=2 . Tested-by: Miles Lane Signed-off-by: Reinette Chatre Acked-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 7 ------- drivers/net/wireless/iwlwifi/iwl-scan.c | 7 ++++--- drivers/net/wireless/iwlwifi/iwl3945-base.c | 9 ++------- 3 files changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 3bb28db4a40f..f46ba2475776 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -669,13 +669,6 @@ static int iwl_set_mode(struct iwl_priv *priv, int mode) if (!iwl_is_ready_rf(priv)) return -EAGAIN; - cancel_delayed_work(&priv->scan_check); - if (iwl_scan_cancel_timeout(priv, 100)) { - IWL_WARN(priv, "Aborted scan still in progress after 100ms\n"); - IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); - return -EAGAIN; - } - iwl_commit_rxon(priv); return 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index e7c65c4f741b..6330b91e37ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -227,9 +227,6 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, /* The HW is no longer scanning */ clear_bit(STATUS_SCAN_HW, &priv->status); - /* The scan completion notification came in, so kill that timer... */ - cancel_delayed_work(&priv->scan_check); - IWL_DEBUG_INFO(priv, "Scan pass on %sGHz took %dms\n", (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ? "2.4" : "5.2", @@ -712,6 +709,8 @@ static void iwl_bg_request_scan(struct work_struct *data) mutex_lock(&priv->mutex); + cancel_delayed_work(&priv->scan_check); + if (!iwl_is_ready(priv)) { IWL_WARN(priv, "request scan called when driver not ready.\n"); goto done; @@ -925,6 +924,8 @@ void iwl_bg_scan_completed(struct work_struct *work) IWL_DEBUG_SCAN(priv, "SCAN complete scan\n"); + cancel_delayed_work(&priv->scan_check); + ieee80211_scan_completed(priv->hw, false); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 4cce66133500..ff4d0e41d7c4 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -782,13 +782,6 @@ static int iwl3945_set_mode(struct iwl_priv *priv, int mode) if (!iwl_is_ready_rf(priv)) return -EAGAIN; - cancel_delayed_work(&priv->scan_check); - if (iwl_scan_cancel_timeout(priv, 100)) { - IWL_WARN(priv, "Aborted scan still in progress after 100ms\n"); - IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); - return -EAGAIN; - } - iwl3945_commit_rxon(priv); return 0; @@ -3298,6 +3291,8 @@ static void iwl3945_bg_request_scan(struct work_struct *data) mutex_lock(&priv->mutex); + cancel_delayed_work(&priv->scan_check); + if (!iwl_is_ready(priv)) { IWL_WARN(priv, "request scan called when driver not ready.\n"); goto done; -- cgit v1.2.3-55-g7522 From 875690c378d64d9ee2de15cad8206d3f11ae5096 Mon Sep 17 00:00:00 2001 From: Fabio Rossi Date: Wed, 1 Apr 2009 20:37:50 +0200 Subject: ath5k: fix interpolation with equal power levels When the EEPROM contains weird values for the power levels we have to fix the interpolation process. Signed-off-by: Fabio Rossi Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/phy.c | 49 +++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c index 9e2faae5ae94..b48b29dca3d2 100644 --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -1487,28 +1487,35 @@ ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, { s8 tmp; s16 min_pwrL, min_pwrR; - s16 pwr_i = pwrL[0]; - - do { - pwr_i--; - tmp = (s8) ath5k_get_interpolated_value(pwr_i, - pwrL[0], pwrL[1], - stepL[0], stepL[1]); - - } while (tmp > 1); - - min_pwrL = pwr_i; - - pwr_i = pwrR[0]; - do { - pwr_i--; - tmp = (s8) ath5k_get_interpolated_value(pwr_i, - pwrR[0], pwrR[1], - stepR[0], stepR[1]); - - } while (tmp > 1); + s16 pwr_i; + + if (pwrL[0] == pwrL[1]) + min_pwrL = pwrL[0]; + else { + pwr_i = pwrL[0]; + do { + pwr_i--; + tmp = (s8) ath5k_get_interpolated_value(pwr_i, + pwrL[0], pwrL[1], + stepL[0], stepL[1]); + } while (tmp > 1); + + min_pwrL = pwr_i; + } - min_pwrR = pwr_i; + if (pwrR[0] == pwrR[1]) + min_pwrR = pwrR[0]; + else { + pwr_i = pwrR[0]; + do { + pwr_i--; + tmp = (s8) ath5k_get_interpolated_value(pwr_i, + pwrR[0], pwrR[1], + stepR[0], stepR[1]); + } while (tmp > 1); + + min_pwrR = pwr_i; + } /* Keep the right boundary so that it works for both curves */ return max(min_pwrL, min_pwrR); -- cgit v1.2.3-55-g7522 From 267d493b322b05984048aef8ea9b5b213490bbe0 Mon Sep 17 00:00:00 2001 From: John W. Linville Date: Wed, 20 May 2009 10:51:41 -0400 Subject: airo: fix airo_get_encode{,ext} buffer overflow like I mean it... "airo: airo_get_encode{,ext} potential buffer overflow" was actually a no-op, due to an unrecognized type overflow in an assignment. Oddly, gcc only seems to tell me about it when using -Wextra...grrr... Signed-off-by: John W. Linville --- drivers/net/wireless/airo.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index d73475739127..9eabf4d1f2e7 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -6467,6 +6467,7 @@ static int airo_get_encode(struct net_device *dev, { struct airo_info *local = dev->ml_priv; int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + int wep_key_len; u8 buf[16]; if (!local->wep_capable) @@ -6500,11 +6501,13 @@ static int airo_get_encode(struct net_device *dev, dwrq->flags |= index + 1; /* Copy the key to the user buffer */ - dwrq->length = get_wep_key(local, index, &buf[0], sizeof(buf)); - if (dwrq->length != -1) - memcpy(extra, buf, dwrq->length); - else + wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf)); + if (wep_key_len < 0) { dwrq->length = 0; + } else { + dwrq->length = wep_key_len; + memcpy(extra, buf, dwrq->length); + } return 0; } @@ -6617,7 +6620,7 @@ static int airo_get_encodeext(struct net_device *dev, struct airo_info *local = dev->ml_priv; struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, max_key_len; + int idx, max_key_len, wep_key_len; u8 buf[16]; if (!local->wep_capable) @@ -6661,11 +6664,13 @@ static int airo_get_encodeext(struct net_device *dev, memset(extra, 0, 16); /* Copy the key to the user buffer */ - ext->key_len = get_wep_key(local, idx, &buf[0], sizeof(buf)); - if (ext->key_len != -1) - memcpy(extra, buf, ext->key_len); - else + wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf)); + if (wep_key_len < 0) { ext->key_len = 0; + } else { + ext->key_len = wep_key_len; + memcpy(extra, buf, ext->key_len); + } return 0; } -- cgit v1.2.3-55-g7522 From c9d2fbf36df5e04efa226614093bb1bacc6fe131 Mon Sep 17 00:00:00 2001 From: Jay Sternberg Date: Tue, 19 May 2009 14:56:36 -0700 Subject: iwlwifi: update 5000 ucode support to version 2 of API enable iwl driver to support 5000 ucode having version 2 of API Signed-off-by: Jay Sternberg Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-5000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index e5ca2511a81a..9452461ce864 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -46,7 +46,7 @@ #include "iwl-6000-hw.h" /* Highest firmware API version supported */ -#define IWL5000_UCODE_API_MAX 1 +#define IWL5000_UCODE_API_MAX 2 #define IWL5150_UCODE_API_MAX 2 /* Lowest firmware API version supported */ -- cgit v1.2.3-55-g7522 From a6c67339784db5763d6f20ae1881aeebe8c5a9f4 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 20 May 2009 02:12:56 +0200 Subject: wireless: beyond ARRAY_SIZE of intf->crypto_stats Do not go beyond ARRAY_SIZE of intf->crypto_stats Signed-off-by: Roel Kluin Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 07d378ef0b46..7b3ee8c2eaef 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -138,7 +138,7 @@ void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, if (cipher == CIPHER_TKIP_NO_MIC) cipher = CIPHER_TKIP; - if (cipher == CIPHER_NONE || cipher > CIPHER_MAX) + if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX) return; /* Remove CIPHER_NONE index */ -- cgit v1.2.3-55-g7522 From c40499e04b2005e61f989824251f9343b55f96bb Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 21 May 2009 15:04:15 -0700 Subject: gigaset: beyond ARRAY_SIZE of iwb->data Signed-off-by: Roel Kluin Signed-off-by: David S. Miller --- drivers/isdn/gigaset/isocdata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index b171e75cb52e..29808c4fb1cb 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -175,7 +175,7 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) return -EINVAL; } src = iwb->read; - if (unlikely(limit > BAS_OUTBUFSIZE + BAS_OUTBUFPAD || + if (unlikely(limit >= BAS_OUTBUFSIZE + BAS_OUTBUFPAD || (read < src && limit >= src))) { pr_err("isoc write buffer frame reservation violated\n"); return -EFAULT; -- cgit v1.2.3-55-g7522 From e069c0cf7c169ae5a8bfdc8d083a5d66fbef73d8 Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Fri, 8 May 2009 15:51:44 -0700 Subject: wimax/i2400m: usb: fix device reset on autosuspend while not yet idle When the i2400m is connected to a network, the host interface (USB) cannot be suspended. For that to happen, the device has to have negotiated with the basestation to put the link on IDLE state. If the host tries to put the device in standby while it is connected but not idle, the device resets, as the driver should not do that. To avoid triggering that, when the USB susbsytem requires the driver to autosuspend the device, the driver checks if the device is not yet idle. If it is not, the request is rejected (will be retried again later on after the autosuspend timeout). At some point the device will enter idle and the request will succeed (unless of course, there is network traffic, but at that point, there is no idle neither in the link or the host interface). Signed-off-by: Inaky Perez-Gonzalez --- drivers/net/wimax/i2400m/usb.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index ca4151a9e222..17851321b7fd 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -505,27 +505,52 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) #ifdef CONFIG_PM struct usb_device *usb_dev = i2400mu->usb_dev; #endif + unsigned is_autosuspend = 0; struct i2400m *i2400m = &i2400mu->i2400m; +#ifdef CONFIG_PM + if (usb_dev->auto_pm > 0) + is_autosuspend = 1; +#endif + d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); if (i2400m->updown == 0) goto no_firmware; - d_printf(1, dev, "fw up, requesting standby\n"); + if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { + /* ugh -- the device is connected and this suspend + * request is an autosuspend one (not a system standby + * / hibernate). + * + * The only way the device can go to standby is if the + * link with the base station is in IDLE mode; that + * were the case, we'd be in status + * I2400M_SS_CONNECTED_IDLE. But we are not. + * + * If we *tell* him to go power save now, it'll reset + * as a precautionary measure, so if this is an + * autosuspend thing, say no and it'll come back + * later, when the link is IDLE + */ + result = -EBADF; + d_printf(1, dev, "fw up, link up, not-idle, autosuspend: " + "not entering powersave\n"); + goto error_not_now; + } + d_printf(1, dev, "fw up: entering powersave\n"); atomic_dec(&i2400mu->do_autopm); result = i2400m_cmd_enter_powersave(i2400m); atomic_inc(&i2400mu->do_autopm); -#ifdef CONFIG_PM - if (result < 0 && usb_dev->auto_pm == 0) { + if (result < 0 && !is_autosuspend) { /* System suspend, can't fail */ dev_err(dev, "failed to suspend, will reset on resume\n"); result = 0; } -#endif if (result < 0) goto error_enter_powersave; i2400mu_notification_release(i2400mu); - d_printf(1, dev, "fw up, got standby\n"); + d_printf(1, dev, "powersave requested\n"); error_enter_powersave: +error_not_now: no_firmware: d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", iface, pm_msg.event, result); -- cgit v1.2.3-55-g7522 From f47edc6dab11801c2e97088ba7bbce042ded867c Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 22 May 2009 16:46:52 +0800 Subject: fsldma: fix check on potential fdev->chan[] overflow Fix the check of potential array overflow when using corrupted channel device tree nodes. Signed-off-by: Roel Kluin Signed-off-by: Li Yang --- drivers/dma/fsldma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index da8a8ed9e411..391b1bd7098b 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -830,7 +830,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev, new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1); new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7; - if (new_fsl_chan->id > FSL_DMA_MAX_CHANS_PER_DEVICE) { + if (new_fsl_chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) { dev_err(fdev->dev, "There is no %d channel!\n", new_fsl_chan->id); err = -EINVAL; -- cgit v1.2.3-55-g7522 From 138ef0185177a6d221d24b6aa8f12d867fbbef90 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Tue, 19 May 2009 15:42:13 -0700 Subject: fsldma: fix "DMA halt timeout!" errors When using the DMA controller from multiple threads at the same time, it is possible to get lots of "DMA halt timeout!" errors printed to the kernel log. This occurs due to a race between fsl_dma_memcpy_issue_pending() and the interrupt handler, fsl_dma_chan_do_interrupt(). Both call the fsl_chan_xfer_ld_queue() function, which does not protect against concurrent accesses to dma_halt() and dma_start(). The existing spinlock is moved to cover the dma_halt() and dma_start() functions. Testing shows that the "DMA halt timeout!" errors disappear. Signed-off-by: Ira W. Snyder Signed-off-by: Li Yang --- drivers/dma/fsldma.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 391b1bd7098b..a4151c3bb78b 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -598,15 +598,16 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan) dma_addr_t next_dest_addr; unsigned long flags; + spin_lock_irqsave(&fsl_chan->desc_lock, flags); + if (!dma_is_idle(fsl_chan)) - return; + goto out_unlock; dma_halt(fsl_chan); /* If there are some link descriptors * not transfered in queue. We need to start it. */ - spin_lock_irqsave(&fsl_chan->desc_lock, flags); /* Find the first un-transfer desciptor */ for (ld_node = fsl_chan->ld_queue.next; @@ -617,8 +618,6 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan) fsl_chan->common.cookie) == DMA_SUCCESS); ld_node = ld_node->next); - spin_unlock_irqrestore(&fsl_chan->desc_lock, flags); - if (ld_node != &fsl_chan->ld_queue) { /* Get the ld start address from ld_queue */ next_dest_addr = to_fsl_desc(ld_node)->async_tx.phys; @@ -630,6 +629,9 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan) set_cdar(fsl_chan, 0); set_ndar(fsl_chan, 0); } + +out_unlock: + spin_unlock_irqrestore(&fsl_chan->desc_lock, flags); } /** -- cgit v1.2.3-55-g7522 From bcfb7465c03a8c62c89da374677df56f6b894d44 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Fri, 15 May 2009 14:27:16 -0700 Subject: fsldma: fix infinite loop on multi-descriptor DMA chain completion When creating a DMA transaction with multiple descriptors, the async_tx cookie is set to 0 for each descriptor in the chain, excluding the last descriptor, whose cookie is set to -EBUSY. When fsl_dma_tx_submit() is run, it only assigns a cookie to the first descriptor. All of the remaining descriptors keep their original value, including the last descriptor, which is set to -EBUSY. After the DMA completes, the driver will update the last completed cookie to be -EBUSY, which is an error code instead of a valid cookie. This causes dma_async_is_complete() to always return DMA_IN_PROGRESS. This causes the fsldma driver to never cleanup the queue of link descriptors, and the driver will re-run the DMA transaction on the hardware each time it receives the End-of-Chain interrupt. This causes an infinite loop. With this patch, fsl_dma_tx_submit() is changed to assign a cookie to every descriptor in the chain. The rest of the code then works without problems. Signed-off-by: Ira W. Snyder Signed-off-by: Li Yang --- drivers/dma/fsldma.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index a4151c3bb78b..7313a1ae5f83 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -313,8 +313,8 @@ static void fsl_chan_toggle_ext_start(struct fsl_dma_chan *fsl_chan, int enable) static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) { - struct fsl_desc_sw *desc = tx_to_fsl_desc(tx); struct fsl_dma_chan *fsl_chan = to_fsl_chan(tx->chan); + struct fsl_desc_sw *desc; unsigned long flags; dma_cookie_t cookie; @@ -322,14 +322,17 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx) spin_lock_irqsave(&fsl_chan->desc_lock, flags); cookie = fsl_chan->common.cookie; - cookie++; - if (cookie < 0) - cookie = 1; - desc->async_tx.cookie = cookie; - fsl_chan->common.cookie = desc->async_tx.cookie; - - append_ld_queue(fsl_chan, desc); - list_splice_init(&desc->async_tx.tx_list, fsl_chan->ld_queue.prev); + list_for_each_entry(desc, &tx->tx_list, node) { + cookie++; + if (cookie < 0) + cookie = 1; + + desc->async_tx.cookie = cookie; + } + + fsl_chan->common.cookie = cookie; + append_ld_queue(fsl_chan, tx_to_fsl_desc(tx)); + list_splice_init(&tx->tx_list, fsl_chan->ld_queue.prev); spin_unlock_irqrestore(&fsl_chan->desc_lock, flags); -- cgit v1.2.3-55-g7522 From 776c8943f2766f2819fafd88fdfbaf418ecd6e41 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Fri, 15 May 2009 11:33:20 -0700 Subject: fsldma: snooping is not enabled for last entry in descriptor chain On the 83xx controller, snooping is necessary for the DMA controller to ensure cache coherence with the CPU when transferring to/from RAM. The last descriptor in a chain will always have the End-of-Chain interrupt bit set, so we can set the snoop bit while adding the End-of-Chain interrupt bit. Signed-off-by: Ira W. Snyder Signed-off-by: Li Yang --- drivers/dma/fsldma.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 7313a1ae5f83..ff9194d7ebb7 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -179,9 +179,14 @@ static void dma_halt(struct fsl_dma_chan *fsl_chan) static void set_ld_eol(struct fsl_dma_chan *fsl_chan, struct fsl_desc_sw *desc) { + u64 snoop_bits; + + snoop_bits = ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX) + ? FSL_DMA_SNEN : 0; + desc->hw.next_ln_addr = CPU_TO_DMA(fsl_chan, - DMA_TO_CPU(fsl_chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL, - 64); + DMA_TO_CPU(fsl_chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL + | snoop_bits, 64); } static void append_ld_queue(struct fsl_dma_chan *fsl_chan, -- cgit v1.2.3-55-g7522 From 2e077f8e8337e52eef3c39c24c31e103b11a0326 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Fri, 15 May 2009 09:59:46 -0700 Subject: fsldma: fix memory leak on error path in fsl_dma_prep_memcpy() When preparing a memcpy operation, if the kernel fails to allocate memory for a link descriptor after the first link descriptor has already been allocated, then some memory will never be released. Fix the problem by walking the list of allocated descriptors backwards, and freeing the allocated descriptors back into the DMA pool. Signed-off-by: Ira W. Snyder Signed-off-by: Li Yang --- drivers/dma/fsldma.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index ff9194d7ebb7..15783102bf17 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -462,8 +462,8 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( { struct fsl_dma_chan *fsl_chan; struct fsl_desc_sw *first = NULL, *prev = NULL, *new; + struct list_head *list; size_t copy; - LIST_HEAD(link_chain); if (!chan) return NULL; @@ -480,7 +480,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( if (!new) { dev_err(fsl_chan->dev, "No free memory for link descriptor\n"); - return NULL; + goto fail; } #ifdef FSL_DMA_LD_DEBUG dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new); @@ -515,7 +515,19 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( /* Set End-of-link to the last link descriptor of new list*/ set_ld_eol(fsl_chan, new); - return first ? &first->async_tx : NULL; + return &first->async_tx; + +fail: + if (!first) + return NULL; + + list = &first->async_tx.tx_list; + list_for_each_entry_safe_reverse(new, prev, list, node) { + list_del(&new->node); + dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys); + } + + return NULL; } /** -- cgit v1.2.3-55-g7522 From 14b60391587ab9b2207c4fb6281763a93ae85e0f Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 20 May 2009 16:47:08 -0400 Subject: i915: support 8xx desktop cursors For some reason we never added 8xx desktop cursor support to the kernel. This patch fixes that. [krh: Also set the size on pre-i915 hw.] Tested-by: Kristian Høgsberg Signed-off-by: Jesse Barnes Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 4 ++-- drivers/gpu/drm/i915/i915_reg.h | 17 +++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 26 ++++++++++++++++++++------ 3 files changed, 39 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4a24c90fb940..717b6a854bcd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1145,7 +1145,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) mutex_unlock(&dev->struct_mutex); return VM_FAULT_SIGBUS; } - list_add(&obj_priv->list, &dev_priv->mm.inactive_list); + list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); } /* Need a new fence register? */ @@ -1375,7 +1375,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, mutex_unlock(&dev->struct_mutex); return ret; } - list_add(&obj_priv->list, &dev_priv->mm.inactive_list); + list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); } drm_gem_object_unreference(obj); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 15da44cf21b1..9668cc0d7f4e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1410,9 +1410,25 @@ /* Cursor A & B regs */ #define CURACNTR 0x70080 +/* Old style CUR*CNTR flags (desktop 8xx) */ +#define CURSOR_ENABLE 0x80000000 +#define CURSOR_GAMMA_ENABLE 0x40000000 +#define CURSOR_STRIDE_MASK 0x30000000 +#define CURSOR_FORMAT_SHIFT 24 +#define CURSOR_FORMAT_MASK (0x07 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_2C (0x00 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_3C (0x01 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_4C (0x02 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_ARGB (0x04 << CURSOR_FORMAT_SHIFT) +#define CURSOR_FORMAT_XRGB (0x05 << CURSOR_FORMAT_SHIFT) +/* New style CUR*CNTR flags */ +#define CURSOR_MODE 0x27 #define CURSOR_MODE_DISABLE 0x00 #define CURSOR_MODE_64_32B_AX 0x07 #define CURSOR_MODE_64_ARGB_AX ((1 << 5) | CURSOR_MODE_64_32B_AX) +#define MCURSOR_PIPE_SELECT (1 << 28) +#define MCURSOR_PIPE_A 0x00 +#define MCURSOR_PIPE_B (1 << 28) #define MCURSOR_GAMMA_ENABLE (1 << 26) #define CURABASE 0x70084 #define CURAPOS 0x70088 @@ -1420,6 +1436,7 @@ #define CURSOR_POS_SIGN 0x8000 #define CURSOR_X_SHIFT 0 #define CURSOR_Y_SHIFT 16 +#define CURSIZE 0x700a0 #define CURBCNTR 0x700c0 #define CURBBASE 0x700c4 #define CURBPOS 0x700c8 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3387cf32f385..c9d6f10ba92e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1357,7 +1357,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, int pipe = intel_crtc->pipe; uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; - uint32_t temp; + uint32_t temp = I915_READ(control); size_t addr; int ret; @@ -1366,7 +1366,12 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, /* if we want to turn off the cursor ignore width and height */ if (!handle) { DRM_DEBUG("cursor off\n"); - temp = CURSOR_MODE_DISABLE; + if (IS_MOBILE(dev) || IS_I9XX(dev)) { + temp &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); + temp |= CURSOR_MODE_DISABLE; + } else { + temp &= ~(CURSOR_ENABLE | CURSOR_GAMMA_ENABLE); + } addr = 0; bo = NULL; mutex_lock(&dev->struct_mutex); @@ -1409,10 +1414,19 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, addr = obj_priv->phys_obj->handle->busaddr; } - temp = 0; - /* set the pipe for the cursor */ - temp |= (pipe << 28); - temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + if (!IS_I9XX(dev)) + I915_WRITE(CURSIZE, (height << 12) | width); + + /* Hooray for CUR*CNTR differences */ + if (IS_MOBILE(dev) || IS_I9XX(dev)) { + temp &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT); + temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + temp |= (pipe << 28); /* Connect to correct pipe */ + } else { + temp &= ~(CURSOR_FORMAT_MASK); + temp |= CURSOR_ENABLE; + temp |= CURSOR_FORMAT_ARGB | CURSOR_GAMMA_ENABLE; + } finish: I915_WRITE(control, temp); -- cgit v1.2.3-55-g7522 From 8863170628da4b0b461eb96bf797df1dca0bd03e Mon Sep 17 00:00:00 2001 From: Ma Ling Date: Wed, 13 May 2009 11:19:55 +0800 Subject: drm/i915: Fetch SDVO LVDS mode lines from VBT, then reserve them Signed-off-by: Ma Ling Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_drv.h | 3 +- drivers/gpu/drm/i915/intel_bios.c | 102 +++++++++++++++++++++++++++----------- drivers/gpu/drm/i915/intel_bios.h | 17 +++++++ drivers/gpu/drm/i915/intel_lvds.c | 4 +- 4 files changed, 93 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9b149fe824c3..c431fa54bbb5 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -180,7 +180,8 @@ typedef struct drm_i915_private { int backlight_duty_cycle; /* restore backlight to this value */ bool panel_wants_dither; struct drm_display_mode *panel_fixed_mode; - struct drm_display_mode *vbt_mode; /* if any */ + 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; diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index fc28e2bbd542..9d78cff33b24 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -57,9 +57,43 @@ find_section(struct bdb_header *bdb, int section_id) return NULL; } -/* Try to find panel data */ static void -parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) +fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, + struct lvds_dvo_timing *dvo_timing) +{ + panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | + dvo_timing->hactive_lo; + panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + + ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); + panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + + dvo_timing->hsync_pulse_width; + panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + + ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); + + panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | + dvo_timing->vactive_lo; + panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + + dvo_timing->vsync_off; + panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + + dvo_timing->vsync_pulse_width; + panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + + ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); + panel_fixed_mode->clock = dvo_timing->clock * 10; + panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + + /* Some VBTs have bogus h/vtotal values */ + if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) + panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; + if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) + panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; + + drm_mode_set_name(panel_fixed_mode); +} + +/* Try to find integrated panel data */ +static void +parse_lfp_panel_data(struct drm_i915_private *dev_priv, + struct bdb_header *bdb) { struct bdb_lvds_options *lvds_options; struct bdb_lvds_lfp_data *lvds_lfp_data; @@ -91,38 +125,45 @@ parse_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode), DRM_MEM_DRIVER); - panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | - dvo_timing->hactive_lo; - panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + - ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); - panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + - dvo_timing->hsync_pulse_width; - panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + - ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); + fill_detail_timing_data(panel_fixed_mode, dvo_timing); - panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | - dvo_timing->vactive_lo; - panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + - dvo_timing->vsync_off; - panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + - dvo_timing->vsync_pulse_width; - panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + - ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); - panel_fixed_mode->clock = dvo_timing->clock * 10; - panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; - /* Some VBTs have bogus h/vtotal values */ - if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) - panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; - if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) - panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; + DRM_DEBUG("Found panel mode in BIOS VBT tables:\n"); + drm_mode_debug_printmodeline(panel_fixed_mode); - drm_mode_set_name(panel_fixed_mode); + return; +} + +/* Try to find sdvo panel data */ +static void +parse_sdvo_panel_data(struct drm_i915_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_sdvo_lvds_options *sdvo_lvds_options; + struct lvds_dvo_timing *dvo_timing; + struct drm_display_mode *panel_fixed_mode; - dev_priv->vbt_mode = panel_fixed_mode; + dev_priv->sdvo_lvds_vbt_mode = NULL; - DRM_DEBUG("Found panel mode in BIOS VBT tables:\n"); - drm_mode_debug_printmodeline(panel_fixed_mode); + sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); + if (!sdvo_lvds_options) + return; + + dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS); + if (!dvo_timing) + return; + + panel_fixed_mode = drm_calloc(1, sizeof(*panel_fixed_mode), + DRM_MEM_DRIVER); + + if (!panel_fixed_mode) + return; + + fill_detail_timing_data(panel_fixed_mode, + dvo_timing + sdvo_lvds_options->panel_type); + + dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode; return; } @@ -199,7 +240,8 @@ intel_init_bios(struct drm_device *dev) /* Grab useful general definitions */ parse_general_features(dev_priv, bdb); - parse_panel_data(dev_priv, bdb); + parse_lfp_panel_data(dev_priv, bdb); + parse_sdvo_panel_data(dev_priv, bdb); pci_unmap_rom(pdev, bios); diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index de621aad85b5..8ca2cde15804 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -279,6 +279,23 @@ struct vch_bdb_22 { struct vch_panel_data panels[16]; } __attribute__((packed)); +struct bdb_sdvo_lvds_options { + u8 panel_backlight; + u8 h40_set_panel_type; + u8 panel_type; + u8 ssc_clk_freq; + u16 als_low_trip; + u16 als_high_trip; + u8 sclalarcoeff_tab_row_num; + u8 sclalarcoeff_tab_row_size; + u8 coefficient[8]; + u8 panel_misc_bits_1; + u8 panel_misc_bits_2; + u8 panel_misc_bits_3; + u8 panel_misc_bits_4; +} __attribute__((packed)); + + bool intel_init_bios(struct drm_device *dev); /* diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 439a86514993..53731f0ffcb5 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -511,10 +511,10 @@ void intel_lvds_init(struct drm_device *dev) } /* Failed to get EDID, what about VBT? */ - if (dev_priv->vbt_mode) { + if (dev_priv->lfp_lvds_vbt_mode) { mutex_lock(&dev->mode_config.mutex); dev_priv->panel_fixed_mode = - drm_mode_duplicate(dev, dev_priv->vbt_mode); + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); mutex_unlock(&dev->mode_config.mutex); if (dev_priv->panel_fixed_mode) { dev_priv->panel_fixed_mode->type |= -- cgit v1.2.3-55-g7522 From 7086c87fb1446ceb37918ffa0941359a7c2ec6cf Mon Sep 17 00:00:00 2001 From: Ma Ling Date: Wed, 13 May 2009 11:20:06 +0800 Subject: drm/i915: Return SDVO LVDS VBT mode if no EDID modes are detected. Some new SDVO LVDS hardware doesn't have DDC available, and this should fix the display on it. Signed-off-by: Ma Ling Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_sdvo.c | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 9913651c1e17..f79ebf4de63c 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -69,6 +69,10 @@ struct intel_sdvo_priv { * This is set if we treat the device as HDMI, instead of DVI. */ bool is_hdmi; + /** + * This is set if we detect output of sdvo device as LVDS. + */ + bool is_lvds; /** * Returned SDTV resolutions allowed for the current format, if the @@ -1543,6 +1547,37 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector) } } +static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) +{ + struct intel_output *intel_output = to_intel_output(connector); + struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; + struct drm_i915_private *dev_priv = connector->dev->dev_private; + + /* + * Attempt to get the mode list from DDC. + * Assume that the preferred modes are + * arranged in priority order. + */ + /* set the bus switch and get the modes */ + intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus); + intel_ddc_get_modes(intel_output); + if (list_empty(&connector->probed_modes) == false) + return; + + /* Fetch modes from VBT */ + if (dev_priv->sdvo_lvds_vbt_mode != NULL) { + struct drm_display_mode *newmode; + newmode = drm_mode_duplicate(connector->dev, + dev_priv->sdvo_lvds_vbt_mode); + if (newmode != NULL) { + /* Guarantee the mode is preferred */ + newmode->type = (DRM_MODE_TYPE_PREFERRED | + DRM_MODE_TYPE_DRIVER); + drm_mode_probed_add(connector, newmode); + } + } +} + static int intel_sdvo_get_modes(struct drm_connector *connector) { struct intel_output *output = to_intel_output(connector); @@ -1550,6 +1585,8 @@ static int intel_sdvo_get_modes(struct drm_connector *connector) if (sdvo_priv->is_tv) intel_sdvo_get_tv_modes(connector); + else if (sdvo_priv->is_lvds == true) + intel_sdvo_get_lvds_modes(connector); else intel_sdvo_get_ddc_modes(connector); @@ -1720,6 +1757,8 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) } } + /* In defaut case sdvo lvds is false */ + sdvo_priv->is_lvds = false; intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps); if (sdvo_priv->caps.output_flags & @@ -1773,6 +1812,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_LVDS; connector_type = DRM_MODE_CONNECTOR_LVDS; + sdvo_priv->is_lvds = true; } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1) { @@ -1780,6 +1820,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_LVDS; connector_type = DRM_MODE_CONNECTOR_LVDS; + sdvo_priv->is_lvds = true; } else { -- cgit v1.2.3-55-g7522 From ad5b2a6db3eddc41358d8a73f5cfe1c38e7e3a19 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Fri, 15 May 2009 09:10:41 +0200 Subject: drm/i915: Determine type before initialising connector drm_connector_init sets both the connector type and the connector type_id on the newly initialised connector. As the connector type_id is coupled to the connector type, the connector type cannot simply be modified on an initialised connector. This patch changes the order of operations on intel_sdvo_init so that the type is determined before the connector is intialised. This fixes a bug whereby the name card0-VGA-1 would be allocted to both a CRT and an SDVO connector since the SDVO connector would be initialised with type 'unknown' and hence have its type_id assigned from the wrong pool. Signed-off-by: Jonas Bonn Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_sdvo.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f79ebf4de63c..ded122c1ae2d 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1713,17 +1713,9 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) return false; } - connector = &intel_output->base; - - drm_connector_init(dev, connector, &intel_sdvo_connector_funcs, - DRM_MODE_CONNECTOR_Unknown); - drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs); sdvo_priv = (struct intel_sdvo_priv *)(intel_output + 1); intel_output->type = INTEL_OUTPUT_SDVO; - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - /* setup the DDC bus. */ if (output_device == SDVOB) i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB"); @@ -1731,7 +1723,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) i2cbus = intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC"); if (!i2cbus) - goto err_connector; + goto err_inteloutput; sdvo_priv->i2c_bus = i2cbus; @@ -1747,7 +1739,6 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) intel_output->i2c_bus = i2cbus; intel_output->dev_priv = sdvo_priv; - /* Read the regs to test if we can talk to the device */ for (i = 0; i < 0x40; i++) { if (!intel_sdvo_read_byte(intel_output, i, &ch[i])) { @@ -1768,7 +1759,6 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) else sdvo_priv->controlled_output = SDVO_OUTPUT_TMDS1; - connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_TMDS; connector_type = DRM_MODE_CONNECTOR_DVID; @@ -1786,7 +1776,6 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_SVID0) { sdvo_priv->controlled_output = SDVO_OUTPUT_SVID0; - connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_TVDAC; connector_type = DRM_MODE_CONNECTOR_SVIDEO; sdvo_priv->is_tv = true; @@ -1795,21 +1784,18 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) { sdvo_priv->controlled_output = SDVO_OUTPUT_RGB0; - connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_DAC; connector_type = DRM_MODE_CONNECTOR_VGA; } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) { sdvo_priv->controlled_output = SDVO_OUTPUT_RGB1; - connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_DAC; connector_type = DRM_MODE_CONNECTOR_VGA; } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS0) { sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0; - connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_LVDS; connector_type = DRM_MODE_CONNECTOR_LVDS; sdvo_priv->is_lvds = true; @@ -1817,7 +1803,6 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_LVDS1) { sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS1; - connector->display_info.subpixel_order = SubPixelHorizontalRGB; encoder_type = DRM_MODE_ENCODER_LVDS; connector_type = DRM_MODE_CONNECTOR_LVDS; sdvo_priv->is_lvds = true; @@ -1836,9 +1821,16 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) goto err_i2c; } + connector = &intel_output->base; + drm_connector_init(dev, connector, &intel_sdvo_connector_funcs, + connector_type); + drm_connector_helper_add(connector, &intel_sdvo_connector_helper_funcs); + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + drm_encoder_init(dev, &intel_output->enc, &intel_sdvo_enc_funcs, encoder_type); drm_encoder_helper_add(&intel_output->enc, &intel_sdvo_helper_funcs); - connector->connector_type = connector_type; drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); drm_sysfs_connector_add(connector); @@ -1876,8 +1868,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) err_i2c: intel_i2c_destroy(intel_output->i2c_bus); -err_connector: - drm_connector_cleanup(connector); +err_inteloutput: kfree(intel_output); return false; -- cgit v1.2.3-55-g7522 From 619ac3b75a1e9b2df66857f6a0fb466f1da5fa9e Mon Sep 17 00:00:00 2001 From: Ma Ling Date: Mon, 18 May 2009 16:12:46 +0800 Subject: drm/i915: Use an I2C algo to do the flip to SDVO DDC bus. Previously, we would set the control bus switch before calls were made to request EDID information over DDC. But recently the DDC code started doing multiple I2C transfers to get the EDID extensions as well. This tripped up SDVO, because the control bus switch is only in effect until the next STOP after a START. By doing our own algo, we can wrap each i2c transaction on the DDC I2C bus with the control bus switch it requires. freedesktop.org bug #21042 Signed-off-by: Ma Ling [anholt: Hand application for conflict, fixed error path] Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_sdvo.c | 67 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index ded122c1ae2d..f3ef6bfd8ffc 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1402,10 +1402,8 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) { struct intel_output *intel_output = to_intel_output(connector); - struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv; /* set the bus switch and get the modes */ - intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus); intel_ddc_get_modes(intel_output); #if 0 @@ -1601,6 +1599,9 @@ static void intel_sdvo_destroy(struct drm_connector *connector) if (intel_output->i2c_bus) intel_i2c_destroy(intel_output->i2c_bus); + if (intel_output->ddc_bus) + intel_i2c_destroy(intel_output->ddc_bus); + drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(intel_output); @@ -1697,12 +1698,56 @@ intel_sdvo_get_digital_encoding_mode(struct intel_output *output) return true; } +static struct intel_output * +intel_sdvo_chan_to_intel_output(struct intel_i2c_chan *chan) +{ + struct drm_device *dev = chan->drm_dev; + struct drm_connector *connector; + struct intel_output *intel_output = NULL; + + list_for_each_entry(connector, + &dev->mode_config.connector_list, head) { + if (to_intel_output(connector)->ddc_bus == chan) { + intel_output = to_intel_output(connector); + break; + } + } + return intel_output; +} + +static int intel_sdvo_master_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct intel_output *intel_output; + struct intel_sdvo_priv *sdvo_priv; + struct i2c_algo_bit_data *algo_data; + struct i2c_algorithm *algo; + + algo_data = (struct i2c_algo_bit_data *)i2c_adap->algo_data; + intel_output = + intel_sdvo_chan_to_intel_output( + (struct intel_i2c_chan *)(algo_data->data)); + if (intel_output == NULL) + return -EINVAL; + + sdvo_priv = intel_output->dev_priv; + algo = (struct i2c_algorithm *)intel_output->i2c_bus->adapter.algo; + + intel_sdvo_set_control_bus_switch(intel_output, sdvo_priv->ddc_bus); + return algo->master_xfer(i2c_adap, msgs, num); +} + +static struct i2c_algorithm intel_sdvo_i2c_bit_algo = { + .master_xfer = intel_sdvo_master_xfer, +}; + bool intel_sdvo_init(struct drm_device *dev, int output_device) { struct drm_connector *connector; struct intel_output *intel_output; struct intel_sdvo_priv *sdvo_priv; struct intel_i2c_chan *i2cbus = NULL; + struct intel_i2c_chan *ddcbus = NULL; int connector_type; u8 ch[0x40]; int i; @@ -1748,6 +1793,20 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) } } + /* setup the DDC bus. */ + if (output_device == SDVOB) + ddcbus = intel_i2c_create(dev, GPIOE, "SDVOB DDC BUS"); + else + ddcbus = intel_i2c_create(dev, GPIOE, "SDVOC DDC BUS"); + + if (ddcbus == NULL) + goto err_i2c; + + intel_sdvo_i2c_bit_algo.functionality = + intel_output->i2c_bus->adapter.algo->functionality; + ddcbus->adapter.algo = &intel_sdvo_i2c_bit_algo; + intel_output->ddc_bus = ddcbus; + /* In defaut case sdvo lvds is false */ sdvo_priv->is_lvds = false; intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps); @@ -1862,11 +1921,11 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device) sdvo_priv->caps.output_flags & (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N'); - intel_output->ddc_bus = i2cbus; - return true; err_i2c: + if (ddcbus != NULL) + intel_i2c_destroy(intel_output->ddc_bus); intel_i2c_destroy(intel_output->i2c_bus); err_inteloutput: kfree(intel_output); -- cgit v1.2.3-55-g7522 From df391e0eda1e678add56a8e34226edf05d89af6a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 23 May 2009 09:51:20 -0700 Subject: Input: multitouch - add tracking ID to the protocol There are a few multi-touch devices that support finger tracking well in hardware, Stantum being the prime example. By exposing the tracking ID in the MT protocol, evdev bandwidth and cpu usage in user space can be reduced. This patch adds the ABS_MT_TRACKING_ID to the MT protocol. Signed-off-by: Henrik Rydberg Tested-by: Stéphane Chatty Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 1 + include/linux/input.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/input/input.c b/drivers/input/input.c index e54e002665b0..5d445f48789b 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -42,6 +42,7 @@ static unsigned int input_abs_bypass_init_data[] __initdata = { ABS_MT_POSITION_Y, ABS_MT_TOOL_TYPE, ABS_MT_BLOB_ID, + ABS_MT_TRACKING_ID, 0 }; static unsigned long input_abs_bypass[BITS_TO_LONGS(ABS_CNT)]; diff --git a/include/linux/input.h b/include/linux/input.h index 0e6ff5de3588..6fed4f6a9c9e 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -656,6 +656,7 @@ struct input_absinfo { #define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ +#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ #define ABS_MAX 0x3f #define ABS_CNT (ABS_MAX+1) -- cgit v1.2.3-55-g7522 From 32bdfac5462d777f35b00838893c4f87baf23efe Mon Sep 17 00:00:00 2001 From: Rafael J. Wysocki Date: Sun, 24 May 2009 21:15:07 +0200 Subject: PM: Do not hold dpm_list_mtx while disabling/enabling nonboot CPUs We shouldn't hold dpm_list_mtx while executing [disable|enable]_nonboot_cpus(), because theoretically this may lead to a deadlock as shown by the following example (provided by Johannes Berg): CPU 3 CPU 2 CPU 1 suspend/hibernate something: rtnl_lock() device_pm_lock() -> mutex_lock(&dpm_list_mtx) mutex_lock(&dpm_list_mtx) linkwatch_work -> rtnl_lock() disable_nonboot_cpus() -> flush CPU 3 workqueue Fortunately, device drivers are supposed to stop any activities that might lead to the registration of new device objects way before disable_nonboot_cpus() is called, so it shouldn't be necessary to hold dpm_list_mtx over the entire late part of device suspend and early part of device resume. Thus, during the late suspend and the early resume of devices acquire dpm_list_mtx only when dpm_list is going to be traversed and release it right after that. This patch is reported to fix the regressions tracked as http://bugzilla.kernel.org/show_bug.cgi?id=13245. Signed-off-by: Rafael J. Wysocki Acked-by: Alan Stern Reported-by: Miles Lane Tested-by: Ming Lei --- drivers/base/power/main.c | 4 ++++ kernel/kexec.c | 2 -- kernel/power/disk.c | 21 +++------------------ kernel/power/main.c | 7 +------ 4 files changed, 8 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 69b4ddb7de3b..3e4bc699bc0f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -357,6 +357,7 @@ static void dpm_power_up(pm_message_t state) { struct device *dev; + mutex_lock(&dpm_list_mtx); list_for_each_entry(dev, &dpm_list, power.entry) if (dev->power.status > DPM_OFF) { int error; @@ -366,6 +367,7 @@ static void dpm_power_up(pm_message_t state) if (error) pm_dev_err(dev, state, " early", error); } + mutex_unlock(&dpm_list_mtx); } /** @@ -614,6 +616,7 @@ int device_power_down(pm_message_t state) int error = 0; suspend_device_irqs(); + mutex_lock(&dpm_list_mtx); list_for_each_entry_reverse(dev, &dpm_list, power.entry) { error = suspend_device_noirq(dev, state); if (error) { @@ -622,6 +625,7 @@ int device_power_down(pm_message_t state) } dev->power.status = DPM_OFF_IRQ; } + mutex_unlock(&dpm_list_mtx); if (error) device_power_up(resume_event(state)); return error; diff --git a/kernel/kexec.c b/kernel/kexec.c index 5a758c6e4950..e4983770913b 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1451,7 +1451,6 @@ int kernel_kexec(void) error = device_suspend(PMSG_FREEZE); if (error) goto Resume_console; - device_pm_lock(); /* At this point, device_suspend() has been called, * but *not* device_power_down(). We *must* * device_power_down() now. Otherwise, drivers for @@ -1489,7 +1488,6 @@ int kernel_kexec(void) enable_nonboot_cpus(); device_power_up(PMSG_RESTORE); Resume_devices: - device_pm_unlock(); device_resume(PMSG_RESTORE); Resume_console: resume_console(); diff --git a/kernel/power/disk.c b/kernel/power/disk.c index b0dc9e7a0d17..5cb080e7eebd 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -215,8 +215,6 @@ static int create_image(int platform_mode) if (error) return error; - device_pm_lock(); - /* At this point, device_suspend() has been called, but *not* * device_power_down(). We *must* call device_power_down() now. * Otherwise, drivers for some devices (e.g. interrupt controllers) @@ -227,7 +225,7 @@ static int create_image(int platform_mode) if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting hibernation\n"); - goto Unlock; + return error; } error = platform_pre_snapshot(platform_mode); @@ -280,9 +278,6 @@ static int create_image(int platform_mode) device_power_up(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); - Unlock: - device_pm_unlock(); - return error; } @@ -344,13 +339,11 @@ static int resume_target_kernel(bool platform_mode) { int error; - device_pm_lock(); - error = device_power_down(PMSG_QUIESCE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting resume\n"); - goto Unlock; + return error; } error = platform_pre_restore(platform_mode); @@ -403,9 +396,6 @@ static int resume_target_kernel(bool platform_mode) device_power_up(PMSG_RECOVER); - Unlock: - device_pm_unlock(); - return error; } @@ -464,11 +454,9 @@ int hibernation_platform_enter(void) goto Resume_devices; } - device_pm_lock(); - error = device_power_down(PMSG_HIBERNATE); if (error) - goto Unlock; + goto Resume_devices; error = hibernation_ops->prepare(); if (error) @@ -493,9 +481,6 @@ int hibernation_platform_enter(void) device_power_up(PMSG_RESTORE); - Unlock: - device_pm_unlock(); - Resume_devices: entering_platform_hibernation = false; device_resume(PMSG_RESTORE); diff --git a/kernel/power/main.c b/kernel/power/main.c index f99ed6a75eac..868028280d13 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -289,12 +289,10 @@ static int suspend_enter(suspend_state_t state) { int error; - device_pm_lock(); - if (suspend_ops->prepare) { error = suspend_ops->prepare(); if (error) - goto Done; + return error; } error = device_power_down(PMSG_SUSPEND); @@ -343,9 +341,6 @@ static int suspend_enter(suspend_state_t state) if (suspend_ops->finish) suspend_ops->finish(); - Done: - device_pm_unlock(); - return error; } -- cgit v1.2.3-55-g7522 From 4e2fd555199977c5994d1a4d2d3b8761b20ca4c7 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 25 May 2009 00:42:34 -0700 Subject: gianfar: fix BUG under load after introduction of skb recycling Since commit 0fd56bb5be6455d0d42241e65aed057244665e5e ("gianfar: Add support for skb recycling"), gianfar puts skbuffs that are in the rx ring back onto the recycle list as-is in case there was a receive error, but this breaks the following invariant: that all skbuffs on the recycle list have skb->data = skb->head + NET_SKB_PAD. The RXBUF_ALIGNMENT realignment done in gfar_new_skb() will be done twice on skbuffs recycled in this way, causing there not to be enough room in the skb anymore to receive a full packet, eventually leading to an skb_over_panic from gfar_clean_rx_ring() -> skb_put(). Resetting the skb->data pointer to skb->head + NET_SKB_PAD before putting the skb back onto the recycle list restores the mentioned invariant, and should fix this issue. Reported-by: Michael Guntsche Tested-by: Michael Guntsche Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index b2c49679bba7..a0519184e54e 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -1885,8 +1885,17 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) if (unlikely(!newskb)) newskb = skb; - else if (skb) + else if (skb) { + /* + * We need to reset ->data to what it + * was before gfar_new_skb() re-aligned + * it to an RXBUF_ALIGNMENT boundary + * before we put the skb back on the + * recycle list. + */ + skb->data = skb->head + NET_SKB_PAD; __skb_queue_head(&priv->rx_recycle, skb); + } } else { /* Increment the number of packets */ dev->stats.rx_packets++; -- cgit v1.2.3-55-g7522 From 62e1e389f87a8839ad83b08c44691d1df8320846 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 26 May 2009 09:40:59 +1000 Subject: md: always update level / chunk_size / layout when writing v1.x metadata. We previously didn't update these fields when writing the metadata because they could never change. They can now, so we better write them. v0.90 metadata always updated these fields. Signed-off-by: NeilBrown --- drivers/md/md.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index fccc8343a250..aa79d55875f0 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1375,6 +1375,9 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->raid_disks = cpu_to_le32(mddev->raid_disks); sb->size = cpu_to_le64(mddev->dev_sectors); + sb->chunksize = cpu_to_le32(mddev->chunk_size >> 9); + sb->level = cpu_to_le32(mddev->level); + sb->layout = cpu_to_le32(mddev->layout); if (mddev->bitmap && mddev->bitmap_file == NULL) { sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset); -- cgit v1.2.3-55-g7522 From 2b69c83924396ad1eda36fdd267c9d2f360f5555 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 26 May 2009 09:41:17 +1000 Subject: md: improve errno return when setting array_size Instead of always returns EINVAL if anything goes wrong when setting the array size, add the option of E2BIG if the size requested is too large. This makes it easier for user-space to be sure what went wrong. Signed-off-by: NeilBrown --- drivers/md/md.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index aa79d55875f0..58e0b02a74c2 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3683,7 +3683,7 @@ array_size_store(mddev_t *mddev, const char *buf, size_t len) if (strict_blocks_to_sectors(buf, §ors) < 0) return -EINVAL; if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors) - return -EINVAL; + return -E2BIG; mddev->external_size = 1; } -- cgit v1.2.3-55-g7522 From be512691036cc989c11d0f418187efbbf14468e6 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 26 May 2009 09:41:17 +1000 Subject: md: bitmap: improve bitmap maintenance code. The code for checking which bits in the bitmap can be cleared has 2 problems: 1/ it repeatedly takes and drops a spinlock, where it would make more sense to just hold on to it most of the time. 2/ it doesn't make use of some opportunities to skip large sections of the bitmap This patch fixes those. It will only affect CPU consumption, not correctness. Signed-off-by: NeilBrown --- drivers/md/bitmap.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 47c68bc75a17..56df1cee8fb3 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -1097,14 +1097,12 @@ void bitmap_daemon_work(struct bitmap *bitmap) } bitmap->allclean = 1; + spin_lock_irqsave(&bitmap->lock, flags); for (j = 0; j < bitmap->chunks; j++) { bitmap_counter_t *bmc; - spin_lock_irqsave(&bitmap->lock, flags); - if (!bitmap->filemap) { + if (!bitmap->filemap) /* error or shutdown */ - spin_unlock_irqrestore(&bitmap->lock, flags); break; - } page = filemap_get_page(bitmap, j); @@ -1121,6 +1119,8 @@ void bitmap_daemon_work(struct bitmap *bitmap) write_page(bitmap, page, 0); bitmap->allclean = 0; } + spin_lock_irqsave(&bitmap->lock, flags); + j |= (PAGE_BITS - 1); continue; } @@ -1181,9 +1181,10 @@ void bitmap_daemon_work(struct bitmap *bitmap) ext2_clear_bit(file_page_offset(j), paddr); kunmap_atomic(paddr, KM_USER0); } - } - spin_unlock_irqrestore(&bitmap->lock, flags); + } else + j |= PAGE_COUNTER_MASK; } + spin_unlock_irqrestore(&bitmap->lock, flags); /* now sync the final page */ if (lastpage != NULL) { -- cgit v1.2.3-55-g7522 From b6a9ce688f613e2ee5f15e6720e0bb8520efc36e Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 26 May 2009 09:41:17 +1000 Subject: md: export 'frozen' resync state through sysfs The md resync engine has a 'frozen' state which ensures that no resync/recovery. This is used to avoid races. Export this state through the 'sync_action' sysfs attribute so that user-space can benefit and also avoid some races. Signed-off-by: NeilBrown --- drivers/md/md.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 58e0b02a74c2..384e4f0904c8 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3306,7 +3306,9 @@ static ssize_t action_show(mddev_t *mddev, char *page) { char *type = "idle"; - if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || + if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) + type = "frozen"; + else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || (!mddev->ro && test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))) { if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) type = "reshape"; @@ -3329,7 +3331,12 @@ action_store(mddev_t *mddev, const char *page, size_t len) if (!mddev->pers || !mddev->pers->sync_request) return -EINVAL; - if (cmd_match(page, "idle")) { + if (cmd_match(page, "frozen")) + set_bit(MD_RECOVERY_FROZEN, &mddev->recovery); + else + clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); + + if (cmd_match(page, "idle") || cmd_match(page, "frozen")) { if (mddev->sync_thread) { set_bit(MD_RECOVERY_INTR, &mddev->recovery); md_unregister_thread(mddev->sync_thread); -- cgit v1.2.3-55-g7522 From 848b3182365fdf5a05bcd5ed949071cac2c894b3 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 26 May 2009 12:41:08 +1000 Subject: md: raid5: avoid sector values going negative when testing reshape progress. As sector_t in unsigned, we cannot afford to let 'safepos' etc go negative. So replace a -= b; by a -= min(b,a); Signed-off-by: NeilBrown --- drivers/md/raid5.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 4616bc3a6e71..3c3626d2a1f9 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3811,13 +3811,13 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped safepos = conf->reshape_safe; sector_div(safepos, data_disks); if (mddev->delta_disks < 0) { - writepos -= reshape_sectors; + writepos -= min(reshape_sectors, writepos); readpos += reshape_sectors; safepos += reshape_sectors; } else { writepos += reshape_sectors; - readpos -= reshape_sectors; - safepos -= reshape_sectors; + readpos -= min(reshape_sectors, readpos); + safepos -= min(reshape_sectors, safepos); } /* 'writepos' is the most advanced device address we might write. -- cgit v1.2.3-55-g7522 From 7a91ee1f628ef6bfe3f13067c0ddf9db520cb86b Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 26 May 2009 12:57:21 +1000 Subject: md: don't update curr_resync_completed without also updating reshape_position. In order for the metadata to always be consistent, we mustn't updated curr_resync_completed without also updating reshape_position. The reshape code updates both at the same time. However since commit 97e4f42d62badb0f9fbc27c013e89bc1336a03bc the common md_do_sync will sometimes update curr_resync_completed but is not in a position to update reshape_position. So if MD_RECOVERY_RESHAPE is set (indicating that a reshape is happening, so reshape_position might change), don't update curr_resync_completed in md_do_sync, leave it to the per-personality reshape code. Signed-off-by: NeilBrown --- drivers/md/md.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 384e4f0904c8..954456532acc 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6362,12 +6362,13 @@ void md_do_sync(mddev_t *mddev) skipped = 0; - if ((mddev->curr_resync > mddev->curr_resync_completed && - (mddev->curr_resync - mddev->curr_resync_completed) - > (max_sectors >> 4)) || - (j - mddev->curr_resync_completed)*2 - >= mddev->resync_max - mddev->curr_resync_completed - ) { + if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) && + ((mddev->curr_resync > mddev->curr_resync_completed && + (mddev->curr_resync - mddev->curr_resync_completed) + > (max_sectors >> 4)) || + (j - mddev->curr_resync_completed)*2 + >= mddev->resync_max - mddev->curr_resync_completed + )) { /* time to update curr_resync_completed */ blk_unplug(mddev->queue); wait_event(mddev->recovery_wait, -- cgit v1.2.3-55-g7522 From b492b852cd8c99505708152c29a5e09a787af9de Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 26 May 2009 12:57:36 +1000 Subject: md: don't use locked_ioctl. md has no need for the BKL - it does its own locking. So md_ioctl doesn't need to be a locked_ioctl. Signed-off-by: NeilBrown --- drivers/md/md.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 954456532acc..641b211fe3fe 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5567,7 +5567,7 @@ static struct block_device_operations md_fops = .owner = THIS_MODULE, .open = md_open, .release = md_release, - .locked_ioctl = md_ioctl, + .ioctl = md_ioctl, .getgeo = md_getgeo, .media_changed = md_media_changed, .revalidate_disk= md_revalidate, -- cgit v1.2.3-55-g7522 From 217cbfa856dc1cbc2890781626c4032d9e3ec59f Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Mon, 25 May 2009 22:43:49 -0700 Subject: mac8390: fix regression caused during net_device_ops conversion Changeset ca17584bf2ad1b1e37a5c0e4386728cc5fc9dabc ("mac8390: update to net_device_ops") broke mac8390 by adding 8390.o to the link. That meant that lib8390.c was included twice, once in mac8390.c and once in 8390.c, subject to different macros. This patch reverts that by avoiding the wrappers in 8390.c. They seem to be of no value since COMPAT_NET_DEV_OPS is going away soon. Tested with a Kinetics EtherPort card. Signed-off-by: Finn Thain Signed-off-by: David S. Miller --- drivers/net/Makefile | 2 +- drivers/net/mac8390.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 1fc4602a6ff2..a1c25cb4669f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -102,7 +102,7 @@ obj-$(CONFIG_HAMACHI) += hamachi.o obj-$(CONFIG_NET) += Space.o loopback.o obj-$(CONFIG_SEEQ8005) += seeq8005.o obj-$(CONFIG_NET_SB1000) += sb1000.o -obj-$(CONFIG_MAC8390) += mac8390.o 8390.o +obj-$(CONFIG_MAC8390) += mac8390.o obj-$(CONFIG_APNE) += apne.o 8390.o obj-$(CONFIG_PCMCIA_PCNET) += 8390.o obj-$(CONFIG_HP100) += hp100.o diff --git a/drivers/net/mac8390.c b/drivers/net/mac8390.c index 8e884869a05b..f26667d5eaae 100644 --- a/drivers/net/mac8390.c +++ b/drivers/net/mac8390.c @@ -304,7 +304,7 @@ struct net_device * __init mac8390_probe(int unit) if (!MACH_IS_MAC) return ERR_PTR(-ENODEV); - dev = alloc_ei_netdev(); + dev = ____alloc_ei_netdev(0); if (!dev) return ERR_PTR(-ENOMEM); @@ -481,10 +481,10 @@ void cleanup_module(void) static const struct net_device_ops mac8390_netdev_ops = { .ndo_open = mac8390_open, .ndo_stop = mac8390_close, - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_start_xmit = __ei_start_xmit, + .ndo_tx_timeout = __ei_tx_timeout, + .ndo_get_stats = __ei_get_stats, + .ndo_set_multicast_list = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_change_mtu = eth_change_mtu, -- cgit v1.2.3-55-g7522 From f11a377b3f4e897d11f0e8d1fc688667e2f19708 Mon Sep 17 00:00:00 2001 From: David Dillow Date: Fri, 22 May 2009 15:29:34 +0000 Subject: r8169: avoid losing MSI interrupts The 8169 chip only generates MSI interrupts when all enabled event sources are quiescent and one or more sources transition to active. If not all of the active events are acknowledged, or a new event becomes active while the existing ones are cleared in the handler, we will not see a new interrupt. The current interrupt handler masks off the Rx and Tx events once the NAPI handler has been scheduled, which opens a race window in which we can get another Rx or Tx event and never ACK'ing it, stopping all activity until the link is reset (ifconfig down/up). Fix this by always ACK'ing all event sources, and loop in the handler until we have all sources quiescent. Signed-off-by: David Dillow Tested-by: Michael Buesch Signed-off-by: David S. Miller --- drivers/net/r8169.c | 102 +++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 0b6e8c896835..8247a945a1d9 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -3554,54 +3554,64 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) int handled = 0; int status; + /* loop handling interrupts until we have no new ones or + * we hit a invalid/hotplug case. + */ status = RTL_R16(IntrStatus); + while (status && status != 0xffff) { + handled = 1; - /* hotplug/major error/no more work/shared irq */ - if ((status == 0xffff) || !status) - goto out; - - handled = 1; + /* Handle all of the error cases first. These will reset + * the chip, so just exit the loop. + */ + if (unlikely(!netif_running(dev))) { + rtl8169_asic_down(ioaddr); + break; + } - if (unlikely(!netif_running(dev))) { - rtl8169_asic_down(ioaddr); - goto out; - } + /* Work around for rx fifo overflow */ + if (unlikely(status & RxFIFOOver) && + (tp->mac_version == RTL_GIGA_MAC_VER_11)) { + netif_stop_queue(dev); + rtl8169_tx_timeout(dev); + break; + } - status &= tp->intr_mask; - RTL_W16(IntrStatus, - (status & RxFIFOOver) ? (status | RxOverflow) : status); + if (unlikely(status & SYSErr)) { + rtl8169_pcierr_interrupt(dev); + break; + } - if (!(status & tp->intr_event)) - goto out; + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); - /* Work around for rx fifo overflow */ - if (unlikely(status & RxFIFOOver) && - (tp->mac_version == RTL_GIGA_MAC_VER_11)) { - netif_stop_queue(dev); - rtl8169_tx_timeout(dev); - goto out; - } + /* We need to see the lastest version of tp->intr_mask to + * avoid ignoring an MSI interrupt and having to wait for + * another event which may never come. + */ + smp_rmb(); + if (status & tp->intr_mask & tp->napi_event) { + RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event); + tp->intr_mask = ~tp->napi_event; + + if (likely(napi_schedule_prep(&tp->napi))) + __napi_schedule(&tp->napi); + else if (netif_msg_intr(tp)) { + printk(KERN_INFO "%s: interrupt %04x in poll\n", + dev->name, status); + } + } - if (unlikely(status & SYSErr)) { - rtl8169_pcierr_interrupt(dev); - goto out; + /* We only get a new MSI interrupt when all active irq + * sources on the chip have been acknowledged. So, ack + * everything we've seen and check if new sources have become + * active to avoid blocking all interrupts from the chip. + */ + RTL_W16(IntrStatus, + (status & RxFIFOOver) ? (status | RxOverflow) : status); + status = RTL_R16(IntrStatus); } - if (status & LinkChg) - rtl8169_check_link_status(dev, tp, ioaddr); - - if (status & tp->napi_event) { - RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event); - tp->intr_mask = ~tp->napi_event; - - if (likely(napi_schedule_prep(&tp->napi))) - __napi_schedule(&tp->napi); - else if (netif_msg_intr(tp)) { - printk(KERN_INFO "%s: interrupt %04x in poll\n", - dev->name, status); - } - } -out: return IRQ_RETVAL(handled); } @@ -3617,13 +3627,15 @@ static int rtl8169_poll(struct napi_struct *napi, int budget) if (work_done < budget) { napi_complete(napi); - tp->intr_mask = 0xffff; - /* - * 20040426: the barrier is not strictly required but the - * behavior of the irq handler could be less predictable - * without it. Btw, the lack of flush for the posted pci - * write is safe - FR + + /* We need for force the visibility of tp->intr_mask + * for other CPUs, as we can loose an MSI interrupt + * and potentially wait for a retransmit timeout if we don't. + * The posted write to IntrMask is safe, as it will + * eventually make it to the chip and we won't loose anything + * until it does. */ + tp->intr_mask = 0xffff; smp_wmb(); RTL_W16(IntrMask, tp->intr_event); } -- cgit v1.2.3-55-g7522 From 42a06f2166f2f6f7bf04f32b4e823eacdceafdc9 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Sun, 17 May 2009 10:23:52 -0400 Subject: [CPUFREQ] remove rwsem lock from CPUFREQ_GOV_STOP call * Rafael J. Wysocki (rjw@sisk.pl) wrote: > This message has been generated automatically as a part of a report > of regressions introduced between 2.6.28 and 2.6.29. > > The following bug entry is on the current list of known regressions > introduced between 2.6.28 and 2.6.29. Please verify if it still should > be listed and let me know (either way). > > > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=13186 > Subject : cpufreq timer teardown problem > Submitter : Mathieu Desnoyers > Date : 2009-04-23 14:00 (24 days old) > References : http://marc.info/?l=linux-kernel&m=124049523515036&w=4 > Handled-By : Mathieu Desnoyers > Patch : http://patchwork.kernel.org/patch/19754/ > http://patchwork.kernel.org/patch/19753/ The patches linked above depend on the following patch to remove circular locking dependency : cpufreq: remove rwsem lock from CPUFREQ_GOV_STOP call (the following issue was faced when using cancel_delayed_work_sync() in the timer teardown (which fixes a race). * KOSAKI Motohiro (kosaki.motohiro@jp.fujitsu.com) wrote: > Hi > > my box output following warnings. > it seems regression by commit 7ccc7608b836e58fbacf65ee4f8eefa288e86fac. > > A: work -> do_dbs_timer() -> cpu_policy_rwsem > B: store() -> cpu_policy_rwsem -> cpufreq_governor_dbs() -> work > > Hrm, I think it must be due to my attempt to fix the timer teardown race in ondemand governor mixed with new locking behavior in 2.6.30-rc. The rwlock seems to be taken around the whole call to cpufreq_governor_dbs(), when it should be only taken around accesses to the locked data, and especially *not* around the call to dbs_timer_exit(). Reverting my fix attempt would put the teardown race back in place (replacing the cancel_delayed_work_sync by cancel_delayed_work). Instead, a proper fix would imply modifying this critical section : cpufreq.c: __cpufreq_remove_dev() ... if (cpufreq_driver->target) __cpufreq_governor(data, CPUFREQ_GOV_STOP); unlock_policy_rwsem_write(cpu); To make sure the __cpufreq_governor() callback is not called with rwsem held. This would allow execution of cancel_delayed_work_sync() without being nested within the rwsem. Applies on top of the 2.6.30-rc5 tree. Required to remove circular dep in teardown of both conservative and ondemande governors so they can use cancel_delayed_work_sync(). CPUFREQ_GOV_STOP does not modify the policy, therefore this locking seemed unneeded. Signed-off-by: Mathieu Desnoyers CC: KOSAKI Motohiro Cc: Greg KH CC: Ingo Molnar CC: "Rafael J. Wysocki" CC: Ben Slusky CC: Chris Wright CC: Andrew Morton Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d270e8eb3e67..47d2ad0ae079 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1070,11 +1070,11 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) spin_unlock_irqrestore(&cpufreq_driver_lock, flags); #endif + unlock_policy_rwsem_write(cpu); + if (cpufreq_driver->target) __cpufreq_governor(data, CPUFREQ_GOV_STOP); - unlock_policy_rwsem_write(cpu); - kobject_put(&data->kobj); /* we need to make sure that the underlying kobj is actually -- cgit v1.2.3-55-g7522 From b253d2b2d28ead6fed012feb54694b3d0562839a Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Sun, 17 May 2009 10:29:33 -0400 Subject: [CPUFREQ] fix timer teardown in conservative governor * Rafael J. Wysocki (rjw@sisk.pl) wrote: > This message has been generated automatically as a part of a report > of regressions introduced between 2.6.28 and 2.6.29. > > The following bug entry is on the current list of known regressions > introduced between 2.6.28 and 2.6.29. Please verify if it still should > be listed and let me know (either way). > > > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=13186 > Subject : cpufreq timer teardown problem > Submitter : Mathieu Desnoyers > Date : 2009-04-23 14:00 (24 days old) > References : http://marc.info/?l=linux-kernel&m=124049523515036&w=4 > Handled-By : Mathieu Desnoyers > Patch : http://patchwork.kernel.org/patch/19754/ > http://patchwork.kernel.org/patch/19753/ > (re-send with updated changelog) cpufreq fix timer teardown in conservative governor The problem is that dbs_timer_exit() uses cancel_delayed_work() when it should use cancel_delayed_work_sync(). cancel_delayed_work() does not wait for the workqueue handler to exit. The ondemand governor does not seem to be affected because the "if (!dbs_info->enable)" check at the beginning of the workqueue handler returns immediately without rescheduling the work. The conservative governor in 2.6.30-rc has the same check as the ondemand governor, which makes things usually run smoothly. However, if the governor is quickly stopped and then started, this could lead to the following race : dbs_enable could be reenabled and multiple do_dbs_timer handlers would run. This is why a synchronized teardown is required. Depends on patch cpufreq: remove rwsem lock from CPUFREQ_GOV_STOP call The following patch applies to 2.6.30-rc2. Stable kernels have a similar issue which should also be fixed, but the code changed between 2.6.29 and 2.6.30, so this patch only applies to 2.6.30-rc. Signed-off-by: Mathieu Desnoyers CC: Andrew Morton CC: gregkh@suse.de CC: stable@kernel.org CC: cpufreq@vger.kernel.org CC: Ingo Molnar CC: rjw@sisk.pl CC: Ben Slusky Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_conservative.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 2ecd95e4ab1a..7a74d175287b 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -91,6 +91,9 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ * (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then * cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock * is recursive for the same process. -Venki + * DEADLOCK ALERT! (2) : do_dbs_timer() must not take the dbs_mutex, because it + * would deadlock with cancel_delayed_work_sync(), which is needed for proper + * raceless workqueue teardown. */ static DEFINE_MUTEX(dbs_mutex); @@ -542,7 +545,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) { dbs_info->enable = 0; - cancel_delayed_work(&dbs_info->work); + cancel_delayed_work_sync(&dbs_info->work); } static int cpufreq_governor_dbs(struct cpufreq_policy *policy, -- cgit v1.2.3-55-g7522 From b14893a62c73af0eca414cfed505b8c09efc613c Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Sun, 17 May 2009 10:30:45 -0400 Subject: [CPUFREQ] fix timer teardown in ondemand governor * Rafael J. Wysocki (rjw@sisk.pl) wrote: > This message has been generated automatically as a part of a report > of regressions introduced between 2.6.28 and 2.6.29. > > The following bug entry is on the current list of known regressions > introduced between 2.6.28 and 2.6.29. Please verify if it still should > be listed and let me know (either way). > > > Bug-Entry : http://bugzilla.kernel.org/show_bug.cgi?id=13186 > Subject : cpufreq timer teardown problem > Submitter : Mathieu Desnoyers > Date : 2009-04-23 14:00 (24 days old) > References : http://marc.info/?l=linux-kernel&m=124049523515036&w=4 > Handled-By : Mathieu Desnoyers > Patch : http://patchwork.kernel.org/patch/19754/ > http://patchwork.kernel.org/patch/19753/ > (updated changelog) cpufreq fix timer teardown in ondemand governor The problem is that dbs_timer_exit() uses cancel_delayed_work() when it should use cancel_delayed_work_sync(). cancel_delayed_work() does not wait for the workqueue handler to exit. The ondemand governor does not seem to be affected because the "if (!dbs_info->enable)" check at the beginning of the workqueue handler returns immediately without rescheduling the work. The conservative governor in 2.6.30-rc has the same check as the ondemand governor, which makes things usually run smoothly. However, if the governor is quickly stopped and then started, this could lead to the following race : dbs_enable could be reenabled and multiple do_dbs_timer handlers would run. This is why a synchronized teardown is required. The following patch applies to, at least, 2.6.28.x, 2.6.29.1, 2.6.30-rc2. Depends on patch cpufreq: remove rwsem lock from CPUFREQ_GOV_STOP call Signed-off-by: Mathieu Desnoyers CC: Andrew Morton CC: gregkh@suse.de CC: stable@kernel.org CC: cpufreq@vger.kernel.org CC: Ingo Molnar CC: rjw@sisk.pl CC: Ben Slusky Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_ondemand.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 338f428a15b7..e741c339df76 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -98,6 +98,9 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ * (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then * cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock * is recursive for the same process. -Venki + * DEADLOCK ALERT! (2) : do_dbs_timer() must not take the dbs_mutex, because it + * would deadlock with cancel_delayed_work_sync(), which is needed for proper + * raceless workqueue teardown. */ static DEFINE_MUTEX(dbs_mutex); @@ -562,7 +565,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) { dbs_info->enable = 0; - cancel_delayed_work(&dbs_info->work); + cancel_delayed_work_sync(&dbs_info->work); } static int cpufreq_governor_dbs(struct cpufreq_policy *policy, -- cgit v1.2.3-55-g7522 From e4a5d54f924ea5ce2913d9d0687d034004816465 Mon Sep 17 00:00:00 2001 From: Ma Ling Date: Tue, 26 May 2009 11:31:00 +0800 Subject: drm/i915: Add support for VGA load detection (pre-945). Two approaches for VGA detections: hot plug detection for 945G onwards and load pipe detection for Pre-945G. Load pipe detection will get one free pipe, set border color as red and blue, then check CRT status by swf register. This is a sync-up with the 2D driver. Signed-off-by: Ma Ling Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/intel_crt.c | 149 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 19148c3df637..640f5158effc 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -198,9 +198,142 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) return intel_ddc_probe(intel_output); } +static enum drm_connector_status +intel_crt_load_detect(struct drm_crtc *crtc, struct intel_output *intel_output) +{ + struct drm_encoder *encoder = &intel_output->enc; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + uint32_t pipe = intel_crtc->pipe; + uint32_t save_bclrpat; + uint32_t save_vtotal; + uint32_t vtotal, vactive; + uint32_t vsample; + uint32_t vblank, vblank_start, vblank_end; + uint32_t dsl; + uint32_t bclrpat_reg; + uint32_t vtotal_reg; + uint32_t vblank_reg; + uint32_t vsync_reg; + uint32_t pipeconf_reg; + uint32_t pipe_dsl_reg; + uint8_t st00; + enum drm_connector_status status; + + if (pipe == 0) { + bclrpat_reg = BCLRPAT_A; + vtotal_reg = VTOTAL_A; + vblank_reg = VBLANK_A; + vsync_reg = VSYNC_A; + pipeconf_reg = PIPEACONF; + pipe_dsl_reg = PIPEADSL; + } else { + bclrpat_reg = BCLRPAT_B; + vtotal_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + vsync_reg = VSYNC_B; + pipeconf_reg = PIPEBCONF; + pipe_dsl_reg = PIPEBDSL; + } + + save_bclrpat = I915_READ(bclrpat_reg); + save_vtotal = I915_READ(vtotal_reg); + vblank = I915_READ(vblank_reg); + + vtotal = ((save_vtotal >> 16) & 0xfff) + 1; + vactive = (save_vtotal & 0x7ff) + 1; + + vblank_start = (vblank & 0xfff) + 1; + vblank_end = ((vblank >> 16) & 0xfff) + 1; + + /* Set the border color to purple. */ + I915_WRITE(bclrpat_reg, 0x500050); + + if (IS_I9XX(dev)) { + uint32_t pipeconf = I915_READ(pipeconf_reg); + I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); + /* Wait for next Vblank to substitue + * border color for Color info */ + intel_wait_for_vblank(dev); + st00 = I915_READ8(VGA_MSR_WRITE); + status = ((st00 & (1 << 4)) != 0) ? + connector_status_connected : + connector_status_disconnected; + + I915_WRITE(pipeconf_reg, pipeconf); + } else { + bool restore_vblank = false; + int count, detect; + + /* + * If there isn't any border, add some. + * Yes, this will flicker + */ + if (vblank_start <= vactive && vblank_end >= vtotal) { + uint32_t vsync = I915_READ(vsync_reg); + uint32_t vsync_start = (vsync & 0xffff) + 1; + + vblank_start = vsync_start; + I915_WRITE(vblank_reg, + (vblank_start - 1) | + ((vblank_end - 1) << 16)); + restore_vblank = true; + } + /* sample in the vertical border, selecting the larger one */ + if (vblank_start - vactive >= vtotal - vblank_end) + vsample = (vblank_start + vactive) >> 1; + else + vsample = (vtotal + vblank_end) >> 1; + + /* + * Wait for the border to be displayed + */ + while (I915_READ(pipe_dsl_reg) >= vactive) + ; + while ((dsl = I915_READ(pipe_dsl_reg)) <= vsample) + ; + /* + * Watch ST00 for an entire scanline + */ + detect = 0; + count = 0; + do { + count++; + /* Read the ST00 VGA status register */ + st00 = I915_READ8(VGA_MSR_WRITE); + if (st00 & (1 << 4)) + detect++; + } while ((I915_READ(pipe_dsl_reg) == dsl)); + + /* restore vblank if necessary */ + if (restore_vblank) + I915_WRITE(vblank_reg, vblank); + /* + * If more than 3/4 of the scanline detected a monitor, + * then it is assumed to be present. This works even on i830, + * where there isn't any way to force the border color across + * the screen + */ + status = detect * 4 > count * 3 ? + connector_status_connected : + connector_status_disconnected; + } + + /* Restore previous settings */ + I915_WRITE(bclrpat_reg, save_bclrpat); + + return status; +} + static enum drm_connector_status intel_crt_detect(struct drm_connector *connector) { struct drm_device *dev = connector->dev; + struct intel_output *intel_output = to_intel_output(connector); + struct drm_encoder *encoder = &intel_output->enc; + struct drm_crtc *crtc; + int dpms_mode; + enum drm_connector_status status; if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { if (intel_crt_detect_hotplug(connector)) @@ -212,8 +345,20 @@ static enum drm_connector_status intel_crt_detect(struct drm_connector *connecto if (intel_crt_detect_ddc(connector)) return connector_status_connected; - /* TODO use load detect */ - return connector_status_unknown; + /* for pre-945g platforms use load detect */ + if (encoder->crtc && encoder->crtc->enabled) { + status = intel_crt_load_detect(encoder->crtc, intel_output); + } else { + crtc = intel_get_load_detect_pipe(intel_output, + NULL, &dpms_mode); + if (crtc) { + status = intel_crt_load_detect(crtc, intel_output); + intel_release_load_detect_pipe(intel_output, dpms_mode); + } else + status = connector_status_unknown; + } + + return status; } static void intel_crt_destroy(struct drm_connector *connector) -- cgit v1.2.3-55-g7522 From 564346224daaa8f7222d7a92cdbb7bafde59ae6e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 May 2009 20:54:41 +0930 Subject: lguest: fix on Intel when KVM loaded (unhandled trap 13) When KVM is loaded, and hence VT set up, the vmcall instruction in an lguest guest causes a #GP, not #UD. Signed-off-by: Rusty Russell Signed-off-by: Linus Torvalds --- drivers/lguest/x86/core.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index 1a83910f674f..eaf722fe309a 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -358,6 +358,16 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) if (emulate_insn(cpu)) return; } + /* If KVM is active, the vmcall instruction triggers a + * General Protection Fault. Normally it triggers an + * invalid opcode fault (6): */ + case 6: + /* We need to check if ring == GUEST_PL and + * faulting instruction == vmcall. */ + if (is_hypercall(cpu)) { + rewrite_hypercall(cpu); + return; + } break; case 14: /* We've intercepted a Page Fault. */ /* The Guest accessed a virtual address that wasn't mapped. @@ -403,15 +413,6 @@ void lguest_arch_handle_trap(struct lg_cpu *cpu) * up the pointer now to indicate a hypercall is pending. */ cpu->hcall = (struct hcall_args *)cpu->regs; return; - case 6: - /* kvm hypercalls trigger an invalid opcode fault (6). - * We need to check if ring == GUEST_PL and - * faulting instruction == vmcall. */ - if (is_hypercall(cpu)) { - rewrite_hypercall(cpu); - return; - } - break; } /* We didn't handle the trap, so it needs to go to the Guest. */ -- cgit v1.2.3-55-g7522 From e76a16deb8785317a23cca7204331af053e0fb4e Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 26 May 2009 17:44:56 -0700 Subject: drm/i915: Fix tiling pitch handling on 8xx. The pitch field is an exponent on pre-965, so we were rejecting buffers on 8xx that we shouldn't have. 915 got lucky in that the largest legal value happened to match (8KB / 512 = 0x10), but 8xx has a smaller tile width. Additionally, we programmed that bad value into the register on 8xx, so the only pitch that would work correctly was 4096 (512-1023 pixels), while others would probably give bad rendering or hangs. Signed-off-by: Eric Anholt fd.o bug #20473. --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++-- drivers/gpu/drm/i915/i915_gem_tiling.c | 14 +++++++++++--- drivers/gpu/drm/i915/i915_reg.h | 3 ++- 3 files changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 717b6a854bcd..e4408daf8cef 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2128,8 +2128,10 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) return; } - pitch_val = (obj_priv->stride / 128) - 1; - WARN_ON(pitch_val & ~0x0000000f); + pitch_val = obj_priv->stride / 128; + pitch_val = ffs(pitch_val) - 1; + WARN_ON(pitch_val > I830_FENCE_MAX_PITCH_VAL); + val = obj_priv->gtt_offset; if (obj_priv->tiling_mode == I915_TILING_Y) val |= 1 << I830_FENCE_TILING_Y_SHIFT; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 52a059354e83..540dd336e6ec 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -213,7 +213,8 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) if (tiling_mode == I915_TILING_NONE) return true; - if (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)) + if (!IS_I9XX(dev) || + (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))) tile_width = 128; else tile_width = 512; @@ -225,11 +226,18 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) if (stride / 128 > I965_FENCE_MAX_PITCH_VAL) return false; } else if (IS_I9XX(dev)) { - if (stride / tile_width > I830_FENCE_MAX_PITCH_VAL || + uint32_t pitch_val = ffs(stride / tile_width) - 1; + + /* XXX: For Y tiling, FENCE_MAX_PITCH_VAL is actually 6 (8KB) + * instead of 4 (2KB) on 945s. + */ + if (pitch_val > I915_FENCE_MAX_PITCH_VAL || size > (I830_FENCE_MAX_SIZE_VAL << 20)) return false; } else { - if (stride / 128 > I830_FENCE_MAX_PITCH_VAL || + uint32_t pitch_val = ffs(stride / tile_width) - 1; + + if (pitch_val > I830_FENCE_MAX_PITCH_VAL || size > (I830_FENCE_MAX_SIZE_VAL << 19)) return false; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 9668cc0d7f4e..375569d01d01 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -190,7 +190,8 @@ #define I830_FENCE_SIZE_BITS(size) ((ffs((size) >> 19) - 1) << 8) #define I830_FENCE_PITCH_SHIFT 4 #define I830_FENCE_REG_VALID (1<<0) -#define I830_FENCE_MAX_PITCH_VAL 0x10 +#define I915_FENCE_MAX_PITCH_VAL 0x10 +#define I830_FENCE_MAX_PITCH_VAL 6 #define I830_FENCE_MAX_SIZE_VAL (1<<8) #define I915_FENCE_START_MASK 0x0ff00000 -- cgit v1.2.3-55-g7522 From cfa16a0de5392c54db553ec2233a7110e4b4da7a Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 26 May 2009 18:46:16 -0700 Subject: drm/i915: Apply a big hammer to 865 GEM object CPU cache flushing. On the 865, but not the 855, the clflush we do appears to not actually make it out to the hardware all the time. An easy way to safely reproduce was X -retro, which would show that some of the blits involved in drawing the lovely root weave didn't make it out to the hardware. Those blits are 32 bytes each, and 1-2 would be missing at various points around the screen. Other experimentation (doing more clflush, doing more AGP chipset flush, poking at some more device registers to maybe trigger more flushing) didn't help. krh came up with the wbinvd as a way to successfully get all those blits to appear. Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e4408daf8cef..e2421869a40c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2423,6 +2423,16 @@ i915_gem_clflush_object(struct drm_gem_object *obj) if (obj_priv->pages == NULL) return; + /* XXX: The 865 in particular appears to be weird in how it handles + * cache flushing. We haven't figured it out, but the + * clflush+agp_chipset_flush doesn't appear to successfully get the + * data visible to the PGU, while wbinvd + agp_chipset_flush does. + */ + if (IS_I865G(obj->dev)) { + wbinvd(); + return; + } + drm_clflush_pages(obj_priv->pages, obj->size / PAGE_SIZE); } -- cgit v1.2.3-55-g7522 From 87ad57bacb25c3f24c54f142ef445f68277705f0 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 19 May 2009 16:09:42 +0800 Subject: cpuidle: makes AMD C1E work in acpi_idle When AMD C1E is enabled, local APIC timer will stop even in C1. This patch uses broadcast IPI to replace local APIC timer in C1. http://bugzilla.kernel.org/show_bug.cgi?id=13233 [ impact: avoid boot hang in AMD CPU with C1E enabled ] Tested-by: Dmitry Lyzhyn Signed-off-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 72069ba5f1ed..6b7bcc7e3e1d 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -148,6 +148,9 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr, if (cpu_has(&cpu_data(pr->id), X86_FEATURE_ARAT)) return; + if (boot_cpu_has(X86_FEATURE_AMDC1E)) + type = ACPI_STATE_C1; + /* * Check, if one of the previous states already marked the lapic * unstable @@ -611,6 +614,7 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) switch (cx->type) { case ACPI_STATE_C1: cx->valid = 1; + acpi_timer_check_state(i, pr, cx); break; case ACPI_STATE_C2: @@ -835,6 +839,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, return 0; } + acpi_state_timer_broadcast(pr, cx, 1); kt1 = ktime_get_real(); acpi_idle_do_entry(cx); kt2 = ktime_get_real(); @@ -842,6 +847,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, local_irq_enable(); cx->usage++; + acpi_state_timer_broadcast(pr, cx, 0); return idle_time; } -- cgit v1.2.3-55-g7522 From 7d60e8ab0d5507229dfbdf456501cc378610fa01 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 19 May 2009 16:09:54 +0800 Subject: cpuidle: fix AMD C1E suspend hang When AMD C1E is enabled, local APIC timer will stop even in C1. To avoid suspend/resume hang, this patch removes C1 and replace it with a cpu_relax() in suspend/resume path. This hasn't any impact in runtime path. http://bugzilla.kernel.org/show_bug.cgi?id=13233 [ impact: avoid suspend/resume hang in AMD CPU with C1E enabled ] Tested-by: Dmitry Lyzhyn Signed-off-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 6b7bcc7e3e1d..10a2d913635a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -834,8 +834,8 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, /* Do not access any ACPI IO ports in suspend path */ if (acpi_idle_suspend) { - acpi_safe_halt(); local_irq_enable(); + cpu_relax(); return 0; } -- cgit v1.2.3-55-g7522 From 18a36c1a398055e87a3646738abf3c1b0e98e7e2 Mon Sep 17 00:00:00 2001 From: Xiaotian Feng Date: Tue, 26 May 2009 20:48:04 -0700 Subject: gianfar: fix babbling rx error event bug Gianfar interrupt handler uses IEVENT_ERR_MASK to check and handle errors. Babbling RX error (IEVENT_BABR) should be included in IEVENT_ERROR_MASK. Otherwise if BABR is raised, it never gets handled nor cleared, and an interrupt storm results. This has been observed to happen on sending a burst of ethernet frames to a gianfar based board. Signed-off-by: Xiaotian Feng Signed-off-by: David S. Miller --- drivers/net/gianfar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 0642d52aef5c..cf352961ae9b 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -259,7 +259,7 @@ extern const char gfar_driver_version[]; (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \ - | IEVENT_MAG) + | IEVENT_MAG | IEVENT_BABR) #define IMASK_INIT_CLEAR 0x00000000 #define IMASK_BABR 0x80000000 -- cgit v1.2.3-55-g7522 From 5ad18900355743757e7f415bcb850ea8406a13e2 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Tue, 26 May 2009 20:50:12 -0700 Subject: atlx: move modinfo data from atlx.h to atl1.c Both atl1.c and atl2.c include atlx.h, which defines some modinfo stuff. But atl2.c seems like it doesn't want the modinfo data from atlx.h, as it defines its own. Running modinfo on atl2.ko, we get conflicting information: $ /sbin/modinfo drivers/net/atlx/atl2.ko | egrep "version|description|author" version: 2.2.3 description: Atheros Fast Ethernet Network Driver author: Atheros Corporation , Chris Snook version: 2.1.3 author: Xiong Huang , Chris Snook , Jay Cliburn Move the modinfo data out of atlx.h and into atl1.c to eliminate the confusion: $ /sbin/modinfo drivers/net/atlx/atl1.ko | egrep "version|description|author" version: 2.1.3 author: Xiong Huang , Chris Snook , Jay Cliburn description: Atheros L1 Gigabit Ethernet Driver $ /sbin/modinfo drivers/net/atlx/atl2.ko | egrep "version|description|author" version: 2.2.3 description: Atheros Fast Ethernet Network Driver author: Atheros Corporation , Chris Snook Reported-by: Scott Scriven Signed-off-by: Alex Chiang Acked-by: Jay Cliburn Signed-off-by: David S. Miller --- drivers/net/atlx/atl1.c | 6 ++++++ drivers/net/atlx/atlx.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 0ab22540bf59..4e817126e280 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -82,6 +82,12 @@ #include "atl1.h" +#define ATLX_DRIVER_VERSION "2.1.3" +MODULE_AUTHOR("Xiong Huang , \ + Chris Snook , Jay Cliburn "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(ATLX_DRIVER_VERSION); + /* Temporary hack for merging atl1 and atl2 */ #include "atlx.c" diff --git a/drivers/net/atlx/atlx.h b/drivers/net/atlx/atlx.h index 297a03da6b7f..14054b75aa62 100644 --- a/drivers/net/atlx/atlx.h +++ b/drivers/net/atlx/atlx.h @@ -29,12 +29,6 @@ #include #include -#define ATLX_DRIVER_VERSION "2.1.3" -MODULE_AUTHOR("Xiong Huang , \ - Chris Snook , Jay Cliburn "); -MODULE_LICENSE("GPL"); -MODULE_VERSION(ATLX_DRIVER_VERSION); - #define ATLX_ERR_PHY 2 #define ATLX_ERR_PHY_SPEED 7 #define ATLX_ERR_PHY_RES 8 -- cgit v1.2.3-55-g7522 From b63dc8fef7ca5c51d163295d824e78c770d48ccf Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 26 May 2009 20:55:33 -0700 Subject: bfin_mac: fix build error due to net_device_ops convert The previous commit "convert to net_device_ops" broke the Blackfin MAC driver as it declared the new structure before the function it used: CC drivers/net/bfin_mac.o drivers/net/bfin_mac.c:984: error: ‘bfin_mac_close’ undeclared here (not in a function) make[1]: *** [drivers/net/bfin_mac.o] Error 1 Signed-off-by: Mike Frysinger Signed-off-by: David S. Miller --- drivers/net/bfin_mac.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index 9f971ed6b58d..b4da18213324 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -979,22 +979,7 @@ static int bfin_mac_open(struct net_device *dev) return 0; } -static const struct net_device_ops bfin_mac_netdev_ops = { - .ndo_open = bfin_mac_open, - .ndo_stop = bfin_mac_close, - .ndo_start_xmit = bfin_mac_hard_start_xmit, - .ndo_set_mac_address = bfin_mac_set_mac_address, - .ndo_tx_timeout = bfin_mac_timeout, - .ndo_set_multicast_list = bfin_mac_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, - .ndo_change_mtu = eth_change_mtu, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = bfin_mac_poll, -#endif -}; - /* - * * this makes the board clean up everything that it can * and not talk to the outside world. Caused by * an 'ifconfig ethX down' @@ -1019,6 +1004,20 @@ static int bfin_mac_close(struct net_device *dev) return 0; } +static const struct net_device_ops bfin_mac_netdev_ops = { + .ndo_open = bfin_mac_open, + .ndo_stop = bfin_mac_close, + .ndo_start_xmit = bfin_mac_hard_start_xmit, + .ndo_set_mac_address = bfin_mac_set_mac_address, + .ndo_tx_timeout = bfin_mac_timeout, + .ndo_set_multicast_list = bfin_mac_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = bfin_mac_poll, +#endif +}; + static int __devinit bfin_mac_probe(struct platform_device *pdev) { struct net_device *ndev; -- cgit v1.2.3-55-g7522 From 9d911d7903926a65ef49ec671bacd86bcee5eb51 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Thu, 21 May 2009 16:21:15 -0600 Subject: PCI Hotplug: acpiphp: don't store a pci_dev in acpiphp_func An oops can occur if a user attempts to use both PCI logical hotplug and the ACPI physical hotplug driver (acpiphp) in this sequence, where $slot/address == $device. In other words, if acpiphp has claimed a PCI device, and that device is logically removed, then acpiphp may oops when it attempts to access it again. # echo 1 > /sys/bus/pci/devices/$device/remove # echo 0 > /sys/bus/pci/slots/$slot/power Unable to handle kernel NULL pointer dereference (address 0000000000000000) Call Trace: [] show_stack+0x50/0xa0 [] show_regs+0x820/0x860 [] die+0x190/0x2a0 [] ia64_do_page_fault+0x8e0/0xa40 [] ia64_native_leave_kernel+0x0/0x270 [] pci_remove_bus_device+0x120/0x260 [] acpiphp_disable_slot+0x410/0x540 [acpiphp] [] disable_slot+0xc0/0x120 [acpiphp] [] power_write_file+0x1e0/0x2a0 [pci_hotplug] [] pci_slot_attr_store+0x60/0xa0 [] sysfs_write_file+0x230/0x2c0 [] vfs_write+0x190/0x2e0 [] sys_write+0x80/0x100 [] ia64_ret_from_syscall+0x0/0x20 [] __kernel_syscall_via_break+0x0/0x20 The root cause of this oops is that the logical remove ("echo 1 > /sys/bus/pci/devices/$device/remove") destroyed the pci_dev. The pci_dev struct itself wasn't deallocated because acpiphp kept a reference, but some of its fields became invalid. acpiphp doesn't have any real reason to keep a pointer to a pci_dev around. It can always derive it using pci_get_slot(). If a logical remove destroys the pci_dev, acpiphp won't find it and is thus prevented from causing mischief. Reviewed-by: Matthew Wilcox Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Reported-by: Kenji Kaneshige Acked-by: Bjorn Helgaas Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp.h | 1 - drivers/pci/hotplug/acpiphp_glue.c | 63 ++++++++++++++++---------------------- 2 files changed, 26 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 4fc168b70095..e68d5f20ffb3 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -129,7 +129,6 @@ struct acpiphp_func { struct acpiphp_bridge *bridge; /* Ejectable PCI-to-PCI bridge */ struct list_head sibling; - struct pci_dev *pci_dev; struct notifier_block nb; acpi_handle handle; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index a33794d9e0dc..3a6064bce561 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -32,9 +32,6 @@ /* * Lifetime rules for pci_dev: - * - The one in acpiphp_func has its refcount elevated by pci_get_slot() - * when the driver is loaded or when an insertion event occurs. It loses - * a refcount when its ejected or the driver unloads. * - The one in acpiphp_bridge has its refcount elevated by pci_get_slot() * when the bridge is scanned and it loses a refcount when the bridge * is removed. @@ -130,6 +127,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) unsigned long long adr, sun; int device, function, retval; struct pci_bus *pbus = bridge->pci_bus; + struct pci_dev *pdev; if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) return AE_OK; @@ -213,10 +211,10 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) newfunc->slot = slot; list_add_tail(&newfunc->sibling, &slot->funcs); - /* associate corresponding pci_dev */ - newfunc->pci_dev = pci_get_slot(pbus, PCI_DEVFN(device, function)); - if (newfunc->pci_dev) { + pdev = pci_get_slot(pbus, PCI_DEVFN(device, function)); + if (pdev) { slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); + pci_dev_put(pdev); } if (is_dock_device(handle)) { @@ -617,7 +615,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) if (ACPI_FAILURE(status)) err("failed to remove notify handler\n"); } - pci_dev_put(func->pci_dev); list_del(list); kfree(func); } @@ -1101,22 +1098,24 @@ static int __ref enable_device(struct acpiphp_slot *slot) pci_enable_bridges(bus); pci_bus_add_devices(bus); - /* associate pci_dev to our representation */ list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); - func->pci_dev = pci_get_slot(bus, PCI_DEVFN(slot->device, - func->function)); - if (!func->pci_dev) + dev = pci_get_slot(bus, PCI_DEVFN(slot->device, + func->function)); + if (!dev) continue; - if (func->pci_dev->hdr_type != PCI_HEADER_TYPE_BRIDGE && - func->pci_dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) + if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE && + dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) { + pci_dev_put(dev); continue; + } status = find_p2p_bridge(func->handle, (u32)1, bus, NULL); if (ACPI_FAILURE(status)) warn("find_p2p_bridge failed (error code = 0x%x)\n", status); + pci_dev_put(dev); } slot->flags |= SLOT_ENABLED; @@ -1142,17 +1141,14 @@ static void disable_bridges(struct pci_bus *bus) */ static int disable_device(struct acpiphp_slot *slot) { - int retval = 0; struct acpiphp_func *func; - struct list_head *l; + struct pci_dev *pdev; /* is this slot already disabled? */ if (!(slot->flags & SLOT_ENABLED)) goto err_exit; - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - + list_for_each_entry(func, &slot->funcs, sibling) { if (func->bridge) { /* cleanup p2p bridges under this P2P bridge */ cleanup_p2p_bridge(func->bridge->handle, @@ -1160,35 +1156,28 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - if (func->pci_dev) { - pci_stop_bus_device(func->pci_dev); - if (func->pci_dev->subordinate) { - disable_bridges(func->pci_dev->subordinate); - pci_disable_device(func->pci_dev); + pdev = pci_get_slot(slot->bridge->pci_bus, + PCI_DEVFN(slot->device, func->function)); + if (pdev) { + pci_stop_bus_device(pdev); + if (pdev->subordinate) { + disable_bridges(pdev->subordinate); + pci_disable_device(pdev); } + pci_remove_bus_device(pdev); + pci_dev_put(pdev); } } - list_for_each (l, &slot->funcs) { - func = list_entry(l, struct acpiphp_func, sibling); - + list_for_each_entry(func, &slot->funcs, sibling) { acpiphp_unconfigure_ioapics(func->handle); acpiphp_bus_trim(func->handle); - /* try to remove anyway. - * acpiphp_bus_add might have been failed */ - - if (!func->pci_dev) - continue; - - pci_remove_bus_device(func->pci_dev); - pci_dev_put(func->pci_dev); - func->pci_dev = NULL; } slot->flags &= (~SLOT_ENABLED); - err_exit: - return retval; +err_exit: + return 0; } -- cgit v1.2.3-55-g7522 From ed37d83e6aa218192fb28bb6b82498d2a8c74070 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 27 May 2009 21:39:05 +1000 Subject: md: raid5: change incorrect usage of 'min' macro to 'min_t' A recent patch to raid5.c use min on an int and a sector_t. This isn't allowed. So change it to min_t(sector_t,x,y). Signed-off-by: NeilBrown --- drivers/md/raid5.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 3c3626d2a1f9..5d400aef8d9b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3811,13 +3811,13 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped safepos = conf->reshape_safe; sector_div(safepos, data_disks); if (mddev->delta_disks < 0) { - writepos -= min(reshape_sectors, writepos); + writepos -= min_t(sector_t, reshape_sectors, writepos); readpos += reshape_sectors; safepos += reshape_sectors; } else { writepos += reshape_sectors; - readpos -= min(reshape_sectors, readpos); - safepos -= min(reshape_sectors, safepos); + readpos -= min_t(sector_t, reshape_sectors, readpos); + safepos -= min_t(sector_t, reshape_sectors, safepos); } /* 'writepos' is the most advanced device address we might write. -- cgit v1.2.3-55-g7522 From 346a850e3c3a20159cef2b79235e6d34aa497c65 Mon Sep 17 00:00:00 2001 From: Manuel Traut Date: Wed, 27 May 2009 06:20:05 -0700 Subject: Input: usb1400_ts - fix access to "device data" in resume function platform_data != driver_data driver data is actually the "correct" place of the struct however it is not placed there due to the need of the ac97 struct. This is broken since d9105c2b01 aka "[ARM] 5184/1: Split ucb1400_ts into core and touchscreen" Signed-off-by: Manuel Traut Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ucb1400_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index f100c7f4c1db..6954f5500108 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -419,7 +419,7 @@ static int ucb1400_ts_remove(struct platform_device *dev) #ifdef CONFIG_PM static int ucb1400_ts_resume(struct platform_device *dev) { - struct ucb1400_ts *ucb = platform_get_drvdata(dev); + struct ucb1400_ts *ucb = dev->dev.platform_data; if (ucb->ts_task) { /* -- cgit v1.2.3-55-g7522 From 07f4f3e8a24138ca2f3650723d670df25687cd05 Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg Date: Wed, 27 May 2009 14:37:28 -0400 Subject: i915: Set object to gtt domain when faulting it back in When a GEM object is evicted from the GTT we set it to the CPU domain, as it might get swapped in and out or ever mmapped regularly. If the object is mmapped through the GTT it can still get evicted in this way by other objects requiring GTT space. When the GTT mapping is touched again we fault it back into the GTT, but fail to set it back to the GTT domain. This means we fail to flush any cached CPU writes to the pages backing the object which will then happen "eventually", typically after we write to the page through the uncached GTT mapping. [anholt: Note that userland does do a set_domain(GTT, GTT) when starting to access the GTT mapping. That covers getting the existing mapping of the object synchronized if it's bound to the GTT. But set_domain(GTT, GTT) doesn't do anything if the object is currently unbound. This fix covers the transition to being bound for GTT mapping.] Fixes glyph and other pixmap corruption during swapping. fd.o bug #21790 Signed-off-by: Kristian Høgsberg Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_gem.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e2421869a40c..670d12881468 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1145,6 +1145,13 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) mutex_unlock(&dev->struct_mutex); return VM_FAULT_SIGBUS; } + + ret = i915_gem_object_set_to_gtt_domain(obj, write); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return VM_FAULT_SIGBUS; + } + list_add_tail(&obj_priv->list, &dev_priv->mm.inactive_list); } -- cgit v1.2.3-55-g7522 From b787f2e2a37a373a045f4d9b9bed941ccff01663 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 13 May 2009 16:25:57 -0500 Subject: fsldma: Fix compile warnings We we build with dma_addr_t as a 64-bit quantity we get: drivers/dma/fsldma.c: In function 'fsl_chan_xfer_ld_queue': drivers/dma/fsldma.c:625: warning: cast to pointer from integer of different size drivers/dma/fsldma.c: In function 'fsl_dma_chan_do_interrupt': drivers/dma/fsldma.c:737: warning: cast to pointer from integer of different size drivers/dma/fsldma.c:737: warning: cast to pointer from integer of different size drivers/dma/fsldma.c: In function 'of_fsl_dma_probe': drivers/dma/fsldma.c:927: warning: cast to pointer from integer of different Signed-off-by: Kumar Gala Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 15783102bf17..f18d1bde0439 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -641,8 +641,8 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan) if (ld_node != &fsl_chan->ld_queue) { /* Get the ld start address from ld_queue */ next_dest_addr = to_fsl_desc(ld_node)->async_tx.phys; - dev_dbg(fsl_chan->dev, "xfer LDs staring from %p\n", - (void *)next_dest_addr); + dev_dbg(fsl_chan->dev, "xfer LDs staring from 0x%llx\n", + (unsigned long long)next_dest_addr); set_cdar(fsl_chan, next_dest_addr); dma_start(fsl_chan); } else { @@ -756,8 +756,9 @@ static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data) */ if (stat & FSL_DMA_SR_EOSI) { dev_dbg(fsl_chan->dev, "event: End-of-segments INT\n"); - dev_dbg(fsl_chan->dev, "event: clndar %p, nlndar %p\n", - (void *)get_cdar(fsl_chan), (void *)get_ndar(fsl_chan)); + dev_dbg(fsl_chan->dev, "event: clndar 0x%llx, nlndar 0x%llx\n", + (unsigned long long)get_cdar(fsl_chan), + (unsigned long long)get_ndar(fsl_chan)); stat &= ~FSL_DMA_SR_EOSI; update_cookie = 1; } @@ -947,8 +948,8 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, } dev_info(&dev->dev, "Probe the Freescale DMA driver for %s " - "controller at %p...\n", - match->compatible, (void *)fdev->reg.start); + "controller at 0x%llx...\n", + match->compatible, (unsigned long long)fdev->reg.start); fdev->reg_base = ioremap(fdev->reg.start, fdev->reg.end - fdev->reg.start + 1); -- cgit v1.2.3-55-g7522 From dacd2549ca61ddbdd1ed62a76ca95dea3f0e02c6 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Tue, 26 May 2009 09:08:03 +0900 Subject: PCI/ACPI: fix wrong ref count handling in acpi_pci_bind() The 'dev' field of struct acpi_pci_data is having a pointer to struct pci_dev without incrementing the reference counter. Because of this, I got the following kernel oops when I was doing some pci hotplug operations. This patch fixes this bug by replacing wrong hand-made pci_find_slot() with pci_get_slot() in acpi_pci_bind(). BUG: unable to handle kernel NULL pointer dereference at 00000000000000e8 IP: [] acpi_pci_unbind+0xb1/0xdd Call Trace: [] acpi_bus_remove+0x54/0x68 [] acpi_bus_trim+0x75/0xe3 [] acpiphp_disable_slot+0x16d/0x1e0 [acpiphp] [] disable_slot+0x20/0x60 [acpiphp] [] power_write_file+0xc8/0x110 [] pci_slot_attr_store+0x24/0x30 [] sysfs_write_file+0xce/0x140 [] vfs_write+0xc7/0x170 [] sys_write+0x50/0x90 [] system_call_fastpath+0x16/0x1b Signed-off-by: Kenji Kaneshige Reviewed-by: Bjorn Helgaas Reviewed-by: Alex Chiang Tested-by: Alex Chiang Signed-off-by: Len Brown --- drivers/acpi/pci_bind.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 95650f83ce2e..bc46de3d967f 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -116,9 +116,6 @@ int acpi_pci_bind(struct acpi_device *device) struct acpi_pci_data *pdata; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_handle handle; - struct pci_dev *dev; - struct pci_bus *bus; - if (!device || !device->parent) return -EINVAL; @@ -176,20 +173,9 @@ int acpi_pci_bind(struct acpi_device *device) * Locate matching device in PCI namespace. If it doesn't exist * this typically means that the device isn't currently inserted * (e.g. docking station, port replicator, etc.). - * We cannot simply search the global pci device list, since - * PCI devices are added to the global pci list when the root - * bridge start ops are run, which may not have happened yet. */ - bus = pci_find_bus(data->id.segment, data->id.bus); - if (bus) { - list_for_each_entry(dev, &bus->devices, bus_list) { - if (dev->devfn == PCI_DEVFN(data->id.device, - data->id.function)) { - data->dev = dev; - break; - } - } - } + data->dev = pci_get_slot(pdata->bus, + PCI_DEVFN(data->id.device, data->id.function)); if (!data->dev) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device %04x:%02x:%02x.%d not present in PCI namespace\n", @@ -259,9 +245,10 @@ int acpi_pci_bind(struct acpi_device *device) end: kfree(buffer.pointer); - if (result) + if (result) { + pci_dev_put(data->dev); kfree(data); - + } return result; } @@ -303,6 +290,7 @@ static int acpi_pci_unbind(struct acpi_device *device) if (data->dev->subordinate) { acpi_pci_irq_del_prt(data->id.segment, data->bus->number); } + pci_dev_put(data->dev); kfree(data); end: -- cgit v1.2.3-55-g7522 From a3ce6ea46cc0d6397d1b92b1a5983bb2935306ed Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 28 May 2009 09:51:31 -0700 Subject: Input: libps2 - better handle bad scheduler decisions Sometimes devices send us their responses in time but due to unfortunate scheduling decisions the receiving thread does not get scheduled till much later and we erroneously decide that device timed out. Work around this problem by checking whether we received the data we needed instead of checking timeout condition. Tested-by: Sitsofe Wheeler Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 67248c31e19a..be5bbbb8ae4e 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -210,7 +210,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) timeout = wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD1), timeout); - if (ps2dev->cmdcnt && timeout > 0) { + if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) { timeout = ps2_adjust_timeout(ps2dev, command, timeout); wait_event_timeout(ps2dev->wait, -- cgit v1.2.3-55-g7522 From f42706c90470851fd2e97eda7a4109e8949bde8a Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Fri, 1 May 2009 17:21:11 +0200 Subject: USB: atmel-usba-udc : fix control out requests. usbtest #14 was failing with "udc: ep0: TXCOMP: Invalid endpoint state 2, halting endpoint..." This occured since ep0 is bidirectional and ep->is_in is not valid (must always use ep->state) Signed-off-by: Martin Fuzzey Acked-by: David Brownell Acked-by: Haavard Skinnemoen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/atmel_usba_udc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 563d57275448..5644897c1c5d 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -794,7 +794,8 @@ usba_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) if (ep->desc) { list_add_tail(&req->queue, &ep->queue); - if (ep->is_in || (ep_is_control(ep) + if ((!ep_is_control(ep) && ep->is_in) || + (ep_is_control(ep) && (ep->state == DATA_STAGE_IN || ep->state == STATUS_STAGE_IN))) usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); -- cgit v1.2.3-55-g7522 From fe92c9e481a147a9e1e14f55870f32903b967777 Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Mon, 4 May 2009 17:22:43 -0700 Subject: USB: atmel_usb_udc: Use kzalloc() to allocate ep structures This ensures that all fields are properly initialized. Signed-off-by: Haavard Skinnemoen Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/atmel_usba_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 5644897c1c5d..05c913cc3658 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1941,7 +1941,7 @@ static int __init usba_udc_probe(struct platform_device *pdev) usba_writel(udc, CTRL, USBA_DISABLE_MASK); clk_disable(pclk); - usba_ep = kmalloc(sizeof(struct usba_ep) * pdata->num_ep, + usba_ep = kzalloc(sizeof(struct usba_ep) * pdata->num_ep, GFP_KERNEL); if (!usba_ep) goto err_alloc_ep; -- cgit v1.2.3-55-g7522 From cab98a0a349829b145d924c0649a2d30cd6a9e3d Mon Sep 17 00:00:00 2001 From: Xiao Kaijian Date: Fri, 8 May 2009 00:48:23 +0800 Subject: USB: Yet another Conexant Clone to add to cdc-acm.c This patch adds another quirky Conexant USB Modem Clone to usb cdc-acm.c Signed-off-by: Xiao Kaijian Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 0a69c0977e3f..7a1164dd1d37 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1375,6 +1375,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */ }, { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */ -- cgit v1.2.3-55-g7522 From 0afb20e00b5053170c85298fed842b32d20b4ea9 Mon Sep 17 00:00:00 2001 From: Warren Free Date: Fri, 8 May 2009 10:27:08 +0200 Subject: USB: isp1760: urb_dequeue doesn't always find the urbs The option driver (and presumably others) allocates several URBs when it opens and tries to free them when it closes. The isp1760_urb_dequeue function gets called, but the packet being dequeued is not necessarily at the front of one of the 32 queues. If not, the isp1760_urb_done function doesn't get called for the URB and the process trying to free it hangs forever on a wait_queue. This patch does two things. If the URB being dequeued has others queued behind it, it re-queues them. And it searches the queues looking for the URB being dequeued rather than just looking at the one at the front of the queue. [bigeasy@linutronix] whitespace fixes, reformating Cc: stable Signed-off-by: Warren Free Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index cd07ea3f0c63..15438469f21a 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -1658,6 +1658,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, u32 reg_base, or_reg, skip_reg; unsigned long flags; struct ptd ptd; + packet_enqueue *pe; switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: @@ -1669,6 +1670,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, reg_base = INT_REGS_OFFSET; or_reg = HC_INT_IRQ_MASK_OR_REG; skip_reg = HC_INT_PTD_SKIPMAP_REG; + pe = enqueue_an_INT_packet; break; default: @@ -1676,6 +1678,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, reg_base = ATL_REGS_OFFSET; or_reg = HC_ATL_IRQ_MASK_OR_REG; skip_reg = HC_ATL_PTD_SKIPMAP_REG; + pe = enqueue_an_ATL_packet; break; } @@ -1687,6 +1690,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, u32 skip_map; u32 or_map; struct isp1760_qtd *qtd; + struct isp1760_qh *qh = ints->qh; skip_map = isp1760_readl(hcd->regs + skip_reg); skip_map |= 1 << i; @@ -1699,8 +1703,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, priv_write_copy(priv, (u32 *)&ptd, hcd->regs + reg_base + i * sizeof(ptd), sizeof(ptd)); qtd = ints->qtd; - - clean_up_qtdlist(qtd); + qtd = clean_up_qtdlist(qtd); free_mem(priv, ints->payload); @@ -1711,7 +1714,24 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, ints->payload = 0; isp1760_urb_done(priv, urb, status); + if (qtd) + pe(hcd, qh, qtd); break; + + } else if (ints->qtd) { + struct isp1760_qtd *qtd, *prev_qtd = ints->qtd; + + for (qtd = ints->qtd->hw_next; qtd; qtd = qtd->hw_next) { + if (qtd->urb == urb) { + prev_qtd->hw_next = clean_up_qtdlist(qtd); + isp1760_urb_done(priv, urb, status); + break; + } + prev_qtd = qtd; + } + /* we found the urb before the end of the list */ + if (qtd) + break; } ints++; } -- cgit v1.2.3-55-g7522 From 0a3c8549ea7e94d74a41096d42bc6cdf43d183bf Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 27 May 2009 11:25:52 -0400 Subject: usb-serial: fix crash when sub-driver updates firmware This patch (as1244) fixes a crash in usb-serial that occurs when a sub-driver returns a positive value from its attach method, indicating that new firmware was loaded and the device will disconnect and reconnect. The usb-serial core then skips the step of registering the port devices; when the disconnect occurs, the attempt to unregister the ports fails dramatically. This problem shows up with Keyspan devices and it might affect others as well. When the attach method returns a positive value, the patch sets num_ports to 0. This tells usb_serial_disconnect() not to try unregistering any of the ports; instead they are cleaned up by destroy_serial(). Signed-off-by: Alan Stern Tested-by: Benjamin Herrenschmidt Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0a566eea49c0..f331e2bde88a 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -974,6 +974,7 @@ int usb_serial_probe(struct usb_interface *interface, if (retval > 0) { /* quietly accept this device, but don't bind to a serial port as it's about to disappear */ + serial->num_ports = 0; goto exit; } } -- cgit v1.2.3-55-g7522 From 29868b281f6d057b4cbe348f4483f1717c021c5c Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 20 May 2009 08:49:48 -0400 Subject: Revert "USB: Correct Makefile to make isp1760 buildable" This reverts commit 26e1287594864169577327fef233befc9739be3b. A larger patch (f7e7aa585) a few days after this one added the same line to the Makefile, but in a different place. While it'd be more correct to revert that one, it's easier to revert this one because this is a one-liner. Signed-off-by: Mike Frysinger CC: Greg Kroah-Hartman CC: linux-usb@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 0716cdb44cd8..0a3dc5ece634 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_PCI) += host/ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_ISP116X_HCD) += host/ -obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/ -- cgit v1.2.3-55-g7522 From 5c8563d773c0e9f0ac2a552e84806decd98ce732 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 28 May 2009 14:24:07 -0700 Subject: Driver Core: do not oops when driver_unregister() is called for unregistered drivers We also fix a problem with cleaning up properly when initializing drivers and devices, so checks like this will work successfully. Portions of the patch by Linus and Greg and Ingo. Reported-by: Ozan ÇaÄŸlayan Signed-off-by: Kay Sievers Cc: Linus Torvalds Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 4 +++- drivers/base/core.c | 5 ++++- drivers/base/driver.c | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index dc030f1f00f1..c6599618523e 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -700,8 +700,10 @@ int bus_add_driver(struct device_driver *drv) } kobject_uevent(&priv->kobj, KOBJ_ADD); - return error; + return 0; out_unregister: + kfree(drv->p); + drv->p = NULL; kobject_put(&priv->kobj); out_put_bus: bus_put(bus); diff --git a/drivers/base/core.c b/drivers/base/core.c index 4aa527b8a913..1977d4beb89e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -879,7 +879,7 @@ int device_add(struct device *dev) } if (!dev_name(dev)) - goto done; + goto name_error; pr_debug("device: '%s': %s\n", dev_name(dev), __func__); @@ -978,6 +978,9 @@ done: cleanup_device_parent(dev); if (parent) put_device(parent); +name_error: + kfree(dev->p); + dev->p = NULL; goto done; } diff --git a/drivers/base/driver.c b/drivers/base/driver.c index c51f11bb29ae..8ae0f63602e0 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -257,6 +257,10 @@ EXPORT_SYMBOL_GPL(driver_register); */ void driver_unregister(struct device_driver *drv) { + if (!drv || !drv->p) { + WARN(1, "Unexpected driver unregister!\n"); + return; + } driver_remove_groups(drv, drv->groups); bus_remove_driver(drv); } -- cgit v1.2.3-55-g7522 From 2f102607ac77354b02a76cf2748598ce9f270f08 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Wed, 27 May 2009 23:59:58 -0400 Subject: i7300_idle: allow testing on i5000-series hardware w/o re-compile Testing the i7300_idle driver on i5000-series hardware required an edit to i7300_idle.h to "#define SUPPORT_I5000 1" and a re-build of both i7300_idle and ioat_dma. Replace that build-time scheme with a load-time module parameter: "7300_idle.forceload=1" to make it easier to test the driver on hardware that while not officially validated, works fine and is much more commonly available. By default (no modparam) the driver will continue to load only on the i7300. Note that ioat_dma runs a copy of i7300_idle's probe routine to know to reserve an IOAT channel for i7300_idle. This change makes ioat_dma do that always on the i5000, just like it does on the i7300. Signed-off-by: Len Brown Acked-by: Andrew Henroid --- drivers/dma/ioat_dma.c | 2 +- drivers/idle/i7300_idle.c | 6 +++++- include/linux/i7300_idle.h | 20 ++++++++++---------- 3 files changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 1955ee8d6d20..a600fc0f7962 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -173,7 +173,7 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); #ifdef CONFIG_I7300_IDLE_IOAT_CHANNEL - if (i7300_idle_platform_probe(NULL, NULL) == 0) { + if (i7300_idle_platform_probe(NULL, NULL, 1) == 0) { device->common.chancnt--; } #endif diff --git a/drivers/idle/i7300_idle.c b/drivers/idle/i7300_idle.c index bf740394d704..949c97ff57e3 100644 --- a/drivers/idle/i7300_idle.c +++ b/drivers/idle/i7300_idle.c @@ -41,6 +41,10 @@ static int debug; module_param_named(debug, debug, uint, 0644); MODULE_PARM_DESC(debug, "Enable debug printks in this driver"); +static int forceload; +module_param_named(forceload, forceload, uint, 0644); +MODULE_PARM_DESC(debug, "Enable driver testing on unvalidated i5000"); + #define dprintk(fmt, arg...) \ do { if (debug) printk(KERN_INFO I7300_PRINT fmt, ##arg); } while (0) @@ -552,7 +556,7 @@ static int __init i7300_idle_init(void) cpus_clear(idle_cpumask); total_us = 0; - if (i7300_idle_platform_probe(&fbd_dev, &ioat_dev)) + if (i7300_idle_platform_probe(&fbd_dev, &ioat_dev, forceload)) return -ENODEV; if (i7300_idle_thrt_save()) diff --git a/include/linux/i7300_idle.h b/include/linux/i7300_idle.h index 05a80c44513c..1587b7dec505 100644 --- a/include/linux/i7300_idle.h +++ b/include/linux/i7300_idle.h @@ -16,35 +16,33 @@ struct fbd_ioat { unsigned int vendor; unsigned int ioat_dev; + unsigned int enabled; }; /* * The i5000 chip-set has the same hooks as the i7300 - * but support is disabled by default because this driver - * has not been validated on that platform. + * but it is not enabled by default and must be manually + * manually enabled with "forceload=1" because it is + * only lightly validated. */ -#define SUPPORT_I5000 0 static const struct fbd_ioat fbd_ioat_list[] = { - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_CNB}, -#if SUPPORT_I5000 - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT}, -#endif + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_CNB, 1}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT, 0}, {0, 0} }; /* table of devices that work with this driver */ static const struct pci_device_id pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_FBD_CNB) }, -#if SUPPORT_I5000 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) }, -#endif { } /* Terminating entry */ }; /* Check for known platforms with I/O-AT */ static inline int i7300_idle_platform_probe(struct pci_dev **fbd_dev, - struct pci_dev **ioat_dev) + struct pci_dev **ioat_dev, + int enable_all) { int i; struct pci_dev *memdev, *dmadev; @@ -69,6 +67,8 @@ static inline int i7300_idle_platform_probe(struct pci_dev **fbd_dev, for (i = 0; fbd_ioat_list[i].vendor != 0; i++) { if (dmadev->vendor == fbd_ioat_list[i].vendor && dmadev->device == fbd_ioat_list[i].ioat_dev) { + if (!(fbd_ioat_list[i].enabled || enable_all)) + continue; if (fbd_dev) *fbd_dev = memdev; if (ioat_dev) -- cgit v1.2.3-55-g7522 From 10b6d95612672f89deb39b5a60fb677c78ba4844 Mon Sep 17 00:00:00 2001 From: Divy Le Ray Date: Thu, 28 May 2009 11:23:02 +0000 Subject: cxgb3: fix dma mapping regression Commit 5e68b772e6efd189d6aca76f6872fb75d51ace60 cxgb3: map entire Rx page, feed map+offset to Rx ring. introduced a regression on platforms defining DECLARE_PCI_UNMAP_ADDR() and related macros as no-ops. Rx descriptors are fed with the a page buffer bus address + page chunk offset. The page buffer bus address is set and retrieved through pci_unamp_addr_set(), pci_unmap_addr(). These functions being meaningless on x86 (if CONFIG_DMA_API_DEBUG is not set). The HW ends up with a bogus bus address. This patch saves the page buffer bus address for all plaftorms. Signed-off-by: Divy Le Ray Signed-off-by: David S. Miller --- drivers/net/cxgb3/adapter.h | 4 ++-- drivers/net/cxgb3/sge.c | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h index 714df2b675e6..c888e97c9671 100644 --- a/drivers/net/cxgb3/adapter.h +++ b/drivers/net/cxgb3/adapter.h @@ -85,8 +85,8 @@ struct fl_pg_chunk { struct page *page; void *va; unsigned int offset; - u64 *p_cnt; - DECLARE_PCI_UNMAP_ADDR(mapping); + unsigned long *p_cnt; + dma_addr_t mapping; }; struct rx_desc; diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 26d3587f3399..b3ee2bc1a005 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -355,7 +355,7 @@ static void clear_rx_desc(struct pci_dev *pdev, const struct sge_fl *q, (*d->pg_chunk.p_cnt)--; if (!*d->pg_chunk.p_cnt) pci_unmap_page(pdev, - pci_unmap_addr(&d->pg_chunk, mapping), + d->pg_chunk.mapping, q->alloc_size, PCI_DMA_FROMDEVICE); put_page(d->pg_chunk.page); @@ -454,7 +454,7 @@ 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); - pci_unmap_addr_set(&q->pg_chunk, mapping, mapping); + q->pg_chunk.mapping = mapping; } sd->pg_chunk = q->pg_chunk; @@ -511,8 +511,7 @@ static int refill_fl(struct adapter *adap, struct sge_fl *q, int n, gfp_t gfp) nomem: q->alloc_failed++; break; } - mapping = pci_unmap_addr(&sd->pg_chunk, mapping) + - sd->pg_chunk.offset; + mapping = sd->pg_chunk.mapping + sd->pg_chunk.offset; pci_unmap_addr_set(sd, dma_addr, mapping); add_one_rx_chunk(mapping, d, q->gen); @@ -881,7 +880,7 @@ recycle: (*sd->pg_chunk.p_cnt)--; if (!*sd->pg_chunk.p_cnt) pci_unmap_page(adap->pdev, - pci_unmap_addr(&sd->pg_chunk, mapping), + sd->pg_chunk.mapping, fl->alloc_size, PCI_DMA_FROMDEVICE); if (!skb) { @@ -2096,7 +2095,7 @@ static void lro_add_page(struct adapter *adap, struct sge_qset *qs, (*sd->pg_chunk.p_cnt)--; if (!*sd->pg_chunk.p_cnt) pci_unmap_page(adap->pdev, - pci_unmap_addr(&sd->pg_chunk, mapping), + sd->pg_chunk.mapping, fl->alloc_size, PCI_DMA_FROMDEVICE); -- cgit v1.2.3-55-g7522 From c22c8149313ee85c912e7b77a7afd04be8b8cba8 Mon Sep 17 00:00:00 2001 From: Divy Le Ray Date: Thu, 28 May 2009 11:23:08 +0000 Subject: cxgb3: link fault fixes Do not call t3_link_fault() under spinlock, as it calls msleep(). Besides, only the access to pi->link_fault needs to be serialized. Also initialize local variables before checking the link status, link state fields might otherwise end up containing garbage. Signed-off-by: Divy Le Ray Signed-off-by: David S. Miller --- drivers/net/cxgb3/cxgb3_main.c | 8 +++++--- drivers/net/cxgb3/t3_hw.c | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 7ea48414c6cb..17858b9a5830 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -2496,14 +2496,16 @@ static void check_link_status(struct adapter *adapter) for_each_port(adapter, i) { struct net_device *dev = adapter->port[i]; struct port_info *p = netdev_priv(dev); + int link_fault; spin_lock_irq(&adapter->work_lock); - if (p->link_fault) { + link_fault = p->link_fault; + spin_unlock_irq(&adapter->work_lock); + + if (link_fault) { t3_link_fault(adapter, i); - spin_unlock_irq(&adapter->work_lock); continue; } - spin_unlock_irq(&adapter->work_lock); if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) { t3_xgm_intr_disable(adapter, i); diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 4f68aeb2679a..4950d5d789ae 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -1274,6 +1274,11 @@ void t3_link_fault(struct adapter *adapter, int port_id) A_XGM_INT_STATUS + mac->offset); link_fault &= F_LINKFAULTCHANGE; + link_ok = lc->link_ok; + speed = lc->speed; + duplex = lc->duplex; + fc = lc->fc; + phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc); if (link_fault) { -- cgit v1.2.3-55-g7522 From 4e0168fa4842e27795a75b205a510f25b62181d9 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Thu, 28 May 2009 02:05:53 +0000 Subject: mac8390: fix build with NET_POLL_CONTROLLER Fix the build for CONFIG_NET_POLL_CONTROLLER that I broke with 217cbfa856dc1cbc2890781626c4032d9e3ec59f ("mac8390: fix regression caused during net_device_ops conversion"). Signed-off-by: Finn Thain Signed-off-by: David S. Miller --- drivers/net/mac8390.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/mac8390.c b/drivers/net/mac8390.c index f26667d5eaae..22e74a0e0361 100644 --- a/drivers/net/mac8390.c +++ b/drivers/net/mac8390.c @@ -489,7 +489,7 @@ static const struct net_device_ops mac8390_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ei_poll, + .ndo_poll_controller = __ei_poll, #endif }; -- cgit v1.2.3-55-g7522 From 8541c1180a355c4da283fc6b03a92c0233823c1b Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 23 Apr 2009 15:47:22 +0400 Subject: mtd: MXC NAND driver fixes (v5) The following patch fixes: - re-initialization of host->col_addr which is used as byte index between the successive READID flash commands. - compile error when CONFIG_PM is enabled - pass on the error code from clk_get() - return -ENOMEM in case of failed ioremap() - pass on the return value of platform_driver_probe() directly - remove excessive printk - let command line partition table parsing with mxc_nand name. The cmd_line parsing is done via name that differs from mxc_nand by default and looks like "NAND 256MiB 1,8V 8-bit" Signed-off-by: Vladimir Barinov Signed-off-by: Lothar Wassmann Acked-by: Sascha Hauer Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index f3548d048014..40c26080ecda 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -831,6 +831,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_READID: + host->col_addr = 0; send_read_id(host); break; @@ -867,6 +868,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) mtd->priv = this; mtd->owner = THIS_MODULE; mtd->dev.parent = &pdev->dev; + mtd->name = "mxc_nand"; /* 50 us command delay time */ this->chip_delay = 5; @@ -882,8 +884,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->verify_buf = mxc_nand_verify_buf; host->clk = clk_get(&pdev->dev, "nfc"); - if (IS_ERR(host->clk)) + if (IS_ERR(host->clk)) { + err = PTR_ERR(host->clk); goto eclk; + } clk_enable(host->clk); host->clk_act = 1; @@ -896,7 +900,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->regs = ioremap(res->start, res->end - res->start + 1); if (!host->regs) { - err = -EIO; + err = -ENOMEM; goto eres; } @@ -1011,30 +1015,35 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) { - struct mtd_info *info = platform_get_drvdata(pdev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; int ret = 0; DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); - if (info) - ret = info->suspend(info); - - /* Disable the NFC clock */ - clk_disable(nfc_clk); /* FIXME */ + if (mtd) { + ret = mtd->suspend(mtd); + /* Disable the NFC clock */ + clk_disable(host->clk); + } return ret; } static int mxcnd_resume(struct platform_device *pdev) { - struct mtd_info *info = platform_get_drvdata(pdev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; int ret = 0; DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - /* Enable the NFC clock */ - clk_enable(nfc_clk); /* FIXME */ - if (info) - info->resume(info); + if (mtd) { + /* Enable the NFC clock */ + clk_enable(host->clk); + mtd->resume(mtd); + } return ret; } @@ -1055,13 +1064,7 @@ static struct platform_driver mxcnd_driver = { static int __init mxc_nd_init(void) { - /* Register the device driver structure. */ - pr_info("MXC MTD nand Driver\n"); - if (platform_driver_probe(&mxcnd_driver, mxcnd_probe) != 0) { - printk(KERN_ERR "Driver register failed for mxcnd_driver\n"); - return -ENODEV; - } - return 0; + return platform_driver_probe(&mxcnd_driver, mxcnd_probe); } static void __exit mxc_nd_cleanup(void) -- cgit v1.2.3-55-g7522 From 21a4cc00e8e67edcfc1bdb9af6d370ed1226eb86 Mon Sep 17 00:00:00 2001 From: John W. Linville Date: Thu, 28 May 2009 11:39:02 +0200 Subject: at76c50x-usb: avoid mutex deadlock in at76_dwork_hw_scan http://bugzilla.kernel.org/show_bug.cgi?id=13312 at76_dwork_hw_scan holds a mutex while calling ieee80211_scan_completed, which then calls at76_config which needs the same mutex. This reworks the ordering to not hold the lock while calling ieee80211_scan_completed. Signed-off-by: John W. Linville --- drivers/net/wireless/at76c50x-usb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 744f4f4dd3d1..8d93ca4651b9 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1873,18 +1873,18 @@ static void at76_dwork_hw_scan(struct work_struct *work) if (ret != CMD_STATUS_COMPLETE) { queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan, SCAN_POLL_INTERVAL); - goto exit; + mutex_unlock(&priv->mtx); + return; } - ieee80211_scan_completed(priv->hw, false); - if (is_valid_ether_addr(priv->bssid)) at76_join(priv); - ieee80211_wake_queues(priv->hw); - -exit: mutex_unlock(&priv->mtx); + + ieee80211_scan_completed(priv->hw, false); + + ieee80211_wake_queues(priv->hw); } static int at76_hw_scan(struct ieee80211_hw *hw, -- cgit v1.2.3-55-g7522 From aeeab4ff06b8e29cfe2fe730ba626f7e2487ba03 Mon Sep 17 00:00:00 2001 From: John W. Linville Date: Wed, 27 May 2009 09:21:57 +0200 Subject: rtl8187: add USB ID for Linksys WUSB54GC-EU v2 USB wifi dongle http://bugzilla.kernel.org/show_bug.cgi?id=13383 Reported-by: Przemyslaw Kulczycki Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/rtl818x/rtl8187_dev.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 8a0823588c51..3d94e7dfea69 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -430,6 +430,7 @@ config RTL8187 ASUS P5B Deluxe Toshiba Satellite Pro series of laptops Asus Wireless Link + Linksys WUSB54GC-EU Thanks to Realtek for their support! diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index bac6cfba6abd..d51ba0a88c23 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -71,6 +71,8 @@ static struct usb_device_id rtl8187_table[] __devinitdata = { {USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187}, /* AirLive */ {USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187}, + /* Linksys */ + {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B}, {} }; -- cgit v1.2.3-55-g7522 From b898f4f869da5b9d41f297fff87aca4cd42d80b3 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 28 May 2009 14:34:29 -0700 Subject: drivers/serial/mpc52xx_uart.c: fix array overindexing check The check for an overindexing of mpc52xx_uart_{ports,nodes} has an off-by-one. Signed-off-by: Roel Kluin Acked-by: Wolfram Sang Acked-by: Grant Likely Cc: Benjamin Herrenschmidt Cc: Alan Cox Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/serial/mpc52xx_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 7f72f8ceaa6f..b3feb6198d57 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -988,7 +988,7 @@ mpc52xx_console_setup(struct console *co, char *options) pr_debug("mpc52xx_console_setup co=%p, co->index=%i, options=%s\n", co, co->index, options); - if ((co->index < 0) || (co->index > MPC52xx_PSC_MAXNUM)) { + if ((co->index < 0) || (co->index >= MPC52xx_PSC_MAXNUM)) { pr_debug("PSC%x out of range\n", co->index); return -EINVAL; } -- cgit v1.2.3-55-g7522 From b5d598b41aebee67bf95802b68b888e98a449687 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Thu, 28 May 2009 14:34:33 -0700 Subject: parport_gsc: fix printk format error drivers/parport/parport_gsc.c:356: warning: format '%lx' expects type 'long unsigned int', but argument 2 has type 'resource_size_t' [akpm@linux-foundation.org: fix it to handle u64's] Signed-off-by: Alexander Beregalov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/parport/parport_gsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index e6a7e847ee80..ea31a452b153 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -352,8 +352,8 @@ static int __devinit parport_init_chip(struct parisc_device *dev) unsigned long port; if (!dev->irq) { - printk(KERN_WARNING "IRQ not found for parallel device at 0x%lx\n", - dev->hpa.start); + printk(KERN_WARNING "IRQ not found for parallel device at 0x%llx\n", + (unsigned long long)dev->hpa.start); return -ENODEV; } -- cgit v1.2.3-55-g7522 From 8e8e8267f0a08c2415d5f51bc9a9fde6d5400619 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Thu, 28 May 2009 14:34:34 -0700 Subject: serial: 8250_gsc: fix printk format error drivers/serial/8250_gsc.c:44: warning: format '%lx' expects type 'long unsigned int', but argument 2 has type 'resource_size_t' [akpm@linux-foundation.org: fix it to handle u64's] Signed-off-by: Alexander Beregalov Cc: Alan Cox Cc: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/serial/8250_gsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c index 418b4fe9a0a1..33149d982e82 100644 --- a/drivers/serial/8250_gsc.c +++ b/drivers/serial/8250_gsc.c @@ -39,9 +39,9 @@ static int __init serial_init_chip(struct parisc_device *dev) */ if (parisc_parent(dev)->id.hw_type != HPHW_IOA) printk(KERN_INFO - "Serial: device 0x%lx not configured.\n" + "Serial: device 0x%llx not configured.\n" "Enable support for Wax, Lasi, Asp or Dino.\n", - dev->hpa.start); + (unsigned long long)dev->hpa.start); return -ENODEV; } -- cgit v1.2.3-55-g7522 From 17663e59704bea838a9236f299104e30909a43b1 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 28 May 2009 14:34:35 -0700 Subject: S3C-fb: PM fix Correctly restore the FrameBuffer register state in the resume function. Reviewed-by: Kyungmin Park Signed-off-by: Marek Szyprowski Cc: Ben Dooks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/s3c-fb.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 5e9c6302433b..d3a568e6b169 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -947,7 +947,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) int win; for (win = 0; win <= S3C_FB_MAX_WIN; win++) - s3c_fb_release_win(sfb, sfb->windows[win]); + if (sfb->windows[win]) + s3c_fb_release_win(sfb, sfb->windows[win]); iounmap(sfb->regs); @@ -985,11 +986,20 @@ static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) static int s3c_fb_resume(struct platform_device *pdev) { struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb_platdata *pd = sfb->pdata; struct s3c_fb_win *win; int win_no; clk_enable(sfb->bus_clk); + /* setup registers */ + writel(pd->vidcon1, sfb->regs + VIDCON1); + + /* zero all windows before we do anything */ + for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) + s3c_fb_clear_win(sfb, win_no); + + /* restore framebuffers */ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { win = sfb->windows[win_no]; if (!win) -- cgit v1.2.3-55-g7522 From 53b7479bbdaedcc7846c66fd608fe66f1b5aa35b Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 28 May 2009 14:34:36 -0700 Subject: atmel_lcdfb: correct fifo size for some products Remove wrong fifo size definition for some AT91 products. Due to a misunderstanding of some AT91 datasheets, a fifo size of 2048 (words) has been introduced by mistake. In fact, all products (AT91/AT32) are sharing the same fifo size of 512 words. Signed-off-by: Nicolas Ferre Cc: Andrew Victor Acked-by: Haavard Skinnemoen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/atmel_lcdfb.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 9a577a800db5..2fb63f6ea2f1 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -29,14 +29,8 @@ /* configurable parameters */ #define ATMEL_LCDC_CVAL_DEFAULT 0xc8 -#define ATMEL_LCDC_DMA_BURST_LEN 8 - -#if defined(CONFIG_ARCH_AT91SAM9263) || defined(CONFIG_ARCH_AT91CAP9) || \ - defined(CONFIG_ARCH_AT91SAM9RL) -#define ATMEL_LCDC_FIFO_SIZE 2048 -#else -#define ATMEL_LCDC_FIFO_SIZE 512 -#endif +#define ATMEL_LCDC_DMA_BURST_LEN 8 /* words */ +#define ATMEL_LCDC_FIFO_SIZE 512 /* words */ #if defined(CONFIG_ARCH_AT91) #define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \ -- cgit v1.2.3-55-g7522 From 56ec0c7b88c6eb17733e5015f31302f6312511ed Mon Sep 17 00:00:00 2001 From: Harry Ciao Date: Thu, 28 May 2009 14:34:42 -0700 Subject: edac: AMD8111 & AMD8131 use dev_name() The "bus_id" member in the device structure has been obsolete, use dev_name() instead. Signed-off-by: Harry Ciao Cc: Doug Thompson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/edac/amd8111_edac.c | 4 ++-- drivers/edac/amd8131_edac.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/edac/amd8111_edac.c b/drivers/edac/amd8111_edac.c index 614692181120..2cb58ef743e0 100644 --- a/drivers/edac/amd8111_edac.c +++ b/drivers/edac/amd8111_edac.c @@ -389,7 +389,7 @@ static int amd8111_dev_probe(struct pci_dev *dev, dev_info->edac_dev->dev = &dev_info->dev->dev; dev_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR; dev_info->edac_dev->ctl_name = dev_info->ctl_name; - dev_info->edac_dev->dev_name = dev_info->dev->dev.bus_id; + dev_info->edac_dev->dev_name = dev_name(&dev_info->dev->dev); if (edac_op_state == EDAC_OPSTATE_POLL) dev_info->edac_dev->edac_check = dev_info->check; @@ -473,7 +473,7 @@ static int amd8111_pci_probe(struct pci_dev *dev, pci_info->edac_dev->dev = &pci_info->dev->dev; pci_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR; pci_info->edac_dev->ctl_name = pci_info->ctl_name; - pci_info->edac_dev->dev_name = pci_info->dev->dev.bus_id; + pci_info->edac_dev->dev_name = dev_name(&pci_info->dev->dev); if (edac_op_state == EDAC_OPSTATE_POLL) pci_info->edac_dev->edac_check = pci_info->check; diff --git a/drivers/edac/amd8131_edac.c b/drivers/edac/amd8131_edac.c index c083b31cac5a..b432d60c622a 100644 --- a/drivers/edac/amd8131_edac.c +++ b/drivers/edac/amd8131_edac.c @@ -287,7 +287,7 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id) dev_info->edac_dev->dev = &dev_info->dev->dev; dev_info->edac_dev->mod_name = AMD8131_EDAC_MOD_STR; dev_info->edac_dev->ctl_name = dev_info->ctl_name; - dev_info->edac_dev->dev_name = dev_info->dev->dev.bus_id; + dev_info->edac_dev->dev_name = dev_name(&dev_info->dev->dev); if (edac_op_state == EDAC_OPSTATE_POLL) dev_info->edac_dev->edac_check = amd8131_chipset.check; -- cgit v1.2.3-55-g7522 From 715fe7af9fd7328af661742bfadc195e665a837f Mon Sep 17 00:00:00 2001 From: Harry Ciao Date: Thu, 28 May 2009 14:34:43 -0700 Subject: edac: AMD8111 & AMD8131 Kconfig fixup The amd8111_edac.c driver will fail allmodconfig on architectures other than PPC, introduce Kconfig dependency to avoid this, since both AMD8111 and AMD8131 chips are only adopted on Maple so far. Signed-off-by: Harry Ciao Cc: Doug Thompson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/edac/Kconfig | 8 ++++++-- drivers/edac/Makefile | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index e5f5c5a8ba6c..956982f8739b 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -192,16 +192,20 @@ config EDAC_PPC4XX config EDAC_AMD8131 tristate "AMD8131 HyperTransport PCI-X Tunnel" - depends on EDAC_MM_EDAC && PCI + depends on EDAC_MM_EDAC && PCI && PPC_MAPLE help Support for error detection and correction on the AMD8131 HyperTransport PCI-X Tunnel chip. + Note, add more Kconfig dependency if it's adopted + on some machine other than Maple. config EDAC_AMD8111 tristate "AMD8111 HyperTransport I/O Hub" - depends on EDAC_MM_EDAC && PCI + depends on EDAC_MM_EDAC && PCI && PPC_MAPLE help Support for error detection and correction on the AMD8111 HyperTransport I/O Hub chip. + Note, add more Kconfig dependency if it's adopted + on some machine other than Maple. endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index a5fdcf02f591..59076819135d 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -35,3 +35,5 @@ obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac.o obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o obj-$(CONFIG_EDAC_CELL) += cell_edac.o obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o +obj-$(CONFIG_EDAC_AMD8111) += amd8111_edac.o +obj-$(CONFIG_EDAC_AMD8131) += amd8131_edac.o -- cgit v1.2.3-55-g7522 From b8e7e40abeac49644fec4a4f52ffe74c7b05eca0 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 28 May 2009 14:01:35 +0100 Subject: 8250: Fix oops from setserial If you setserial a port which has never been initialised we change the type but don't update the I/O method pointers. The same problem is true if you change the io type of a port - but nobody ever does that so nobody noticed! Remember the old type and when attaching if the type has changed reload the port accessor pointers. We can't do it blindly as some 8250 drivers load custom accessors and we must not stomp those. Tested-by: Victor Seryodkin Closes-bug: #13367 Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index b4b39811b445..a0127e93ade0 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -137,6 +137,7 @@ struct uart_8250_port { unsigned char mcr; unsigned char mcr_mask; /* mask of user bits */ unsigned char mcr_force; /* mask of forced bits */ + unsigned char cur_iotype; /* Running I/O type */ /* * Some bits in registers are cleared on a read, so they must @@ -471,6 +472,7 @@ static void io_serial_out(struct uart_port *p, int offset, int value) static void set_io_from_upio(struct uart_port *p) { + struct uart_8250_port *up = (struct uart_8250_port *)p; switch (p->iotype) { case UPIO_HUB6: p->serial_in = hub6_serial_in; @@ -509,6 +511,8 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = io_serial_out; break; } + /* Remember loaded iotype */ + up->cur_iotype = p->iotype; } static void @@ -1937,6 +1941,9 @@ static int serial8250_startup(struct uart_port *port) up->capabilities = uart_config[up->port.type].flags; up->mcr = 0; + if (up->port.iotype != up->cur_iotype) + set_io_from_upio(port); + if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; @@ -2563,6 +2570,9 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (ret < 0) probeflags &= ~PROBE_RSA; + if (up->port.iotype != up->cur_iotype) + set_io_from_upio(port); + if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags); if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) @@ -2671,6 +2681,11 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) { int i; + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + up->cur_iotype = 0xFF; + } + serial8250_isa_init_ports(); for (i = 0; i < nr_uarts; i++) { -- cgit v1.2.3-55-g7522 From 34d531e640cb805973cf656b15c716b961565cea Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 26 May 2009 15:11:06 -0400 Subject: ACPI: sanity check _PSS frequency to prevent cpufreq crash When BIOS SETUP is changed to disable EIST, some BIOS hand the OS an un-initialized _PSS: Name (_PSS, Package (0x06) { Package (0x06) { 0x80000000, // frequency [MHz] 0x80000000, // power [mW] 0x80000000, // latency [us] 0x80000000, // BM latency [us] 0x80000000, // control 0x80000000 // status }, ... These are outrageous values for frequency, power and latency, raising the question where to draw the line between legal and illegal. We tend to survive garbage in the power and latency fields, but we can BUG_ON when garbage is in the frequency field. Cpufreq multiplies the frequency by 1000 and stores it in a u32 KHz. So disregard a _PSS with a frequency so large that it can't be represented by cpufreq. https://bugzilla.redhat.com/show_bug.cgi?id=500311 Signed-off-by: Len Brown --- drivers/acpi/processor_perflib.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index cafb41000f6b..60e543d3234e 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -309,9 +309,15 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr) (u32) px->bus_master_latency, (u32) px->control, (u32) px->status)); - if (!px->core_frequency) { - printk(KERN_ERR PREFIX - "Invalid _PSS data: freq is zero\n"); + /* + * Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq + */ + if (!px->core_frequency || + ((u32)(px->core_frequency * 1000) != + (px->core_frequency * 1000))) { + printk(KERN_ERR FW_BUG PREFIX + "Invalid BIOS _PSS frequency: 0x%llx MHz\n", + px->core_frequency); result = -EFAULT; kfree(pr->performance->states); goto end; -- cgit v1.2.3-55-g7522 From 34ac272b3aaef11a91e19a72f3ac5772a96ffbc5 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 26 May 2009 23:35:34 -0400 Subject: ACPI: video: DMI workaround broken eMachines E510 BIOS enabling display brightness http://bugzilla.kernel.org/show_bug.cgi?id=13376 Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 810cca90ca7f..ee45e7604221 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -570,6 +570,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"), }, }, + { + .callback = video_set_bqc_offset, + .ident = "eMachines E510", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"), + DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"), + }, + }, {} }; -- cgit v1.2.3-55-g7522 From 93bcece20ef87c29548ec7e66532f1018572cea0 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 19 May 2009 15:08:41 -0400 Subject: ACPI: video: DMI workaround broken Acer 5315 BIOS enabling display brightness http://bugzilla.kernel.org/show_bug.cgi?id=13121 Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index ee45e7604221..6d897bbd3bd0 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -578,6 +578,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"), }, }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 5315", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), + }, + }, {} }; -- cgit v1.2.3-55-g7522 From 1fc8d33acafe68bdcc21b327d22ef3820b819727 Mon Sep 17 00:00:00 2001 From: Jaswinder Singh Rajput Date: Wed, 20 May 2009 11:56:08 +0530 Subject: drm/i915: acpi/video.c fix section mismatch warning Currently acpi_video_exit() is exported as well as using __exit which causes: WARNING: drivers/acpi/video.o(__ksymtab+0x0): Section mismatch in reference from the variable __ksymtab_acpi_video_exit to the function .exit.text:acpi_video_exit() The symbol acpi_video_exit is exported and annotated __exit Fix this by removing the __exit annotation of acpi_video_exit or drop the export. Signed-off-by: Jaswinder Singh Rajput Signed-off-by: Len Brown --- drivers/acpi/video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 810cca90ca7f..a79b88535266 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -2334,7 +2334,7 @@ static int __init acpi_video_init(void) return acpi_video_register(); } -void __exit acpi_video_exit(void) +void acpi_video_exit(void) { acpi_bus_unregister_driver(&acpi_video_bus); -- cgit v1.2.3-55-g7522 From 21671b88be331fb9c95891d5ee7d2e940e6b024c Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Fri, 22 May 2009 10:23:40 +0200 Subject: ACPI processor: remove spurious newline from warning message Commit 4973b22a ("ACPI processor: reset the throttling state once it's invalid") introduced a new warning which prints a spurious newline. The ACPI_WARNING macro that is used already takes care of adding a newline, after adding ACPI_CA_VERSION to the message. Remove the newline to avoid the message getting split into two lines. Signed-off-by: Frans Pop Signed-off-by: Len Brown --- drivers/acpi/processor_throttling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 7f16f5f8e7d3..227543789ba9 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -840,7 +840,7 @@ static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr) state = acpi_get_throttling_state(pr, value); if (state == -1) { ACPI_WARNING((AE_INFO, - "Invalid throttling state, reset\n")); + "Invalid throttling state, reset")); state = 0; ret = acpi_processor_set_throttling(pr, state); if (ret) -- cgit v1.2.3-55-g7522 From 31db5645bda24682dadbc97d5e8a7918ade2a298 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 29 May 2009 21:11:27 -0400 Subject: ACPI, i915: build fix (v2) drivers/built-in.o: In function `intel_opregion_init': (.text+0x9d540): undefined reference to `acpi_video_register' v2: move under DRM_I915 from DRM_I915_KMS Signed-off-by: Len Brown Signed-off-by: Randy Dunlap --- drivers/gpu/drm/Kconfig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 4cd35d8fd799..f5d46e7199d4 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -67,12 +67,18 @@ config DRM_I830 will load the correct one. config DRM_I915 + tristate "i915 driver" select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB select FRAMEBUFFER_CONSOLE if !EMBEDDED - tristate "i915 driver" + # i915 depends on ACPI_VIDEO when ACPI is enabled + # but for select to work, need to select ACPI_VIDEO's dependencies, ick + select VIDEO_OUTPUT_CONTROL if ACPI + select BACKLIGHT_CLASS_DEVICE if ACPI + select INPUT if ACPI + select ACPI_VIDEO if ACPI help Choose this option if you have a system that has Intel 830M, 845G, 852GM, 855GM 865G or 915G integrated graphics. If M is selected, the @@ -84,12 +90,6 @@ config DRM_I915 config DRM_I915_KMS bool "Enable modesetting on intel by default" depends on DRM_I915 - # i915 KMS depends on ACPI_VIDEO when ACPI is enabled - # but for select to work, need to select ACPI_VIDEO's dependencies, ick - select VIDEO_OUTPUT_CONTROL if ACPI - select BACKLIGHT_CLASS_DEVICE if ACPI - select INPUT if ACPI - select ACPI_VIDEO if ACPI help Choose this option if you want kernel modesetting enabled by default, and you have a new enough userspace to support this. Running old -- cgit v1.2.3-55-g7522 From 465440d2720543669841db5b0691ba41892ed0ae Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Mon, 25 May 2009 20:57:21 +0000 Subject: mlx4_en: Fix a kernel panic when waking tx queue When the transmit queue gets full we enable interrupts for TX completions There was a race that we handled the TX queue both from the interrupt context and from the transmit function. Using "spin_trylock_irq()" ensures this doesn't happen. Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/mlx4/en_tx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index ac6fc499b280..e5c98a98ad37 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -426,7 +426,7 @@ void mlx4_en_poll_tx_cq(unsigned long data) INC_PERF_COUNTER(priv->pstats.tx_poll); - if (!spin_trylock(&ring->comp_lock)) { + if (!spin_trylock_irq(&ring->comp_lock)) { mod_timer(&cq->timer, jiffies + MLX4_EN_TX_POLL_TIMEOUT); return; } @@ -439,7 +439,7 @@ void mlx4_en_poll_tx_cq(unsigned long data) if (inflight && priv->port_up) mod_timer(&cq->timer, jiffies + MLX4_EN_TX_POLL_TIMEOUT); - spin_unlock(&ring->comp_lock); + spin_unlock_irq(&ring->comp_lock); } static struct mlx4_en_tx_desc *mlx4_en_bounce_to_desc(struct mlx4_en_priv *priv, @@ -482,9 +482,9 @@ static inline void mlx4_en_xmit_poll(struct mlx4_en_priv *priv, int tx_ind) /* Poll the CQ every mlx4_en_TX_MODER_POLL packets */ if ((++ring->poll_cnt & (MLX4_EN_TX_POLL_MODER - 1)) == 0) - if (spin_trylock(&ring->comp_lock)) { + if (spin_trylock_irq(&ring->comp_lock)) { mlx4_en_process_tx_cq(priv->dev, cq); - spin_unlock(&ring->comp_lock); + spin_unlock_irq(&ring->comp_lock); } } -- cgit v1.2.3-55-g7522 From bdb0e010bf0061a73027cc84dd7ad192c663eca3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 29 May 2009 22:04:54 -0700 Subject: ath1e: add new device id for asus hardware Gary Lin reports that a new device id needs to be added to the atl1e in order to get some new Asus hardware to work properly. Signed-off-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- drivers/net/atl1e/atl1e_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c index fb57b750866b..1342418fb209 100644 --- a/drivers/net/atl1e/atl1e_main.c +++ b/drivers/net/atl1e/atl1e_main.c @@ -37,6 +37,7 @@ char atl1e_driver_version[] = DRV_VERSION; */ static struct pci_device_id atl1e_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1E)}, + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, 0x1066)}, /* required last entry */ { 0 } }; -- cgit v1.2.3-55-g7522 From c339dfdd65b52bfd947ab29d1210314a2f6d622d Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 30 May 2009 20:06:54 +0200 Subject: ide_pci_generic: add quirk for Netcell ATA RAID We need to explicitly mark words 85-87 as valid ones since firmware doesn't do it. This should fix support for LBA48 and FLUSH CACHE [EXT] command which stopped working after we applied more strict checking of identify words in: commit 942dcd85bf8edf38cdc3745306ca250684d99a61 ("ide: idedisk_supports_lba48() -> ata_id_lba48_enabled()") and commit 4b58f17d7c45a8e5f4acda641bec388398b9c0fa ("ide: ide_id_has_flush_cache() -> ata_id_flush_enabled()") Reported-and-tested-by: "Trevor Hemsley" Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-pci-generic.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/ide/ide-pci-generic.c b/drivers/ide/ide-pci-generic.c index 61111fd27130..39d4e01f5c9c 100644 --- a/drivers/ide/ide-pci-generic.c +++ b/drivers/ide/ide-pci-generic.c @@ -33,6 +33,16 @@ static int ide_generic_all; /* Set to claim all devices */ module_param_named(all_generic_ide, ide_generic_all, bool, 0444); MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE storage controllers."); +static void netcell_quirkproc(ide_drive_t *drive) +{ + /* mark words 85-87 as valid */ + drive->id[ATA_ID_CSF_DEFAULT] |= 0x4000; +} + +static const struct ide_port_ops netcell_port_ops = { + .quirkproc = netcell_quirkproc, +}; + #define DECLARE_GENERIC_PCI_DEV(extra_flags) \ { \ .name = DRV_NAME, \ @@ -74,6 +84,7 @@ static const struct ide_port_info generic_chipsets[] __devinitdata = { { /* 6: Revolution */ .name = DRV_NAME, + .port_ops = &netcell_port_ops, .host_flags = IDE_HFLAG_CLEAR_SIMPLEX | IDE_HFLAG_TRUST_BIOS_FOR_DMA | IDE_HFLAG_OFF_BOARD, -- cgit v1.2.3-55-g7522 From cf9f6e21c155d5add733b969c695837ead79eeab Mon Sep 17 00:00:00 2001 From: Maciej W. Rozycki Date: Mon, 1 Jun 2009 03:12:04 -0700 Subject: 3c509: Add missing EISA IDs Several EISA device IDs for 3c509 family network cards are missing from the driver, making the cards unusable in their EISA mode. Here's a fix to add them based on the EISA configuration files distributed by 3Com and our eisa.ids database. Signed-off-by: Maciej W. Rozycki Signed-off-by: David S. Miller --- drivers/net/3c509.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index fbb371921991..682aad897081 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -480,9 +480,13 @@ static int pnp_registered; #ifdef CONFIG_EISA static struct eisa_device_id el3_eisa_ids[] = { + { "TCM5090" }, + { "TCM5091" }, { "TCM5092" }, { "TCM5093" }, + { "TCM5094" }, { "TCM5095" }, + { "TCM5098" }, { "" } }; MODULE_DEVICE_TABLE(eisa, el3_eisa_ids); -- cgit v1.2.3-55-g7522 From 39d8bbedb9571a89d638f5b05358f26ab503d7a6 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 1 Jun 2009 13:46:49 +0200 Subject: hwmon: (lm78) Add missing __devexit_p() The remove function uses __devexit, so the .remove assignment needs __devexit_p() to fix a build error with hotplug disabled. Signed-off-by: Mike Frysinger Signed-off-by: Jean Delvare --- drivers/hwmon/lm78.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index b5e3b2851698..a1787fdf5b9f 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -182,7 +182,7 @@ static struct platform_driver lm78_isa_driver = { .name = "lm78", }, .probe = lm78_isa_probe, - .remove = lm78_isa_remove, + .remove = __devexit_p(lm78_isa_remove), }; -- cgit v1.2.3-55-g7522 From 5a9a8e32ebe269c71d8d3e78f9435fe7729f38e9 Mon Sep 17 00:00:00 2001 From: Ed Swierk Date: Tue, 2 Jun 2009 00:19:52 -0700 Subject: forcedeth: add phy_power_down parameter, leave phy powered up by default (v2) Add a phy_power_down parameter to forcedeth: set to 1 to power down the phy and disable the link when an interface goes down; set to 0 to always leave the phy powered up. The phy power state persists across reboots; Windows, some BIOSes, and older versions of Linux don't bother to power up the phy again, forcing users to remove all power to get the interface working (see http://bugzilla.kernel.org/show_bug.cgi?id=13072). Leaving the phy powered on is the safest default behavior. Users accustomed to seeing the link state reflect the interface state and/or wanting to minimize power consumption can set phy_power_down=1 if compatibility with other OSes is not an issue. Signed-off-by: Ed Swierk Signed-off-by: David S. Miller --- drivers/net/forcedeth.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index f9a846b1b92f..9f6a68fb7b45 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -897,6 +897,12 @@ enum { }; static int phy_cross = NV_CROSSOVER_DETECTION_DISABLED; +/* + * Power down phy when interface is down (persists through reboot; + * older Linux and other OSes may not power it up again) + */ +static int phy_power_down = 0; + static inline struct fe_priv *get_nvpriv(struct net_device *dev) { return netdev_priv(dev); @@ -1485,7 +1491,10 @@ static int phy_init(struct net_device *dev) /* restart auto negotiation, power down phy */ mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ); - mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE | BMCR_PDOWN); + mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE); + if (phy_power_down) { + mii_control |= BMCR_PDOWN; + } if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) { return PHY_ERROR; } @@ -5513,7 +5522,7 @@ static int nv_close(struct net_device *dev) nv_drain_rxtx(dev); - if (np->wolenabled) { + if (np->wolenabled || !phy_power_down) { writel(NVREG_PFF_ALWAYS|NVREG_PFF_MYADDR, base + NvRegPacketFilterFlags); nv_start_rx(dev); } else { @@ -6367,6 +6376,8 @@ module_param(dma_64bit, int, 0); MODULE_PARM_DESC(dma_64bit, "High DMA is enabled by setting to 1 and disabled by setting to 0."); module_param(phy_cross, int, 0); MODULE_PARM_DESC(phy_cross, "Phy crossover detection for Realtek 8201 phy is enabled by setting to 1 and disabled by setting to 0."); +module_param(phy_power_down, int, 0); +MODULE_PARM_DESC(phy_power_down, "Power down phy and disable link when interface is down (1), or leave phy powered up (0)."); MODULE_AUTHOR("Manfred Spraul "); MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); -- cgit v1.2.3-55-g7522 From ea30e11970a96cfe5e32c03a29332554573b4a10 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 2 Jun 2009 01:29:58 -0700 Subject: e1000: add missing length check to e1000 receive routine Patch to fix bad length checking in e1000. E1000 by default does two things: 1) Spans rx descriptors for packets that don't fit into 1 skb on recieve 2) Strips the crc from a frame by subtracting 4 bytes from the length prior to doing an skb_put Since the e1000 driver isn't written to support receiving packets that span multiple rx buffers, it checks the End of Packet bit of every frame, and discards it if its not set. This places us in a situation where, if we have a spanning packet, the first part is discarded, but the second part is not (since it is the end of packet, and it passes the EOP bit test). If the second part of the frame is small (4 bytes or less), we subtract 4 from it to remove its crc, underflow the length, and wind up in skb_over_panic, when we try to skb_put a huge number of bytes into the skb. This amounts to a remote DOS attack through careful selection of frame size in relation to interface MTU. The fix for this is already in the e1000e driver, as well as the e1000 sourceforge driver, but no one ever pushed it to e1000. This is lifted straight from e1000e, and prevents small frames from causing the underflow described above Signed-off-by: Neil Horman Tested-by: Andy Gospodarek Signed-off-by: David S. Miller --- drivers/net/e1000/e1000_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index b1419e21b46b..fffb006b7d95 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -4027,8 +4027,9 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, PCI_DMA_FROMDEVICE); length = le16_to_cpu(rx_desc->length); - - if (unlikely(!(status & E1000_RXD_STAT_EOP))) { + /* !EOP means multiple descriptors were used to store a single + * packet, also make sure the frame isn't just CRC only */ + if (unlikely(!(status & E1000_RXD_STAT_EOP) || (length <= 4))) { /* All receives must fit into a single buffer */ E1000_DBG("%s: Receive packet consumed multiple" " buffers\n", netdev->name); -- cgit v1.2.3-55-g7522 From d3ae33efb8e2f277f9007eb060c9d0b91ab38ae1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 2 Jun 2009 12:34:31 +0100 Subject: pata_netcell: LBA48 force identify bits correct This matches Bartlomiej's patch for ide_pci_generic: c339dfdd65b52bfd947ab29d1210314a2f6d622d In the libata case netcell has its own mini driver. I suspect this fix is actually only needed for some firmware revs but it does no harm either way. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/ata/pata_netcell.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c index bdb236957cb9..9a698097134b 100644 --- a/drivers/ata/pata_netcell.c +++ b/drivers/ata/pata_netcell.c @@ -20,13 +20,24 @@ /* No PIO or DMA methods needed for this device */ +static unsigned int netcell_read_id(struct ata_device *adev, + struct ata_taskfile *tf, u16 *id) +{ + unsigned int err_mask = ata_do_dev_read_id(adev, tf, id); + /* Firmware forgets to mark words 85-87 valid */ + if (err_mask == 0) + id[ATA_ID_CSF_DEFAULT] |= 0x0400; + return err_mask; +} + static struct scsi_host_template netcell_sht = { ATA_BMDMA_SHT(DRV_NAME), }; static struct ata_port_operations netcell_ops = { .inherits = &ata_bmdma_port_ops, - .cable_detect = ata_cable_80wire, + .cable_detect = ata_cable_80wire, + .read_id = netcell_read_id, }; -- cgit v1.2.3-55-g7522 From 05ad709d04799125ed85dd816fdb558258102172 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 2 Jun 2009 16:58:10 +0100 Subject: parport: quickfix the proc registration bug Ideally we should have a directory of drivers and a link to the 'active' driver. For now just show the first device which is effectively the existing semantics without a warning. This is an update on the original buggy patch that I then forgot to resubmit. Confusingly it was proposed by Red Hat, written by Etched Pixels fixed and submitted by Intel ... Resolves-Bug: http://bugzilla.kernel.org/show_bug.cgi?id=9749 Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/parport/share.c | 13 ++++++++++--- include/linux/parport.h | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 0ebca450ed29..dffa5d4fb298 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -614,7 +614,10 @@ parport_register_device(struct parport *port, const char *name, * pardevice fields. -arca */ port->ops->init_state(tmp, tmp->state); - parport_device_proc_register(tmp); + if (!test_and_set_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags)) { + port->proc_device = tmp; + parport_device_proc_register(tmp); + } return tmp; out_free_all: @@ -646,10 +649,14 @@ void parport_unregister_device(struct pardevice *dev) } #endif - parport_device_proc_unregister(dev); - port = dev->port->physport; + if (port->proc_device == dev) { + port->proc_device = NULL; + clear_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags); + parport_device_proc_unregister(dev); + } + if (port->cad == dev) { printk(KERN_DEBUG "%s: %s forgot to release port\n", port->name, dev->name); diff --git a/include/linux/parport.h b/include/linux/parport.h index e1f83c5065c5..38a423ed3c01 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -324,6 +324,10 @@ struct parport { int spintime; atomic_t ref_count; + unsigned long devflags; +#define PARPORT_DEVPROC_REGISTERED 0 + struct pardevice *proc_device; /* Currently register proc device */ + struct list_head full_list; struct parport *slaves[3]; }; -- cgit v1.2.3-55-g7522 From 6462c6160af557c310d5941f4700ea2c7f6c67b2 Mon Sep 17 00:00:00 2001 From: Thomas Reitmayr Date: Mon, 1 Jun 2009 13:38:33 +0200 Subject: [ARM] orion5x: Change names of defines for Reset-Out-Mask register The name of the define for the Reset-Out-Mask register as well as its bit for the watchdog reset are changed to match the names used for Kirkwood (which in turn match the processor specification more closely). There is no functional change. This patch prepares for adding orion5x_wdt as a platform device to Kirkwood. Signed-off-by: Thomas Reitmayr Signed-off-by: Nicolas Pitre --- arch/arm/mach-orion5x/include/mach/bridge-regs.h | 4 ++-- arch/arm/mach-orion5x/include/mach/system.h | 2 +- arch/arm/mach-orion5x/mss2-setup.c | 4 ++-- drivers/watchdog/orion5x_wdt.c | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-orion5x/include/mach/bridge-regs.h b/arch/arm/mach-orion5x/include/mach/bridge-regs.h index be896e59d3e7..5c9744cd8ef6 100644 --- a/arch/arm/mach-orion5x/include/mach/bridge-regs.h +++ b/arch/arm/mach-orion5x/include/mach/bridge-regs.h @@ -17,8 +17,8 @@ #define CPU_CTRL (ORION5X_BRIDGE_VIRT_BASE | 0x104) -#define CPU_RESET_MASK (ORION5X_BRIDGE_VIRT_BASE | 0x108) -#define WDT_RESET 0x0002 +#define RSTOUTn_MASK (ORION5X_BRIDGE_VIRT_BASE | 0x108) +#define WDT_RESET_OUT_EN 0x0002 #define CPU_SOFT_RESET (ORION5X_BRIDGE_VIRT_BASE | 0x10c) diff --git a/arch/arm/mach-orion5x/include/mach/system.h b/arch/arm/mach-orion5x/include/mach/system.h index e912490fff23..60e734c10458 100644 --- a/arch/arm/mach-orion5x/include/mach/system.h +++ b/arch/arm/mach-orion5x/include/mach/system.h @@ -23,7 +23,7 @@ static inline void arch_reset(char mode, const char *cmd) /* * Enable and issue soft reset */ - orion5x_setbits(CPU_RESET_MASK, (1 << 2)); + orion5x_setbits(RSTOUTn_MASK, (1 << 2)); orion5x_setbits(CPU_SOFT_RESET, 1); } diff --git a/arch/arm/mach-orion5x/mss2-setup.c b/arch/arm/mach-orion5x/mss2-setup.c index 41e6d5033d54..61c086b66723 100644 --- a/arch/arm/mach-orion5x/mss2-setup.c +++ b/arch/arm/mach-orion5x/mss2-setup.c @@ -181,9 +181,9 @@ static void mss2_power_off(void) /* * Enable and issue soft reset */ - reg = readl(CPU_RESET_MASK); + reg = readl(RSTOUTn_MASK); reg |= 1 << 2; - writel(reg, CPU_RESET_MASK); + writel(reg, RSTOUTn_MASK); reg = readl(CPU_SOFT_RESET); reg |= 1; diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c index 2cde568e4fb0..d2dc9762a8c9 100644 --- a/drivers/watchdog/orion5x_wdt.c +++ b/drivers/watchdog/orion5x_wdt.c @@ -73,9 +73,9 @@ static void orion5x_wdt_enable(void) writel(reg, TIMER_CTRL); /* Enable reset on watchdog */ - reg = readl(CPU_RESET_MASK); - reg |= WDT_RESET; - writel(reg, CPU_RESET_MASK); + reg = readl(RSTOUTn_MASK); + reg |= WDT_RESET_OUT_EN; + writel(reg, RSTOUTn_MASK); spin_unlock(&wdt_lock); } @@ -87,9 +87,9 @@ static void orion5x_wdt_disable(void) spin_lock(&wdt_lock); /* Disable reset on watchdog */ - reg = readl(CPU_RESET_MASK); - reg &= ~WDT_RESET; - writel(reg, CPU_RESET_MASK); + reg = readl(RSTOUTn_MASK); + reg &= ~WDT_RESET_OUT_EN; + writel(reg, RSTOUTn_MASK); /* Disable watchdog timer */ reg = readl(TIMER_CTRL); -- cgit v1.2.3-55-g7522 From 054bd3f053de54c81b20df11f354476389826e61 Mon Sep 17 00:00:00 2001 From: Thomas Reitmayr Date: Mon, 1 Jun 2009 13:38:34 +0200 Subject: [ARM] Kirkwood: Add the watchdog timer as a platform device. The Kirkwood architecture uses the same watchdog device as the Orion architecture. This patch adds orion5x_wdt as a platform device for Kirkwood. Signed-off-by: Thomas Reitmayr Tested-by: Martin Michlmayr Signed-off-by: Nicolas Pitre --- arch/arm/mach-kirkwood/common.c | 25 +++++++++++++++++++++++ arch/arm/mach-kirkwood/include/mach/bridge-regs.h | 3 +++ drivers/watchdog/Kconfig | 4 ++-- 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index d127731f47dc..a053184bdee5 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "common.h" @@ -767,6 +768,29 @@ static void __init kirkwood_xor1_init(void) } +/***************************************************************************** + * Watchdog + ****************************************************************************/ +static struct orion5x_wdt_platform_data kirkwood_wdt_data = { + .tclk = 0, +}; + +static struct platform_device kirkwood_wdt_device = { + .name = "orion5x_wdt", + .id = -1, + .dev = { + .platform_data = &kirkwood_wdt_data, + }, + .num_resources = 0, +}; + +static void __init kirkwood_wdt_init(void) +{ + kirkwood_wdt_data.tclk = kirkwood_tclk; + platform_device_register(&kirkwood_wdt_device); +} + + /***************************************************************************** * Time handling ****************************************************************************/ @@ -859,6 +883,7 @@ void __init kirkwood_init(void) /* internal devices that every board has */ kirkwood_rtc_init(); + kirkwood_wdt_init(); kirkwood_xor0_init(); kirkwood_xor1_init(); } diff --git a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h index 00d96abb718c..9e80d9232c83 100644 --- a/arch/arm/mach-kirkwood/include/mach/bridge-regs.h +++ b/arch/arm/mach-kirkwood/include/mach/bridge-regs.h @@ -17,12 +17,15 @@ #define CPU_RESET 0x00000002 #define RSTOUTn_MASK (BRIDGE_VIRT_BASE | 0x0108) +#define WDT_RESET_OUT_EN 0x00000002 #define SOFT_RESET_OUT_EN 0x00000004 #define SYSTEM_SOFT_RESET (BRIDGE_VIRT_BASE | 0x010c) #define SOFT_RESET 0x00000001 #define BRIDGE_CAUSE (BRIDGE_VIRT_BASE | 0x0110) +#define WDT_INT_REQ 0x0008 + #define BRIDGE_MASK (BRIDGE_VIRT_BASE | 0x0114) #define BRIDGE_INT_TIMER0 0x0002 #define BRIDGE_INT_TIMER1 0x0004 diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5eb8f21da82e..1e983b1717d3 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -233,10 +233,10 @@ config DAVINCI_WATCHDOG config ORION5X_WATCHDOG tristate "Orion5x watchdog" - depends on ARCH_ORION5X + depends on ARCH_ORION5X || ARCH_KIRKWOOD help Say Y here if to include support for the watchdog timer - in the Orion5x ARM SoCs. + in the Orion5x and Kirkwood ARM SoCs. To compile this driver as a module, choose M here: the module will be called orion5x_wdt. -- cgit v1.2.3-55-g7522 From 3b937a7dbddbedd9457b33fcc8fa369c0c229c6e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 1 Jun 2009 13:56:02 -0400 Subject: [ARM] Orion/Kirkwood: rename orion5x_wdt to orion_wdt The Orion watchdog driver is also used on Kirkwood. Convention is to use orion5x for stuff specific to 88F5xxx Orion chips and simply "orion" for shared stuff across SoCs including Kirkwood. Signed-off-by: Nicolas Pitre --- arch/arm/mach-kirkwood/common.c | 6 +- arch/arm/mach-orion5x/common.c | 6 +- arch/arm/plat-orion/include/plat/orion5x_wdt.h | 18 -- arch/arm/plat-orion/include/plat/orion_wdt.h | 18 ++ drivers/watchdog/Kconfig | 8 +- drivers/watchdog/Makefile | 2 +- drivers/watchdog/orion5x_wdt.c | 322 ------------------------- drivers/watchdog/orion_wdt.c | 322 +++++++++++++++++++++++++ 8 files changed, 351 insertions(+), 351 deletions(-) delete mode 100644 arch/arm/plat-orion/include/plat/orion5x_wdt.h create mode 100644 arch/arm/plat-orion/include/plat/orion_wdt.h delete mode 100644 drivers/watchdog/orion5x_wdt.c create mode 100644 drivers/watchdog/orion_wdt.c (limited to 'drivers') diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index a053184bdee5..3da32b1834dc 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "common.h" @@ -771,12 +771,12 @@ static void __init kirkwood_xor1_init(void) /***************************************************************************** * Watchdog ****************************************************************************/ -static struct orion5x_wdt_platform_data kirkwood_wdt_data = { +static struct orion_wdt_platform_data kirkwood_wdt_data = { .tclk = 0, }; static struct platform_device kirkwood_wdt_device = { - .name = "orion5x_wdt", + .name = "orion_wdt", .id = -1, .dev = { .platform_data = &kirkwood_wdt_data, diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index b1c7778d9f96..c3e2bea2d054 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include "common.h" @@ -540,12 +540,12 @@ void __init orion5x_xor_init(void) /***************************************************************************** * Watchdog ****************************************************************************/ -static struct orion5x_wdt_platform_data orion5x_wdt_data = { +static struct orion_wdt_platform_data orion5x_wdt_data = { .tclk = 0, }; static struct platform_device orion5x_wdt_device = { - .name = "orion5x_wdt", + .name = "orion_wdt", .id = -1, .dev = { .platform_data = &orion5x_wdt_data, diff --git a/arch/arm/plat-orion/include/plat/orion5x_wdt.h b/arch/arm/plat-orion/include/plat/orion5x_wdt.h deleted file mode 100644 index 3c9cf6a305ef..000000000000 --- a/arch/arm/plat-orion/include/plat/orion5x_wdt.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * arch/arm/plat-orion/include/plat/orion5x_wdt.h - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __PLAT_ORION5X_WDT_H -#define __PLAT_ORION5X_WDT_H - -struct orion5x_wdt_platform_data { - u32 tclk; /* no support yet */ -}; - - -#endif - diff --git a/arch/arm/plat-orion/include/plat/orion_wdt.h b/arch/arm/plat-orion/include/plat/orion_wdt.h new file mode 100644 index 000000000000..665c362a2fba --- /dev/null +++ b/arch/arm/plat-orion/include/plat/orion_wdt.h @@ -0,0 +1,18 @@ +/* + * arch/arm/plat-orion/include/plat/orion_wdt.h + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __PLAT_ORION_WDT_H +#define __PLAT_ORION_WDT_H + +struct orion_wdt_platform_data { + u32 tclk; /* no support yet */ +}; + + +#endif + diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 1e983b1717d3..5744cac4864b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -231,14 +231,14 @@ config DAVINCI_WATCHDOG NOTE: once enabled, this timer cannot be disabled. Say N if you are unsure. -config ORION5X_WATCHDOG - tristate "Orion5x watchdog" +config ORION_WATCHDOG + tristate "Orion watchdog" depends on ARCH_ORION5X || ARCH_KIRKWOOD help Say Y here if to include support for the watchdog timer - in the Orion5x and Kirkwood ARM SoCs. + in the Marvell Orion5x and Kirkwood ARM SoCs. To compile this driver as a module, choose M here: the - module will be called orion5x_wdt. + module will be called orion_wdt. # AVR32 Architecture diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7f8c56b14f58..c3afa14d5be1 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o -obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o +obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c deleted file mode 100644 index d2dc9762a8c9..000000000000 --- a/drivers/watchdog/orion5x_wdt.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * drivers/watchdog/orion5x_wdt.c - * - * Watchdog driver for Orion5x processors - * - * Author: Sylver Bruneau - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Watchdog timer block registers. - */ -#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000) -#define WDT_EN 0x0010 -#define WDT_VAL (TIMER_VIRT_BASE + 0x0024) - -#define WDT_MAX_CYCLE_COUNT 0xffffffff -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 - -static int nowayout = WATCHDOG_NOWAYOUT; -static int heartbeat = -1; /* module parameter (seconds) */ -static unsigned int wdt_max_duration; /* (seconds) */ -static unsigned int wdt_tclk; -static unsigned long wdt_status; -static spinlock_t wdt_lock; - -static void orion5x_wdt_ping(void) -{ - spin_lock(&wdt_lock); - - /* Reload watchdog duration */ - writel(wdt_tclk * heartbeat, WDT_VAL); - - spin_unlock(&wdt_lock); -} - -static void orion5x_wdt_enable(void) -{ - u32 reg; - - spin_lock(&wdt_lock); - - /* Set watchdog duration */ - writel(wdt_tclk * heartbeat, WDT_VAL); - - /* Clear watchdog timer interrupt */ - reg = readl(BRIDGE_CAUSE); - reg &= ~WDT_INT_REQ; - writel(reg, BRIDGE_CAUSE); - - /* Enable watchdog timer */ - reg = readl(TIMER_CTRL); - reg |= WDT_EN; - writel(reg, TIMER_CTRL); - - /* Enable reset on watchdog */ - reg = readl(RSTOUTn_MASK); - reg |= WDT_RESET_OUT_EN; - writel(reg, RSTOUTn_MASK); - - spin_unlock(&wdt_lock); -} - -static void orion5x_wdt_disable(void) -{ - u32 reg; - - spin_lock(&wdt_lock); - - /* Disable reset on watchdog */ - reg = readl(RSTOUTn_MASK); - reg &= ~WDT_RESET_OUT_EN; - writel(reg, RSTOUTn_MASK); - - /* Disable watchdog timer */ - reg = readl(TIMER_CTRL); - reg &= ~WDT_EN; - writel(reg, TIMER_CTRL); - - spin_unlock(&wdt_lock); -} - -static int orion5x_wdt_get_timeleft(int *time_left) -{ - spin_lock(&wdt_lock); - *time_left = readl(WDT_VAL) / wdt_tclk; - spin_unlock(&wdt_lock); - return 0; -} - -static int orion5x_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - orion5x_wdt_enable(); - return nonseekable_open(inode, file); -} - -static ssize_t orion5x_wdt_write(struct file *file, const char *data, - size_t len, loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - orion5x_wdt_ping(); - } - return len; -} - -static int orion5x_wdt_settimeout(int new_time) -{ - if ((new_time <= 0) || (new_time > wdt_max_duration)) - return -EINVAL; - - /* Set new watchdog time to be used when - * orion5x_wdt_enable() or orion5x_wdt_ping() is called. */ - heartbeat = new_time; - return 0; -} - -static const struct watchdog_info ident = { - .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING, - .identity = "Orion5x Watchdog", -}; - -static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = -ENOTTY; - int time; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_KEEPALIVE: - orion5x_wdt_ping(); - ret = 0; - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(time, (int *)arg); - if (ret) - break; - - if (orion5x_wdt_settimeout(time)) { - ret = -EINVAL; - break; - } - orion5x_wdt_ping(); - /* Fall through */ - - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; - - case WDIOC_GETTIMELEFT: - if (orion5x_wdt_get_timeleft(&time)) { - ret = -EINVAL; - break; - } - ret = put_user(time, (int *)arg); - break; - } - return ret; -} - -static int orion5x_wdt_release(struct inode *inode, struct file *file) -{ - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - orion5x_wdt_disable(); - else - printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " - "timer will not stop\n"); - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - - -static const struct file_operations orion5x_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = orion5x_wdt_write, - .unlocked_ioctl = orion5x_wdt_ioctl, - .open = orion5x_wdt_open, - .release = orion5x_wdt_release, -}; - -static struct miscdevice orion5x_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &orion5x_wdt_fops, -}; - -static int __devinit orion5x_wdt_probe(struct platform_device *pdev) -{ - struct orion5x_wdt_platform_data *pdata = pdev->dev.platform_data; - int ret; - - if (pdata) { - wdt_tclk = pdata->tclk; - } else { - printk(KERN_ERR "Orion5x Watchdog misses platform data\n"); - return -ENODEV; - } - - if (orion5x_wdt_miscdev.parent) - return -EBUSY; - orion5x_wdt_miscdev.parent = &pdev->dev; - - wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; - if (orion5x_wdt_settimeout(heartbeat)) - heartbeat = wdt_max_duration; - - ret = misc_register(&orion5x_wdt_miscdev); - if (ret) - return ret; - - printk(KERN_INFO "Orion5x Watchdog Timer: Initial timeout %d sec%s\n", - heartbeat, nowayout ? ", nowayout" : ""); - return 0; -} - -static int __devexit orion5x_wdt_remove(struct platform_device *pdev) -{ - int ret; - - if (test_bit(WDT_IN_USE, &wdt_status)) { - orion5x_wdt_disable(); - clear_bit(WDT_IN_USE, &wdt_status); - } - - ret = misc_deregister(&orion5x_wdt_miscdev); - if (!ret) - orion5x_wdt_miscdev.parent = NULL; - - return ret; -} - -static void orion5x_wdt_shutdown(struct platform_device *pdev) -{ - if (test_bit(WDT_IN_USE, &wdt_status)) - orion5x_wdt_disable(); -} - -static struct platform_driver orion5x_wdt_driver = { - .probe = orion5x_wdt_probe, - .remove = __devexit_p(orion5x_wdt_remove), - .shutdown = orion5x_wdt_shutdown, - .driver = { - .owner = THIS_MODULE, - .name = "orion5x_wdt", - }, -}; - -static int __init orion5x_wdt_init(void) -{ - spin_lock_init(&wdt_lock); - return platform_driver_register(&orion5x_wdt_driver); -} - -static void __exit orion5x_wdt_exit(void) -{ - platform_driver_unregister(&orion5x_wdt_driver); -} - -module_init(orion5x_wdt_init); -module_exit(orion5x_wdt_exit); - -MODULE_AUTHOR("Sylver Bruneau "); -MODULE_DESCRIPTION("Orion5x Processor Watchdog"); - -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); - -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c new file mode 100644 index 000000000000..2d9fb96a9ee9 --- /dev/null +++ b/drivers/watchdog/orion_wdt.c @@ -0,0 +1,322 @@ +/* + * drivers/watchdog/orion_wdt.c + * + * Watchdog driver for Orion/Kirkwood processors + * + * Author: Sylver Bruneau + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Watchdog timer block registers. + */ +#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000) +#define WDT_EN 0x0010 +#define WDT_VAL (TIMER_VIRT_BASE + 0x0024) + +#define WDT_MAX_CYCLE_COUNT 0xffffffff +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = -1; /* module parameter (seconds) */ +static unsigned int wdt_max_duration; /* (seconds) */ +static unsigned int wdt_tclk; +static unsigned long wdt_status; +static spinlock_t wdt_lock; + +static void orion_wdt_ping(void) +{ + spin_lock(&wdt_lock); + + /* Reload watchdog duration */ + writel(wdt_tclk * heartbeat, WDT_VAL); + + spin_unlock(&wdt_lock); +} + +static void orion_wdt_enable(void) +{ + u32 reg; + + spin_lock(&wdt_lock); + + /* Set watchdog duration */ + writel(wdt_tclk * heartbeat, WDT_VAL); + + /* Clear watchdog timer interrupt */ + reg = readl(BRIDGE_CAUSE); + reg &= ~WDT_INT_REQ; + writel(reg, BRIDGE_CAUSE); + + /* Enable watchdog timer */ + reg = readl(TIMER_CTRL); + reg |= WDT_EN; + writel(reg, TIMER_CTRL); + + /* Enable reset on watchdog */ + reg = readl(RSTOUTn_MASK); + reg |= WDT_RESET_OUT_EN; + writel(reg, RSTOUTn_MASK); + + spin_unlock(&wdt_lock); +} + +static void orion_wdt_disable(void) +{ + u32 reg; + + spin_lock(&wdt_lock); + + /* Disable reset on watchdog */ + reg = readl(RSTOUTn_MASK); + reg &= ~WDT_RESET_OUT_EN; + writel(reg, RSTOUTn_MASK); + + /* Disable watchdog timer */ + reg = readl(TIMER_CTRL); + reg &= ~WDT_EN; + writel(reg, TIMER_CTRL); + + spin_unlock(&wdt_lock); +} + +static int orion_wdt_get_timeleft(int *time_left) +{ + spin_lock(&wdt_lock); + *time_left = readl(WDT_VAL) / wdt_tclk; + spin_unlock(&wdt_lock); + return 0; +} + +static int orion_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + orion_wdt_enable(); + return nonseekable_open(inode, file); +} + +static ssize_t orion_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + orion_wdt_ping(); + } + return len; +} + +static int orion_wdt_settimeout(int new_time) +{ + if ((new_time <= 0) || (new_time > wdt_max_duration)) + return -EINVAL; + + /* Set new watchdog time to be used when + * orion_wdt_enable() or orion_wdt_ping() is called. */ + heartbeat = new_time; + return 0; +} + +static const struct watchdog_info ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "Orion Watchdog", +}; + +static long orion_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + orion_wdt_ping(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (orion_wdt_settimeout(time)) { + ret = -EINVAL; + break; + } + orion_wdt_ping(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_GETTIMELEFT: + if (orion_wdt_get_timeleft(&time)) { + ret = -EINVAL; + break; + } + ret = put_user(time, (int *)arg); + break; + } + return ret; +} + +static int orion_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + orion_wdt_disable(); + else + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "timer will not stop\n"); + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + + +static const struct file_operations orion_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = orion_wdt_write, + .unlocked_ioctl = orion_wdt_ioctl, + .open = orion_wdt_open, + .release = orion_wdt_release, +}; + +static struct miscdevice orion_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &orion_wdt_fops, +}; + +static int __devinit orion_wdt_probe(struct platform_device *pdev) +{ + struct orion_wdt_platform_data *pdata = pdev->dev.platform_data; + int ret; + + if (pdata) { + wdt_tclk = pdata->tclk; + } else { + printk(KERN_ERR "Orion Watchdog misses platform data\n"); + return -ENODEV; + } + + if (orion_wdt_miscdev.parent) + return -EBUSY; + orion_wdt_miscdev.parent = &pdev->dev; + + wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk; + if (orion_wdt_settimeout(heartbeat)) + heartbeat = wdt_max_duration; + + ret = misc_register(&orion_wdt_miscdev); + if (ret) + return ret; + + printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s\n", + heartbeat, nowayout ? ", nowayout" : ""); + return 0; +} + +static int __devexit orion_wdt_remove(struct platform_device *pdev) +{ + int ret; + + if (test_bit(WDT_IN_USE, &wdt_status)) { + orion_wdt_disable(); + clear_bit(WDT_IN_USE, &wdt_status); + } + + ret = misc_deregister(&orion_wdt_miscdev); + if (!ret) + orion_wdt_miscdev.parent = NULL; + + return ret; +} + +static void orion_wdt_shutdown(struct platform_device *pdev) +{ + if (test_bit(WDT_IN_USE, &wdt_status)) + orion_wdt_disable(); +} + +static struct platform_driver orion_wdt_driver = { + .probe = orion_wdt_probe, + .remove = __devexit_p(orion_wdt_remove), + .shutdown = orion_wdt_shutdown, + .driver = { + .owner = THIS_MODULE, + .name = "orion_wdt", + }, +}; + +static int __init orion_wdt_init(void) +{ + spin_lock_init(&wdt_lock); + return platform_driver_register(&orion_wdt_driver); +} + +static void __exit orion_wdt_exit(void) +{ + platform_driver_unregister(&orion_wdt_driver); +} + +module_init(orion_wdt_init); +module_exit(orion_wdt_exit); + +MODULE_AUTHOR("Sylver Bruneau "); +MODULE_DESCRIPTION("Orion Processor Watchdog"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -- cgit v1.2.3-55-g7522