pl330 dmac驱动分析2----关键函数

来源:互联网 发布:反网络控制软件 编辑:程序博客网 时间:2024/06/01 09:17

下面介绍pl330驱动关键函数:

1 在驱动probe函数中,注册pl330中断服务函数


2927         ret = request_irq(irq, pl330_irq_handler, 0,

  2928                         dev_name(&adev->dev), pi);



  2878 static irqreturn_t pl330_irq_handler(int irq, void *data)
  2879 {
  2880         if (pl330_update(data))
  2881                 return IRQ_HANDLED;
  2882         else
  2883                 return IRQ_NONE;

  2884 }



1690 /* Returns 1 if state was updated, 0 otherwise */
  1691 static int pl330_update(const struct pl330_info *pi)
  1692 {
  1693         struct pl330_req *rqdone, *tmp;
  1694         struct pl330_dmac *pl330;
  1695         unsigned long flags;
  1696         void __iomem *regs;
  1697         u32 val;
  1698         int id, ev, ret = 0;
  1699
  1700         if (!pi || !pi->pl330_data)
  1701                 return 0;
  1702
  1703         regs = pi->base;
  1704         pl330 = pi->pl330_data;
  1705
  1706         spin_lock_irqsave(&pl330->lock, flags);
  1707
  1708         val = readl(regs + FSM) & 0x1;                         //读取管理通道的fsm寄存器,判断是否需要复位管理通道
  1709         if (val)
  1710                 pl330->dmac_tbd.reset_mngr = true;
  1711         else
  1712                 pl330->dmac_tbd.reset_mngr = false;
  1713
  1714         val = readl(regs + FSC) & ((1 << pi->pcfg.num_chan) - 1);  //读取普通通道的fsc寄存器,判断哪些通道需要复位
  1715         pl330->dmac_tbd.reset_chan |= val;
  1716         if (val) {
  1717                 int i = 0;
  1718                 while (i < pi->pcfg.num_chan) {    //循环复位相应的通道
  1719                         if (val & (1 << i)) {
  1720                                 dev_info(pi->dev,
  1721                                         "Reset Channel-%d\t CS-%x FTC-%x\n",
  1722                                                 i, readl(regs + CS(i)),
  1723                                                 readl(regs + FTC(i)));
  1724                                 _stop(&pl330->channels[i]);               //停止相应通道线程
  1725                         }

1726                         i++;
  1727                 }
  1728         }
  1729
  1730         /* Check which event happened i.e, thread notified */
  1731         val = readl(regs + ES);                            //读取es寄存器,检查发生的事件
  1732         if (pi->pcfg.num_events < 32
  1733                         && val & ~((1 << pi->pcfg.num_events) - 1)) {
  1734                 pl330->dmac_tbd.reset_dmac = true;
  1735                 dev_err(pi->dev, "%s:%d Unexpected!\n", __func__, __LINE__);
  1736                 ret = 1;
  1737                 goto updt_exit;
  1738         }
  1739
  1740         for (ev = 0; ev < pi->pcfg.num_events; ev++) {   //循环处理已经发生的事件
  1741                 if (val & (1 << ev)) { /* Event occurred */           
  1742                         struct pl330_thread *thrd;
  1743                         u32 inten = readl(regs + INTEN);   //读取中断使能寄存器
  1744                         int active;
  1745
  1746                         /* Clear the event */
  1747                         if (inten & (1 << ev))                   //如果相应事件的中断已经发生,清除相应中断
  1748                                 writel(1 << ev, regs + INTCLR);   //清除中断
  1749
  1750                         ret = 1;
  1751
  1752                         id = pl330->events[ev];          //获取事件对应的通道id
  1753
  1754                         thrd = &pl330->channels[id];       //获取dmac处理线程
  1755
  1756                         active = thrd->req_running;   //获取当前正在处理的请求       
  1757                         if (active == -1) /* Aborted */
  1758                                 continue;
  1759
  1760                         /* Detach the req */
  1761                         rqdone = thrd->req[active].r;           //获取已经处理的请求
  1762                         thrd->req[active].r = NULL;            //将该请求指针置空
  1763
  1764                         mark_free(thrd, active);         //将该dmac处理线程的请求标记为free
  1765

1766                         /* Get going again ASAP */
  1767                         _start(thrd);                           //继续该dmac处理线程
  1768
  1769                         /* For now, just make a list of callbacks to be done */
  1770                         list_add_tail(&rqdone->rqd, &pl330->req_done);         //将该请求加入到已经处理完成链表
  1771                 }
  1772         }
  1773
  1774         /* Now that we are in no hurry, do the callbacks */
  1775         list_for_each_entry_safe(rqdone, tmp, &pl330->req_done, rqd) {            //处理pl330 dmac中的已经完成处理链表的每个成员
  1776                 list_del(&rqdone->rqd);   //从链表删除该节点
  1777
  1778                 spin_unlock_irqrestore(&pl330->lock, flags);
  1779                 _callback(rqdone, PL330_ERR_NONE);   //调用请求中的回调处理该完成请求。
  1780                 spin_lock_irqsave(&pl330->lock, flags);
  1781         }
  1782
  1783 updt_exit:
  1784         spin_unlock_irqrestore(&pl330->lock, flags);
  1785
  1786         if (pl330->dmac_tbd.reset_dmac
  1787                         || pl330->dmac_tbd.reset_mngr
  1788                         || pl330->dmac_tbd.reset_chan) {
  1789                 ret = 1;
  1790                 tasklet_schedule(&pl330->tasks);
  1791         }
  1792
  1793         return ret;
  1794 }

该函数主要功能为处理dmac管理通道和普通通道的复位,以及处理请求完成事件处理,同时通过回调函数处理完成的请求。


1139 static void _stop(struct pl330_thread *thrd)
  1140 {
  1141         void __iomem *regs = thrd->dmac->pinfo->base;
  1142         u8 insn[6] = {0, 0, 0, 0, 0, 0};
  1143
  1144         if (_state(thrd) == PL330_STATE_FAULT_COMPLETING)   //判断dmac线程状态
  1145                 UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING);   //等待其状态改变
  1146
  1147         /* Return if nothing needs to be done */
  1148         if (_state(thrd) == PL330_STATE_COMPLETING
  1149                   || _state(thrd) == PL330_STATE_KILLING
  1150                   || _state(thrd) == PL330_STATE_STOPPED)
  1151                 return;
  1152
  1153         _emit_KILL(0, insn);  //设置kill指令到insn
  1154
  1155         /* Stop generating interrupts for SEV */
  1156         writel(readl(regs + INTEN) & ~(1 << thrd->ev), regs + INTEN);   //清除事件的中断
  1157
  1158         _execute_DBGINSN(thrd, insn, is_manager(thrd));  //使相应的处理线程执行kill指令
  1159 }

该函数停止相应的dmac线程



1071 static void mark_free(struct pl330_thread *thrd, int idx)
  1072 {
  1073         struct _pl330_req *req = &thrd->req[idx];
  1074
  1075         _emit_END(0, req->mc_cpu);   //设置请求的指令缓存区指令为end
  1076         req->mc_len = 0;
  1077
  1078         thrd->req_running = -1;

  1079 }

 该函数将一个请求标记为free


1161 /* Start doing req 'idx' of thread 'thrd' */
  1162 static bool _trigger(struct pl330_thread *thrd)
  1163 {
  1164         void __iomem *regs = thrd->dmac->pinfo->base;
  1165         struct _pl330_req *req;
  1166         struct pl330_req *r;
  1167         struct _arg_GO go;
  1168         unsigned ns;
  1169         u8 insn[6] = {0, 0, 0, 0, 0, 0};
  1170         int idx;
  1171
  1172         /* Return if already ACTIVE */
  1173         if (_state(thrd) != PL330_STATE_STOPPED)
  1174                 return true;
  1175
  1176         idx = 1 - thrd->lstenq;               //获取需要被执行的请求索引
  1177         if (!IS_FREE(&thrd->req[idx]))           //检查该请求是否free
  1178                 req = &thrd->req[idx];           //获取需要被执行的请求
  1179         else {
  1180                 idx = thrd->lstenq;
  1181                 if (!IS_FREE(&thrd->req[idx]))
  1182                         req = &thrd->req[idx];
  1183                 else
  1184                         req = NULL;
  1185         }
  1186
  1187         /* Return if no request */
  1188         if (!req || !req->r)
  1189                 return true;
  1190
  1191         r = req->r;
  1192
  1193         if (r->cfg)
  1194                 ns = r->cfg->nonsecure ? 1 : 0;            //查看该请求配置的模式
  1195         else if (readl(regs + CS(thrd->id)) & CS_CNS)
  1196                 ns = 1;
  1197         else
  1198                 ns = 0;
  1199
  1200         /* See 'Abort Sources' point-4 at Page 2-25 */
  1201         if (_manager_ns(thrd) && !ns)
  1202                 dev_info(thrd->dmac->pinfo->dev, "%s:%d Recipe for ABORT!\n",
  1203                         __func__, __LINE__);
  1204
  1205         go.chan = thrd->id;
  1206         go.addr = req->mc_bus;
  1207         go.ns = ns;
  1208         _emit_GO(0, insn, &go);  //设置go指令
  1209
  1210         /* Set to generate interrupts for SEV */
  1211         writel(readl(regs + INTEN) | (1 << thrd->ev), regs + INTEN);  //设置相应的事件产生中断
  1212
  1213         /* Only manager can execute GO */
  1214         _execute_DBGINSN(thrd, insn, true);  //相应线程执行指令
  1215
  1216         thrd->req_running = idx;          //设置线程正在执行的请求索引
  1217
  1218         return true;
  1219 }
该函数使相应线程重新开始处理下一个请求




 616 static inline void _callback(struct pl330_req *r, enum pl330_op_err err)
   617 {
   618         if (r && r->xfer_cb)
   619                 r->xfer_cb(r->token, err);
   620 }


调用请求回调处理函数处理完成请求




2 添加pl330 dmac实例

 2932         ret = pl330_add(pi);

  2079 static int pl330_add(struct pl330_info *pi)
  2080 {
  2081         struct pl330_dmac *pl330;
  2082         void __iomem *regs;
  2083         int i, ret;
  2084
  2085         pr_info("%s:enter!\n", __func__);
  2086
  2087         if (!pi || !pi->dev)
  2088                 return -EINVAL;
  2089
  2090         /* If already added */
  2091         if (pi->pl330_data)
  2092                 return -EINVAL;
  2093
  2094         /*
  2095          * If the SoC can perform reset on the DMAC, then do it
  2096          * before reading its configuration.
  2097          */
  2098         if (pi->dmac_reset)
  2099                 pi->dmac_reset(pi);            //执行平台指定的dmac复位程序
  2100
  2101         regs = pi->base;
  2102
  2103         /* Check if we can handle this DMAC */
  2104         if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL               //通过读取dmac寄存器检查是否可以操作该dmac
  2105            || get_id(pi, PCELL_ID) != PCELL_ID_VAL) {
  2106                 dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n",
  2107                         get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID));
  2108                 return -EINVAL;
  2109         }

2110
  2111         pr_info("%s:read_dmac_config()\n", __func__);
  2112         /* Read the configuration of the DMAC */
  2113         read_dmac_config(pi);                   //读取dmac配置,填充struct pl330_info的struct pl330_config成员
  2114
  2115
  2116         if (pi->pcfg.num_events == 0) {
  2117                 dev_err(pi->dev, "%s:%d Can't work without events!\n",
  2118                         __func__, __LINE__);
  2119                 return -EINVAL;
  2120         }
  2121
  2122         pl330 = kzalloc(sizeof(*pl330), GFP_KERNEL);                      //分配pl330 dmac实例内存空间
  2123         if (!pl330) {
  2124                 dev_err(pi->dev, "%s:%d Can't allocate memory!\n",
  2125                         __func__, __LINE__);
  2126                 return -ENOMEM;
  2127         }
  2128
  2129         /* Assign the info structure and private data */
  2130         pl330->pinfo = pi;                                                      //设置pl330 dmac
  2131         pi->pl330_data = pl330;                                              //将pl330 dmac实例连接到上层pl330 dmac抽象
  2132
  2133         spin_lock_init(&pl330->lock);
  2134
  2135         INIT_LIST_HEAD(&pl330->req_done);*/
  2136
  2137         /* Use default MC buffer size if not provided */
  2138       if (!pi->mcbufsz)
  2139               pi->mcbufsz = MCODE_BUFF_PER_REQ * 2;             //设置pl330 info

2140
  2141         /* Mark all events as free */
  2142       for (i = 0; i < pi->pcfg.num_events; i++)
  2143                 pl330->events[i] = -1;                              //free全部的pl330 dmac 事件
  2144
  2145         pr_info("%s:dmac_alloc_resources()\n", __func__);
  2146         /* Allocate resources needed by the DMAC */
  2147        ret = dmac_alloc_resources(pl330);                           //分配pl330 dmac需要的资源
  2148         if (ret) {
  2149                 dev_err(pi->dev, "Unable to create channels for DMAC\n");
  2150                 kfree(pl330);
  2151                 return ret;
  2152         }
  2153
  2154         tasklet_init(&pl330->tasks, pl330_dotask, (unsigned long) pl330);          //初始化pl330 dmac的struct tasklet_struct软中断处理,该操作主要处理一些dmac出现的错误
  2155
  2156         pl330->state = INIT;
  2157
  2158         return 0;
  2159 }


  1950 static void read_dmac_config(struct pl330_info *pi)
  1951 {
  1952         void __iomem *regs = pi->base;
  1953         u32 val;
  1954
  1955         val = readl(regs + CRD) >> CRD_DATA_WIDTH_SHIFT;               //读取配置寄存器
  1956         val &= CRD_DATA_WIDTH_MASK;    
  1957         pi->pcfg.data_bus_width = 8 * (1 << val);
  1958
  1959         val = readl(regs + CRD) >> CRD_DATA_BUFF_SHIFT;
  1960         val &= CRD_DATA_BUFF_MASK;     
  1961         pi->pcfg.data_buf_dep = val + 1;
  1962
  1963         val = readl(regs + CR0) >> CR0_NUM_CHANS_SHIFT;
  1964         val &= CR0_NUM_CHANS_MASK;     
  1965         val += 1;
  1966         pi->pcfg.num_chan = val;       
  1967
  1968         val = readl(regs + CR0);       
  1969         if (val & CR0_PERIPH_REQ_SET) {
  1970                 val = (val >> CR0_NUM_PERIPH_SHIFT) & CR0_NUM_PERIPH_MASK;
  1971                 val += 1;
  1972                 pi->pcfg.num_peri = val;       
  1973                 pi->pcfg.peri_ns = readl(regs + CR4);
  1974         } else {
  1975                 pi->pcfg.num_peri = 0;         
  1976         }
  1977
  1978         val = readl(regs + CR0);       
  1979         if (val & CR0_BOOT_MAN_NS)     
  1980                 pi->pcfg.mode |= DMAC_MODE_NS;
  1981         else
  1982                 pi->pcfg.mode &= ~DMAC_MODE_NS;
  1983
  1984         val = readl(regs + CR0) >> CR0_NUM_EVENTS_SHIFT;
  1985         val &= CR0_NUM_EVENTS_MASK;    
  1986         val += 1;
  1987         pi->pcfg.num_events = val;     
  1988
  1989         pi->pcfg.irq_ns = readl(regs + CR3);
  1990
  1991         pi->pcfg.periph_id = get_id(pi, PERIPH_ID);
  1992         pi->pcfg.pcell_id = get_id(pi, PCELL_ID);
  1993 }

此函数通过读取配置寄存器初始化pl330 info的struct pl330_config结构




  2047 static int dmac_alloc_resources(struct pl330_dmac *pl330)
  2048 {
  2049         struct pl330_info *pi = pl330->pinfo;
  2050         int chans = pi->pcfg.num_chan;                //获取pl330 dmac通道数量
  2051         int ret;
  2052         
  2053         /*
  2054          * Alloc MicroCode buffer for 'chans' Channel threads.
  2055          * A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN)
  2056          */
  2057         pl330->mcode_cpu = dma_alloc_coherent(pi->dev,
  2058                                 chans * pi->mcbufsz,
  2059                                 &pl330->mcode_bus, GFP_KERNEL);                //分配pl330 dmac 指令缓存总大小的dma地址和虚拟地址
  2060         if (!pl330->mcode_cpu) {
  2061                 dev_err(pi->dev, "%s:%d Can't allocate memory!\n",
  2062                         __func__, __LINE__);
  2063                 return -ENOMEM;
  2064         }
  2065         
  2066         ret = dmac_alloc_threads(pl330);                //给pl330 dmac各线程分配资源
  2067         if (ret) {
  2068                 dev_err(pi->dev, "%s:%d Can't to create channels for DMAC!\n",
  2069                         __func__, __LINE__);
  2070                 dma_free_coherent(pi->dev,
  2071                                 chans * pi->mcbufsz,
  2072                                 pl330->mcode_cpu, pl330->mcode_bus);
  2073                 return ret;
  2074         }
  2075         
  2076         return 0;
  2077 }

此函数给pl330 dmac分配资源



  2015 static int dmac_alloc_threads(struct pl330_dmac *pl330)
  2016 {
  2017         struct pl330_info *pi = pl330->pinfo;
  2018         int chans = pi->pcfg.num_chan;           //获取pl330 dmac通道数量
  2019         struct pl330_thread *thrd;
  2020         int i;
  2021         
  2022         /* Allocate 1 Manager and 'chans' Channel threads */
  2023         pl330->channels = kzalloc((1 + chans) * sizeof(*thrd),
  2024                                         GFP_KERNEL);         //分配一个管理线程和n个通道线程
  2025         if (!pl330->channels)
  2026                 return -ENOMEM;
  2027         
  2028         /* Init Channel threads */
  2029         for (i = 0; i < chans; i++) {                //初始化每一个通道线程
  2030                 thrd = &pl330->channels[i];
  2031                 thrd->id = i;                   //线程分配id
  2032                 thrd->dmac = pl330;       //线程和pl330 dmac关联
  2033                 _reset_thread(thrd);       //复位线程
  2034                 thrd->free = true;          //设置线程free
  2035         }
  2036
  2037         /* MANAGER is indexed at the end */
  2038         thrd = &pl330->channels[chans];      //初始化管理线程
  2039         thrd->id = chans;
  2040         thrd->dmac = pl330;
  2041         thrd->free = false;                  //管理线程默认占用
  2042         pl330->manager = thrd;          //设在pl330 dmac和管理线程关联
  2043
  2044         return 0;
  2045 }

此函数分配pl330 dmac线程空间,并初始化各线程


  1995 static inline void _reset_thread(struct pl330_thread *thrd)
  1996 {
  1997         struct pl330_dmac *pl330 = thrd->dmac;
  1998         struct pl330_info *pi = pl330->pinfo;
  1999         
  2000         thrd->req[0].mc_cpu = pl330->mcode_cpu
  2001                                 + (thrd->id * pi->mcbufsz);          //设置线程指令缓存虚拟地址
  2002         thrd->req[0].mc_bus = pl330->mcode_bus        //设置线程指令物理地址
  2003                                 + (thrd->id * pi->mcbufsz);
  2004         thrd->req[0].r = NULL;                                         //dmac请求置空
  2005         mark_free(thrd, 0);                            //标记线程free
  2006
  2007         thrd->req[1].mc_cpu = thrd->req[0].mc_cpu
  2008                                 + pi->mcbufsz / 2;
  2009         thrd->req[1].mc_bus = thrd->req[0].mc_bus
  2010                                 + pi->mcbufsz / 2;
  2011         thrd->req[1].r = NULL;
  2012         mark_free(thrd, 1);
  2013 }

此函数主要设置线程请求为复位状态


  1071 static void mark_free(struct pl330_thread *thrd, int idx)
  1072 {       
  1073         struct _pl330_req *req = &thrd->req[idx];  //获取线程一个请求
  1074
  1075         _emit_END(0, req->mc_cpu);        //设置请求指令为end
  1076         req->mc_len = 0;                       //设置指令长度0
  1077  
  1078         thrd->req_running = -1;                //设置线程正在运行请求为-1
  1079 }


此函数通过设置请求指令为end来标记其为free


  1628 static void pl330_dotask(unsigned long data)
  1629 {
  1630         struct pl330_dmac *pl330 = (struct pl330_dmac *) data;
  1631         struct pl330_info *pi = pl330->pinfo;
  1632         unsigned long flags;
  1633         int i;
  1634         
  1635         spin_lock_irqsave(&pl330->lock, flags);
  1636         
  1637         /* The DMAC itself gone nuts */
  1638         if (pl330->dmac_tbd.reset_dmac) {          //判断pl330 dmac是否需要复位
  1639                 pl330->state = DYING;             //标记dmac状态为dying
  1640                 /* Reset the manager too */
  1641                 pl330->dmac_tbd.reset_mngr = true;        //标记复位dmac管理线程
  1642                 /* Clear the reset flag */
  1643                 pl330->dmac_tbd.reset_dmac = false;
  1644         }
  1645
  1646         if (pl330->dmac_tbd.reset_mngr) {             //判读是否需要复位dmac管理线程
  1647                 _stop(pl330->manager);            //停止管理线程
  1648                 /* Reset all channels */
  1649                 pl330->dmac_tbd.reset_chan = (1 << pi->pcfg.num_chan) - 1;     //标记复位所有通道
  1650                 /* Clear the reset flag */
  1651                 pl330->dmac_tbd.reset_mngr = false;
  1652         }
  1653
  1654         for (i = 0; i < pi->pcfg.num_chan; i++) {             //循环复位所有通道线程
  1655
  1656                 if (pl330->dmac_tbd.reset_chan & (1 << i)) {
  1657                         struct pl330_thread *thrd = &pl330->channels[i];
  1658                         void __iomem *regs = pi->base;
  1659                         enum pl330_op_err err;
  1660
  1661                         _stop(thrd);              //停止相应线程
  1662
  1663                         if (readl(regs + FSC) & (1 << thrd->id))      //读取通道状态寄存器,设置err
  1664                                 err = PL330_ERR_FAIL;
  1665                         else
  1666                                 err = PL330_ERR_ABORT;
  1667
  1668                         spin_unlock_irqrestore(&pl330->lock, flags);
  1669
  1670                         _callback(thrd->req[1 - thrd->lstenq].r, err);             //调用请求中的回调处理请求
  1671                         _callback(thrd->req[thrd->lstenq].r, err);
  1672
  1673                         spin_lock_irqsave(&pl330->lock, flags);
  1674
  1675                         thrd->req[0].r = NULL;             //设置请求为复位状态
  1676                         thrd->req[1].r = NULL;
  1677                         mark_free(thrd, 0);            //标记线程请求为free
  1678                         mark_free(thrd, 1);
  1679
  1680                         /* Clear the reset flag */
  1681                         pl330->dmac_tbd.reset_chan &= ~(1 << i);
  1682                 }
  1683         }
  1684
  1685         spin_unlock_irqrestore(&pl330->lock, flags);
  1686
  1687         return;
  1688 }




3 创建描述符池

if (!add_desc(pdmac, GFP_KERNEL, NR_DEFAULT_DESC))
  2942                 dev_warn(&adev->dev, "unable to allocate desc\n");

2566 /* Returns the number of descriptors added to the DMAC pool */
  2567 static int add_desc(struct dma_pl330_dmac *pdmac, gfp_t flg, int count)
  2568 {
  2569         struct dma_pl330_desc *desc;
  2570         unsigned long flags;
  2571         int i;
  2572
  2573         if (!pdmac)
  2574                 return 0;
  2575
  2576         desc = kmalloc(count * sizeof(*desc), flg);     //分配所需添加的描述符空间
  2577         if (!desc)
  2578                 return 0;
  2579
  2580         spin_lock_irqsave(&pdmac->pool_lock, flags);
  2581
  2582         for (i = 0; i < count; i++) {          //循环初始化各个描述符并将其添加到描述符池链表中
  2583                 _init_desc(&desc[i]);           
  2584                 list_add_tail(&desc[i].node, &pdmac->desc_pool);
  2585         }
  2586
  2587         spin_unlock_irqrestore(&pdmac->pool_lock, flags);
  2588
  2589         return count;
  2590 }


此函数分配描述符空间,并初始化描述符,然后将其添加到pl330 dmac 描述符池链表


  2549 static inline void _init_desc(struct dma_pl330_desc *desc)
  2550 {       
  2551         desc->pchan = NULL;                    //将描述符所属通道置空
  2552         desc->req.x = &desc->px;              //设置请求的传输为描述符传输   
  2553         desc->req.token = desc;                 //设置请求令牌为描述符
  2554         desc->rqcfg.swap = SWAP_NO;      //设置请求配置swap
  2555         desc->rqcfg.privileged = 0;              //设置请求配置权限
  2556         desc->rqcfg.insnaccess = 0;         
  2557         desc->rqcfg.scctl = SCCTRL0;
  2558         desc->rqcfg.dcctl = DCCTRL0;
  2559         desc->req.cfg = &desc->rqcfg;               //设置请求的请求配置
  2560         desc->req.xfer_cb = dma_pl330_rqcb;    //设置请求的回调处理
  2561         desc->txd.tx_submit = pl330_tx_submit;    //设置dma引擎层描述符的发送函数
  2562
  2563         INIT_LIST_HEAD(&desc->node);        //初始化链表节点,用来加入到pl330 dmac描述符池链表
  2564 }

此函数初始化pl330 dmac描述符成员



  2348 static void dma_pl330_rqcb(void *token, enum pl330_op_err err)
  2349 {       
  2350         struct dma_pl330_desc *desc = token;
  2351         struct dma_pl330_chan *pch = desc->pchan;
  2352         unsigned long flags;
  2353         
  2354         /* If desc aborted */
  2355         if (!pch)
  2356                 return;
  2357
  2358         spin_lock_irqsave(&pch->lock, flags);
  2359
  2360         desc->status = DONE;                 //设置传输描述符为已经操作完成
  2361
  2362         spin_unlock_irqrestore(&pch->lock, flags);
  2363
  2364         tasklet_schedule(&pch->task);             //调用通道处理任务
  2365 }

此函数为处理万传输描述后的回调函数,主要是标记处理完成,然后调用通道处理函数。


2524 static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)
  2525 {
  2526         struct dma_pl330_desc *desc, *last = to_desc(tx);    //获取pl330 dmac层的传输描述符
  2527         struct dma_pl330_chan *pch = to_pchan(tx->chan);    //获取pl330 dmac的传输层通道
  2528         dma_cookie_t cookie;
  2529         unsigned long flags;
  2530
  2531         spin_lock_irqsave(&pch->lock, flags);
  2532
  2533         /* Assign cookies to all nodes */
  2534         while (!list_empty(&last->node)) {              //判断链表是否为空
  2535                 desc = list_entry(last->node.next, struct dma_pl330_desc, node);        //获取下一个pl330 dmac层传输描述符
  2536
  2537                 dma_cookie_assign(&desc->txd); //分配dma引擎层传输描述符cookie
  2538
  2539                 list_move_tail(&desc->node, &pch->work_list);        //将此节点移到pl330 dmac层通道工作链表尾
  2540         }
  2541
  2542         cookie = dma_cookie_assign(&last->txd);  //给最后一个dma引擎层传输描述符分配cookie
  2543         list_add_tail(&last->node, &pch->work_list);       //将其添加到通道工作链表尾
  2544         spin_unlock_irqrestore(&pch->lock, flags);
  2545
  2546         return cookie;
  2547 }

此函数将传输描述符及其队列dma引擎层传输描述符添加cookie,并将其添加到pl330 dmac通道工作队列以备处理







4 dma引擎层dma device的关键操作函数

 4.1

  2413 static int pl330_alloc_chan_resources(struct dma_chan *chan)
  2414 {
  2415         struct dma_pl330_chan *pch = to_pchan(chan);       //获取pl330 dmac层通道
  2416         struct dma_pl330_dmac *pdmac = pch->dmac;     
  2417         unsigned long flags;
  2418
  2419         spin_lock_irqsave(&pch->lock, flags);
  2420
  2421         dma_cookie_init(chan);                  //初始化dma通道cookie
  2422         pch->cyclic = false;                        //通道默认不支持循环操作
  2423
  2424         pch->pl330_chid = pl330_request_channel(&pdmac->pif);              //给pl330 dmac通道分配一个处理线程
  2425         if (!pch->pl330_chid) {
  2426                 spin_unlock_irqrestore(&pch->lock, flags);
  2427                 return -ENOMEM;
  2428         }
  2429
  2430         tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch);        //初始化通道处理任务
  2431
  2432         spin_unlock_irqrestore(&pch->lock, flags);
  2433
  2434         return 1;
  2435 }

  1871 static void *pl330_request_channel(const struct pl330_info *pi)
  1872 {
  1873         struct pl330_thread *thrd = NULL;
  1874         struct pl330_dmac *pl330;
  1875         unsigned long flags;
  1876         int chans, i;
  1877
  1878         if (!pi || !pi->pl330_data)
  1879                 return NULL;
  1880
  1881         pl330 = pi->pl330_data;
  1882
  1883         if (pl330->state == DYING)
  1884                 return NULL;
  1885
  1886         chans = pi->pcfg.num_chan;
  1887
  1888         spin_lock_irqsave(&pl330->lock, flags);
  1889
  1890         for (i = 0; i < chans; i++) {              //循环找一个free的dmac 线程
  1891                 thrd = &pl330->channels[i];
  1892                 if ((thrd->free) && (!_manager_ns(thrd) ||                   //如果线程free 不是管理线程
  1893                                         _chan_ns(pi, i))) {
  1894                         thrd->ev = _alloc_event(thrd);                  //线程分配事件
  1895                         if (thrd->ev >= 0) {                           //初始化线程                                              
  1896                                 thrd->free = false;
  1897                                 thrd->lstenq = 1;
  1898                                 thrd->req[0].r = NULL;
  1899                                 mark_free(thrd, 0);
  1900                                 thrd->req[1].r = NULL;
  1901                                 mark_free(thrd, 1);
  1902                                 break;
  1903                         }
  1904                 }
  1905                 thrd = NULL;
  1906         }
  1907
  1908         spin_unlock_irqrestore(&pl330->lock, flags);
  1909
  1910         return thrd;
  1911 }

 此函数给一个pl330 damc通道分配一个dmac处理线程,并初始化该线程


1848 static inline int _alloc_event(struct pl330_thread *thrd)
  1849 {
  1850         struct pl330_dmac *pl330 = thrd->dmac;
  1851         struct pl330_info *pi = pl330->pinfo;
  1852         int ev;
  1853
  1854         for (ev = 0; ev < pi->pcfg.num_events; ev++)      //从pl330 dmac自由事件中分配一个给线程
  1855                 if (pl330->events[ev] == -1) {
  1856                         pl330->events[ev] = thrd->id;       //事件和线程绑定
  1857                         return ev;
  1858                 }
  1859
  1860         return -1;
  1861 }
此函数从pl330 dmac自由事件中分配一个给线程


  2317 static void pl330_tasklet(unsigned long data)      
  2318 {
  2319         struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
  2320         struct dma_pl330_desc *desc, *_dt;
  2321         unsigned long flags;
  2322         LIST_HEAD(list);
  2323  
  2324         spin_lock_irqsave(&pch->lock, flags);
  2325  
  2326         /* Pick up ripe tomatoes */
  2327         list_for_each_entry_safe(desc, _dt, &pch->work_list, node)  //遍历通道工作队列
  2328                 if (desc->status == DONE) {        //如果此描述被处理完成
  2329                         if (!pch->cyclic)         //通道不是循环操作
  2330                                 dma_cookie_complete(&desc->txd);            //标记dma引擎层传输描述完成cookie
  2331                         list_move_tail(&desc->node, &list);       /从工作队列删除节点
  2332                 }
  2333  
  2334         /* Try to submit a req imm. next to the last completed cookie */
  2335         fill_queue(pch);          //填充线程处理请求,准备处理的下个描述符请求
  2336  
  2337         /* Make sure the PL330 Channel thread is active */
  2338         pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START);         //设置dmac线程继续工作
  2339  
  2340         spin_unlock_irqrestore(&pch->lock, flags);
  2341  
  2342         if (pch->cyclic)
  2343                 handle_cyclic_desc_list(&list);
  2344         else
  2345                 free_desc_list(&list);
  2346 }
此函数在传输描述处理完成后调用


  2289 static inline void fill_queue(struct dma_pl330_chan *pch)
  2290 {
  2291         struct dma_pl330_desc *desc;
  2292         int ret;
  2293
  2294         list_for_each_entry(desc, &pch->work_list, node) {        //遍历通道工作队列
  2295
  2296                 /* If already submitted */
  2297                 if (desc->status == BUSY)          //跳过正在处理的节点
  2298                         continue;
  2299          
  2300                 ret = pl330_submit_req(pch->pl330_chid,
  2301                                                 &desc->req);                           //将描述请求添加到线程
  2302                 if (!ret) {
  2303                         desc->status = BUSY;
  2304                 } else if (ret == -EAGAIN) {
  2305                         /* QFull or DMAC Dying */
  2306                         break;
  2307                 } else {
  2308                         /* Unacceptable request */
  2309                         desc->status = DONE;
  2310                         dev_err(pch->dmac->pif.dev, "%s:%d Bad Desc(%d)\n",
  2311                                         __func__, __LINE__, desc->txd.cookie);
  2312                         tasklet_schedule(&pch->task);
  2313                 }
  2314         }
  2315 }

此函数给通道处理线程提交一个描述请求,以备处理



  1533 static int pl330_submit_req(void *ch_id, struct pl330_req *r)
  1534 {
  1535         struct pl330_thread *thrd = ch_id;
  1536         struct pl330_dmac *pl330;
  1537         struct pl330_info *pi;
  1538         struct _xfer_spec xs;
  1539         unsigned long flags;
  1540         void __iomem *regs;
  1541         unsigned idx;
  1542         u32 ccr;
  1543         int ret = 0;
  1544
  1545         /* No Req or Unacquired Channel or DMAC */
  1546         if (!r || !thrd || thrd->free)
  1547                 return -EINVAL;
  1548
  1549         pl330 = thrd->dmac;
  1550         pi = pl330->pinfo;
  1551         regs = pi->base;
  1552
  1553         if (pl330->state == DYING                                        
  1554                 || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) {
  1555                 dev_info(thrd->dmac->pinfo->dev, "%s:%d\n",
  1556                         __func__, __LINE__);
  1557                 return -EAGAIN;
  1558         }
  1559
  1560         /* If request for non-existing peripheral */
  1561         if (r->rqtype != MEMTOMEM && r->peri >= pi->pcfg.num_peri) {
  1562                 dev_info(thrd->dmac->pinfo->dev,
  1563                                 "%s:%d Invalid peripheral(%u)!\n",
  1564                                 __func__, __LINE__, r->peri);
  1565                 return -EINVAL;
  1566         }
  1567
  1568         spin_lock_irqsave(&pl330->lock, flags);
  1569
  1570         if (_queue_full(thrd)) {              //判断线程处理队列是否已满
  1571                 ret = -EAGAIN;
  1572                 goto xfer_exit;
  1573         }
  1574
  1575
  1576         /* Use last settings, if not provided */
  1577         if (r->cfg) {
  1578                 /* Prefer Secure Channel */
  1579                 if (!_manager_ns(thrd))      //判断线程是否是安全模式
  1580                         r->cfg->nonsecure = 0;
  1581                 else
  1582                         r->cfg->nonsecure = 1;
  1583
  1584                 ccr = _prepare_ccr(r->cfg);     //以及请求配置设置通道控制寄存器
  1585         } else {
  1586                 ccr = readl(regs + CC(thrd->id));
  1587         }
  1588
  1589         /* If this req doesn't have valid xfer settings */
  1590         if (!_is_valid(ccr)) {           //判断此请求配置是否有效
  1591                 ret = -EINVAL;
  1592                 dev_info(thrd->dmac->pinfo->dev, "%s:%d Invalid CCR(%x)!\n",
  1593                         __func__, __LINE__, ccr);
  1594                 goto xfer_exit;
  1595         }
  1596
  1597         idx = IS_FREE(&thrd->req[0]) ? 0 : 1;     //在处理线程找到一个free的请求节点
  1598
  1599         xs.ccr = ccr;       //设置请求空间
  1600         xs.r = r;
  1601
  1602         /* First dry run to check if req is acceptable */
  1603         ret = _setup_req(1, thrd, idx, &xs);       //先不真正处理请求,此操作主要为了检查可执行性
  1604         if (ret < 0)
  1605                 goto xfer_exit;
  1606
  1607         if (ret > pi->mcbufsz / 2) {
  1608                 dev_info(thrd->dmac->pinfo->dev,
  1609                         "%s:%d Trying increasing mcbufsz\n",
  1610                                 __func__, __LINE__);
  1611                 ret = -ENOMEM;
  1612                 goto xfer_exit;
  1613         }
  1614
  1615         /* Hook the request */
  1616         thrd->lstenq = idx;                //设置线程即将执行请求队列节点
  1617         thrd->req[idx].mc_len = _setup_req(0, thrd, idx, &xs);      //设置线程的请求的指令集
  1618         thrd->req[idx].r = r;
  1619
  1620         ret = 0;
  1621
  1622 xfer_exit:
  1623         spin_unlock_irqrestore(&pl330->lock, flags);
  1624
  1625         return ret;
  1626 }

此函数用来给线程请求队列提交即将处理的请求,并设置线程请求空间


1481 static inline u32 _prepare_ccr(const struct pl330_reqcfg *rqc)
  1482 {       
  1483         u32 ccr = 0;
  1484         
  1485         if (rqc->src_inc)
  1486                 ccr |= CC_SRCINC;
  1487
  1488         if (rqc->dst_inc)
  1489                 ccr |= CC_DSTINC;
  1490
  1491         /* We set same protection levels for Src and DST for now */
  1492         if (rqc->privileged)
1493                 ccr |= CC_SRCPRI | CC_DSTPRI;
  1494         if (rqc->nonsecure)
  1495                 ccr |= CC_SRCNS | CC_DSTNS;
  1496         if (rqc->insnaccess)
  1497                 ccr |= CC_SRCIA | CC_DSTIA;
  1498
  1499         ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT);
  1500         ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT);
  1501
  1502         ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT);
  1503         ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT);
  1504
  1505         ccr |= (rqc->scctl << CC_SRCCCTRL_SHFT);

1506         ccr |= (rqc->dcctl << CC_DSTCCTRL_SHFT);
  1507
  1508         ccr |= (rqc->swap << CC_SWAP_SHFT);
  1509
  1510         return ccr;
  1511 }

此函数依据请求配置来设置通道控制寄存器值


  1448 static int _setup_req(unsigned dry_run, struct pl330_thread *thrd,
  1449                 unsigned index, struct _xfer_spec *pxs)
  1450 {
  1451         struct _pl330_req *req = &thrd->req[index];       //获取请求队列节点
  1452         struct pl330_xfer *x;               
  1453         u8 *buf = req->mc_cpu;
  1454         int off = 0;
  1455
  1456         PL330_DBGMC_START(req->mc_bus); //设置pl300处理指令缓存区物理地址     
  1457
  1458         /* DMAMOV CCR, ccr */
  1459         off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr);   //设置请求指令缓存区mov指令
  1460         
  1461         x = pxs->r->x;     //从请求获取传输
  1462         do {
  1463                 /* Error if xfer length is not aligned at burst size */
  1464                 if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr)))
  1465                         return -EINVAL;
  1466
  1467                 pxs->x = x;
  1468                 off += _setup_xfer(dry_run, &buf[off], pxs);  //设置请求指令缓存区传输指令
  1469
  1470                 x = x->next;
  1471         } while (x);          //循环设置请求指令缓存区传输指令
  1472
  1473         /* DMASEV peripheral/event */
  1474         off += _emit_SEV(dry_run, &buf[off], thrd->ev);         //设置请求指令缓存区设置事件
  1475         /* DMAEND */
  1476         off += _emit_END(dry_run, &buf[off]);                //设置请求指令缓存区end命令
  1477
  1478         return off;
  1479 }  



此函数是实际设置请求指令缓存核心函数,封装了设置dmac 汇编指令程序,方便用户调用



1427 static inline int _setup_xfer(unsigned dry_run, u8 buf[],
  1428                 const struct _xfer_spec *pxs)
  1429 {
  1430         struct pl330_xfer *x = pxs->x;
  1431         int off = 0;
  1432
  1433         /* DMAMOV SAR, x->src_addr */
  1434         off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr);    //设置指令缓存区mov 源地址
  1435         /* DMAMOV DAR, x->dst_addr */
  1436         off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr);     //设置指令缓存区mov 目的地址 
  1437         
  1438         /* Setup Loop(s) */
  1439         off += _setup_loops(dry_run, &buf[off], pxs);
  1440         
  1441         return off;
  1442 }







  2762 static struct dma_async_tx_descriptor *
  2763 pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
  2764                 dma_addr_t src, size_t len, unsigned long flags)            
  2765 {
  2766         struct dma_pl330_desc *desc;
  2767         struct dma_pl330_chan *pch = to_pchan(chan);     //获取pl330 dmac层通道
  2768         struct pl330_info *pi;
  2769         int burst;
  2770
  2771         if (unlikely(!pch || !len))
  2772                 return NULL;
  2773
  2774         pi = &pch->dmac->pif;
  2775
  2776         desc = __pl330_prep_dma_memcpy(pch, dst, src, len);    //调用pl330 dmac层准备内存拷贝描述符
  2777         if (!desc)
  2778                 return NULL;
  2779
  2780         desc->rqcfg.src_inc = 1;       //设置描述请求配置
  2781         desc->rqcfg.dst_inc = 1;
  2782         desc->req.rqtype = MEMTOMEM;
  2783
  2784         /* Select max possible burst size */
  2785         burst = pi->pcfg.data_bus_width / 8;      //设置突发长度
  2786
  2787         while (burst > 1) {
  2788                 if (!(len % burst))
  2789                         break;
  2790                 burst /= 2;
  2791         }
  2792
  2793         desc->rqcfg.brst_size = 0;
  2794         while (burst != (1 << desc->rqcfg.brst_size))
  2795                 desc->rqcfg.brst_size++;
  2796
  2797         desc->rqcfg.brst_len = get_burst_len(desc, len);
  2798
  2799         desc->txd.flags = flags;
  2800
  2801         return &desc->txd;
  2802 }

此函数为准备dma引擎层内存拷贝传输描述符


 2663 static struct dma_pl330_desc *
  2664 __pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst,
  2665                 dma_addr_t src, size_t len)
  2666 {
  2667         struct dma_pl330_desc *desc = pl330_get_desc(pch);     //从pl330 dmac 描述池获得一个描述
  2668
  2669         if (!desc) {
  2670                 dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
  2671                         __func__, __LINE__);
  2672                 return NULL;
  2673         }
  2674
  2675         /*
  2676          * Ideally we should lookout for reqs bigger than
  2677          * those that can be programmed with 256 bytes of
  2678          * MC buffer, but considering a req size is seldom
  2679          * going to be word-unaligned and more than 200MB,
  2680          * we take it easy.
  2681          * Also, should the limit is reached we'd rather
  2682          * have the platform increase MC buffer size than
  2683          * complicating this API driver.
  2684          */
  2685         fill_px(&desc->px, dst, src, len);  //填充描述符中的传输内容
  2686
  2687         return desc;
  2688 }


2654 static inline void fill_px(struct pl330_xfer *px,
  2655                 dma_addr_t dst, dma_addr_t src, size_t len)
  2656 {
  2657         px->next = NULL;
  2658         px->bytes = len;
  2659         px->dst_addr = dst;
  2660         px->src_addr = src;
  2661 }



 2514 static void pl330_issue_pending(struct dma_chan *chan)
  2515 {
  2516         pl330_tasklet((unsigned long) to_pchan(chan));  //处理通道中完成的描述符,填充下一个待处理的描述符
  2517 }

此函数开始执行通道传输



  2317 static void pl330_tasklet(unsigned long data)
  2318 {
  2319         struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;  //获取pl330 dmac层通道
  2320         struct dma_pl330_desc *desc, *_dt;
  2321         unsigned long flags;
  2322         LIST_HEAD(list);
  2323
  2324         spin_lock_irqsave(&pch->lock, flags);
  2325
  2326         /* Pick up ripe tomatoes */
  2327         list_for_each_entry_safe(desc, _dt, &pch->work_list, node)  //遍历同搭配工作队列,查找已经完成的描述符,标记其为完成,并将其从链表删除
  2328                 if (desc->status == DONE) {
  2329                         if (!pch->cyclic)
  2330                                 dma_cookie_complete(&desc->txd);
  2331                         list_move_tail(&desc->node, &list);
  2332                 }
  2333
  2334         /* Try to submit a req imm. next to the last completed cookie */
  2335         fill_queue(pch);    //填充下一个带出来描述符
  2336
  2337         /* Make sure the PL330 Channel thread is active */
  2338         pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START);  //开始通道
  2339
  2340         spin_unlock_irqrestore(&pch->lock, flags);
  2341
  2342         if (pch->cyclic)
  2343                 handle_cyclic_desc_list(&list);
  2344         else
  2345                 free_desc_list(&list);
  2346 }

理通道中完成的描述符,填充下一个待处理的描述符





2437 static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg)
  2438 {
  2439         struct dma_pl330_chan *pch = to_pchan(chan);  //获取pl330 dmac层通道
  2440         struct dma_pl330_desc *desc, *_dt;
  2441         unsigned long flags;
  2442         struct dma_pl330_dmac *pdmac = pch->dmac;
  2443         struct dma_slave_config *slave_config;
  2444         LIST_HEAD(list);
  2445
  2446         switch (cmd) {
  2447         case DMA_TERMINATE_ALL: //关闭通道
  2448                 spin_lock_irqsave(&pch->lock, flags);
  2449
  2450                 /* FLUSH the PL330 Channel thread */
  2451                 pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);  //清空线程操作
  2452
  2453                 /* Mark all desc done */
  2454                 list_for_each_entry_safe(desc, _dt, &pch->work_list , node) {      //遍历链表标记所有描述符为完成 ,并将其移到pl330 dmac 描述池链表
  2455                         desc->status = DONE;
  2456                         list_move_tail(&desc->node, &list);
  2457                 }
  2458
  2459                 list_splice_tail_init(&list, &pdmac->desc_pool);
  2460                 spin_unlock_irqrestore(&pch->lock, flags);

2461                 break;
  2462         case DMA_SLAVE_CONFIG:
  2463                 slave_config = (struct dma_slave_config *)arg;
  2464
  2465                 if (slave_config->direction == DMA_MEM_TO_DEV) {
  2466                         if (slave_config->dst_addr)
  2467                                 pch->fifo_addr = slave_config->dst_addr;
  2468                         if (slave_config->dst_addr_width)
  2469                                 pch->burst_sz = __ffs(slave_config->dst_addr_width);
  2470                         if (slave_config->dst_maxburst)
  2471                                 pch->burst_len = slave_config->dst_maxburst;
  2472                 } else if (slave_config->direction == DMA_DEV_TO_MEM) {
  2473                         if (slave_config->src_addr)
  2474                                 pch->fifo_addr = slave_config->src_addr;
  2475                         if (slave_config->src_addr_width)
  2476                                 pch->burst_sz = __ffs(slave_config->src_addr_width);
  2477                         if (slave_config->src_maxburst)
  2478                                 pch->burst_len = slave_config->src_maxburst;
  2479                 }
  2480                 break;
  2481         default:
  2482                 dev_err(pch->dmac->pif.dev, "Not supported command.\n");
  2483                 return -ENXIO;
  2484         }
  2485
  2486         return 0;
  2487 }

 此函数控制dma通道

0 0
原创粉丝点击