From: Andrey Shvetsov Date: Thu, 15 Sep 2016 14:19:13 +0000 (+0200) Subject: staging: most: hdm-dim2: monitor atx DBR space X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=8f5334651a14661f945c2d440479bb9bfa6c2b49;p=linux-beck.git 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: Andrey Shvetsov Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.c b/drivers/staging/most/hdm-dim2/dim2_hal.c index 695f3169004e..0b9816ce1761 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hal.c +++ b/drivers/staging/most/hdm-dim2/dim2_hal.c @@ -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); diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.h b/drivers/staging/most/hdm-dim2/dim2_hal.h index 777a56e22635..6df6ea5f7da4 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hal.h +++ b/drivers/staging/most/hdm-dim2/dim2_hal.h @@ -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); diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.c b/drivers/staging/most/hdm-dim2/dim2_hdm.c index 0350798d02d4..71f4ae7c3cb1 100644 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.c +++ b/drivers/staging/most/hdm-dim2/dim2_hdm.c @@ -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, diff --git a/drivers/staging/most/hdm-dim2/dim2_reg.h b/drivers/staging/most/hdm-dim2/dim2_reg.h index 3b1c2004e520..01fe499411ff 100644 --- a/drivers/staging/most/hdm-dim2/dim2_reg.h +++ b/drivers/staging/most/hdm-dim2/dim2_reg.h @@ -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),