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 struct stedma40_half_channel_info *info, 131 unsigned int flags) 132 { 133 bool addr_inc = flags & LLI_ADDR_INC; 134 bool term_int = flags & LLI_TERM_INT; 135 unsigned int data_width = info->data_width; 136 int psize = info->psize; 137 int num_elems; 138 139 if (psize == STEDMA40_PSIZE_PHY_1) 140 num_elems = 1; 141 else 142 num_elems = 2 << psize; 143 144 /* Must be aligned */ 145 if (!IS_ALIGNED(data, 0x1 << data_width)) 146 return -EINVAL; 147 148 /* Transfer size can't be smaller than (num_elms * elem_size) */ 149 if (data_size < num_elems * (0x1 << data_width)) 150 return -EINVAL; 151 152 /* The number of elements. IE now many chunks */ 153 lli->reg_elt = (data_size >> data_width) << D40_SREG_ELEM_PHY_ECNT_POS; 154 155 /* 156 * Distance to next element sized entry. 157 * Usually the size of the element unless you want gaps. 158 */ 159 if (addr_inc) 160 lli->reg_elt |= (0x1 << data_width) << 161 D40_SREG_ELEM_PHY_EIDX_POS; 162 163 /* Where the data is */ 164 lli->reg_ptr = data; 165 lli->reg_cfg = reg_cfg; 166 167 /* If this scatter list entry is the last one, no next link */ 168 if (next_lli == 0) 169 lli->reg_lnk = 0x1 << D40_SREG_LNK_PHY_TCP_POS; 170 else 171 lli->reg_lnk = next_lli; 172 173 /* Set/clear interrupt generation on this link item.*/ 174 if (term_int) 175 lli->reg_cfg |= 0x1 << D40_SREG_CFG_TIM_POS; 176 else 177 lli->reg_cfg &= ~(0x1 << D40_SREG_CFG_TIM_POS); 178 179 /* Post link */ 180 lli->reg_lnk |= 0 << D40_SREG_LNK_PHY_PRE_POS; 181 182 return 0; 183 } 184 185 static int d40_seg_size(int size, int data_width1, int data_width2) 186 { 187 u32 max_w = max(data_width1, data_width2); 188 u32 min_w = min(data_width1, data_width2); 189 u32 seg_max = ALIGN(STEDMA40_MAX_SEG_SIZE << min_w, 1 << max_w); 190 191 if (seg_max > STEDMA40_MAX_SEG_SIZE) 192 seg_max -= (1 << max_w); 193 194 if (size <= seg_max) 195 return size; 196 197 if (size <= 2 * seg_max) 198 return ALIGN(size / 2, 1 << max_w); 199 200 return seg_max; 201 } 202 203 static struct d40_phy_lli * 204 d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size, 205 dma_addr_t lli_phys, dma_addr_t first_phys, u32 reg_cfg, 206 struct stedma40_half_channel_info *info, 207 struct stedma40_half_channel_info *otherinfo, 208 unsigned long flags) 209 { 210 bool lastlink = flags & LLI_LAST_LINK; 211 bool addr_inc = flags & LLI_ADDR_INC; 212 bool term_int = flags & LLI_TERM_INT; 213 bool cyclic = flags & LLI_CYCLIC; 214 int err; 215 dma_addr_t next = lli_phys; 216 int size_rest = size; 217 int size_seg = 0; 218 219 /* 220 * This piece may be split up based on d40_seg_size(); we only want the 221 * term int on the last part. 222 */ 223 if (term_int) 224 flags &= ~LLI_TERM_INT; 225 226 do { 227 size_seg = d40_seg_size(size_rest, info->data_width, 228 otherinfo->data_width); 229 size_rest -= size_seg; 230 231 if (size_rest == 0 && term_int) 232 flags |= LLI_TERM_INT; 233 234 if (size_rest == 0 && lastlink) 235 next = cyclic ? first_phys : 0; 236 else 237 next = ALIGN(next + sizeof(struct d40_phy_lli), 238 D40_LLI_ALIGN); 239 240 err = d40_phy_fill_lli(lli, addr, size_seg, next, 241 reg_cfg, info, flags); 242 243 if (err) 244 goto err; 245 246 lli++; 247 if (addr_inc) 248 addr += size_seg; 249 } while (size_rest); 250 251 return lli; 252 253 err: 254 return NULL; 255 } 256 257 int d40_phy_sg_to_lli(struct scatterlist *sg, 258 int sg_len, 259 dma_addr_t target, 260 struct d40_phy_lli *lli_sg, 261 dma_addr_t lli_phys, 262 u32 reg_cfg, 263 struct stedma40_half_channel_info *info, 264 struct stedma40_half_channel_info *otherinfo, 265 unsigned long flags) 266 { 267 int total_size = 0; 268 int i; 269 struct scatterlist *current_sg = sg; 270 struct d40_phy_lli *lli = lli_sg; 271 dma_addr_t l_phys = lli_phys; 272 273 if (!target) 274 flags |= LLI_ADDR_INC; 275 276 for_each_sg(sg, current_sg, sg_len, i) { 277 dma_addr_t sg_addr = sg_dma_address(current_sg); 278 unsigned int len = sg_dma_len(current_sg); 279 dma_addr_t dst = target ?: sg_addr; 280 281 total_size += sg_dma_len(current_sg); 282 283 if (i == sg_len - 1) 284 flags |= LLI_TERM_INT | LLI_LAST_LINK; 285 286 l_phys = ALIGN(lli_phys + (lli - lli_sg) * 287 sizeof(struct d40_phy_lli), D40_LLI_ALIGN); 288 289 lli = d40_phy_buf_to_lli(lli, dst, len, l_phys, lli_phys, 290 reg_cfg, info, otherinfo, flags); 291 292 if (lli == NULL) 293 return -EINVAL; 294 } 295 296 return total_size; 297 } 298 299 300 /* DMA logical lli operations */ 301 302 static void d40_log_lli_link(struct d40_log_lli *lli_dst, 303 struct d40_log_lli *lli_src, 304 int next, unsigned int flags) 305 { 306 bool interrupt = flags & LLI_TERM_INT; 307 u32 slos = 0; 308 u32 dlos = 0; 309 310 if (next != -EINVAL) { 311 slos = next * 2; 312 dlos = next * 2 + 1; 313 } 314 315 if (interrupt) { 316 lli_dst->lcsp13 |= D40_MEM_LCSP1_SCFG_TIM_MASK; 317 lli_dst->lcsp13 |= D40_MEM_LCSP3_DTCP_MASK; 318 } 319 320 lli_src->lcsp13 = (lli_src->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) | 321 (slos << D40_MEM_LCSP1_SLOS_POS); 322 323 lli_dst->lcsp13 = (lli_dst->lcsp13 & ~D40_MEM_LCSP1_SLOS_MASK) | 324 (dlos << D40_MEM_LCSP1_SLOS_POS); 325 } 326 327 void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa, 328 struct d40_log_lli *lli_dst, 329 struct d40_log_lli *lli_src, 330 int next, unsigned int flags) 331 { 332 d40_log_lli_link(lli_dst, lli_src, next, flags); 333 334 writel(lli_src->lcsp02, &lcpa[0].lcsp0); 335 writel(lli_src->lcsp13, &lcpa[0].lcsp1); 336 writel(lli_dst->lcsp02, &lcpa[0].lcsp2); 337 writel(lli_dst->lcsp13, &lcpa[0].lcsp3); 338 } 339 340 void d40_log_lli_lcla_write(struct d40_log_lli *lcla, 341 struct d40_log_lli *lli_dst, 342 struct d40_log_lli *lli_src, 343 int next, unsigned int flags) 344 { 345 d40_log_lli_link(lli_dst, lli_src, next, flags); 346 347 writel(lli_src->lcsp02, &lcla[0].lcsp02); 348 writel(lli_src->lcsp13, &lcla[0].lcsp13); 349 writel(lli_dst->lcsp02, &lcla[1].lcsp02); 350 writel(lli_dst->lcsp13, &lcla[1].lcsp13); 351 } 352 353 static void d40_log_fill_lli(struct d40_log_lli *lli, 354 dma_addr_t data, u32 data_size, 355 u32 reg_cfg, 356 u32 data_width, 357 unsigned int flags) 358 { 359 bool addr_inc = flags & LLI_ADDR_INC; 360 361 lli->lcsp13 = reg_cfg; 362 363 /* The number of elements to transfer */ 364 lli->lcsp02 = ((data_size >> data_width) << 365 D40_MEM_LCSP0_ECNT_POS) & D40_MEM_LCSP0_ECNT_MASK; 366 367 BUG_ON((data_size >> data_width) > STEDMA40_MAX_SEG_SIZE); 368 369 /* 16 LSBs address of the current element */ 370 lli->lcsp02 |= data & D40_MEM_LCSP0_SPTR_MASK; 371 /* 16 MSBs address of the current element */ 372 lli->lcsp13 |= data & D40_MEM_LCSP1_SPTR_MASK; 373 374 if (addr_inc) 375 lli->lcsp13 |= D40_MEM_LCSP1_SCFG_INCR_MASK; 376 377 } 378 379 static struct d40_log_lli *d40_log_buf_to_lli(struct d40_log_lli *lli_sg, 380 dma_addr_t addr, 381 int size, 382 u32 lcsp13, /* src or dst*/ 383 u32 data_width1, 384 u32 data_width2, 385 unsigned int flags) 386 { 387 bool addr_inc = flags & LLI_ADDR_INC; 388 struct d40_log_lli *lli = lli_sg; 389 int size_rest = size; 390 int size_seg = 0; 391 392 do { 393 size_seg = d40_seg_size(size_rest, data_width1, data_width2); 394 size_rest -= size_seg; 395 396 d40_log_fill_lli(lli, 397 addr, 398 size_seg, 399 lcsp13, data_width1, 400 flags); 401 if (addr_inc) 402 addr += size_seg; 403 lli++; 404 } while (size_rest); 405 406 return lli; 407 } 408 409 int d40_log_sg_to_lli(struct scatterlist *sg, 410 int sg_len, 411 dma_addr_t dev_addr, 412 struct d40_log_lli *lli_sg, 413 u32 lcsp13, /* src or dst*/ 414 u32 data_width1, u32 data_width2) 415 { 416 int total_size = 0; 417 struct scatterlist *current_sg = sg; 418 int i; 419 struct d40_log_lli *lli = lli_sg; 420 unsigned long flags = 0; 421 422 if (!dev_addr) 423 flags |= LLI_ADDR_INC; 424 425 for_each_sg(sg, current_sg, sg_len, i) { 426 dma_addr_t sg_addr = sg_dma_address(current_sg); 427 unsigned int len = sg_dma_len(current_sg); 428 dma_addr_t addr = dev_addr ?: sg_addr; 429 430 total_size += sg_dma_len(current_sg); 431 432 lli = d40_log_buf_to_lli(lli, addr, len, 433 lcsp13, 434 data_width1, 435 data_width2, 436 flags); 437 } 438 439 return total_size; 440 } 441