linux/drivers/misc/mei/dma-ring.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright(c) 2016-2018 Intel Corporation. All rights reserved.
   4 */
   5#include <linux/dma-mapping.h>
   6#include <linux/mei.h>
   7
   8#include "mei_dev.h"
   9
  10/**
  11 * mei_dmam_dscr_alloc() - allocate a managed coherent buffer
  12 *     for the dma descriptor
  13 * @dev: mei_device
  14 * @dscr: dma descriptor
  15 *
  16 * Return:
  17 * * 0       - on success or zero allocation request
  18 * * -EINVAL - if size is not power of 2
  19 * * -ENOMEM - of allocation has failed
  20 */
  21static int mei_dmam_dscr_alloc(struct mei_device *dev,
  22                               struct mei_dma_dscr *dscr)
  23{
  24        if (!dscr->size)
  25                return 0;
  26
  27        if (WARN_ON(!is_power_of_2(dscr->size)))
  28                return -EINVAL;
  29
  30        if (dscr->vaddr)
  31                return 0;
  32
  33        dscr->vaddr = dmam_alloc_coherent(dev->dev, dscr->size, &dscr->daddr,
  34                                          GFP_KERNEL);
  35        if (!dscr->vaddr)
  36                return -ENOMEM;
  37
  38        return 0;
  39}
  40
  41/**
  42 * mei_dmam_dscr_free() - free a managed coherent buffer
  43 *     from the dma descriptor
  44 * @dev: mei_device
  45 * @dscr: dma descriptor
  46 */
  47static void mei_dmam_dscr_free(struct mei_device *dev,
  48                               struct mei_dma_dscr *dscr)
  49{
  50        if (!dscr->vaddr)
  51                return;
  52
  53        dmam_free_coherent(dev->dev, dscr->size, dscr->vaddr, dscr->daddr);
  54        dscr->vaddr = NULL;
  55}
  56
  57/**
  58 * mei_dmam_ring_free() - free dma ring buffers
  59 * @dev: mei device
  60 */
  61void mei_dmam_ring_free(struct mei_device *dev)
  62{
  63        int i;
  64
  65        for (i = 0; i < DMA_DSCR_NUM; i++)
  66                mei_dmam_dscr_free(dev, &dev->dr_dscr[i]);
  67}
  68
  69/**
  70 * mei_dmam_ring_alloc() - allocate dma ring buffers
  71 * @dev: mei device
  72 *
  73 * Return: -ENOMEM on allocation failure 0 otherwise
  74 */
  75int mei_dmam_ring_alloc(struct mei_device *dev)
  76{
  77        int i;
  78
  79        for (i = 0; i < DMA_DSCR_NUM; i++)
  80                if (mei_dmam_dscr_alloc(dev, &dev->dr_dscr[i]))
  81                        goto err;
  82
  83        return 0;
  84
  85err:
  86        mei_dmam_ring_free(dev);
  87        return -ENOMEM;
  88}
  89
  90/**
  91 * mei_dma_ring_is_allocated() - check if dma ring is allocated
  92 * @dev: mei device
  93 *
  94 * Return: true if dma ring is allocated
  95 */
  96bool mei_dma_ring_is_allocated(struct mei_device *dev)
  97{
  98        return !!dev->dr_dscr[DMA_DSCR_HOST].vaddr;
  99}
 100
 101static inline
 102struct hbm_dma_ring_ctrl *mei_dma_ring_ctrl(struct mei_device *dev)
 103{
 104        return (struct hbm_dma_ring_ctrl *)dev->dr_dscr[DMA_DSCR_CTRL].vaddr;
 105}
 106
 107/**
 108 * mei_dma_ring_reset() - reset the dma control block
 109 * @dev: mei device
 110 */
 111void mei_dma_ring_reset(struct mei_device *dev)
 112{
 113        struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
 114
 115        if (!ctrl)
 116                return;
 117
 118        memset(ctrl, 0, sizeof(*ctrl));
 119}
 120
 121/**
 122 * mei_dma_copy_from() - copy from dma ring into buffer
 123 * @dev: mei device
 124 * @buf: data buffer
 125 * @offset: offset in slots.
 126 * @n: number of slots to copy.
 127 */
 128static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
 129                                u32 offset, u32 n)
 130{
 131        unsigned char *dbuf = dev->dr_dscr[DMA_DSCR_DEVICE].vaddr;
 132
 133        size_t b_offset = offset << 2;
 134        size_t b_n = n << 2;
 135
 136        memcpy(buf, dbuf + b_offset, b_n);
 137
 138        return b_n;
 139}
 140
 141/**
 142 * mei_dma_copy_to() - copy to a buffer to the dma ring
 143 * @dev: mei device
 144 * @buf: data buffer
 145 * @offset: offset in slots.
 146 * @n: number of slots to copy.
 147 */
 148static size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf,
 149                              u32 offset, u32 n)
 150{
 151        unsigned char *hbuf = dev->dr_dscr[DMA_DSCR_HOST].vaddr;
 152
 153        size_t b_offset = offset << 2;
 154        size_t b_n = n << 2;
 155
 156        memcpy(hbuf + b_offset, buf, b_n);
 157
 158        return b_n;
 159}
 160
 161/**
 162 * mei_dma_ring_read() - read data from the ring
 163 * @dev: mei device
 164 * @buf: buffer to read into: may be NULL in case of droping the data.
 165 * @len: length to read.
 166 */
 167void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len)
 168{
 169        struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
 170        u32 dbuf_depth;
 171        u32 rd_idx, rem, slots;
 172
 173        if (WARN_ON(!ctrl))
 174                return;
 175
 176        dev_dbg(dev->dev, "reading from dma %u bytes\n", len);
 177
 178        if (!len)
 179                return;
 180
 181        dbuf_depth = dev->dr_dscr[DMA_DSCR_DEVICE].size >> 2;
 182        rd_idx = READ_ONCE(ctrl->dbuf_rd_idx) & (dbuf_depth - 1);
 183        slots = mei_data2slots(len);
 184
 185        /* if buf is NULL we drop the packet by advancing the pointer.*/
 186        if (!buf)
 187                goto out;
 188
 189        if (rd_idx + slots > dbuf_depth) {
 190                buf += mei_dma_copy_from(dev, buf, rd_idx, dbuf_depth - rd_idx);
 191                rem = slots - (dbuf_depth - rd_idx);
 192                rd_idx = 0;
 193        } else {
 194                rem = slots;
 195        }
 196
 197        mei_dma_copy_from(dev, buf, rd_idx, rem);
 198out:
 199        WRITE_ONCE(ctrl->dbuf_rd_idx, ctrl->dbuf_rd_idx + slots);
 200}
 201
 202static inline u32 mei_dma_ring_hbuf_depth(struct mei_device *dev)
 203{
 204        return dev->dr_dscr[DMA_DSCR_HOST].size >> 2;
 205}
 206
 207/**
 208 * mei_dma_ring_empty_slots() - calaculate number of empty slots in dma ring
 209 * @dev: mei_device
 210 *
 211 * Return: number of empty slots
 212 */
 213u32 mei_dma_ring_empty_slots(struct mei_device *dev)
 214{
 215        struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
 216        u32 wr_idx, rd_idx, hbuf_depth, empty;
 217
 218        if (!mei_dma_ring_is_allocated(dev))
 219                return 0;
 220
 221        if (WARN_ON(!ctrl))
 222                return 0;
 223
 224        /* easier to work in slots */
 225        hbuf_depth = mei_dma_ring_hbuf_depth(dev);
 226        rd_idx = READ_ONCE(ctrl->hbuf_rd_idx);
 227        wr_idx = READ_ONCE(ctrl->hbuf_wr_idx);
 228
 229        if (rd_idx > wr_idx)
 230                empty = rd_idx - wr_idx;
 231        else
 232                empty = hbuf_depth - (wr_idx - rd_idx);
 233
 234        return empty;
 235}
 236
 237/**
 238 * mei_dma_ring_write - write data to dma ring host buffer
 239 *
 240 * @dev: mei_device
 241 * @buf: data will be written
 242 * @len: data length
 243 */
 244void mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len)
 245{
 246        struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
 247        u32 hbuf_depth;
 248        u32 wr_idx, rem, slots;
 249
 250        if (WARN_ON(!ctrl))
 251                return;
 252
 253        dev_dbg(dev->dev, "writing to dma %u bytes\n", len);
 254        hbuf_depth = mei_dma_ring_hbuf_depth(dev);
 255        wr_idx = READ_ONCE(ctrl->hbuf_wr_idx) & (hbuf_depth - 1);
 256        slots = mei_data2slots(len);
 257
 258        if (wr_idx + slots > hbuf_depth) {
 259                buf += mei_dma_copy_to(dev, buf, wr_idx, hbuf_depth - wr_idx);
 260                rem = slots - (hbuf_depth - wr_idx);
 261                wr_idx = 0;
 262        } else {
 263                rem = slots;
 264        }
 265
 266        mei_dma_copy_to(dev, buf, wr_idx, rem);
 267
 268        WRITE_ONCE(ctrl->hbuf_wr_idx, ctrl->hbuf_wr_idx + slots);
 269}
 270