2018-04-10 14:23:45

by Rakesh Pillai

[permalink] [raw]
Subject: [PATCH 0/4] Support for STA idle mode power save(IMPS)

From: Rakesh Pillai <[email protected]>

Enable STA idle mode power save(IMPS) for WCN3990 TLV target.

In-order to support STA idle mode ps direct access to CE registers are
protected via 2 mechanism. As target can power collapse based on idle
inactivity and any target register access during that state can lead to
un-clocked access.

2) Caching SRRI/DRRI in DDR.

WN3990 has a shadow block in HW which is always on domain and allows HOST/APPS
access irrespective of power state of the target(Common subsystem(includes CE block)
might be down due to idle/inactivity).
Any operation on the shadow registers are directly reflected back in the actual
CE registers( SRWI/DRWI) once common subsystem gets up.
The shadow registers configuration is supplied via QMI message.

WC3990 has 24 shadow registers and mapping of shadow register(0-23) to CE registers(0-12)
is as following.

-----------------------------------------------------------
Shadow Register | CE | src/dst write index
-----------------------------------------------------------
0 | 0 | src
3 | 3 | src
4 | 4 | src
5 | 5 | src
7 | 7 | src
13 | 1 | dst
14 | 2 | dst
19 | 7 | dst
20 | 8 | dst
21 | 9 | dst
22 | 10 | dst
23 | 11 | dst


1) Caching SRWI/SRWI in HW shadow block.

Since shadow block allows only WRITE access, for read access(SRRI/DRRI) driver allocates
region in DDR(12CE*half WORD) which is being configured in CE UPD control register.
CE SRRI/DRRI are restored and replayed on the configured region(DDR) on each update.
HOST/APPS reads SRRI/DRRI from the DDR to make the access independent of target power state.

Govind Singh (2):
ath10k: Enable SRRI/DRRI support on ddr for WCN3990
ath10k: Enable sta idle power save

Rakesh Pillai (2):
ath10k: Add hw params for shadow register support
ath10k: Add support for shadow register for WNC3990

drivers/net/wireless/ath/ath10k/ce.c | 245 +++++++++++++++++++++++++++++++--
drivers/net/wireless/ath/ath10k/ce.h | 14 ++
drivers/net/wireless/ath/ath10k/core.c | 26 ++++
drivers/net/wireless/ath/ath10k/hw.c | 9 +-
drivers/net/wireless/ath/ath10k/hw.h | 17 ++-
drivers/net/wireless/ath/ath10k/mac.c | 7 +
drivers/net/wireless/ath/ath10k/snoc.c | 3 +
7 files changed, 310 insertions(+), 11 deletions(-)

--
2.14.1


2018-04-10 14:24:06

by Rakesh Pillai

[permalink] [raw]
Subject: [PATCH 4/4] ath10k: Enable sta idle power save

From: Govind Singh <[email protected]>

Enable sta power save in fw for the targets that
supports idle power save. The idle ps enable command
will be ignored by the firmware which does not support
this feature.

Signed-off-by: Govind Singh <[email protected]>
Signed-off-by: Rakesh Pillai <[email protected]>
---
drivers/net/wireless/ath/ath10k/mac.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 7e02ca02b28e..1d9222af1bb2 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -4679,6 +4679,13 @@ static int ath10k_start(struct ieee80211_hw *hw)
}
}

+ param = ar->wmi.pdev_param->idle_ps_config;
+ ret = ath10k_wmi_pdev_set_param(ar, param, 1);
+ if (ret && ret != -EOPNOTSUPP) {
+ ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret);
+ goto err_core_stop;
+ }
+
__ath10k_set_antenna(ar, ar->cfg_tx_chainmask, ar->cfg_rx_chainmask);

/*
--
2.14.1

2018-04-10 14:23:59

by Rakesh Pillai

[permalink] [raw]
Subject: [PATCH 2/4] ath10k: Add support for shadow register for WNC3990

From: Rakesh Pillai <[email protected]>

WCN3990 needs shadow register write operation support
for copy engine for regular operation in powersave mode.
Add support for copy engine shadow register write in
datapath tx for WCN3990

Signed-off-by: Rakesh Pillai <[email protected]>
---
drivers/net/wireless/ath/ath10k/ce.c | 143 ++++++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath10k/ce.h | 4 +
2 files changed, 145 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index e7e7b342e5b8..5053dd92bf01 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -58,6 +59,74 @@
* the buffer is sent/received.
*/

+static inline u32 shadow_sr_wr_ind_addr(struct ath10k *ar,
+ struct ath10k_ce_pipe *ce_state)
+{
+ u32 ce_id = ce_state->id;
+ u32 addr = 0;
+
+ switch (ce_id) {
+ case 0:
+ addr = 0x00032000;
+ break;
+ case 3:
+ addr = 0x0003200C;
+ break;
+ case 4:
+ addr = 0x00032010;
+ break;
+ case 5:
+ addr = 0x00032014;
+ break;
+ case 7:
+ addr = 0x0003201C;
+ break;
+ default:
+ ath10k_warn(ar, "invalid CE id: %d", ce_id);
+ break;
+ }
+ return addr;
+}
+
+static inline u32 shadow_dst_wr_ind_addr(struct ath10k *ar,
+ struct ath10k_ce_pipe *ce_state)
+{
+ u32 ce_id = ce_state->id;
+ u32 addr = 0;
+
+ switch (ce_id) {
+ case 1:
+ addr = 0x00032034;
+ break;
+ case 2:
+ addr = 0x00032038;
+ break;
+ case 5:
+ addr = 0x00032044;
+ break;
+ case 7:
+ addr = 0x0003204C;
+ break;
+ case 8:
+ addr = 0x00032050;
+ break;
+ case 9:
+ addr = 0x00032054;
+ break;
+ case 10:
+ addr = 0x00032058;
+ break;
+ case 11:
+ addr = 0x0003205C;
+ break;
+ default:
+ ath10k_warn(ar, "invalid CE id: %d", ce_id);
+ break;
+ }
+
+ return addr;
+}
+
static inline unsigned int
ath10k_set_ring_byte(unsigned int offset,
struct ath10k_hw_ce_regs_addr_map *addr_map)
@@ -123,6 +192,22 @@ static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
ar->hw_ce_regs->current_srri_addr);
}

+static inline void
+ath10k_ce_shadow_src_ring_write_index_set(struct ath10k *ar,
+ struct ath10k_ce_pipe *ce_state,
+ unsigned int value)
+{
+ ath10k_ce_write32(ar, shadow_sr_wr_ind_addr(ar, ce_state), value);
+}
+
+static inline void
+ath10k_ce_shadow_dest_ring_write_index_set(struct ath10k *ar,
+ struct ath10k_ce_pipe *ce_state,
+ unsigned int value)
+{
+ ath10k_ce_write32(ar, shadow_dst_wr_ind_addr(ar, ce_state), value);
+}
+
static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar,
u32 ce_ctrl_addr,
unsigned int addr)
@@ -376,8 +461,14 @@ static int _ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
write_index = CE_RING_IDX_INCR(nentries_mask, write_index);

/* WORKAROUND */
- if (!(flags & CE_SEND_FLAG_GATHER))
- ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index);
+ if (!(flags & CE_SEND_FLAG_GATHER)) {
+ if (ar->hw_params.shadow_reg_support)
+ ath10k_ce_shadow_src_ring_write_index_set(ar, ce_state,
+ write_index);
+ else
+ ath10k_ce_src_ring_write_index_set(ar, ctrl_addr,
+ write_index);
+ }

src_ring->write_index = write_index;
exit:
@@ -1251,6 +1342,22 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
return 0;
}

+static int ath10k_ce_alloc_shadow_base(struct ath10k *ar,
+ struct ath10k_ce_ring *src_ring,
+ u32 nentries)
+{
+ src_ring->shadow_base_unaligned = kcalloc(nentries,
+ sizeof(struct ce_desc),
+ GFP_KERNEL);
+ if (!src_ring->shadow_base_unaligned)
+ return -ENOMEM;
+
+ src_ring->shadow_base = (struct ce_desc *)
+ PTR_ALIGN(src_ring->shadow_base_unaligned,
+ CE_DESC_RING_ALIGN);
+ return 0;
+}
+
static struct ath10k_ce_ring *
ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
const struct ce_attr *attr)
@@ -1258,6 +1365,7 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
struct ath10k_ce_ring *src_ring;
u32 nentries = attr->src_nentries;
dma_addr_t base_addr;
+ int ret;

nentries = roundup_pow_of_two(nentries);

@@ -1294,6 +1402,19 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id,
ALIGN(src_ring->base_addr_ce_space_unaligned,
CE_DESC_RING_ALIGN);

+ if (ar->hw_params.shadow_reg_support) {
+ ret = ath10k_ce_alloc_shadow_base(ar, src_ring, nentries);
+ if (ret) {
+ dma_free_coherent(ar->dev,
+ (nentries * sizeof(struct ce_desc) +
+ CE_DESC_RING_ALIGN),
+ src_ring->base_addr_owner_space_unaligned,
+ base_addr);
+ kfree(src_ring);
+ return ERR_PTR(ret);
+ }
+ }
+
return src_ring;
}

@@ -1304,6 +1425,7 @@ ath10k_ce_alloc_src_ring_64(struct ath10k *ar, unsigned int ce_id,
struct ath10k_ce_ring *src_ring;
u32 nentries = attr->src_nentries;
dma_addr_t base_addr;
+ int ret;

nentries = roundup_pow_of_two(nentries);

@@ -1339,6 +1461,19 @@ ath10k_ce_alloc_src_ring_64(struct ath10k *ar, unsigned int ce_id,
ALIGN(src_ring->base_addr_ce_space_unaligned,
CE_DESC_RING_ALIGN);

+ if (ar->hw_params.shadow_reg_support) {
+ ret = ath10k_ce_alloc_shadow_base(ar, src_ring, nentries);
+ if (ret) {
+ dma_free_coherent(ar->dev,
+ (nentries * sizeof(struct ce_desc) +
+ CE_DESC_RING_ALIGN),
+ src_ring->base_addr_owner_space_unaligned,
+ base_addr);
+ kfree(src_ring);
+ return ERR_PTR(ret);
+ }
+ }
+
return src_ring;
}

@@ -1505,6 +1640,8 @@ static void _ath10k_ce_free_pipe(struct ath10k *ar, int ce_id)
struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];

if (ce_state->src_ring) {
+ if (ar->hw_params.shadow_reg_support)
+ kfree(ce_state->src_ring->shadow_base_unaligned);
dma_free_coherent(ar->dev,
(ce_state->src_ring->nentries *
sizeof(struct ce_desc) +
@@ -1534,6 +1671,8 @@ static void _ath10k_ce_free_pipe_64(struct ath10k *ar, int ce_id)
struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];

if (ce_state->src_ring) {
+ if (ar->hw_params.shadow_reg_support)
+ kfree(ce_state->src_ring->shadow_base_unaligned);
dma_free_coherent(ar->dev,
(ce_state->src_ring->nentries *
sizeof(struct ce_desc_64) +
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index d8f9da334529..9aea89133209 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -113,6 +114,9 @@ struct ath10k_ce_ring {
/* CE address space */
u32 base_addr_ce_space;

+ char *shadow_base_unaligned;
+ struct ce_desc *shadow_base;
+
/* keep last */
void *per_transfer_context[0];
};
--
2.14.1

2018-04-17 12:10:35

by Rakesh Pillai

[permalink] [raw]
Subject: Re: [PATCH 3/4] ath10k: Enable SRRI/DRRI support on ddr for WCN3990

Hi Kalle,

The checkpatch warning has been addressed in v2 for this patchset.

Thanks,
Rakesh Pillai.

On 2018-04-16 18:57, Kalle Valo wrote:
> [email protected] writes:
>
>> From: Govind Singh <[email protected]>
>>
>> SRRI/DRRI are not mapped in the HW Shadow block and can lead
>> to un-clocked access if common subsystem in the target is
>> powered down due to idle mode.
>>
>> To mitigate this problem SRRI/DRRI can be read from
>> DDR instead of doing an actual hardware read.
>> Host allocates non cached memory on ddr and configures
>> the physical address of this memory to the CE hardware.
>> The hardware updates the RRI on this particular location.
>> Read SRRI/DRRI from DDR location instead of
>> direct target read.
>>
>> Enable retention restore on ddr using hw params to enable
>> in specific targets.
>>
>> Signed-off-by: Govind Singh <[email protected]>
>> Signed-off-by: Rakesh Pillai <[email protected]>
>
> [...]
>
>> + for (i = 0; i < CE_COUNT; i++) {
>> + ctrl1_regs = ar->hw_ce_regs->ctrl1_regs->addr;
>> + ce_base_addr = ath10k_ce_base_address(ar, i);
>> + ath10k_ce_write32(ar, ce_base_addr + ctrl1_regs,
>> + ath10k_ce_read32(ar,
>> + ce_base_addr + ctrl1_regs) |
>> + ar->hw_ce_regs->upd->mask);
>> + }
>
> This gives a checkpatch warning:
>
> drivers/net/wireless/ath/ath10k/ce.c:1917: Alignment should match open
> parenthesis
>
> You could fix that, and make the code a lot more readable, with
> something like this:
>
> tmp = ath10k_ce_read32(ar, ce_base_addr + ctrl1_regs);
> tmp |= ar->hw_ce_regs->upd->mask;
> ath10k_ce_write32(ar, ce_base_addr + ctrl1_regs, tmp);
>
> Usually it's a good practise avoid making clever tricks, simple code is
> a lot easier to read.

2018-04-10 14:24:03

by Rakesh Pillai

[permalink] [raw]
Subject: [PATCH 3/4] ath10k: Enable SRRI/DRRI support on ddr for WCN3990

From: Govind Singh <[email protected]>

SRRI/DRRI are not mapped in the HW Shadow block and can lead
to un-clocked access if common subsystem in the target is
powered down due to idle mode.

To mitigate this problem SRRI/DRRI can be read from
DDR instead of doing an actual hardware read.
Host allocates non cached memory on ddr and configures
the physical address of this memory to the CE hardware.
The hardware updates the RRI on this particular location.
Read SRRI/DRRI from DDR location instead of
direct target read.

Enable retention restore on ddr using hw params to enable
in specific targets.

Signed-off-by: Govind Singh <[email protected]>
Signed-off-by: Rakesh Pillai <[email protected]>
---
drivers/net/wireless/ath/ath10k/ce.c | 102 +++++++++++++++++++++++++++++++--
drivers/net/wireless/ath/ath10k/ce.h | 10 ++++
drivers/net/wireless/ath/ath10k/core.c | 13 +++++
drivers/net/wireless/ath/ath10k/hw.c | 9 ++-
drivers/net/wireless/ath/ath10k/hw.h | 13 ++++-
drivers/net/wireless/ath/ath10k/snoc.c | 3 +
6 files changed, 141 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index 5053dd92bf01..b21fbedd0cad 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -185,11 +185,30 @@ static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar,
ar->hw_ce_regs->sr_wr_index_addr);
}

+static inline u32 ath10k_ce_src_ring_read_index_from_ddr(struct ath10k *ar,
+ u32 ce_id)
+{
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+ return ce->vaddr_rri[ce_id] & CE_DDR_RRI_MASK;
+}
+
static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ath10k_ce_read32(ar, ce_ctrl_addr +
- ar->hw_ce_regs->current_srri_addr);
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+ u32 ce_id = COPY_ENGINE_ID(ce_ctrl_addr);
+ struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];
+ u32 index;
+
+ if (ar->hw_params.rri_on_ddr &&
+ (ce_state->attr_flags & CE_ATTR_DIS_INTR))
+ index = ath10k_ce_src_ring_read_index_from_ddr(ar, ce_id);
+ else
+ index = ath10k_ce_read32(ar, ce_ctrl_addr +
+ ar->hw_ce_regs->current_srri_addr);
+
+ return index;
}

static inline void
@@ -266,11 +285,31 @@ static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar,
ath10k_set_ring_byte(n, ctrl_regs->dst_ring));
}

+static inline
+ u32 ath10k_ce_dest_ring_read_index_from_ddr(struct ath10k *ar, u32 ce_id)
+{
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+ return (ce->vaddr_rri[ce_id] >> CE_DDR_DRRI_SHIFT) &
+ CE_DDR_RRI_MASK;
+}
+
static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar,
u32 ce_ctrl_addr)
{
- return ath10k_ce_read32(ar, ce_ctrl_addr +
- ar->hw_ce_regs->current_drri_addr);
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+ u32 ce_id = COPY_ENGINE_ID(ce_ctrl_addr);
+ struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id];
+ u32 index;
+
+ if (ar->hw_params.rri_on_ddr &&
+ (ce_state->attr_flags & CE_ATTR_DIS_INTR))
+ index = ath10k_ce_dest_ring_read_index_from_ddr(ar, ce_id);
+ else
+ index = ath10k_ce_read32(ar, ce_ctrl_addr +
+ ar->hw_ce_regs->current_drri_addr);
+
+ return index;
}

static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar,
@@ -486,7 +525,7 @@ static int _ath10k_ce_send_nolock_64(struct ath10k_ce_pipe *ce_state,
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
struct ce_desc_64 *desc, sdesc;
unsigned int nentries_mask = src_ring->nentries_mask;
- unsigned int sw_index = src_ring->sw_index;
+ unsigned int sw_index;
unsigned int write_index = src_ring->write_index;
u32 ctrl_addr = ce_state->ctrl_addr;
__le32 *addr;
@@ -500,6 +539,11 @@ static int _ath10k_ce_send_nolock_64(struct ath10k_ce_pipe *ce_state,
ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n",
__func__, nbytes, ce_state->src_sz_max);

+ if (ar->hw_params.rri_on_ddr)
+ sw_index = ath10k_ce_src_ring_read_index_from_ddr(ar, ce_state->id);
+ else
+ sw_index = src_ring->sw_index;
+
if (unlikely(CE_RING_DELTA(nentries_mask,
write_index, sw_index - 1) <= 0)) {
ret = -ENOSR;
@@ -1016,7 +1060,10 @@ int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
src_ring->hw_index = read_index;
}

- read_index = src_ring->hw_index;
+ if (ar->hw_params.rri_on_ddr)
+ read_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+ else
+ read_index = src_ring->hw_index;

if (read_index == sw_index)
return -EIO;
@@ -1841,3 +1888,46 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
return 0;
}
EXPORT_SYMBOL(ath10k_ce_alloc_pipe);
+
+void ath10k_ce_alloc_rri(struct ath10k *ar)
+{
+ int i;
+ u32 ctrl1_regs;
+ u32 ce_base_addr;
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+ ce->vaddr_rri = dma_alloc_coherent(ar->dev,
+ (CE_COUNT * sizeof(u32)),
+ &ce->paddr_rri, GFP_KERNEL);
+
+ if (!ce->vaddr_rri)
+ return;
+
+ ath10k_ce_write32(ar, ar->hw_ce_regs->ce_rri_low,
+ lower_32_bits(ce->paddr_rri));
+ ath10k_ce_write32(ar, ar->hw_ce_regs->ce_rri_high,
+ (upper_32_bits(ce->paddr_rri) &
+ CE_DESC_FLAGS_GET_MASK));
+
+ for (i = 0; i < CE_COUNT; i++) {
+ ctrl1_regs = ar->hw_ce_regs->ctrl1_regs->addr;
+ ce_base_addr = ath10k_ce_base_address(ar, i);
+ ath10k_ce_write32(ar, ce_base_addr + ctrl1_regs,
+ ath10k_ce_read32(ar,
+ ce_base_addr + ctrl1_regs) |
+ ar->hw_ce_regs->upd->mask);
+ }
+
+ memset(ce->vaddr_rri, 0, CE_COUNT * sizeof(u32));
+}
+EXPORT_SYMBOL(ath10k_ce_alloc_rri);
+
+void ath10k_ce_free_rri(struct ath10k *ar)
+{
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+ dma_free_coherent(ar->dev, (CE_COUNT * sizeof(u32)),
+ ce->vaddr_rri,
+ ce->paddr_rri);
+}
+EXPORT_SYMBOL(ath10k_ce_free_rri);
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index 9aea89133209..dbeffaef6024 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -49,6 +49,9 @@ struct ath10k_ce_pipe;
#define CE_DESC_FLAGS_META_DATA_MASK ar->hw_values->ce_desc_meta_data_mask
#define CE_DESC_FLAGS_META_DATA_LSB ar->hw_values->ce_desc_meta_data_lsb

+#define CE_DDR_RRI_MASK GENMASK(15, 0)
+#define CE_DDR_DRRI_SHIFT 16
+
struct ce_desc {
__le32 addr;
__le16 nbytes;
@@ -157,6 +160,8 @@ struct ath10k_ce {
spinlock_t ce_lock;
const struct ath10k_bus_ops *bus_ops;
struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
+ u32 *vaddr_rri;
+ dma_addr_t paddr_rri;
};

/*==================Send====================*/
@@ -265,6 +270,8 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar);
void ath10k_ce_enable_interrupts(struct ath10k *ar);
void ath10k_ce_dump_registers(struct ath10k *ar,
struct ath10k_fw_crash_data *crash_data);
+void ath10k_ce_alloc_rri(struct ath10k *ar);
+void ath10k_ce_free_rri(struct ath10k *ar);

/* ce_attr.flags values */
/* Use NonSnooping PCIe accesses? */
@@ -331,6 +338,9 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
}

+#define COPY_ENGINE_ID(COPY_ENGINE_BASE_ADDRESS) (((COPY_ENGINE_BASE_ADDRESS) \
+ - CE0_BASE_ADDRESS) / (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS))
+
#define CE_SRC_RING_TO_DESC(baddr, idx) \
(&(((struct ce_desc *)baddr)[idx]))

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 7e451b76c8a4..643e4a1c9eac 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -121,6 +121,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA9887_HW_1_0_VERSION,
@@ -152,6 +153,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA6174_HW_2_1_VERSION,
@@ -182,6 +184,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA6174_HW_2_1_VERSION,
@@ -212,6 +215,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA6174_HW_3_0_VERSION,
@@ -242,6 +246,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA6174_HW_3_2_VERSION,
@@ -275,6 +280,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA99X0_HW_2_0_DEV_VERSION,
@@ -311,6 +317,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA9984_HW_1_0_DEV_VERSION,
@@ -352,6 +359,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA9888_HW_2_0_DEV_VERSION,
@@ -392,6 +400,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA9377_HW_1_0_DEV_VERSION,
@@ -422,6 +431,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA9377_HW_1_1_DEV_VERSION,
@@ -454,6 +464,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = QCA4019_HW_1_0_DEV_VERSION,
@@ -491,6 +502,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
.shadow_reg_support = false,
+ .rri_on_ddr = false,
},
{
.id = WCN3990_HW_1_0_DEV_VERSION,
@@ -513,6 +525,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL_DUAL_MAC,
.per_ce_irq = true,
.shadow_reg_support = true,
+ .rri_on_ddr = true,
},
};

diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c
index 497ac33e0fbf..677535b3d207 100644
--- a/drivers/net/wireless/ath/ath10k/hw.c
+++ b/drivers/net/wireless/ath/ath10k/hw.c
@@ -310,6 +310,12 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = {
.wm_high = &wcn3990_dst_wm_high,
};

+static struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = {
+ .shift = 19,
+ .mask = 0x00080000,
+ .enable = 0x00000000,
+};
+
const struct ath10k_hw_ce_regs wcn3990_ce_regs = {
.sr_base_addr = 0x00000000,
.sr_size_addr = 0x00000008,
@@ -320,8 +326,6 @@ const struct ath10k_hw_ce_regs wcn3990_ce_regs = {
.dst_wr_index_addr = 0x00000040,
.current_srri_addr = 0x00000044,
.current_drri_addr = 0x00000048,
- .ddr_addr_for_rri_low = 0x00000004,
- .ddr_addr_for_rri_high = 0x00000008,
.ce_rri_low = 0x0024C004,
.ce_rri_high = 0x0024C008,
.host_ie_addr = 0x0000002c,
@@ -331,6 +335,7 @@ const struct ath10k_hw_ce_regs wcn3990_ce_regs = {
.misc_regs = &wcn3990_misc_reg,
.wm_srcr = &wcn3990_wm_src_ring,
.wm_dstr = &wcn3990_wm_dst_ring,
+ .upd = &wcn3990_ctrl1_upd,
};

const struct ath10k_hw_values wcn3990_values = {
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index cf18adda9f20..3d4207818904 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -336,6 +336,12 @@ struct ath10k_hw_ce_dst_src_wm_regs {
struct ath10k_hw_ce_regs_addr_map *wm_low;
struct ath10k_hw_ce_regs_addr_map *wm_high; };

+struct ath10k_hw_ce_ctrl1_upd {
+ u32 shift;
+ u32 mask;
+ u32 enable;
+};
+
struct ath10k_hw_ce_regs {
u32 sr_base_addr;
u32 sr_size_addr;
@@ -358,7 +364,9 @@ struct ath10k_hw_ce_regs {
struct ath10k_hw_ce_cmd_halt *cmd_halt;
struct ath10k_hw_ce_host_ie *host_ie;
struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr;
- struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; };
+ struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr;
+ struct ath10k_hw_ce_ctrl1_upd *upd;
+};

struct ath10k_hw_values {
u32 rtc_state_val_on;
@@ -575,6 +583,9 @@ struct ath10k_hw_params {

/* target supporting shadow register for ce write */
bool shadow_reg_support;
+
+ /* target supporting retention restore on ddr */
+ bool rri_on_ddr;
};

struct htt_rx_desc;
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 2e490ff124f1..47a4d2a5bd4c 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -767,6 +767,7 @@ static void ath10k_snoc_hif_power_down(struct ath10k *ar)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n");

ath10k_snoc_wlan_disable(ar);
+ ath10k_ce_free_rri(ar);
}

static int ath10k_snoc_hif_power_up(struct ath10k *ar)
@@ -782,6 +783,8 @@ static int ath10k_snoc_hif_power_up(struct ath10k *ar)
return ret;
}

+ ath10k_ce_alloc_rri(ar);
+
ret = ath10k_snoc_init_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to initialize CE: %d\n", ret);
--
2.14.1

2018-04-16 13:27:25

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 3/4] ath10k: Enable SRRI/DRRI support on ddr for WCN3990

[email protected] writes:

> From: Govind Singh <[email protected]>
>
> SRRI/DRRI are not mapped in the HW Shadow block and can lead
> to un-clocked access if common subsystem in the target is
> powered down due to idle mode.
>
> To mitigate this problem SRRI/DRRI can be read from
> DDR instead of doing an actual hardware read.
> Host allocates non cached memory on ddr and configures
> the physical address of this memory to the CE hardware.
> The hardware updates the RRI on this particular location.
> Read SRRI/DRRI from DDR location instead of
> direct target read.
>
> Enable retention restore on ddr using hw params to enable
> in specific targets.
>
> Signed-off-by: Govind Singh <[email protected]>
> Signed-off-by: Rakesh Pillai <[email protected]>

[...]

> + for (i = 0; i < CE_COUNT; i++) {
> + ctrl1_regs = ar->hw_ce_regs->ctrl1_regs->addr;
> + ce_base_addr = ath10k_ce_base_address(ar, i);
> + ath10k_ce_write32(ar, ce_base_addr + ctrl1_regs,
> + ath10k_ce_read32(ar,
> + ce_base_addr + ctrl1_regs) |
> + ar->hw_ce_regs->upd->mask);
> + }

This gives a checkpatch warning:

drivers/net/wireless/ath/ath10k/ce.c:1917: Alignment should match open parenthesis

You could fix that, and make the code a lot more readable, with
something like this:

tmp = ath10k_ce_read32(ar, ce_base_addr + ctrl1_regs);
tmp |= ar->hw_ce_regs->upd->mask;
ath10k_ce_write32(ar, ce_base_addr + ctrl1_regs, tmp);

Usually it's a good practise avoid making clever tricks, simple code is
a lot easier to read.

--
Kalle Valo

2018-04-10 14:23:53

by Rakesh Pillai

[permalink] [raw]
Subject: [PATCH 1/4] ath10k: Add hw params for shadow register support

From: Rakesh Pillai <[email protected]>

wcn3990 supports shadow register for ce write.

Add a hw param for shadow register support.

Signed-off-by: Rakesh Pillai <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.c | 13 +++++++++++++
drivers/net/wireless/ath/ath10k/hw.h | 4 ++++
2 files changed, 17 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 610bb32ec9f4..7e451b76c8a4 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -120,6 +120,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA9887_HW_1_0_VERSION,
@@ -150,6 +151,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA6174_HW_2_1_VERSION,
@@ -179,6 +181,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA6174_HW_2_1_VERSION,
@@ -208,6 +211,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA6174_HW_3_0_VERSION,
@@ -237,6 +241,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA6174_HW_3_2_VERSION,
@@ -269,6 +274,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA99X0_HW_2_0_DEV_VERSION,
@@ -304,6 +310,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA9984_HW_1_0_DEV_VERSION,
@@ -344,6 +351,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA9888_HW_2_0_DEV_VERSION,
@@ -383,6 +391,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA9377_HW_1_0_DEV_VERSION,
@@ -412,6 +421,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA9377_HW_1_1_DEV_VERSION,
@@ -443,6 +453,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = QCA4019_HW_1_0_DEV_VERSION,
@@ -479,6 +490,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = false,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
.per_ce_irq = false,
+ .shadow_reg_support = false,
},
{
.id = WCN3990_HW_1_0_DEV_VERSION,
@@ -500,6 +512,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.target_64bit = true,
.rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL_DUAL_MAC,
.per_ce_irq = true,
+ .shadow_reg_support = true,
},
};

diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 57dad208ef16..cf18adda9f20 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -571,6 +572,9 @@ struct ath10k_hw_params {

/* target supporting per ce IRQ */
bool per_ce_irq;
+
+ /* target supporting shadow register for ce write */
+ bool shadow_reg_support;
};

struct htt_rx_desc;
--
2.14.1