linux/drivers/hid/intel-ish-hid/ishtp/dma-if.c
<<
>>
Prefs
   1/*
   2 * ISHTP DMA I/F functions
   3 *
   4 * Copyright (c) 2003-2016, Intel Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13 * more details.
  14 *
  15 */
  16
  17#include <linux/slab.h>
  18#include <linux/sched.h>
  19#include <linux/wait.h>
  20#include <linux/delay.h>
  21#include <linux/dma-mapping.h>
  22#include "ishtp-dev.h"
  23#include "client.h"
  24
  25/**
  26 * ishtp_cl_alloc_dma_buf() - Allocate DMA RX and TX buffer
  27 * @dev: ishtp device
  28 *
  29 * Allocate RX and TX DMA buffer once during bus setup.
  30 * It allocates 1MB, RX and TX DMA buffer, which are divided
  31 * into slots.
  32 */
  33void    ishtp_cl_alloc_dma_buf(struct ishtp_device *dev)
  34{
  35        dma_addr_t      h;
  36
  37        if (dev->ishtp_host_dma_tx_buf)
  38                return;
  39
  40        dev->ishtp_host_dma_tx_buf_size = 1024*1024;
  41        dev->ishtp_host_dma_rx_buf_size = 1024*1024;
  42
  43        /* Allocate Tx buffer and init usage bitmap */
  44        dev->ishtp_host_dma_tx_buf = dma_alloc_coherent(dev->devc,
  45                                        dev->ishtp_host_dma_tx_buf_size,
  46                                        &h, GFP_KERNEL);
  47        if (dev->ishtp_host_dma_tx_buf)
  48                dev->ishtp_host_dma_tx_buf_phys = h;
  49
  50        dev->ishtp_dma_num_slots = dev->ishtp_host_dma_tx_buf_size /
  51                                                DMA_SLOT_SIZE;
  52
  53        dev->ishtp_dma_tx_map = kcalloc(dev->ishtp_dma_num_slots,
  54                                        sizeof(uint8_t),
  55                                        GFP_KERNEL);
  56        spin_lock_init(&dev->ishtp_dma_tx_lock);
  57
  58        /* Allocate Rx buffer */
  59        dev->ishtp_host_dma_rx_buf = dma_alloc_coherent(dev->devc,
  60                                        dev->ishtp_host_dma_rx_buf_size,
  61                                         &h, GFP_KERNEL);
  62
  63        if (dev->ishtp_host_dma_rx_buf)
  64                dev->ishtp_host_dma_rx_buf_phys = h;
  65}
  66
  67/**
  68 * ishtp_cl_free_dma_buf() - Free DMA RX and TX buffer
  69 * @dev: ishtp device
  70 *
  71 * Free DMA buffer when all clients are released. This is
  72 * only happens during error path in ISH built in driver
  73 * model
  74 */
  75void    ishtp_cl_free_dma_buf(struct ishtp_device *dev)
  76{
  77        dma_addr_t      h;
  78
  79        if (dev->ishtp_host_dma_tx_buf) {
  80                h = dev->ishtp_host_dma_tx_buf_phys;
  81                dma_free_coherent(dev->devc, dev->ishtp_host_dma_tx_buf_size,
  82                                  dev->ishtp_host_dma_tx_buf, h);
  83        }
  84
  85        if (dev->ishtp_host_dma_rx_buf) {
  86                h = dev->ishtp_host_dma_rx_buf_phys;
  87                dma_free_coherent(dev->devc, dev->ishtp_host_dma_rx_buf_size,
  88                                  dev->ishtp_host_dma_rx_buf, h);
  89        }
  90
  91        kfree(dev->ishtp_dma_tx_map);
  92        dev->ishtp_host_dma_tx_buf = NULL;
  93        dev->ishtp_host_dma_rx_buf = NULL;
  94        dev->ishtp_dma_tx_map = NULL;
  95}
  96
  97/*
  98 * ishtp_cl_get_dma_send_buf() - Get a DMA memory slot
  99 * @dev:        ishtp device
 100 * @size:       Size of memory to get
 101 *
 102 * Find and return free address of "size" bytes in dma tx buffer.
 103 * the function will mark this address as "in-used" memory.
 104 *
 105 * Return: NULL when no free buffer else a buffer to copy
 106 */
 107void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev,
 108                                uint32_t size)
 109{
 110        unsigned long   flags;
 111        int i, j, free;
 112        /* additional slot is needed if there is rem */
 113        int required_slots = (size / DMA_SLOT_SIZE)
 114                + 1 * (size % DMA_SLOT_SIZE != 0);
 115
 116        spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
 117        for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) {
 118                free = 1;
 119                for (j = 0; j < required_slots; j++)
 120                        if (dev->ishtp_dma_tx_map[i+j]) {
 121                                free = 0;
 122                                i += j;
 123                                break;
 124                        }
 125                if (free) {
 126                        /* mark memory as "caught" */
 127                        for (j = 0; j < required_slots; j++)
 128                                dev->ishtp_dma_tx_map[i+j] = 1;
 129                        spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
 130                        return (i * DMA_SLOT_SIZE) +
 131                                (unsigned char *)dev->ishtp_host_dma_tx_buf;
 132                }
 133        }
 134        spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
 135        dev_err(dev->devc, "No free DMA buffer to send msg\n");
 136        return NULL;
 137}
 138
 139/*
 140 * ishtp_cl_release_dma_acked_mem() - Release DMA memory slot
 141 * @dev:        ishtp device
 142 * @msg_addr:   message address of slot
 143 * @size:       Size of memory to get
 144 *
 145 * Release_dma_acked_mem - returnes the acked memory to free list.
 146 * (from msg_addr, size bytes long)
 147 */
 148void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev,
 149                                    void *msg_addr,
 150                                    uint8_t size)
 151{
 152        unsigned long   flags;
 153        int acked_slots = (size / DMA_SLOT_SIZE)
 154                + 1 * (size % DMA_SLOT_SIZE != 0);
 155        int i, j;
 156
 157        if ((msg_addr - dev->ishtp_host_dma_tx_buf) % DMA_SLOT_SIZE) {
 158                dev_err(dev->devc, "Bad DMA Tx ack address\n");
 159                return;
 160        }
 161
 162        i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE;
 163        spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
 164        for (j = 0; j < acked_slots; j++) {
 165                if ((i + j) >= dev->ishtp_dma_num_slots ||
 166                                        !dev->ishtp_dma_tx_map[i+j]) {
 167                        /* no such slot, or memory is already free */
 168                        spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
 169                        dev_err(dev->devc, "Bad DMA Tx ack address\n");
 170                        return;
 171                }
 172                dev->ishtp_dma_tx_map[i+j] = 0;
 173        }
 174        spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags);
 175}
 176