18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * LocalPlus Bus FIFO driver for the Freescale MPC52xx. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Secret Lab Technologies Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Todo: 88c2ecf20Sopenharmony_ci * - Add support for multiple requests to be queued. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <asm/io.h> 188c2ecf20Sopenharmony_ci#include <asm/prom.h> 198c2ecf20Sopenharmony_ci#include <asm/mpc52xx.h> 208c2ecf20Sopenharmony_ci#include <asm/time.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/bestcomm.h> 238c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/bestcomm_priv.h> 248c2ecf20Sopenharmony_ci#include <linux/fsl/bestcomm/gen_bd.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver"); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define LPBFIFO_REG_PACKET_SIZE (0x00) 318c2ecf20Sopenharmony_ci#define LPBFIFO_REG_START_ADDRESS (0x04) 328c2ecf20Sopenharmony_ci#define LPBFIFO_REG_CONTROL (0x08) 338c2ecf20Sopenharmony_ci#define LPBFIFO_REG_ENABLE (0x0C) 348c2ecf20Sopenharmony_ci#define LPBFIFO_REG_BYTES_DONE_STATUS (0x14) 358c2ecf20Sopenharmony_ci#define LPBFIFO_REG_FIFO_DATA (0x40) 368c2ecf20Sopenharmony_ci#define LPBFIFO_REG_FIFO_STATUS (0x44) 378c2ecf20Sopenharmony_ci#define LPBFIFO_REG_FIFO_CONTROL (0x48) 388c2ecf20Sopenharmony_ci#define LPBFIFO_REG_FIFO_ALARM (0x4C) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct mpc52xx_lpbfifo { 418c2ecf20Sopenharmony_ci struct device *dev; 428c2ecf20Sopenharmony_ci phys_addr_t regs_phys; 438c2ecf20Sopenharmony_ci void __iomem *regs; 448c2ecf20Sopenharmony_ci int irq; 458c2ecf20Sopenharmony_ci spinlock_t lock; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci struct bcom_task *bcom_tx_task; 488c2ecf20Sopenharmony_ci struct bcom_task *bcom_rx_task; 498c2ecf20Sopenharmony_ci struct bcom_task *bcom_cur_task; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Current state data */ 528c2ecf20Sopenharmony_ci struct mpc52xx_lpbfifo_request *req; 538c2ecf20Sopenharmony_ci int dma_irqs_enabled; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* The MPC5200 has only one fifo, so only need one instance structure */ 578c2ecf20Sopenharmony_cistatic struct mpc52xx_lpbfifo lpbfifo; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/** 608c2ecf20Sopenharmony_ci * mpc52xx_lpbfifo_kick - Trigger the next block of data to be transferred 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci size_t transfer_size = req->size - req->pos; 658c2ecf20Sopenharmony_ci struct bcom_bd *bd; 668c2ecf20Sopenharmony_ci void __iomem *reg; 678c2ecf20Sopenharmony_ci u32 *data; 688c2ecf20Sopenharmony_ci int i; 698c2ecf20Sopenharmony_ci int bit_fields; 708c2ecf20Sopenharmony_ci int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); 718c2ecf20Sopenharmony_ci int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; 728c2ecf20Sopenharmony_ci int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Set and clear the reset bits; is good practice in User Manual */ 758c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* set master enable bit */ 788c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001); 798c2ecf20Sopenharmony_ci if (!dma) { 808c2ecf20Sopenharmony_ci /* While the FIFO can be setup for transfer sizes as large as 818c2ecf20Sopenharmony_ci * 16M-1, the FIFO itself is only 512 bytes deep and it does 828c2ecf20Sopenharmony_ci * not generate interrupts for FIFO full events (only transfer 838c2ecf20Sopenharmony_ci * complete will raise an IRQ). Therefore when not using 848c2ecf20Sopenharmony_ci * Bestcomm to drive the FIFO it needs to either be polled, or 858c2ecf20Sopenharmony_ci * transfers need to constrained to the size of the fifo. 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * This driver restricts the size of the transfer 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci if (transfer_size > 512) 908c2ecf20Sopenharmony_ci transfer_size = 512; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Load the FIFO with data */ 938c2ecf20Sopenharmony_ci if (write) { 948c2ecf20Sopenharmony_ci reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA; 958c2ecf20Sopenharmony_ci data = req->data + req->pos; 968c2ecf20Sopenharmony_ci for (i = 0; i < transfer_size; i += 4) 978c2ecf20Sopenharmony_ci out_be32(reg, *data++); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Unmask both error and completion irqs */ 1018c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301); 1028c2ecf20Sopenharmony_ci } else { 1038c2ecf20Sopenharmony_ci /* Choose the correct direction 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * Configure the watermarks so DMA will always complete correctly. 1068c2ecf20Sopenharmony_ci * It may be worth experimenting with the ALARM value to see if 1078c2ecf20Sopenharmony_ci * there is a performance impacit. However, if it is wrong there 1088c2ecf20Sopenharmony_ci * is a risk of DMA not transferring the last chunk of data 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci if (write) { 1118c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4); 1128c2ecf20Sopenharmony_ci out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7); 1138c2ecf20Sopenharmony_ci lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task; 1148c2ecf20Sopenharmony_ci } else { 1158c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff); 1168c2ecf20Sopenharmony_ci out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0); 1178c2ecf20Sopenharmony_ci lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (poll_dma) { 1208c2ecf20Sopenharmony_ci if (lpbfifo.dma_irqs_enabled) { 1218c2ecf20Sopenharmony_ci disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task)); 1228c2ecf20Sopenharmony_ci lpbfifo.dma_irqs_enabled = 0; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } else { 1258c2ecf20Sopenharmony_ci if (!lpbfifo.dma_irqs_enabled) { 1268c2ecf20Sopenharmony_ci enable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task)); 1278c2ecf20Sopenharmony_ci lpbfifo.dma_irqs_enabled = 1; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task); 1338c2ecf20Sopenharmony_ci bd->status = transfer_size; 1348c2ecf20Sopenharmony_ci if (!write) { 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * In the DMA read case, the DMA doesn't complete, 1378c2ecf20Sopenharmony_ci * possibly due to incorrect watermarks in the ALARM 1388c2ecf20Sopenharmony_ci * and CONTROL regs. For now instead of trying to 1398c2ecf20Sopenharmony_ci * determine the right watermarks that will make this 1408c2ecf20Sopenharmony_ci * work, just increase the number of bytes the FIFO is 1418c2ecf20Sopenharmony_ci * expecting. 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * When submitting another operation, the FIFO will get 1448c2ecf20Sopenharmony_ci * reset, so the condition of the FIFO waiting for a 1458c2ecf20Sopenharmony_ci * non-existent 4 bytes will get cleared. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci transfer_size += 4; /* BLECH! */ 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci bd->data[0] = req->data_phys + req->pos; 1508c2ecf20Sopenharmony_ci bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* error irq & master enabled bit */ 1538c2ecf20Sopenharmony_ci bit_fields = 0x00000201; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Unmask irqs */ 1568c2ecf20Sopenharmony_ci if (write && (!poll_dma)) 1578c2ecf20Sopenharmony_ci bit_fields |= 0x00000100; /* completion irq too */ 1588c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Set transfer size, width, chip select and READ mode */ 1628c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS, 1638c2ecf20Sopenharmony_ci req->offset + req->pos); 1648c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci bit_fields = req->cs << 24 | 0x000008; 1678c2ecf20Sopenharmony_ci if (!write) 1688c2ecf20Sopenharmony_ci bit_fields |= 0x010000; /* read mode */ 1698c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Kick it off */ 1728c2ecf20Sopenharmony_ci if (!lpbfifo.req->defer_xfer_start) 1738c2ecf20Sopenharmony_ci out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01); 1748c2ecf20Sopenharmony_ci if (dma) 1758c2ecf20Sopenharmony_ci bcom_enable(lpbfifo.bcom_cur_task); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * mpc52xx_lpbfifo_irq - IRQ handler for LPB FIFO 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * On transmit, the dma completion irq triggers before the fifo completion 1828c2ecf20Sopenharmony_ci * triggers. Handle the dma completion here instead of the LPB FIFO Bestcomm 1838c2ecf20Sopenharmony_ci * task completion irq because everything is not really done until the LPB FIFO 1848c2ecf20Sopenharmony_ci * completion irq triggers. 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * In other words: 1878c2ecf20Sopenharmony_ci * For DMA, on receive, the "Fat Lady" is the bestcom completion irq. on 1888c2ecf20Sopenharmony_ci * transmit, the fifo completion irq is the "Fat Lady". The opera (or in this 1898c2ecf20Sopenharmony_ci * case the DMA/FIFO operation) is not finished until the "Fat Lady" sings. 1908c2ecf20Sopenharmony_ci * 1918c2ecf20Sopenharmony_ci * Reasons for entering this routine: 1928c2ecf20Sopenharmony_ci * 1) PIO mode rx and tx completion irq 1938c2ecf20Sopenharmony_ci * 2) DMA interrupt mode tx completion irq 1948c2ecf20Sopenharmony_ci * 3) DMA polled mode tx 1958c2ecf20Sopenharmony_ci * 1968c2ecf20Sopenharmony_ci * Exit conditions: 1978c2ecf20Sopenharmony_ci * 1) Transfer aborted 1988c2ecf20Sopenharmony_ci * 2) FIFO complete without DMA; more data to do 1998c2ecf20Sopenharmony_ci * 3) FIFO complete without DMA; all data transferred 2008c2ecf20Sopenharmony_ci * 4) FIFO complete using DMA 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Condition 1 can occur regardless of whether or not DMA is used. 2038c2ecf20Sopenharmony_ci * It requires executing the callback to report the error and exiting 2048c2ecf20Sopenharmony_ci * immediately. 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * Condition 2 requires programming the FIFO with the next block of data 2078c2ecf20Sopenharmony_ci * 2088c2ecf20Sopenharmony_ci * Condition 3 requires executing the callback to report completion 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * Condition 4 means the same as 3, except that we also retrieve the bcom 2118c2ecf20Sopenharmony_ci * buffer so DMA doesn't get clogged up. 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * To make things trickier, the spinlock must be dropped before 2148c2ecf20Sopenharmony_ci * executing the callback, otherwise we could end up with a deadlock 2158c2ecf20Sopenharmony_ci * or nested spinlock condition. The out path is non-trivial, so 2168c2ecf20Sopenharmony_ci * extra fiddling is done to make sure all paths lead to the same 2178c2ecf20Sopenharmony_ci * outbound code. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct mpc52xx_lpbfifo_request *req; 2228c2ecf20Sopenharmony_ci u32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS); 2238c2ecf20Sopenharmony_ci void __iomem *reg; 2248c2ecf20Sopenharmony_ci u32 *data; 2258c2ecf20Sopenharmony_ci int count, i; 2268c2ecf20Sopenharmony_ci int do_callback = 0; 2278c2ecf20Sopenharmony_ci u32 ts; 2288c2ecf20Sopenharmony_ci unsigned long flags; 2298c2ecf20Sopenharmony_ci int dma, write, poll_dma; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 2328c2ecf20Sopenharmony_ci ts = get_tbl(); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci req = lpbfifo.req; 2358c2ecf20Sopenharmony_ci if (!req) { 2368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 2378c2ecf20Sopenharmony_ci pr_err("bogus LPBFIFO IRQ\n"); 2388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); 2428c2ecf20Sopenharmony_ci write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; 2438c2ecf20Sopenharmony_ci poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (dma && !write) { 2468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 2478c2ecf20Sopenharmony_ci pr_err("bogus LPBFIFO IRQ (dma and not writing)\n"); 2488c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if ((status & 0x01) == 0) { 2528c2ecf20Sopenharmony_ci goto out; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* check abort bit */ 2568c2ecf20Sopenharmony_ci if (status & 0x10) { 2578c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); 2588c2ecf20Sopenharmony_ci do_callback = 1; 2598c2ecf20Sopenharmony_ci goto out; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Read result from hardware */ 2638c2ecf20Sopenharmony_ci count = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS); 2648c2ecf20Sopenharmony_ci count &= 0x00ffffff; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (!dma && !write) { 2678c2ecf20Sopenharmony_ci /* copy the data out of the FIFO */ 2688c2ecf20Sopenharmony_ci reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA; 2698c2ecf20Sopenharmony_ci data = req->data + req->pos; 2708c2ecf20Sopenharmony_ci for (i = 0; i < count; i += 4) 2718c2ecf20Sopenharmony_ci *data++ = in_be32(reg); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Update transfer position and count */ 2758c2ecf20Sopenharmony_ci req->pos += count; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* Decide what to do next */ 2788c2ecf20Sopenharmony_ci if (req->size - req->pos) 2798c2ecf20Sopenharmony_ci mpc52xx_lpbfifo_kick(req); /* more work to do */ 2808c2ecf20Sopenharmony_ci else 2818c2ecf20Sopenharmony_ci do_callback = 1; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci out: 2848c2ecf20Sopenharmony_ci /* Clear the IRQ */ 2858c2ecf20Sopenharmony_ci out_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (dma && (status & 0x11)) { 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * Count the DMA as complete only when the FIFO completion 2908c2ecf20Sopenharmony_ci * status or abort bits are set. 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * (status & 0x01) should always be the case except sometimes 2938c2ecf20Sopenharmony_ci * when using polled DMA. 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * (status & 0x10) {transfer aborted}: This case needs more 2968c2ecf20Sopenharmony_ci * testing. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci req->last_byte = ((u8 *)req->data)[req->size - 1]; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* When the do_callback flag is set; it means the transfer is finished 3038c2ecf20Sopenharmony_ci * so set the FIFO as idle */ 3048c2ecf20Sopenharmony_ci if (do_callback) 3058c2ecf20Sopenharmony_ci lpbfifo.req = NULL; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (irq != 0) /* don't increment on polled case */ 3088c2ecf20Sopenharmony_ci req->irq_count++; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci req->irq_ticks += get_tbl() - ts; 3118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Spinlock is released; it is now safe to call the callback */ 3148c2ecf20Sopenharmony_ci if (do_callback && req->callback) 3158c2ecf20Sopenharmony_ci req->callback(req); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/** 3218c2ecf20Sopenharmony_ci * mpc52xx_lpbfifo_bcom_irq - IRQ handler for LPB FIFO Bestcomm task 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * Only used when receiving data. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_cistatic irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct mpc52xx_lpbfifo_request *req; 3288c2ecf20Sopenharmony_ci unsigned long flags; 3298c2ecf20Sopenharmony_ci u32 status; 3308c2ecf20Sopenharmony_ci u32 ts; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 3338c2ecf20Sopenharmony_ci ts = get_tbl(); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci req = lpbfifo.req; 3368c2ecf20Sopenharmony_ci if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) { 3378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 3388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (irq != 0) /* don't increment on polled case */ 3428c2ecf20Sopenharmony_ci req->irq_count++; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) { 3458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci req->buffer_not_done_cnt++; 3488c2ecf20Sopenharmony_ci if ((req->buffer_not_done_cnt % 1000) == 0) 3498c2ecf20Sopenharmony_ci pr_err("transfer stalled\n"); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci req->last_byte = ((u8 *)req->data)[req->size - 1]; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci req->pos = status & 0x00ffffff; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Mark the FIFO as idle */ 3618c2ecf20Sopenharmony_ci lpbfifo.req = NULL; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Release the lock before calling out to the callback. */ 3648c2ecf20Sopenharmony_ci req->irq_ticks += get_tbl() - ts; 3658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (req->callback) 3688c2ecf20Sopenharmony_ci req->callback(req); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/** 3748c2ecf20Sopenharmony_ci * mpc52xx_lpbfifo_bcom_poll - Poll for DMA completion 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_civoid mpc52xx_lpbfifo_poll(void) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct mpc52xx_lpbfifo_request *req = lpbfifo.req; 3798c2ecf20Sopenharmony_ci int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); 3808c2ecf20Sopenharmony_ci int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* 3838c2ecf20Sopenharmony_ci * For more information, see comments on the "Fat Lady" 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci if (dma && write) 3868c2ecf20Sopenharmony_ci mpc52xx_lpbfifo_irq(0, NULL); 3878c2ecf20Sopenharmony_ci else 3888c2ecf20Sopenharmony_ci mpc52xx_lpbfifo_bcom_irq(0, NULL); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_lpbfifo_poll); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * mpc52xx_lpbfifo_submit - Submit an LPB FIFO transfer request. 3948c2ecf20Sopenharmony_ci * @req: Pointer to request structure 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ciint mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci unsigned long flags; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!lpbfifo.regs) 4018c2ecf20Sopenharmony_ci return -ENODEV; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* If the req pointer is already set, then a transfer is in progress */ 4068c2ecf20Sopenharmony_ci if (lpbfifo.req) { 4078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 4088c2ecf20Sopenharmony_ci return -EBUSY; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Setup the transfer */ 4128c2ecf20Sopenharmony_ci lpbfifo.req = req; 4138c2ecf20Sopenharmony_ci req->irq_count = 0; 4148c2ecf20Sopenharmony_ci req->irq_ticks = 0; 4158c2ecf20Sopenharmony_ci req->buffer_not_done_cnt = 0; 4168c2ecf20Sopenharmony_ci req->pos = 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci mpc52xx_lpbfifo_kick(req); 4198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_lpbfifo_submit); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciint mpc52xx_lpbfifo_start_xfer(struct mpc52xx_lpbfifo_request *req) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci unsigned long flags; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!lpbfifo.regs) 4298c2ecf20Sopenharmony_ci return -ENODEV; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* 4348c2ecf20Sopenharmony_ci * If the req pointer is already set and a transfer was 4358c2ecf20Sopenharmony_ci * started on submit, then this transfer is in progress 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci if (lpbfifo.req && !lpbfifo.req->defer_xfer_start) { 4388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 4398c2ecf20Sopenharmony_ci return -EBUSY; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * If the req was previously submitted but not 4448c2ecf20Sopenharmony_ci * started, start it now 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci if (lpbfifo.req && lpbfifo.req == req && 4478c2ecf20Sopenharmony_ci lpbfifo.req->defer_xfer_start) { 4488c2ecf20Sopenharmony_ci out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_lpbfifo_start_xfer); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_civoid mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci unsigned long flags; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci spin_lock_irqsave(&lpbfifo.lock, flags); 4618c2ecf20Sopenharmony_ci if (lpbfifo.req == req) { 4628c2ecf20Sopenharmony_ci /* Put it into reset and clear the state */ 4638c2ecf20Sopenharmony_ci bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task); 4648c2ecf20Sopenharmony_ci bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task); 4658c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); 4668c2ecf20Sopenharmony_ci lpbfifo.req = NULL; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lpbfifo.lock, flags); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_lpbfifo_abort); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int mpc52xx_lpbfifo_probe(struct platform_device *op) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct resource res; 4758c2ecf20Sopenharmony_ci int rc = -ENOMEM; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (lpbfifo.dev != NULL) 4788c2ecf20Sopenharmony_ci return -ENOSPC; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci lpbfifo.irq = irq_of_parse_and_map(op->dev.of_node, 0); 4818c2ecf20Sopenharmony_ci if (!lpbfifo.irq) 4828c2ecf20Sopenharmony_ci return -ENODEV; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (of_address_to_resource(op->dev.of_node, 0, &res)) 4858c2ecf20Sopenharmony_ci return -ENODEV; 4868c2ecf20Sopenharmony_ci lpbfifo.regs_phys = res.start; 4878c2ecf20Sopenharmony_ci lpbfifo.regs = of_iomap(op->dev.of_node, 0); 4888c2ecf20Sopenharmony_ci if (!lpbfifo.regs) 4898c2ecf20Sopenharmony_ci return -ENOMEM; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci spin_lock_init(&lpbfifo.lock); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* Put FIFO into reset */ 4948c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Register the interrupt handler */ 4978c2ecf20Sopenharmony_ci rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0, 4988c2ecf20Sopenharmony_ci "mpc52xx-lpbfifo", &lpbfifo); 4998c2ecf20Sopenharmony_ci if (rc) 5008c2ecf20Sopenharmony_ci goto err_irq; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Request the Bestcomm receive (fifo --> memory) task and IRQ */ 5038c2ecf20Sopenharmony_ci lpbfifo.bcom_rx_task = 5048c2ecf20Sopenharmony_ci bcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA, 5058c2ecf20Sopenharmony_ci BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC, 5068c2ecf20Sopenharmony_ci 16*1024*1024); 5078c2ecf20Sopenharmony_ci if (!lpbfifo.bcom_rx_task) 5088c2ecf20Sopenharmony_ci goto err_bcom_rx; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci rc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), 5118c2ecf20Sopenharmony_ci mpc52xx_lpbfifo_bcom_irq, 0, 5128c2ecf20Sopenharmony_ci "mpc52xx-lpbfifo-rx", &lpbfifo); 5138c2ecf20Sopenharmony_ci if (rc) 5148c2ecf20Sopenharmony_ci goto err_bcom_rx_irq; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci lpbfifo.dma_irqs_enabled = 1; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Request the Bestcomm transmit (memory --> fifo) task and IRQ */ 5198c2ecf20Sopenharmony_ci lpbfifo.bcom_tx_task = 5208c2ecf20Sopenharmony_ci bcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA, 5218c2ecf20Sopenharmony_ci BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC); 5228c2ecf20Sopenharmony_ci if (!lpbfifo.bcom_tx_task) 5238c2ecf20Sopenharmony_ci goto err_bcom_tx; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci lpbfifo.dev = &op->dev; 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci err_bcom_tx: 5298c2ecf20Sopenharmony_ci free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo); 5308c2ecf20Sopenharmony_ci err_bcom_rx_irq: 5318c2ecf20Sopenharmony_ci bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task); 5328c2ecf20Sopenharmony_ci err_bcom_rx: 5338c2ecf20Sopenharmony_ci free_irq(lpbfifo.irq, &lpbfifo); 5348c2ecf20Sopenharmony_ci err_irq: 5358c2ecf20Sopenharmony_ci iounmap(lpbfifo.regs); 5368c2ecf20Sopenharmony_ci lpbfifo.regs = NULL; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci dev_err(&op->dev, "mpc52xx_lpbfifo_probe() failed\n"); 5398c2ecf20Sopenharmony_ci return -ENODEV; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int mpc52xx_lpbfifo_remove(struct platform_device *op) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci if (lpbfifo.dev != &op->dev) 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* Put FIFO in reset */ 5498c2ecf20Sopenharmony_ci out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Release the bestcomm transmit task */ 5528c2ecf20Sopenharmony_ci free_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo); 5538c2ecf20Sopenharmony_ci bcom_gen_bd_tx_release(lpbfifo.bcom_tx_task); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* Release the bestcomm receive task */ 5568c2ecf20Sopenharmony_ci free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo); 5578c2ecf20Sopenharmony_ci bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci free_irq(lpbfifo.irq, &lpbfifo); 5608c2ecf20Sopenharmony_ci iounmap(lpbfifo.regs); 5618c2ecf20Sopenharmony_ci lpbfifo.regs = NULL; 5628c2ecf20Sopenharmony_ci lpbfifo.dev = NULL; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic const struct of_device_id mpc52xx_lpbfifo_match[] = { 5688c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5200-lpbfifo", }, 5698c2ecf20Sopenharmony_ci {}, 5708c2ecf20Sopenharmony_ci}; 5718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpc52xx_lpbfifo_match); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic struct platform_driver mpc52xx_lpbfifo_driver = { 5748c2ecf20Sopenharmony_ci .driver = { 5758c2ecf20Sopenharmony_ci .name = "mpc52xx-lpbfifo", 5768c2ecf20Sopenharmony_ci .of_match_table = mpc52xx_lpbfifo_match, 5778c2ecf20Sopenharmony_ci }, 5788c2ecf20Sopenharmony_ci .probe = mpc52xx_lpbfifo_probe, 5798c2ecf20Sopenharmony_ci .remove = mpc52xx_lpbfifo_remove, 5808c2ecf20Sopenharmony_ci}; 5818c2ecf20Sopenharmony_cimodule_platform_driver(mpc52xx_lpbfifo_driver); 582