linux/drivers/dma/ste_dma40_ll.c
<<
>>
Prefs
   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 <linux/platform_data/dma-ste-dma40.h>
  10
  11#include "ste_dma40_ll.h"
  12
  13/* Sets up proper LCSP1 and LCSP3 register for a logical channel */
  14void 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 */
  54void 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
 125static 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
 185static 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
 203static struct d40_phy_lli *
 204d40_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
 257int 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
 302static 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
 327void 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
 340void 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
 353static 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
 379static 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
 409int 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.