18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The driver for Freescale MPC512x LocalPlus Bus FIFO 48c2ecf20Sopenharmony_ci * (called SCLPC in the Reference Manual). 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2013-2015 Alexander Popov <alex.popov@linux.com>. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 168c2ecf20Sopenharmony_ci#include <asm/mpc5121.h> 178c2ecf20Sopenharmony_ci#include <asm/io.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-direction.h> 228c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRV_NAME "mpc512x_lpbfifo" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct cs_range { 278c2ecf20Sopenharmony_ci u32 csnum; 288c2ecf20Sopenharmony_ci u32 base; /* must be zero */ 298c2ecf20Sopenharmony_ci u32 addr; 308c2ecf20Sopenharmony_ci u32 size; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct lpbfifo_data { 348c2ecf20Sopenharmony_ci spinlock_t lock; /* for protecting lpbfifo_data */ 358c2ecf20Sopenharmony_ci phys_addr_t regs_phys; 368c2ecf20Sopenharmony_ci resource_size_t regs_size; 378c2ecf20Sopenharmony_ci struct mpc512x_lpbfifo __iomem *regs; 388c2ecf20Sopenharmony_ci int irq; 398c2ecf20Sopenharmony_ci struct cs_range *cs_ranges; 408c2ecf20Sopenharmony_ci size_t cs_n; 418c2ecf20Sopenharmony_ci struct dma_chan *chan; 428c2ecf20Sopenharmony_ci struct mpc512x_lpbfifo_request *req; 438c2ecf20Sopenharmony_ci dma_addr_t ram_bus_addr; 448c2ecf20Sopenharmony_ci bool wait_lpbfifo_irq; 458c2ecf20Sopenharmony_ci bool wait_lpbfifo_callback; 468c2ecf20Sopenharmony_ci} lpbfifo; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * A data transfer from RAM to some device on LPB is finished 508c2ecf20Sopenharmony_ci * when both mpc512x_lpbfifo_irq() and mpc512x_lpbfifo_callback() 518c2ecf20Sopenharmony_ci * have been called. We execute the callback registered in 528c2ecf20Sopenharmony_ci * mpc512x_lpbfifo_request just after that. 538c2ecf20Sopenharmony_ci * But for a data transfer from some device on LPB to RAM we don't enable 548c2ecf20Sopenharmony_ci * LPBFIFO interrupt because clearing MPC512X_SCLPC_SUCCESS interrupt flag 558c2ecf20Sopenharmony_ci * automatically disables LPBFIFO reading request to the DMA controller 568c2ecf20Sopenharmony_ci * and the data transfer hangs. So the callback registered in 578c2ecf20Sopenharmony_ci * mpc512x_lpbfifo_request is executed at the end of mpc512x_lpbfifo_callback(). 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * mpc512x_lpbfifo_irq - IRQ handler for LPB FIFO 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic irqreturn_t mpc512x_lpbfifo_irq(int irq, void *param) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct device *dev = (struct device *)param; 668c2ecf20Sopenharmony_ci struct mpc512x_lpbfifo_request *req = NULL; 678c2ecf20Sopenharmony_ci unsigned long flags; 688c2ecf20Sopenharmony_ci u32 status; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!lpbfifo.regs) 738c2ecf20Sopenharmony_ci goto end; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci req = lpbfifo.req; 768c2ecf20Sopenharmony_ci if (!req || req->dir == MPC512X_LPBFIFO_REQ_DIR_READ) { 778c2ecf20Sopenharmony_ci dev_err(dev, "bogus LPBFIFO IRQ\n"); 788c2ecf20Sopenharmony_ci goto end; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci status = in_be32(&lpbfifo.regs->status); 828c2ecf20Sopenharmony_ci if (status != MPC512X_SCLPC_SUCCESS) { 838c2ecf20Sopenharmony_ci dev_err(dev, "DMA transfer from RAM to peripheral failed\n"); 848c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->enable, 858c2ecf20Sopenharmony_ci MPC512X_SCLPC_RESET | MPC512X_SCLPC_FIFO_RESET); 868c2ecf20Sopenharmony_ci goto end; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci /* Clear the interrupt flag */ 898c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->status, MPC512X_SCLPC_SUCCESS); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci lpbfifo.wait_lpbfifo_irq = false; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (lpbfifo.wait_lpbfifo_callback) 948c2ecf20Sopenharmony_ci goto end; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Transfer is finished, set the FIFO as idle */ 978c2ecf20Sopenharmony_ci lpbfifo.req = NULL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (req->callback) 1028c2ecf20Sopenharmony_ci req->callback(req); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci end: 1078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 1088c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * mpc512x_lpbfifo_callback is called by DMA driver when 1138c2ecf20Sopenharmony_ci * DMA transaction is finished. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic void mpc512x_lpbfifo_callback(void *param) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci unsigned long flags; 1188c2ecf20Sopenharmony_ci struct mpc512x_lpbfifo_request *req = NULL; 1198c2ecf20Sopenharmony_ci enum dma_data_direction dir; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!lpbfifo.regs) { 1248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 1258c2ecf20Sopenharmony_ci return; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci req = lpbfifo.req; 1298c2ecf20Sopenharmony_ci if (!req) { 1308c2ecf20Sopenharmony_ci pr_err("bogus LPBFIFO callback\n"); 1318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Release the mapping */ 1368c2ecf20Sopenharmony_ci if (req->dir == MPC512X_LPBFIFO_REQ_DIR_WRITE) 1378c2ecf20Sopenharmony_ci dir = DMA_TO_DEVICE; 1388c2ecf20Sopenharmony_ci else 1398c2ecf20Sopenharmony_ci dir = DMA_FROM_DEVICE; 1408c2ecf20Sopenharmony_ci dma_unmap_single(lpbfifo.chan->device->dev, 1418c2ecf20Sopenharmony_ci lpbfifo.ram_bus_addr, req->size, dir); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci lpbfifo.wait_lpbfifo_callback = false; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (!lpbfifo.wait_lpbfifo_irq) { 1468c2ecf20Sopenharmony_ci /* Transfer is finished, set the FIFO as idle */ 1478c2ecf20Sopenharmony_ci lpbfifo.req = NULL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (req->callback) 1528c2ecf20Sopenharmony_ci req->callback(req); 1538c2ecf20Sopenharmony_ci } else { 1548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int mpc512x_lpbfifo_kick(void) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci u32 bits; 1618c2ecf20Sopenharmony_ci bool no_incr = false; 1628c2ecf20Sopenharmony_ci u32 bpt = 32; /* max bytes per LPBFIFO transaction involving DMA */ 1638c2ecf20Sopenharmony_ci u32 cs = 0; 1648c2ecf20Sopenharmony_ci size_t i; 1658c2ecf20Sopenharmony_ci struct dma_device *dma_dev = NULL; 1668c2ecf20Sopenharmony_ci struct scatterlist sg; 1678c2ecf20Sopenharmony_ci enum dma_data_direction dir; 1688c2ecf20Sopenharmony_ci struct dma_slave_config dma_conf = {}; 1698c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *dma_tx = NULL; 1708c2ecf20Sopenharmony_ci dma_cookie_t cookie; 1718c2ecf20Sopenharmony_ci int ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * 1. Fit the requirements: 1758c2ecf20Sopenharmony_ci * - the packet size must be a multiple of 4 since FIFO Data Word 1768c2ecf20Sopenharmony_ci * Register allows only full-word access according the Reference 1778c2ecf20Sopenharmony_ci * Manual; 1788c2ecf20Sopenharmony_ci * - the physical address of the device on LPB and the packet size 1798c2ecf20Sopenharmony_ci * must be aligned on BPT (bytes per transaction) or 8-bytes 1808c2ecf20Sopenharmony_ci * boundary according the Reference Manual; 1818c2ecf20Sopenharmony_ci * - but we choose DMA maxburst equal (or very close to) BPT to prevent 1828c2ecf20Sopenharmony_ci * DMA controller from overtaking FIFO and causing FIFO underflow 1838c2ecf20Sopenharmony_ci * error. So we force the packet size to be aligned on BPT boundary 1848c2ecf20Sopenharmony_ci * not to confuse DMA driver which requires the packet size to be 1858c2ecf20Sopenharmony_ci * aligned on maxburst boundary; 1868c2ecf20Sopenharmony_ci * - BPT should be set to the LPB device port size for operation with 1878c2ecf20Sopenharmony_ci * disabled auto-incrementing according Reference Manual. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci if (lpbfifo.req->size == 0 || !IS_ALIGNED(lpbfifo.req->size, 4)) 1908c2ecf20Sopenharmony_ci return -EINVAL; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (lpbfifo.req->portsize != LPB_DEV_PORTSIZE_UNDEFINED) { 1938c2ecf20Sopenharmony_ci bpt = lpbfifo.req->portsize; 1948c2ecf20Sopenharmony_ci no_incr = true; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci while (bpt > 1) { 1988c2ecf20Sopenharmony_ci if (IS_ALIGNED(lpbfifo.req->dev_phys_addr, min(bpt, 0x8u)) && 1998c2ecf20Sopenharmony_ci IS_ALIGNED(lpbfifo.req->size, bpt)) { 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (no_incr) 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci bpt >>= 1; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci dma_conf.dst_maxburst = max(bpt, 0x4u) / 4; 2098c2ecf20Sopenharmony_ci dma_conf.src_maxburst = max(bpt, 0x4u) / 4; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (i = 0; i < lpbfifo.cs_n; i++) { 2128c2ecf20Sopenharmony_ci phys_addr_t cs_start = lpbfifo.cs_ranges[i].addr; 2138c2ecf20Sopenharmony_ci phys_addr_t cs_end = cs_start + lpbfifo.cs_ranges[i].size; 2148c2ecf20Sopenharmony_ci phys_addr_t access_start = lpbfifo.req->dev_phys_addr; 2158c2ecf20Sopenharmony_ci phys_addr_t access_end = access_start + lpbfifo.req->size; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (access_start >= cs_start && access_end <= cs_end) { 2188c2ecf20Sopenharmony_ci cs = lpbfifo.cs_ranges[i].csnum; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci if (i == lpbfifo.cs_n) 2238c2ecf20Sopenharmony_ci return -EFAULT; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* 2. Prepare DMA */ 2268c2ecf20Sopenharmony_ci dma_dev = lpbfifo.chan->device; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (lpbfifo.req->dir == MPC512X_LPBFIFO_REQ_DIR_WRITE) { 2298c2ecf20Sopenharmony_ci dir = DMA_TO_DEVICE; 2308c2ecf20Sopenharmony_ci dma_conf.direction = DMA_MEM_TO_DEV; 2318c2ecf20Sopenharmony_ci dma_conf.dst_addr = lpbfifo.regs_phys + 2328c2ecf20Sopenharmony_ci offsetof(struct mpc512x_lpbfifo, data_word); 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci dir = DMA_FROM_DEVICE; 2358c2ecf20Sopenharmony_ci dma_conf.direction = DMA_DEV_TO_MEM; 2368c2ecf20Sopenharmony_ci dma_conf.src_addr = lpbfifo.regs_phys + 2378c2ecf20Sopenharmony_ci offsetof(struct mpc512x_lpbfifo, data_word); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 2408c2ecf20Sopenharmony_ci dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Make DMA channel work with LPB FIFO data register */ 2438c2ecf20Sopenharmony_ci if (dma_dev->device_config(lpbfifo.chan, &dma_conf)) { 2448c2ecf20Sopenharmony_ci ret = -EINVAL; 2458c2ecf20Sopenharmony_ci goto err_dma_prep; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci sg_init_table(&sg, 1); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci sg_dma_address(&sg) = dma_map_single(dma_dev->dev, 2518c2ecf20Sopenharmony_ci lpbfifo.req->ram_virt_addr, lpbfifo.req->size, dir); 2528c2ecf20Sopenharmony_ci if (dma_mapping_error(dma_dev->dev, sg_dma_address(&sg))) 2538c2ecf20Sopenharmony_ci return -EFAULT; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci lpbfifo.ram_bus_addr = sg_dma_address(&sg); /* For freeing later */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci sg_dma_len(&sg) = lpbfifo.req->size; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci dma_tx = dmaengine_prep_slave_sg(lpbfifo.chan, &sg, 2608c2ecf20Sopenharmony_ci 1, dma_conf.direction, 0); 2618c2ecf20Sopenharmony_ci if (!dma_tx) { 2628c2ecf20Sopenharmony_ci ret = -ENOSPC; 2638c2ecf20Sopenharmony_ci goto err_dma_prep; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci dma_tx->callback = mpc512x_lpbfifo_callback; 2668c2ecf20Sopenharmony_ci dma_tx->callback_param = NULL; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* 3. Prepare FIFO */ 2698c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->enable, 2708c2ecf20Sopenharmony_ci MPC512X_SCLPC_RESET | MPC512X_SCLPC_FIFO_RESET); 2718c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->enable, 0x0); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * Configure the watermarks for write operation (RAM->DMA->FIFO->dev): 2758c2ecf20Sopenharmony_ci * - high watermark 7 words according the Reference Manual, 2768c2ecf20Sopenharmony_ci * - low watermark 512 bytes (half of the FIFO). 2778c2ecf20Sopenharmony_ci * These watermarks don't work for read operation since the 2788c2ecf20Sopenharmony_ci * MPC512X_SCLPC_FLUSH bit is set (according the Reference Manual). 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->fifo_ctrl, MPC512X_SCLPC_FIFO_CTRL(0x7)); 2818c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->fifo_alarm, MPC512X_SCLPC_FIFO_ALARM(0x200)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * Start address is a physical address of the region which belongs 2858c2ecf20Sopenharmony_ci * to the device on the LocalPlus Bus 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->start_addr, lpbfifo.req->dev_phys_addr); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * Configure chip select, transfer direction, address increment option 2918c2ecf20Sopenharmony_ci * and bytes per transaction option 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci bits = MPC512X_SCLPC_CS(cs); 2948c2ecf20Sopenharmony_ci if (lpbfifo.req->dir == MPC512X_LPBFIFO_REQ_DIR_READ) 2958c2ecf20Sopenharmony_ci bits |= MPC512X_SCLPC_READ | MPC512X_SCLPC_FLUSH; 2968c2ecf20Sopenharmony_ci if (no_incr) 2978c2ecf20Sopenharmony_ci bits |= MPC512X_SCLPC_DAI; 2988c2ecf20Sopenharmony_ci bits |= MPC512X_SCLPC_BPT(bpt); 2998c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->ctrl, bits); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Unmask irqs */ 3028c2ecf20Sopenharmony_ci bits = MPC512X_SCLPC_ENABLE | MPC512X_SCLPC_ABORT_INT_ENABLE; 3038c2ecf20Sopenharmony_ci if (lpbfifo.req->dir == MPC512X_LPBFIFO_REQ_DIR_WRITE) 3048c2ecf20Sopenharmony_ci bits |= MPC512X_SCLPC_NORM_INT_ENABLE; 3058c2ecf20Sopenharmony_ci else 3068c2ecf20Sopenharmony_ci lpbfifo.wait_lpbfifo_irq = false; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->enable, bits); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* 4. Set packet size and kick FIFO off */ 3118c2ecf20Sopenharmony_ci bits = lpbfifo.req->size | MPC512X_SCLPC_START; 3128c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->pkt_size, bits); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 5. Finally kick DMA off */ 3158c2ecf20Sopenharmony_ci cookie = dma_tx->tx_submit(dma_tx); 3168c2ecf20Sopenharmony_ci if (dma_submit_error(cookie)) { 3178c2ecf20Sopenharmony_ci ret = -ENOSPC; 3188c2ecf20Sopenharmony_ci goto err_dma_submit; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci err_dma_submit: 3248c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->enable, 3258c2ecf20Sopenharmony_ci MPC512X_SCLPC_RESET | MPC512X_SCLPC_FIFO_RESET); 3268c2ecf20Sopenharmony_ci err_dma_prep: 3278c2ecf20Sopenharmony_ci dma_unmap_single(dma_dev->dev, sg_dma_address(&sg), 3288c2ecf20Sopenharmony_ci lpbfifo.req->size, dir); 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int mpc512x_lpbfifo_submit_locked(struct mpc512x_lpbfifo_request *req) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int ret = 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!lpbfifo.regs) 3378c2ecf20Sopenharmony_ci return -ENODEV; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Check whether a transfer is in progress */ 3408c2ecf20Sopenharmony_ci if (lpbfifo.req) 3418c2ecf20Sopenharmony_ci return -EBUSY; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci lpbfifo.wait_lpbfifo_irq = true; 3448c2ecf20Sopenharmony_ci lpbfifo.wait_lpbfifo_callback = true; 3458c2ecf20Sopenharmony_ci lpbfifo.req = req; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = mpc512x_lpbfifo_kick(); 3488c2ecf20Sopenharmony_ci if (ret != 0) 3498c2ecf20Sopenharmony_ci lpbfifo.req = NULL; /* Set the FIFO as idle */ 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ciint mpc512x_lpbfifo_submit(struct mpc512x_lpbfifo_request *req) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci unsigned long flags; 3578c2ecf20Sopenharmony_ci int ret = 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 3608c2ecf20Sopenharmony_ci ret = mpc512x_lpbfifo_submit_locked(req); 3618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return ret; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mpc512x_lpbfifo_submit); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* 3688c2ecf20Sopenharmony_ci * LPBFIFO driver uses "ranges" property of "localbus" device tree node 3698c2ecf20Sopenharmony_ci * for being able to determine the chip select number of a client device 3708c2ecf20Sopenharmony_ci * ordering a DMA transfer. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_cistatic int get_cs_ranges(struct device *dev) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci int ret = -ENODEV; 3758c2ecf20Sopenharmony_ci struct device_node *lb_node; 3768c2ecf20Sopenharmony_ci const u32 *addr_cells_p; 3778c2ecf20Sopenharmony_ci const u32 *size_cells_p; 3788c2ecf20Sopenharmony_ci int proplen; 3798c2ecf20Sopenharmony_ci size_t i; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci lb_node = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-localbus"); 3828c2ecf20Sopenharmony_ci if (!lb_node) 3838c2ecf20Sopenharmony_ci return ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * The node defined as compatible with 'fsl,mpc5121-localbus' 3878c2ecf20Sopenharmony_ci * should have two address cells and one size cell. 3888c2ecf20Sopenharmony_ci * Every item of its ranges property should consist of: 3898c2ecf20Sopenharmony_ci * - the first address cell which is the chipselect number; 3908c2ecf20Sopenharmony_ci * - the second address cell which is the offset in the chipselect, 3918c2ecf20Sopenharmony_ci * must be zero. 3928c2ecf20Sopenharmony_ci * - CPU address of the beginning of an access window; 3938c2ecf20Sopenharmony_ci * - the only size cell which is the size of an access window. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci addr_cells_p = of_get_property(lb_node, "#address-cells", NULL); 3968c2ecf20Sopenharmony_ci size_cells_p = of_get_property(lb_node, "#size-cells", NULL); 3978c2ecf20Sopenharmony_ci if (addr_cells_p == NULL || *addr_cells_p != 2 || 3988c2ecf20Sopenharmony_ci size_cells_p == NULL || *size_cells_p != 1) { 3998c2ecf20Sopenharmony_ci goto end; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci proplen = of_property_count_u32_elems(lb_node, "ranges"); 4038c2ecf20Sopenharmony_ci if (proplen <= 0 || proplen % 4 != 0) 4048c2ecf20Sopenharmony_ci goto end; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci lpbfifo.cs_n = proplen / 4; 4078c2ecf20Sopenharmony_ci lpbfifo.cs_ranges = devm_kcalloc(dev, lpbfifo.cs_n, 4088c2ecf20Sopenharmony_ci sizeof(struct cs_range), GFP_KERNEL); 4098c2ecf20Sopenharmony_ci if (!lpbfifo.cs_ranges) 4108c2ecf20Sopenharmony_ci goto end; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (of_property_read_u32_array(lb_node, "ranges", 4138c2ecf20Sopenharmony_ci (u32 *)lpbfifo.cs_ranges, proplen) != 0) { 4148c2ecf20Sopenharmony_ci goto end; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (i = 0; i < lpbfifo.cs_n; i++) { 4188c2ecf20Sopenharmony_ci if (lpbfifo.cs_ranges[i].base != 0) 4198c2ecf20Sopenharmony_ci goto end; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci ret = 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci end: 4258c2ecf20Sopenharmony_ci of_node_put(lb_node); 4268c2ecf20Sopenharmony_ci return ret; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int mpc512x_lpbfifo_probe(struct platform_device *pdev) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct resource r; 4328c2ecf20Sopenharmony_ci int ret = 0; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci memset(&lpbfifo, 0, sizeof(struct lpbfifo_data)); 4358c2ecf20Sopenharmony_ci spin_lock_init(&lpbfifo.lock); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci lpbfifo.chan = dma_request_chan(&pdev->dev, "rx-tx"); 4388c2ecf20Sopenharmony_ci if (IS_ERR(lpbfifo.chan)) 4398c2ecf20Sopenharmony_ci return PTR_ERR(lpbfifo.chan); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (of_address_to_resource(pdev->dev.of_node, 0, &r) != 0) { 4428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "bad 'reg' in 'sclpc' device tree node\n"); 4438c2ecf20Sopenharmony_ci ret = -ENODEV; 4448c2ecf20Sopenharmony_ci goto err0; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci lpbfifo.regs_phys = r.start; 4488c2ecf20Sopenharmony_ci lpbfifo.regs_size = resource_size(&r); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, lpbfifo.regs_phys, 4518c2ecf20Sopenharmony_ci lpbfifo.regs_size, DRV_NAME)) { 4528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to request region\n"); 4538c2ecf20Sopenharmony_ci ret = -EBUSY; 4548c2ecf20Sopenharmony_ci goto err0; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci lpbfifo.regs = devm_ioremap(&pdev->dev, 4588c2ecf20Sopenharmony_ci lpbfifo.regs_phys, lpbfifo.regs_size); 4598c2ecf20Sopenharmony_ci if (!lpbfifo.regs) { 4608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "mapping registers failed\n"); 4618c2ecf20Sopenharmony_ci ret = -ENOMEM; 4628c2ecf20Sopenharmony_ci goto err0; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci out_be32(&lpbfifo.regs->enable, 4668c2ecf20Sopenharmony_ci MPC512X_SCLPC_RESET | MPC512X_SCLPC_FIFO_RESET); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (get_cs_ranges(&pdev->dev) != 0) { 4698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "bad '/localbus' device tree node\n"); 4708c2ecf20Sopenharmony_ci ret = -ENODEV; 4718c2ecf20Sopenharmony_ci goto err0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci lpbfifo.irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 4758c2ecf20Sopenharmony_ci if (!lpbfifo.irq) { 4768c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "mapping irq failed\n"); 4778c2ecf20Sopenharmony_ci ret = -ENODEV; 4788c2ecf20Sopenharmony_ci goto err0; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (request_irq(lpbfifo.irq, mpc512x_lpbfifo_irq, 0, 4828c2ecf20Sopenharmony_ci DRV_NAME, &pdev->dev) != 0) { 4838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "requesting irq failed\n"); 4848c2ecf20Sopenharmony_ci ret = -ENODEV; 4858c2ecf20Sopenharmony_ci goto err1; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "probe succeeded\n"); 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci err1: 4928c2ecf20Sopenharmony_ci irq_dispose_mapping(lpbfifo.irq); 4938c2ecf20Sopenharmony_ci err0: 4948c2ecf20Sopenharmony_ci dma_release_channel(lpbfifo.chan); 4958c2ecf20Sopenharmony_ci return ret; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int mpc512x_lpbfifo_remove(struct platform_device *pdev) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci unsigned long flags; 5018c2ecf20Sopenharmony_ci struct dma_device *dma_dev = lpbfifo.chan->device; 5028c2ecf20Sopenharmony_ci struct mpc512x_lpbfifo __iomem *regs = NULL; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 5058c2ecf20Sopenharmony_ci regs = lpbfifo.regs; 5068c2ecf20Sopenharmony_ci lpbfifo.regs = NULL; 5078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci dma_dev->device_terminate_all(lpbfifo.chan); 5108c2ecf20Sopenharmony_ci out_be32(®s->enable, MPC512X_SCLPC_RESET | MPC512X_SCLPC_FIFO_RESET); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci free_irq(lpbfifo.irq, &pdev->dev); 5138c2ecf20Sopenharmony_ci irq_dispose_mapping(lpbfifo.irq); 5148c2ecf20Sopenharmony_ci dma_release_channel(lpbfifo.chan); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct of_device_id mpc512x_lpbfifo_match[] = { 5208c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc512x-lpbfifo", }, 5218c2ecf20Sopenharmony_ci {}, 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpc512x_lpbfifo_match); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic struct platform_driver mpc512x_lpbfifo_driver = { 5268c2ecf20Sopenharmony_ci .probe = mpc512x_lpbfifo_probe, 5278c2ecf20Sopenharmony_ci .remove = mpc512x_lpbfifo_remove, 5288c2ecf20Sopenharmony_ci .driver = { 5298c2ecf20Sopenharmony_ci .name = DRV_NAME, 5308c2ecf20Sopenharmony_ci .of_match_table = mpc512x_lpbfifo_match, 5318c2ecf20Sopenharmony_ci }, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cimodule_platform_driver(mpc512x_lpbfifo_driver); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Popov <alex.popov@linux.com>"); 5378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MPC512x LocalPlus Bus FIFO device driver"); 5388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 539