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