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