wireless/bcm43xxx: add dynamic kso control support

1. PMU control by KeepSdioOn(KSO) if firmware support save restore
2. Remove watchdog timer

Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an 2022-06-23 12:54:30 +08:00 committed by Xiang Xiao
parent 2eb1124db5
commit 43497df78b
3 changed files with 133 additions and 54 deletions

View File

@ -63,7 +63,7 @@
#define BCMF_THREAD_NAME "bcmf"
#define BCMF_THREAD_STACK_SIZE 2048
#define BCMF_WAITDOG_TIMEOUT_TICK (5*CLOCKS_PER_SEC)
#define BCMF_LOWPOWER_TIMEOUT_TICK SEC2TICK(2)
/* Chip-common registers */
@ -104,7 +104,6 @@ static int bcmf_oob_irq(FAR void *arg);
static int bcmf_sdio_bus_sleep(FAR struct bcmf_sdio_dev_s *sbus,
bool sleep);
static void bcmf_sdio_waitdog_timeout(wdparm_t arg);
static int bcmf_sdio_thread(int argc, char **argv);
static int bcmf_sdio_find_block_size(unsigned int size);
@ -132,23 +131,96 @@ static struct bcmf_sdio_frame
int bcmf_oob_irq(FAR void *arg)
{
FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s *)arg;
int semcount;
if (sbus->ready)
{
/* Signal bmcf thread */
sbus->irq_pending = true;
nxsem_post(&sbus->thread_signal);
nxsem_get_value(&sbus->thread_signal, &semcount);
if (semcount < 1)
{
nxsem_post(&sbus->thread_signal);
}
}
return OK;
}
int bcmf_sdio_kso_enable(FAR struct bcmf_sdio_dev_s *sbus, bool enable)
{
uint8_t value;
int loops;
int ret;
if (!sbus->ready)
{
return -EPERM;
}
if (sbus->kso_enable == enable)
{
return OK;
}
if (enable)
{
loops = 200;
while (--loops > 0)
{
ret = bcmf_write_reg(sbus, 1, SBSDIO_FUNC1_SLEEPCSR,
SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK);
if (ret != OK)
{
wlerr("HT Avail request failed %d\n", ret);
return ret;
}
nxsig_usleep(100 * 1000);
ret = bcmf_read_reg(sbus, 1, SBSDIO_FUNC1_SLEEPCSR, &value);
if (ret != OK)
{
return ret;
}
if ((value & (SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK)) != 0)
{
break;
}
}
if (loops <= 0)
{
return -ETIMEDOUT;
}
}
else
{
ret = bcmf_write_reg(sbus, 1, SBSDIO_FUNC1_SLEEPCSR, 0);
}
if (ret == OK)
{
sbus->kso_enable = enable;
}
return ret;
}
int bcmf_sdio_bus_sleep(FAR struct bcmf_sdio_dev_s *sbus, bool sleep)
{
int ret;
int loops;
uint8_t value;
int loops;
int ret;
if (!sbus->ready)
{
return -EPERM;
}
if (sbus->sleeping == sleep)
{
@ -205,6 +277,12 @@ int bcmf_sdio_bus_sleep(FAR struct bcmf_sdio_dev_s *sbus, bool sleep)
return OK;
}
int bcmf_sdio_bus_lowpower(FAR struct bcmf_sdio_dev_s *sbus, bool enable)
{
return sbus->support_sr ? bcmf_sdio_kso_enable(sbus, !enable) :
bcmf_sdio_bus_sleep(sbus, enable);
}
/****************************************************************************
* Name: bcmf_probe
****************************************************************************/
@ -540,12 +618,9 @@ static int bcmf_sdio_sr_init(FAR struct bcmf_sdio_dev_s *sbus)
/* Enable KeepSdioOn (KSO) bit for normal operation */
bcmf_read_reg(sbus, 1, SBSDIO_FUNC1_SLEEPCSR, &data);
if ((data & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK) == 0)
{
data |= SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
bcmf_write_reg(sbus, 1, SBSDIO_FUNC1_SLEEPCSR, data);
}
bcmf_sdio_kso_enable(sbus, true);
sbus->support_sr = true;
}
return OK;
@ -588,6 +663,11 @@ int bcmf_transfer_bytes(FAR struct bcmf_sdio_dev_s *sbus, bool write,
unsigned int blocklen;
unsigned int nblocks;
if (!sbus->ready)
{
return -EPERM;
}
/* Use rw_io_direct method if len is 1 */
if (len == 0)
@ -671,6 +751,8 @@ int bcmf_bus_sdio_active(FAR struct bcmf_dev_s *priv, bool active)
return ret;
}
sbus->ready = active;
/* Probe device */
ret = bcmf_probe(sbus);
@ -689,8 +771,6 @@ int bcmf_bus_sdio_active(FAR struct bcmf_dev_s *priv, bool active)
nxsig_usleep(100 * 1000);
sbus->ready = active;
ret = bcmf_bus_setup_interrupts(sbus);
if (ret != OK)
{
@ -866,22 +946,12 @@ int bcmf_chipinitialize(FAR struct bcmf_sdio_dev_s *sbus)
return OK;
}
void bcmf_sdio_waitdog_timeout(wdparm_t arg)
{
FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)arg;
FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s *)priv->bus;
/* Notify bcmf thread */
wlinfo("Notify bcmf thread\n");
nxsem_post(&sbus->thread_signal);
}
int bcmf_sdio_thread(int argc, char **argv)
{
FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)
((uintptr_t)strtoul(argv[1], NULL, 16));
FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s *)priv->bus;
uint32_t timeout = BCMF_LOWPOWER_TIMEOUT_TICK;
int ret;
wlinfo(" Enter\n");
@ -892,23 +962,44 @@ int bcmf_sdio_thread(int argc, char **argv)
while (true)
{
/* Wait for event (device interrupt, user request or waitdog timer) */
/* Check if RX/TX frames are available */
ret = nxsem_wait_uninterruptible(&sbus->thread_signal);
if (ret < 0)
if ((sbus->intstatus & I_HMB_FRAME_IND) == 0 &&
(sbus->tx_queue.tail == NULL) &&
!sbus->irq_pending)
{
wlerr("Error while waiting for semaphore\n");
break;
/* Wait for event (device interrupt or user request) */
if (timeout == UINT_MAX)
{
ret = nxsem_wait_uninterruptible(&sbus->thread_signal);
}
else
{
ret = nxsem_tickwait_uninterruptible(&sbus->thread_signal,
timeout);
}
if (ret == -ETIMEDOUT)
{
/* Turn off clock request. */
timeout = UINT_MAX;
bcmf_sdio_bus_lowpower(sbus, true);
continue;
}
else if (ret < 0)
{
wlerr("Error while waiting for semaphore\n");
break;
}
}
/* Restart the waitdog timer */
wd_start(&sbus->waitdog, BCMF_WAITDOG_TIMEOUT_TICK,
bcmf_sdio_waitdog_timeout, (wdparm_t)priv);
timeout = BCMF_LOWPOWER_TIMEOUT_TICK;
/* Wake up device */
bcmf_sdio_bus_sleep(sbus, false);
bcmf_sdio_bus_lowpower(sbus, false);
if (sbus->irq_pending)
{
@ -954,24 +1045,6 @@ int bcmf_sdio_thread(int argc, char **argv)
ret = bcmf_sdpcm_sendframe(priv);
}
while (ret == OK);
/* Check if RX frames are available */
if (sbus->intstatus & I_HMB_FRAME_IND)
{
/* Try again */
wlinfo("Try read again\n");
continue;
}
/* If we're done for now, turn off clock request. */
#if 0
/* TODO add wakelock */
bcmf_sdio_bus_sleep(sbus, true);
#endif
}
wlinfo("Exit\n");

View File

@ -86,10 +86,11 @@ struct bcmf_sdio_dev_s
volatile bool ready; /* Current device status */
bool sleeping; /* Current sleep status */
bool kso_enable; /* Current Keep sdio on status */
bool support_sr; /* Firmware support save restore */
pid_t thread_id; /* Processing thread id */
sem_t thread_signal; /* Semaphore for processing thread event */
struct wdog_s waitdog; /* Processing thread waitdog */
uint32_t backplane_current_addr; /* Current function 1 backplane base addr */

View File

@ -440,6 +440,7 @@ int bcmf_sdpcm_queue_frame(FAR struct bcmf_dev_s *priv,
struct bcmf_sdio_frame *sframe = (struct bcmf_sdio_frame *)frame;
struct bcmf_sdpcm_header *header =
(struct bcmf_sdpcm_header *)sframe->data;
int semcount;
/* Prepare sw header */
@ -470,7 +471,11 @@ int bcmf_sdpcm_queue_frame(FAR struct bcmf_dev_s *priv,
/* Notify bcmf thread tx frame is ready */
nxsem_post(&sbus->thread_signal);
nxsem_get_value(&sbus->thread_signal, &semcount);
if (semcount < 1)
{
nxsem_post(&sbus->thread_signal);
}
return OK;
}