1 /* 2 * Copyright (C) ST-Ericsson SA 2007-2010 3 * Author: Per Forlin <per.forlin@stericsson.com> for ST-Ericsson 4 * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson 5 * License terms: GNU General Public License (GPL) version 2 6 */ 7 8 #include <linux/kernel.h> 9 #include <plat/ste_dma40.h> 10 11 #include "ste_dma40_ll.h" 12 13 /* Sets up proper LCSP1 and LCSP3 register for a logical channel */ 14 void d40_log_cfg(struct stedma40_chan_cfg *cfg, 15 u32 *lcsp1, u32 *lcsp3) 16 { 17 u32 l3 = 0; /* dst */ 18 u32 l1 = 0; /* src */ 19 20 /* src is mem? -> increase address pos */ 21 if (cfg->dir == STEDMA40_MEM_TO_PERIPH || 22 cfg->dir == STEDMA40_MEM_TO_MEM) 23 l1 |= 1 << D40_MEM_LCSP1_SCFG_INCR_POS; 24 25 /* dst is mem? -> increase address pos */ 26 if (cfg->dir == STEDMA40_PERIPH_TO_MEM || 27 cfg->dir == STEDMA40_MEM_TO_MEM) 28 l3 |= 1 << D40_MEM_LCSP3_DCFG_INCR_POS; 29 30 /* src is hw? -> master port 1 */ 31 if (cfg->dir == STEDMA40_PERIPH_TO_MEM || 32 cfg->dir == STEDMA40_PERIPH_TO_PERIPH) 33 l1 |= 1 << D40_MEM_LCSP1_SCFG_MST_POS; 34 35 /* dst is hw? -> master port 1 */ 36 if (cfg->dir == STEDMA40_MEM_TO_PERIPH || 37 cfg->dir == STEDMA40_PERIPH_TO_PERIPH) 38 l3 |= 1 << D40_MEM_LCSP3_DCFG_MST_POS; 39 40 l3 |= 1 << D40_MEM_LCSP3_DCFG_EIM_POS; 41 l3 |= cfg->dst_info.psize << D40_MEM_LCSP3_DCFG_PSIZE_POS; 42 l3 |= cfg->dst_info.data_width << D40_MEM_LCSP3_DCFG_ESIZE_POS; 43 44 l1 |= 1 << D40_MEM_LCSP1_SCFG_EIM_POS; 45 l1 |= cfg->src_info.psize << D40_MEM_LCSP1_SCFG_PSIZE_POS; 46 l1 |= cfg->src_info.data_width << D40_MEM_LCSP1_SCFG_ESIZE_POS; 47 48 *lcsp1 = l1; 49 *lcsp3 = l3; 50 51 } 52 53 /* Sets up SRC and DST CFG register for both logical and physical channels */ 54 void d40_phy_cfg(struct stedma40_chan_cfg *cfg, 55 u32 *src_cfg, u32 *dst_cfg, bool is_log) 56 { 57 u32 src = 0; 58 u32 dst = 0; 59 60 if (!is_log) { 61 /* Physical channel */ 62 if ((cfg->dir == STEDMA40_PERIPH_TO_MEM) || 63 (cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) { 64 /* Set master port to 1 */ 65 src |= 1 << D40_SREG_CFG_MST_POS; 66 src |= D40_TYPE_TO_EVENT(cfg->src_dev_type); 67 68 if (cfg->src_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL) 69 src |= 1 << D40_SREG_CFG_PHY_TM_POS; 70 else 71 src |= 3 << D40_SREG_CFG_PHY_TM_POS; 72 } 73 if ((cfg->dir == STEDMA40_MEM_TO_PERIPH) || 74 (cfg->dir == STEDMA40_PERIPH_TO_PERIPH)) { 75 /* Set master port to 1 */ 76 dst |= 1 << D40_SREG_CFG_MST_POS; 77 dst |= D40_TYPE_TO_EVENT(cfg->dst_dev_type); 78 79 if (cfg->dst_info.flow_ctrl == STEDMA40_NO_FLOW_CTRL) 80 dst |= 1 << D40_SREG_CFG_PHY_TM_POS; 81 else 82 dst |= 3 << D40_SREG_CFG_PHY_TM_POS; 83 } 84 /* Interrupt on end of transfer for destination */ 85 dst |= 1 << D40_SREG_CFG_TIM_POS; 86 87 /* Generate interrupt on error */ 88 src |= 1 << D40_SREG_CFG_EIM_POS; 89 dst |= 1 << D40_SREG_CFG_EIM_POS; 90 91 /* PSIZE */ 92 if (cfg->src_info.psize != STEDMA40_PSIZE_PHY_1) { 93 src |= 1 << D40_SREG_CFG_PHY_PEN_POS; 94 src |= cfg->src_info.psize << D40_SREG_CFG_PSIZE_POS; 95 } 96 if (cfg->dst_info.psize != STEDMA40_PSIZE_PHY_1) { 97 dst |= 1 << D40_SREG_CFG_PHY_PEN_POS; 98 dst |= cfg->dst_info.psize << D40_SREG_CFG_PSIZE_POS; 99 } 100 101 /* Element size */ 102 src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS; 103 dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS; 104 105 } else { 106 /* Logical channel */ 107 dst |= 1 << D40_SREG_CFG_LOG_GIM_POS; 108 src |= 1 << D40_SREG_CFG_LOG_GIM_POS; 109 } 110 111 if (cfg->high_priority) { 112 src |= 1 << D40_SREG_CFG_PRI_POS; 113 dst |= 1 << D40_SREG_CFG_PRI_POS; 114 } 115 116 if (cfg->src_info.big_endian) 117 src |= 1 << D40_SREG_CFG_LBE_POS; 118 if (cfg->dst_info.big_endian) 119 dst |= 1 << D40_SREG_CFG_LBE_POS; 120 121 *src_cfg = src; 122 *dst_cfg = dst; 123 } 124 125 static int d40_phy_fill_lli(struct d40_phy_lli *lli, 126 dma_addr_t data, 127 u32 data_size, 128 dma_addr_t next_lli, 129 u32 reg_cfg, 130 bool term_int, 131 bool is_device, 132 struct stedma40_half_channel_info *info) 133 { 134 unsigned int data_width = info->data_width; 135 int psize = info->psize; 136 int num_elems; 137 138 if (psize == STEDMA40_PSIZE_PHY_1) 139 num_elems = 1; 140 else 141 num_elems = 2 << psize; 142 143 /* Must be aligned */ 144 if (!IS_ALIGNED(data, 0x1 << data_width)) 145 return -EINVAL; 146 147 /* Transfer size can't be smaller than (num_elms * elem_size) */ 148 if (data_size < num_elems * (0x1 << data_width)) 149 return -EINVAL; 150 151 /* The number of elements. IE now many chunks */ 152 lli->reg_elt = (data_size >> data_width) << D40_SREG_ELEM_PHY_ECNT_POS; 153 154 /* 155 * Distance to next element sized entry. 156 * Usually the size of the element unless you want gaps. 157 */ 158 if (!is_device) 159 lli->reg_elt |= (0x1 << data_width) << 160 D40_SREG_ELEM_PHY_EIDX_POS; 161 162 /* Where the data is */ 163 lli->reg_ptr = data; 164 lli->reg_cfg = reg_cfg; 165 166 /* If this scatter list entry is the last one, no next link */ 167 if (next_lli == 0) 168 lli->reg_lnk = 0x1 << D40_SREG_LNK_PHY_TCP_POS; 169 else 170 lli->reg_lnk = next_lli; 171 172 /* Set/clear interrupt generation on this link item.*/ 173 if (term_int) 174 lli->reg_cfg |= 0x1 << D40_SREG_CFG_TIM_POS; 175 else 176 lli->reg_cfg &= ~(0x1 << D40_SREG_CFG_TIM_POS); 177 178 /* Post link */ 179 lli->reg_lnk |= 0 << D40_SREG_LNK_PHY_PRE_POS; 180 181 return 0; 182 } 183 184 static int d40_seg_size(int size, int data_width1, int data_width2) 185 { 186 u32 max_w = max(data_width1, data_width2); 187 u32 min_w = min(data_width1, data_width2); 188 u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w); 189 190 if (seg_max > STEDMA40_MAX_SEG_SIZE) 191 seg_max -= (1 << max_w); 192 193 if (size <= seg_max) 194 return size; 195 196 if (size <= 2 * seg_max) 197 return ALIGN(size / 2, 1 << max_w); 198 199 return seg_max; 200 } 201 202 static struct d40_phy_lli * 203 d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size, 204 dma_addr_t lli_phys, u32 reg_cfg, bool term_int, 205 bool is_device, struct stedma40_half_channel_info *info, 206 struct stedma40_half_channel_info *otherinfo) 207 { 208 int err; 209 dma_addr_t next = lli_phys; 210 int size_rest = size; 211 int size_seg = 0; 212 213 do { 214 size_seg = d40_seg_size(size_rest, info->data_width, 215 otherinfo->data_width); 216 size_rest -= size_seg; 217 218 if (term_int && size_rest == 0) 219 next = 0; 220 else 221 next = ALIGN(next + sizeof(struct d40_phy_lli), 222 D40_LLI_ALIGN); 223 224 err = d40_phy_fill_lli(lli, 225 addr, 226 size_seg, 227 next, 228 reg_cfg, 229 !next, 230 is_device, 231 info); 232 233 if (err) 234 goto err; 235 236 lli++; 237 if (!is_device) 238 addr += size_seg; 239 } while (size_rest); 240 241 return lli; 242 243 err: 244 return NULL; 245 } 246 247 int d40_phy_sg_to_lli(struct scatterlist *sg, 248 int sg_len, 249 dma_addr_t target, 250 struct d40_phy_lli *lli_sg, 251 dma_addr_t lli_phys, 252 u32 reg_cfg, 253 struct stedma40_half_channel_info *info, 254 struct stedma40_half_channel_info *otherinfo) 255 { 256 int total_size = 0; 257 int i; 258 struct scatterlist *current_sg = sg; 259 dma_addr_t dst; 260 struct d40_phy_lli *lli = lli_sg; 261 dma_addr_t l_phys = lli_phys; 262 263 for_each_sg(sg, current_sg, sg_len, i) { 264 265 total_size += sg_dma_len(current_sg); 266 267 if (target) 268 dst = target; 269 else 270 dst = sg_dma_address(current_sg); 271 272 l_phys = ALIGN(lli_phys + (lli - lli_sg) * 273 sizeof(struct d40_phy_lli), D40_LLI_ALIGN); 274 275 lli = d40_phy_buf_to_lli(lli, 276 dst, 277 sg_dma_len(current_sg), 278 l_phys, 279 reg_cfg, 280 sg_len - 1 == i, 281 target == dst, 282 info, 283 otherinfo); 284 if (lli == NULL) 285 return -EINVAL; 286 } 287 288 return total_size; 289 } 290 291 292 /* DMA logical lli operations */ 293 294 static void d40_log_lli_link(struct d40_log_lli *lli_dst, 295 struct d40_log_lli *lli_src, 296 int next) 297 { 298 u32 slos = 0; 299 u32 dlos = 0; 300 301 if (next != -EINVAL) { 302 slos = next * 2; 303 dlos = next * 2 + 1; 304 } else { 305 lli_dst->lcsp13 |= D40_MEM_LCSP1_SCFG_TIM_MASK; 306 lli_dst->lcsp13 |= D40_MEM_LCSP3_DTCP_MASK; 307 } 308 309 lli_src->lcsp13 = (lli_src->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) | 310 (slos << D40_MEM_LCSP1_SLOS_POS); 311 312 lli_dst->lcsp13 = (lli_dst->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) | 313 (dlos << D40_MEM_LCSP1_SLOS_POS); 314 } 315 316 void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa, 317 struct d40_log_lli *lli_dst, 318 struct d40_log_lli *lli_src, 319 int next) 320 { 321 d40_log_lli_link(lli_dst, lli_src, next); 322 323 writel(lli_src->lcsp02, &lcpa[0].lcsp0); 324 writel(lli_src->lcsp13, &lcpa[0].lcsp1); 325 writel(lli_dst->lcsp02, &lcpa[0].lcsp2); 326 writel(lli_dst->lcsp13, &lcpa[0].lcsp3); 327 } 328 329 void d40_log_lli_lcla_write(struct d40_log_lli *lcla, 330 struct d40_log_lli *lli_dst, 331 struct d40_log_lli *lli_src, 332 int next) 333 { 334 d40_log_lli_link(lli_dst, lli_src, next); 335 336 writel(lli_src->lcsp02, &lcla[0].lcsp02); 337 writel(lli_src->lcsp13, &lcla[0].lcsp13); 338 writel(lli_dst->lcsp02, &lcla[1].lcsp02); 339 writel(lli_dst->lcsp13, &lcla[1].lcsp13); 340 } 341 342 static void d40_log_fill_lli(struct d40_log_lli *lli, 343 dma_addr_t data, u32 data_size, 344 u32 reg_cfg, 345 u32 data_width, 346 bool addr_inc) 347 { 348 lli->lcsp13 = reg_cfg; 349 350 /* The number of elements to transfer */ 351 lli->lcsp02 = ((data_size >> data_width) << 352 D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK; 353 354 BUG_ON((data_size >> data_width) > STEDMA40_MAX_SEG_SIZE); 355 356 /* 16 LSBs address of the current element */ 357 lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK; 358 /* 16 MSBs address of the current element */ 359 lli->lcsp13 |= data & D40_MEM_LCSP1_SPTR_MASK; 360 361 if (addr_inc) 362 lli->lcsp13 |= D40_MEM_LCSP1_SCFG_INCR_MASK; 363 364 } 365 366 struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg, 367 dma_addr_t addr, 368 int size, 369 u32 lcsp13, /* src or dst*/ 370 u32 data_width1, 371 u32 data_width2, 372 bool addr_inc) 373 { 374 struct d40_log_lli *lli = lli_sg; 375 int size_rest = size; 376 int size_seg = 0; 377 378 do { 379 size_seg = d40_seg_size(size_rest, data_width1, data_width2); 380 size_rest -= size_seg; 381 382 d40_log_fill_lli(lli, 383 addr, 384 size_seg, 385 lcsp13, data_width1, 386 addr_inc); 387 if (addr_inc) 388 addr += size_seg; 389 lli++; 390 } while (size_rest); 391 392 return lli; 393 } 394 395 int d40_log_sg_to_lli(struct scatterlist *sg, 396 int sg_len, 397 dma_addr_t dev_addr, 398 struct d40_log_lli *lli_sg, 399 u32 lcsp13, /* src or dst*/ 400 u32 data_width1, u32 data_width2) 401 { 402 int total_size = 0; 403 struct scatterlist *current_sg = sg; 404 int i; 405 struct d40_log_lli *lli = lli_sg; 406 bool autoinc = !dev_addr; 407 408 for_each_sg(sg, current_sg, sg_len, i) { 409 dma_addr_t sg_addr = sg_dma_address(current_sg); 410 unsigned int len = sg_dma_len(current_sg); 411 dma_addr_t addr = dev_addr ?: sg_addr; 412 413 total_size += sg_dma_len(current_sg); 414 415 lli = d40_log_buf_to_lli(lli, addr, len, 416 lcsp13, 417 data_width1, 418 data_width2, 419 autoinc); 420 } 421 422 return total_size; 423 } 424