mirror of
https://github.com/torvalds/linux.git
synced 2024-12-22 10:56:40 +00:00
a8b50a0a96
Add new lock to be used when accessing some registers. Also move the register lock and iwl_grab_nic_access inside the function for register access. This will prevent from forgetting to hold locks and nic access in the right way and make code easier to maintain. We over use the priv->lock spin lock and I guess we need to add new one for Tx queue after that we might need to change most of these lock to BH and just keep priv->lock as irq type. Signed-off-by: Mohamed Abbas <mohamed.abbas@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
476 lines
14 KiB
C
476 lines
14 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright(c) 2003 - 2009 Intel Corporation. All rights reserved.
|
|
*
|
|
* Portions of this file are derived from the ipw3945 project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
*
|
|
* The full GNU General Public License is included in this distribution in the
|
|
* file called LICENSE.
|
|
*
|
|
* Contact Information:
|
|
* Intel Linux Wireless <ilw@linux.intel.com>
|
|
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef __iwl_io_h__
|
|
#define __iwl_io_h__
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include "iwl-debug.h"
|
|
|
|
/*
|
|
* IO, register, and NIC memory access functions
|
|
*
|
|
* NOTE on naming convention and macro usage for these
|
|
*
|
|
* A single _ prefix before a an access function means that no state
|
|
* check or debug information is printed when that function is called.
|
|
*
|
|
* A double __ prefix before an access function means that state is checked
|
|
* and the current line number and caller function name are printed in addition
|
|
* to any other debug output.
|
|
*
|
|
* The non-prefixed name is the #define that maps the caller into a
|
|
* #define that provides the caller's name and __LINE__ to the double
|
|
* prefix version.
|
|
*
|
|
* If you wish to call the function without any debug or state checking,
|
|
* you should use the single _ prefix version (as is used by dependent IO
|
|
* routines, for example _iwl_read_direct32 calls the non-check version of
|
|
* _iwl_read32.)
|
|
*
|
|
* These declarations are *extremely* useful in quickly isolating code deltas
|
|
* which result in misconfiguration of the hardware I/O. In combination with
|
|
* git-bisect and the IO debug level you can quickly determine the specific
|
|
* commit which breaks the IO sequence to the hardware.
|
|
*
|
|
*/
|
|
|
|
#define _iwl_write32(priv, ofs, val) iowrite32((val), (priv)->hw_base + (ofs))
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline void __iwl_write32(const char *f, u32 l, struct iwl_priv *priv,
|
|
u32 ofs, u32 val)
|
|
{
|
|
IWL_DEBUG_IO(priv, "write32(0x%08X, 0x%08X) - %s %d\n", ofs, val, f, l);
|
|
_iwl_write32(priv, ofs, val);
|
|
}
|
|
#define iwl_write32(priv, ofs, val) \
|
|
__iwl_write32(__FILE__, __LINE__, priv, ofs, val)
|
|
#else
|
|
#define iwl_write32(priv, ofs, val) _iwl_write32(priv, ofs, val)
|
|
#endif
|
|
|
|
#define _iwl_read32(priv, ofs) ioread32((priv)->hw_base + (ofs))
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline u32 __iwl_read32(char *f, u32 l, struct iwl_priv *priv, u32 ofs)
|
|
{
|
|
IWL_DEBUG_IO(priv, "read_direct32(0x%08X) - %s %d\n", ofs, f, l);
|
|
return _iwl_read32(priv, ofs);
|
|
}
|
|
#define iwl_read32(priv, ofs) __iwl_read32(__FILE__, __LINE__, priv, ofs)
|
|
#else
|
|
#define iwl_read32(p, o) _iwl_read32(p, o)
|
|
#endif
|
|
|
|
#define IWL_POLL_INTERVAL 10 /* microseconds */
|
|
static inline int _iwl_poll_bit(struct iwl_priv *priv, u32 addr,
|
|
u32 bits, u32 mask, int timeout)
|
|
{
|
|
int t = 0;
|
|
|
|
do {
|
|
if ((_iwl_read32(priv, addr) & mask) == (bits & mask))
|
|
return t;
|
|
udelay(IWL_POLL_INTERVAL);
|
|
t += IWL_POLL_INTERVAL;
|
|
} while (t < timeout);
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline int __iwl_poll_bit(const char *f, u32 l,
|
|
struct iwl_priv *priv, u32 addr,
|
|
u32 bits, u32 mask, int timeout)
|
|
{
|
|
int ret = _iwl_poll_bit(priv, addr, bits, mask, timeout);
|
|
IWL_DEBUG_IO(priv, "poll_bit(0x%08X, 0x%08X, 0x%08X) - %s- %s %d\n",
|
|
addr, bits, mask,
|
|
unlikely(ret == -ETIMEDOUT) ? "timeout" : "", f, l);
|
|
return ret;
|
|
}
|
|
#define iwl_poll_bit(priv, addr, bits, mask, timeout) \
|
|
__iwl_poll_bit(__FILE__, __LINE__, priv, addr, bits, mask, timeout)
|
|
#else
|
|
#define iwl_poll_bit(p, a, b, m, t) _iwl_poll_bit(p, a, b, m, t)
|
|
#endif
|
|
|
|
static inline void _iwl_set_bit(struct iwl_priv *priv, u32 reg, u32 mask)
|
|
{
|
|
_iwl_write32(priv, reg, _iwl_read32(priv, reg) | mask);
|
|
}
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline void __iwl_set_bit(const char *f, u32 l,
|
|
struct iwl_priv *priv, u32 reg, u32 mask)
|
|
{
|
|
u32 val = _iwl_read32(priv, reg) | mask;
|
|
IWL_DEBUG_IO(priv, "set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
|
|
_iwl_write32(priv, reg, val);
|
|
}
|
|
static inline void iwl_set_bit(struct iwl_priv *p, u32 r, u32 m)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
|
__iwl_set_bit(__FILE__, __LINE__, p, r, m);
|
|
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
|
}
|
|
#else
|
|
static inline void iwl_set_bit(struct iwl_priv *p, u32 r, u32 m)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
|
_iwl_set_bit(p, r, m);
|
|
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
|
}
|
|
#endif
|
|
|
|
static inline void _iwl_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask)
|
|
{
|
|
_iwl_write32(priv, reg, _iwl_read32(priv, reg) & ~mask);
|
|
}
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline void __iwl_clear_bit(const char *f, u32 l,
|
|
struct iwl_priv *priv, u32 reg, u32 mask)
|
|
{
|
|
u32 val = _iwl_read32(priv, reg) & ~mask;
|
|
IWL_DEBUG_IO(priv, "clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
|
|
_iwl_write32(priv, reg, val);
|
|
}
|
|
static inline void iwl_clear_bit(struct iwl_priv *p, u32 r, u32 m)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
|
__iwl_clear_bit(__FILE__, __LINE__, p, r, m);
|
|
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
|
}
|
|
#else
|
|
static inline void iwl_clear_bit(struct iwl_priv *p, u32 r, u32 m)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
|
_iwl_clear_bit(p, r, m);
|
|
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
|
}
|
|
#endif
|
|
|
|
static inline int _iwl_grab_nic_access(struct iwl_priv *priv)
|
|
{
|
|
int ret;
|
|
u32 val;
|
|
|
|
/* this bit wakes up the NIC */
|
|
_iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
ret = _iwl_poll_bit(priv, CSR_GP_CNTRL,
|
|
CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
|
|
(CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
|
|
CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
|
|
if (ret < 0) {
|
|
val = _iwl_read32(priv, CSR_GP_CNTRL);
|
|
IWL_ERR(priv, "MAC is in deep sleep!. CSR_GP_CNTRL = 0x%08X\n", val);
|
|
_iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline int __iwl_grab_nic_access(const char *f, u32 l,
|
|
struct iwl_priv *priv)
|
|
{
|
|
IWL_DEBUG_IO(priv, "grabbing nic access - %s %d\n", f, l);
|
|
return _iwl_grab_nic_access(priv);
|
|
}
|
|
#define iwl_grab_nic_access(priv) \
|
|
__iwl_grab_nic_access(__FILE__, __LINE__, priv)
|
|
#else
|
|
#define iwl_grab_nic_access(priv) \
|
|
_iwl_grab_nic_access(priv)
|
|
#endif
|
|
|
|
static inline void _iwl_release_nic_access(struct iwl_priv *priv)
|
|
{
|
|
_iwl_clear_bit(priv, CSR_GP_CNTRL,
|
|
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
|
}
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline void __iwl_release_nic_access(const char *f, u32 l,
|
|
struct iwl_priv *priv)
|
|
{
|
|
|
|
IWL_DEBUG_IO(priv, "releasing nic access - %s %d\n", f, l);
|
|
_iwl_release_nic_access(priv);
|
|
}
|
|
#define iwl_release_nic_access(priv) \
|
|
__iwl_release_nic_access(__FILE__, __LINE__, priv)
|
|
#else
|
|
#define iwl_release_nic_access(priv) \
|
|
_iwl_release_nic_access(priv)
|
|
#endif
|
|
|
|
static inline u32 _iwl_read_direct32(struct iwl_priv *priv, u32 reg)
|
|
{
|
|
return _iwl_read32(priv, reg);
|
|
}
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline u32 __iwl_read_direct32(const char *f, u32 l,
|
|
struct iwl_priv *priv, u32 reg)
|
|
{
|
|
u32 value = _iwl_read_direct32(priv, reg);
|
|
IWL_DEBUG_IO(priv, "read_direct32(0x%4X) = 0x%08x - %s %d \n", reg, value,
|
|
f, l);
|
|
return value;
|
|
}
|
|
static inline u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg)
|
|
{
|
|
u32 value;
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
iwl_grab_nic_access(priv);
|
|
value = __iwl_read_direct32(__FILE__, __LINE__, priv, reg);
|
|
iwl_release_nic_access(priv);
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
return value;
|
|
}
|
|
|
|
#else
|
|
static inline u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg)
|
|
{
|
|
u32 value;
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
iwl_grab_nic_access(priv);
|
|
value = _iwl_read_direct32(priv, reg);
|
|
iwl_release_nic_access(priv);
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
return value;
|
|
|
|
}
|
|
#endif
|
|
|
|
static inline void _iwl_write_direct32(struct iwl_priv *priv,
|
|
u32 reg, u32 value)
|
|
{
|
|
_iwl_write32(priv, reg, value);
|
|
}
|
|
static inline void iwl_write_direct32(struct iwl_priv *priv, u32 reg, u32 value)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
if (!iwl_grab_nic_access(priv)) {
|
|
_iwl_write_direct32(priv, reg, value);
|
|
iwl_release_nic_access(priv);
|
|
}
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
}
|
|
|
|
static inline void iwl_write_reg_buf(struct iwl_priv *priv,
|
|
u32 reg, u32 len, u32 *values)
|
|
{
|
|
u32 count = sizeof(u32);
|
|
|
|
if ((priv != NULL) && (values != NULL)) {
|
|
for (; 0 < len; len -= count, reg += count, values++)
|
|
iwl_write_direct32(priv, reg, *values);
|
|
}
|
|
}
|
|
|
|
static inline int _iwl_poll_direct_bit(struct iwl_priv *priv, u32 addr,
|
|
u32 mask, int timeout)
|
|
{
|
|
int t = 0;
|
|
|
|
do {
|
|
if ((iwl_read_direct32(priv, addr) & mask) == mask)
|
|
return t;
|
|
udelay(IWL_POLL_INTERVAL);
|
|
t += IWL_POLL_INTERVAL;
|
|
} while (t < timeout);
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
#ifdef CONFIG_IWLWIFI_DEBUG
|
|
static inline int __iwl_poll_direct_bit(const char *f, u32 l,
|
|
struct iwl_priv *priv,
|
|
u32 addr, u32 mask, int timeout)
|
|
{
|
|
int ret = _iwl_poll_direct_bit(priv, addr, mask, timeout);
|
|
|
|
if (unlikely(ret == -ETIMEDOUT))
|
|
IWL_DEBUG_IO(priv, "poll_direct_bit(0x%08X, 0x%08X) - "
|
|
"timedout - %s %d\n", addr, mask, f, l);
|
|
else
|
|
IWL_DEBUG_IO(priv, "poll_direct_bit(0x%08X, 0x%08X) = 0x%08X "
|
|
"- %s %d\n", addr, mask, ret, f, l);
|
|
return ret;
|
|
}
|
|
#define iwl_poll_direct_bit(priv, addr, mask, timeout) \
|
|
__iwl_poll_direct_bit(__FILE__, __LINE__, priv, addr, mask, timeout)
|
|
#else
|
|
#define iwl_poll_direct_bit _iwl_poll_direct_bit
|
|
#endif
|
|
|
|
static inline u32 _iwl_read_prph(struct iwl_priv *priv, u32 reg)
|
|
{
|
|
_iwl_write_direct32(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24));
|
|
rmb();
|
|
return _iwl_read_direct32(priv, HBUS_TARG_PRPH_RDAT);
|
|
}
|
|
static inline u32 iwl_read_prph(struct iwl_priv *priv, u32 reg)
|
|
{
|
|
unsigned long reg_flags;
|
|
u32 val;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
iwl_grab_nic_access(priv);
|
|
val = _iwl_read_prph(priv, reg);
|
|
iwl_release_nic_access(priv);
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
return val;
|
|
}
|
|
|
|
static inline void _iwl_write_prph(struct iwl_priv *priv,
|
|
u32 addr, u32 val)
|
|
{
|
|
_iwl_write_direct32(priv, HBUS_TARG_PRPH_WADDR,
|
|
((addr & 0x0000FFFF) | (3 << 24)));
|
|
wmb();
|
|
_iwl_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val);
|
|
}
|
|
|
|
static inline void iwl_write_prph(struct iwl_priv *priv, u32 addr, u32 val)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
if (!iwl_grab_nic_access(priv)) {
|
|
_iwl_write_prph(priv, addr, val);
|
|
iwl_release_nic_access(priv);
|
|
}
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
}
|
|
|
|
#define _iwl_set_bits_prph(priv, reg, mask) \
|
|
_iwl_write_prph(priv, reg, (_iwl_read_prph(priv, reg) | mask))
|
|
|
|
static inline void iwl_set_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
iwl_grab_nic_access(priv);
|
|
_iwl_set_bits_prph(priv, reg, mask);
|
|
iwl_release_nic_access(priv);
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
}
|
|
|
|
#define _iwl_set_bits_mask_prph(priv, reg, bits, mask) \
|
|
_iwl_write_prph(priv, reg, ((_iwl_read_prph(priv, reg) & mask) | bits))
|
|
|
|
static inline void iwl_set_bits_mask_prph(struct iwl_priv *priv, u32 reg,
|
|
u32 bits, u32 mask)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
iwl_grab_nic_access(priv);
|
|
_iwl_set_bits_mask_prph(priv, reg, bits, mask);
|
|
iwl_release_nic_access(priv);
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
}
|
|
|
|
static inline void iwl_clear_bits_prph(struct iwl_priv
|
|
*priv, u32 reg, u32 mask)
|
|
{
|
|
unsigned long reg_flags;
|
|
u32 val;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
iwl_grab_nic_access(priv);
|
|
val = _iwl_read_prph(priv, reg);
|
|
_iwl_write_prph(priv, reg, (val & ~mask));
|
|
iwl_release_nic_access(priv);
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
}
|
|
|
|
static inline u32 iwl_read_targ_mem(struct iwl_priv *priv, u32 addr)
|
|
{
|
|
unsigned long reg_flags;
|
|
u32 value;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
iwl_grab_nic_access(priv);
|
|
|
|
_iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr);
|
|
rmb();
|
|
value = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
|
|
|
|
iwl_release_nic_access(priv);
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
return value;
|
|
}
|
|
|
|
static inline void iwl_write_targ_mem(struct iwl_priv *priv, u32 addr, u32 val)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
if (!iwl_grab_nic_access(priv)) {
|
|
_iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
|
|
wmb();
|
|
_iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, val);
|
|
iwl_release_nic_access(priv);
|
|
}
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
}
|
|
|
|
static inline void iwl_write_targ_mem_buf(struct iwl_priv *priv, u32 addr,
|
|
u32 len, u32 *values)
|
|
{
|
|
unsigned long reg_flags;
|
|
|
|
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
|
if (!iwl_grab_nic_access(priv)) {
|
|
_iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
|
|
wmb();
|
|
for (; 0 < len; len -= sizeof(u32), values++)
|
|
_iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values);
|
|
|
|
iwl_release_nic_access(priv);
|
|
}
|
|
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
|
}
|
|
#endif
|