Commit fee05843 authored by Lorenzo Bianconi's avatar Lorenzo Bianconi Committed by Kalle Valo

mt7601u: make write with mask access atomic

Introduce __mt7601u_rr and __mt7601u_vendor_single_wr routines in order
to make mt7601u_rmw and mt7601u_rmc atomic. This patch does not fix a
reported issue but makes the usb access more robust to concurrent
operations on the same register since it is theoretically possible that
read and write accesses of mt7601u_rmw/mt7601u_rmc can be interleaved with
a different write operation on the same register.
Moreover using __mt7601u_rr and __mt7601u_vendor_single_wr in
mt7601u_rmw/mt7601u_rmc allows to grab vendor_req_mutex mutex once
instead of twice
Signed-off-by: default avatarLorenzo Bianconi <lorenzo.bianconi@redhat.com>
Acked-by: default avatarJakub Kicinski <kubakici@wp.pl>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 7972326a
...@@ -147,7 +147,8 @@ enum { ...@@ -147,7 +147,8 @@ enum {
* @rx_lock: protects @rx_q. * @rx_lock: protects @rx_q.
* @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi. * @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi.
* @mutex: ensures exclusive access from mac80211 callbacks. * @mutex: ensures exclusive access from mac80211 callbacks.
* @vendor_req_mutex: protects @vend_buf, ensures atomicity of split writes. * @vendor_req_mutex: protects @vend_buf, ensures atomicity of read/write
* accesses
* @reg_atomic_mutex: ensures atomicity of indirect register accesses * @reg_atomic_mutex: ensures atomicity of indirect register accesses
* (accesses to RF and BBP). * (accesses to RF and BBP).
* @hw_atomic_mutex: ensures exclusive access to HW during critical * @hw_atomic_mutex: ensures exclusive access to HW during critical
......
...@@ -129,15 +129,14 @@ void mt7601u_vendor_reset(struct mt7601u_dev *dev) ...@@ -129,15 +129,14 @@ void mt7601u_vendor_reset(struct mt7601u_dev *dev)
MT_VEND_DEV_MODE_RESET, 0, NULL, 0); MT_VEND_DEV_MODE_RESET, 0, NULL, 0);
} }
u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset) /* should be called with vendor_req_mutex held */
static u32 __mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
{ {
int ret; int ret;
u32 val = ~0; u32 val = ~0;
WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset); WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset);
mutex_lock(&dev->vendor_req_mutex);
ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN, ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN,
0, offset, dev->vend_buf, MT_VEND_BUF); 0, offset, dev->vend_buf, MT_VEND_BUF);
if (ret == MT_VEND_BUF) if (ret == MT_VEND_BUF)
...@@ -146,25 +145,41 @@ u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset) ...@@ -146,25 +145,41 @@ u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n", dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n",
ret, offset); ret, offset);
mutex_unlock(&dev->vendor_req_mutex);
trace_reg_read(dev, offset, val); trace_reg_read(dev, offset, val);
return val; return val;
} }
int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset)
const u16 offset, const u32 val)
{ {
int ret; u32 ret;
mutex_lock(&dev->vendor_req_mutex); mutex_lock(&dev->vendor_req_mutex);
ret = __mt7601u_rr(dev, offset);
mutex_unlock(&dev->vendor_req_mutex);
ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, return ret;
}
/* should be called with vendor_req_mutex held */
static int __mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
const u16 offset, const u32 val)
{
int ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
val & 0xffff, offset, NULL, 0); val & 0xffff, offset, NULL, 0);
if (!ret) if (!ret)
ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT,
val >> 16, offset + 2, NULL, 0); val >> 16, offset + 2, NULL, 0);
trace_reg_write(dev, offset, val);
return ret;
}
int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req,
const u16 offset, const u32 val)
{
int ret;
mutex_lock(&dev->vendor_req_mutex);
ret = __mt7601u_vendor_single_wr(dev, req, offset, val);
mutex_unlock(&dev->vendor_req_mutex); mutex_unlock(&dev->vendor_req_mutex);
return ret; return ret;
...@@ -175,23 +190,30 @@ void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val) ...@@ -175,23 +190,30 @@ void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val)
WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset); WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset);
mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val); mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val);
trace_reg_write(dev, offset, val);
} }
u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val)
{ {
val |= mt7601u_rr(dev, offset) & ~mask; mutex_lock(&dev->vendor_req_mutex);
mt7601u_wr(dev, offset, val); val |= __mt7601u_rr(dev, offset) & ~mask;
__mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val);
mutex_unlock(&dev->vendor_req_mutex);
return val; return val;
} }
u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val)
{ {
u32 reg = mt7601u_rr(dev, offset); u32 reg;
mutex_lock(&dev->vendor_req_mutex);
reg = __mt7601u_rr(dev, offset);
val |= reg & ~mask; val |= reg & ~mask;
if (reg != val) if (reg != val)
mt7601u_wr(dev, offset, val); __mt7601u_vendor_single_wr(dev, MT_VEND_WRITE,
offset, val);
mutex_unlock(&dev->vendor_req_mutex);
return val; return val;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment