staging: most: hdm-dim2: monitor atx DBR space
authorAndrey Shvetsov <andrey.shvetsov@k2l.de>
Thu, 15 Sep 2016 14:19:13 +0000 (16:19 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 15 Sep 2016 17:23:29 +0000 (19:23 +0200)
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 <andrey.shvetsov@k2l.de>
Signed-off-by: Christian Gromm <christian.gromm@microchip.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/most/hdm-dim2/dim2_hal.c
drivers/staging/most/hdm-dim2/dim2_hal.h
drivers/staging/most/hdm-dim2/dim2_hdm.c
drivers/staging/most/hdm-dim2/dim2_reg.h

index 695f3169004e35c3ab7e8c9c593b8acc86d1902c..0b9816ce1761533948ccf6084a1931c0b5d8fd49 100644 (file)
@@ -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);
index 777a56e2263589ec1b7e4384a1814fe81358ee0d..6df6ea5f7da4b61a05db684f17ccd16d677f3c4d 100644 (file)
@@ -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);
 
index 0350798d02d4a6516df0f0a001fcc49fdc45e7a7..71f4ae7c3cb10bb35959dda43b05b71e49bd26d6 100644 (file)
@@ -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,
index 3b1c2004e5201b7774f29ba52736862646cb802d..01fe499411ff6c2bd812e15f249dac03ef004d89 100644 (file)
@@ -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),