Commit 8f533465 authored by Andrey Shvetsov's avatar Andrey Shvetsov Committed by Greg Kroah-Hartman

staging: most: hdm-dim2: monitor atx DBR space

This patch implements a workaround for a DIM2 issue where the device
macro, in case the asynchronous channel sends data (to MOST), sporadically
duplicates the synchronous blocks with a size of half the synchronous DBR
buffer.

The patch monitors the size of the unused asynchronous Tx DBR memory
(that includes the ping and pong sizes) to prevent the potential DBR
overflow for the asynchronous Tx DBR buffer.

The patched DIM2 HDM expects that the platform driver delivers the 2nd
platform irq (index 1) as the mlb_int of the DIM2 macro.
Signed-off-by: default avatarAndrey Shvetsov <andrey.shvetsov@k2l.de>
Signed-off-by: default avatarChristian Gromm <christian.gromm@microchip.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a0adbc7a
......@@ -68,10 +68,19 @@ static inline bool dim_on_error(u8 error_id, const char *error_message)
/* -------------------------------------------------------------------------- */
/* types and local variables */
struct async_tx_dbr {
u8 ch_addr;
u16 rpc;
u16 wpc;
u16 rest_size;
u16 sz_queue[CDT0_RPC_MASK + 1];
};
struct lld_global_vars_t {
bool dim_is_initialized;
bool mcm_is_initialized;
struct dim2_regs __iomem *dim2; /* DIM2 core base address */
struct async_tx_dbr atx_dbr;
u32 fcnt;
u32 dbr_map[DBR_MAP_SIZE];
};
......@@ -253,6 +262,13 @@ static void dim2_configure_cdt(u8 ch_addr, u16 dbr_address, u16 hw_buffer_size,
dim2_write_ctr(CDT + ch_addr, cdt);
}
static u16 dim2_rpc(u8 ch_addr)
{
u32 cdt0 = dim2_read_ctr(CDT + ch_addr, 0);
return (cdt0 >> CDT0_RPC_SHIFT) & CDT0_RPC_MASK;
}
static void dim2_clear_cdt(u8 ch_addr)
{
u32 cdt[4] = { 0, 0, 0, 0 };
......@@ -362,6 +378,49 @@ static void dim2_clear_channel(u8 ch_addr)
dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr));
}
/* -------------------------------------------------------------------------- */
/* trace async tx dbr fill state */
static inline u16 norm_pc(u16 pc)
{
return pc & CDT0_RPC_MASK;
}
static void dbrcnt_init(u8 ch_addr, u16 dbr_size)
{
g.atx_dbr.rest_size = dbr_size;
g.atx_dbr.rpc = dim2_rpc(ch_addr);
g.atx_dbr.wpc = g.atx_dbr.rpc;
}
static void dbrcnt_enq(int buf_sz)
{
g.atx_dbr.rest_size -= buf_sz;
g.atx_dbr.sz_queue[norm_pc(g.atx_dbr.wpc)] = buf_sz;
g.atx_dbr.wpc++;
}
u16 dim_dbr_space(struct dim_channel *ch)
{
u16 cur_rpc;
struct async_tx_dbr *dbr = &g.atx_dbr;
if (ch->addr != dbr->ch_addr)
return 0xFFFF;
cur_rpc = dim2_rpc(ch->addr);
while (norm_pc(dbr->rpc) != cur_rpc) {
dbr->rest_size += dbr->sz_queue[norm_pc(dbr->rpc)];
dbr->rpc++;
}
if ((u16)(dbr->wpc - dbr->rpc) >= CDT0_RPC_MASK)
return 0;
return dbr->rest_size;
}
/* -------------------------------------------------------------------------- */
/* channel state helpers */
......@@ -614,6 +673,9 @@ static bool channel_start(struct dim_channel *ch, u32 buf_addr, u16 buf_size)
++state->level;
if (ch->addr == g.atx_dbr.ch_addr)
dbrcnt_enq(buf_size);
if (ch->packet_length || ch->bytes_per_frame)
dim2_start_isoc_sync(ch->addr, state->idx1, buf_addr, buf_size);
else
......@@ -713,6 +775,12 @@ static u8 init_ctrl_async(struct dim_channel *ch, u8 type, u8 is_tx,
return DIM_NO_ERROR;
}
void dim_service_mlb_int_irq(void)
{
dimcb_io_write(&g.dim2->MS0, 0);
dimcb_io_write(&g.dim2->MS1, 0);
}
u16 dim_norm_ctrl_async_buffer_size(u16 buf_size)
{
return norm_ctrl_async_buffer_size(buf_size);
......@@ -756,8 +824,16 @@ u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address,
u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address,
u16 max_buffer_size)
{
return init_ctrl_async(ch, CAT_CT_VAL_ASYNC, is_tx, ch_address,
max_buffer_size);
u8 ret = init_ctrl_async(ch, CAT_CT_VAL_ASYNC, is_tx, ch_address,
max_buffer_size);
if (is_tx && !g.atx_dbr.ch_addr) {
g.atx_dbr.ch_addr = ch->addr;
dbrcnt_init(ch->addr, ch->dbr_size);
dimcb_io_write(&g.dim2->MIEN, bit_mask(20));
}
return ret;
}
u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address,
......@@ -818,6 +894,11 @@ u8 dim_destroy_channel(struct dim_channel *ch)
if (!g.dim_is_initialized || !ch)
return DIM_ERR_DRIVER_NOT_INITIALIZED;
if (ch->addr == g.atx_dbr.ch_addr) {
dimcb_io_write(&g.dim2->MIEN, 0);
g.atx_dbr.ch_addr = 0;
}
dim2_clear_channel(ch->addr);
if (ch->dbr_addr < DBR_SIZE)
free_dbr(ch->dbr_addr, ch->dbr_size);
......
......@@ -87,6 +87,8 @@ u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
u8 dim_destroy_channel(struct dim_channel *ch);
void dim_service_mlb_int_irq(void);
void dim_service_ahb_int_irq(struct dim_channel *const *channels);
u8 dim_service_channel(struct dim_channel *ch);
......@@ -94,6 +96,8 @@ u8 dim_service_channel(struct dim_channel *ch);
struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch,
struct dim_ch_state_t *state_ptr);
u16 dim_dbr_space(struct dim_channel *ch);
bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr,
u16 buffer_size);
......
......@@ -249,6 +249,11 @@ static int try_start_dim_transfer(struct hdm_channel *hdm_ch)
mbo = list_first_entry(head, struct mbo, list);
buf_size = mbo->buffer_length;
if (dim_dbr_space(&hdm_ch->ch) < buf_size) {
spin_unlock_irqrestore(&dim_lock, flags);
return -EAGAIN;
}
BUG_ON(mbo->bus_address == 0);
if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) {
list_del(head->next);
......@@ -406,6 +411,22 @@ static struct dim_channel **get_active_channels(struct dim2_hdm *dev,
return buffer;
}
static irqreturn_t dim2_mlb_isr(int irq, void *_dev)
{
struct dim2_hdm *dev = _dev;
unsigned long flags;
spin_lock_irqsave(&dim_lock, flags);
dim_service_mlb_int_irq();
spin_unlock_irqrestore(&dim_lock, flags);
if (dev->atx_idx >= 0 && dev->hch[dev->atx_idx].is_initialized)
while (!try_start_dim_transfer(dev->hch + dev->atx_idx))
continue;
return IRQ_HANDLED;
}
/**
* dim2_tasklet_fn - tasklet function
* @data: private data
......@@ -743,6 +764,20 @@ static int dim2_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to request ahb0_int irq %d\n", irq);
return ret;
}
irq = platform_get_irq(pdev, 1);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get mlb_int irq\n");
return -ENODEV;
}
ret = devm_request_irq(&pdev->dev, irq, dim2_mlb_isr, 0,
"dim2_mlb_int", dev);
if (ret) {
dev_err(&pdev->dev, "failed to request mlb_int irq %d\n", irq);
return ret;
}
init_waitqueue_head(&dev->netinfo_waitq);
dev->deliver_netinfo = 0;
dev->netinfo_task = kthread_run(&deliver_netinfo_thread, (void *)dev,
......
......@@ -117,6 +117,9 @@ enum {
};
enum {
CDT0_RPC_SHIFT = 16 + 11,
CDT0_RPC_MASK = DIM2_MASK(5),
CDT1_BS_ISOC_SHIFT = 0,
CDT1_BS_ISOC_MASK = DIM2_MASK(9),
......
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