Path: blob/master/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c
10818 views
/*1* LocalPlus Bus FIFO driver for the Freescale MPC52xx.2*3* Copyright (C) 2009 Secret Lab Technologies Ltd.4*5* This file is released under the GPLv26*7* Todo:8* - Add support for multiple requests to be queued.9*/1011#include <linux/interrupt.h>12#include <linux/kernel.h>13#include <linux/of.h>14#include <linux/of_platform.h>15#include <linux/spinlock.h>16#include <asm/io.h>17#include <asm/prom.h>18#include <asm/mpc52xx.h>19#include <asm/time.h>2021#include <sysdev/bestcomm/bestcomm.h>22#include <sysdev/bestcomm/bestcomm_priv.h>23#include <sysdev/bestcomm/gen_bd.h>2425MODULE_AUTHOR("Grant Likely <[email protected]>");26MODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver");27MODULE_LICENSE("GPL");2829#define LPBFIFO_REG_PACKET_SIZE (0x00)30#define LPBFIFO_REG_START_ADDRESS (0x04)31#define LPBFIFO_REG_CONTROL (0x08)32#define LPBFIFO_REG_ENABLE (0x0C)33#define LPBFIFO_REG_BYTES_DONE_STATUS (0x14)34#define LPBFIFO_REG_FIFO_DATA (0x40)35#define LPBFIFO_REG_FIFO_STATUS (0x44)36#define LPBFIFO_REG_FIFO_CONTROL (0x48)37#define LPBFIFO_REG_FIFO_ALARM (0x4C)3839struct mpc52xx_lpbfifo {40struct device *dev;41phys_addr_t regs_phys;42void __iomem *regs;43int irq;44spinlock_t lock;4546struct bcom_task *bcom_tx_task;47struct bcom_task *bcom_rx_task;48struct bcom_task *bcom_cur_task;4950/* Current state data */51struct mpc52xx_lpbfifo_request *req;52int dma_irqs_enabled;53};5455/* The MPC5200 has only one fifo, so only need one instance structure */56static struct mpc52xx_lpbfifo lpbfifo;5758/**59* mpc52xx_lpbfifo_kick - Trigger the next block of data to be transferred60*/61static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req)62{63size_t transfer_size = req->size - req->pos;64struct bcom_bd *bd;65void __iomem *reg;66u32 *data;67int i;68int bit_fields;69int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);70int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;71int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;7273/* Set and clear the reset bits; is good practice in User Manual */74out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);7576/* set master enable bit */77out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001);78if (!dma) {79/* While the FIFO can be setup for transfer sizes as large as80* 16M-1, the FIFO itself is only 512 bytes deep and it does81* not generate interrupts for FIFO full events (only transfer82* complete will raise an IRQ). Therefore when not using83* Bestcomm to drive the FIFO it needs to either be polled, or84* transfers need to constrained to the size of the fifo.85*86* This driver restricts the size of the transfer87*/88if (transfer_size > 512)89transfer_size = 512;9091/* Load the FIFO with data */92if (write) {93reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;94data = req->data + req->pos;95for (i = 0; i < transfer_size; i += 4)96out_be32(reg, *data++);97}9899/* Unmask both error and completion irqs */100out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301);101} else {102/* Choose the correct direction103*104* Configure the watermarks so DMA will always complete correctly.105* It may be worth experimenting with the ALARM value to see if106* there is a performance impacit. However, if it is wrong there107* is a risk of DMA not transferring the last chunk of data108*/109if (write) {110out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4);111out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7);112lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task;113} else {114out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff);115out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0);116lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task;117118if (poll_dma) {119if (lpbfifo.dma_irqs_enabled) {120disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));121lpbfifo.dma_irqs_enabled = 0;122}123} else {124if (!lpbfifo.dma_irqs_enabled) {125enable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task));126lpbfifo.dma_irqs_enabled = 1;127}128}129}130131bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task);132bd->status = transfer_size;133if (!write) {134/*135* In the DMA read case, the DMA doesn't complete,136* possibly due to incorrect watermarks in the ALARM137* and CONTROL regs. For now instead of trying to138* determine the right watermarks that will make this139* work, just increase the number of bytes the FIFO is140* expecting.141*142* When submitting another operation, the FIFO will get143* reset, so the condition of the FIFO waiting for a144* non-existent 4 bytes will get cleared.145*/146transfer_size += 4; /* BLECH! */147}148bd->data[0] = req->data_phys + req->pos;149bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL);150151/* error irq & master enabled bit */152bit_fields = 0x00000201;153154/* Unmask irqs */155if (write && (!poll_dma))156bit_fields |= 0x00000100; /* completion irq too */157out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields);158}159160/* Set transfer size, width, chip select and READ mode */161out_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS,162req->offset + req->pos);163out_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size);164165bit_fields = req->cs << 24 | 0x000008;166if (!write)167bit_fields |= 0x010000; /* read mode */168out_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields);169170/* Kick it off */171out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01);172if (dma)173bcom_enable(lpbfifo.bcom_cur_task);174}175176/**177* mpc52xx_lpbfifo_irq - IRQ handler for LPB FIFO178*179* On transmit, the dma completion irq triggers before the fifo completion180* triggers. Handle the dma completion here instead of the LPB FIFO Bestcomm181* task completion irq because everything is not really done until the LPB FIFO182* completion irq triggers.183*184* In other words:185* For DMA, on receive, the "Fat Lady" is the bestcom completion irq. on186* transmit, the fifo completion irq is the "Fat Lady". The opera (or in this187* case the DMA/FIFO operation) is not finished until the "Fat Lady" sings.188*189* Reasons for entering this routine:190* 1) PIO mode rx and tx completion irq191* 2) DMA interrupt mode tx completion irq192* 3) DMA polled mode tx193*194* Exit conditions:195* 1) Transfer aborted196* 2) FIFO complete without DMA; more data to do197* 3) FIFO complete without DMA; all data transferred198* 4) FIFO complete using DMA199*200* Condition 1 can occur regardless of whether or not DMA is used.201* It requires executing the callback to report the error and exiting202* immediately.203*204* Condition 2 requires programming the FIFO with the next block of data205*206* Condition 3 requires executing the callback to report completion207*208* Condition 4 means the same as 3, except that we also retrieve the bcom209* buffer so DMA doesn't get clogged up.210*211* To make things trickier, the spinlock must be dropped before212* executing the callback, otherwise we could end up with a deadlock213* or nested spinlock condition. The out path is non-trivial, so214* extra fiddling is done to make sure all paths lead to the same215* outbound code.216*/217static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id)218{219struct mpc52xx_lpbfifo_request *req;220u32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);221void __iomem *reg;222u32 *data;223int count, i;224int do_callback = 0;225u32 ts;226unsigned long flags;227int dma, write, poll_dma;228229spin_lock_irqsave(&lpbfifo.lock, flags);230ts = get_tbl();231232req = lpbfifo.req;233if (!req) {234spin_unlock_irqrestore(&lpbfifo.lock, flags);235pr_err("bogus LPBFIFO IRQ\n");236return IRQ_HANDLED;237}238239dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);240write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;241poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA;242243if (dma && !write) {244spin_unlock_irqrestore(&lpbfifo.lock, flags);245pr_err("bogus LPBFIFO IRQ (dma and not writting)\n");246return IRQ_HANDLED;247}248249if ((status & 0x01) == 0) {250goto out;251}252253/* check abort bit */254if (status & 0x10) {255out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);256do_callback = 1;257goto out;258}259260/* Read result from hardware */261count = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS);262count &= 0x00ffffff;263264if (!dma && !write) {265/* copy the data out of the FIFO */266reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA;267data = req->data + req->pos;268for (i = 0; i < count; i += 4)269*data++ = in_be32(reg);270}271272/* Update transfer position and count */273req->pos += count;274275/* Decide what to do next */276if (req->size - req->pos)277mpc52xx_lpbfifo_kick(req); /* more work to do */278else279do_callback = 1;280281out:282/* Clear the IRQ */283out_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01);284285if (dma && (status & 0x11)) {286/*287* Count the DMA as complete only when the FIFO completion288* status or abort bits are set.289*290* (status & 0x01) should always be the case except sometimes291* when using polled DMA.292*293* (status & 0x10) {transfer aborted}: This case needs more294* testing.295*/296bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);297}298req->last_byte = ((u8 *)req->data)[req->size - 1];299300/* When the do_callback flag is set; it means the transfer is finished301* so set the FIFO as idle */302if (do_callback)303lpbfifo.req = NULL;304305if (irq != 0) /* don't increment on polled case */306req->irq_count++;307308req->irq_ticks += get_tbl() - ts;309spin_unlock_irqrestore(&lpbfifo.lock, flags);310311/* Spinlock is released; it is now safe to call the callback */312if (do_callback && req->callback)313req->callback(req);314315return IRQ_HANDLED;316}317318/**319* mpc52xx_lpbfifo_bcom_irq - IRQ handler for LPB FIFO Bestcomm task320*321* Only used when receiving data.322*/323static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id)324{325struct mpc52xx_lpbfifo_request *req;326unsigned long flags;327u32 status;328u32 ts;329330spin_lock_irqsave(&lpbfifo.lock, flags);331ts = get_tbl();332333req = lpbfifo.req;334if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) {335spin_unlock_irqrestore(&lpbfifo.lock, flags);336return IRQ_HANDLED;337}338339if (irq != 0) /* don't increment on polled case */340req->irq_count++;341342if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) {343spin_unlock_irqrestore(&lpbfifo.lock, flags);344345req->buffer_not_done_cnt++;346if ((req->buffer_not_done_cnt % 1000) == 0)347pr_err("transfer stalled\n");348349return IRQ_HANDLED;350}351352bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL);353354req->last_byte = ((u8 *)req->data)[req->size - 1];355356req->pos = status & 0x00ffffff;357358/* Mark the FIFO as idle */359lpbfifo.req = NULL;360361/* Release the lock before calling out to the callback. */362req->irq_ticks += get_tbl() - ts;363spin_unlock_irqrestore(&lpbfifo.lock, flags);364365if (req->callback)366req->callback(req);367368return IRQ_HANDLED;369}370371/**372* mpc52xx_lpbfifo_bcom_poll - Poll for DMA completion373*/374void mpc52xx_lpbfifo_poll(void)375{376struct mpc52xx_lpbfifo_request *req = lpbfifo.req;377int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA);378int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE;379380/*381* For more information, see comments on the "Fat Lady"382*/383if (dma && write)384mpc52xx_lpbfifo_irq(0, NULL);385else386mpc52xx_lpbfifo_bcom_irq(0, NULL);387}388EXPORT_SYMBOL(mpc52xx_lpbfifo_poll);389390/**391* mpc52xx_lpbfifo_submit - Submit an LPB FIFO transfer request.392* @req: Pointer to request structure393*/394int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req)395{396unsigned long flags;397398if (!lpbfifo.regs)399return -ENODEV;400401spin_lock_irqsave(&lpbfifo.lock, flags);402403/* If the req pointer is already set, then a transfer is in progress */404if (lpbfifo.req) {405spin_unlock_irqrestore(&lpbfifo.lock, flags);406return -EBUSY;407}408409/* Setup the transfer */410lpbfifo.req = req;411req->irq_count = 0;412req->irq_ticks = 0;413req->buffer_not_done_cnt = 0;414req->pos = 0;415416mpc52xx_lpbfifo_kick(req);417spin_unlock_irqrestore(&lpbfifo.lock, flags);418return 0;419}420EXPORT_SYMBOL(mpc52xx_lpbfifo_submit);421422void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req)423{424unsigned long flags;425426spin_lock_irqsave(&lpbfifo.lock, flags);427if (lpbfifo.req == req) {428/* Put it into reset and clear the state */429bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task);430bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task);431out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);432lpbfifo.req = NULL;433}434spin_unlock_irqrestore(&lpbfifo.lock, flags);435}436EXPORT_SYMBOL(mpc52xx_lpbfifo_abort);437438static int __devinit mpc52xx_lpbfifo_probe(struct platform_device *op)439{440struct resource res;441int rc = -ENOMEM;442443if (lpbfifo.dev != NULL)444return -ENOSPC;445446lpbfifo.irq = irq_of_parse_and_map(op->dev.of_node, 0);447if (!lpbfifo.irq)448return -ENODEV;449450if (of_address_to_resource(op->dev.of_node, 0, &res))451return -ENODEV;452lpbfifo.regs_phys = res.start;453lpbfifo.regs = of_iomap(op->dev.of_node, 0);454if (!lpbfifo.regs)455return -ENOMEM;456457spin_lock_init(&lpbfifo.lock);458459/* Put FIFO into reset */460out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);461462/* Register the interrupt handler */463rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0,464"mpc52xx-lpbfifo", &lpbfifo);465if (rc)466goto err_irq;467468/* Request the Bestcomm receive (fifo --> memory) task and IRQ */469lpbfifo.bcom_rx_task =470bcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,471BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC,47216*1024*1024);473if (!lpbfifo.bcom_rx_task)474goto err_bcom_rx;475476rc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task),477mpc52xx_lpbfifo_bcom_irq, 0,478"mpc52xx-lpbfifo-rx", &lpbfifo);479if (rc)480goto err_bcom_rx_irq;481482lpbfifo.dma_irqs_enabled = 1;483484/* Request the Bestcomm transmit (memory --> fifo) task and IRQ */485lpbfifo.bcom_tx_task =486bcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA,487BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC);488if (!lpbfifo.bcom_tx_task)489goto err_bcom_tx;490491lpbfifo.dev = &op->dev;492return 0;493494err_bcom_tx:495free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);496err_bcom_rx_irq:497bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);498err_bcom_rx:499err_irq:500iounmap(lpbfifo.regs);501lpbfifo.regs = NULL;502503dev_err(&op->dev, "mpc52xx_lpbfifo_probe() failed\n");504return -ENODEV;505}506507508static int __devexit mpc52xx_lpbfifo_remove(struct platform_device *op)509{510if (lpbfifo.dev != &op->dev)511return 0;512513/* Put FIFO in reset */514out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000);515516/* Release the bestcomm transmit task */517free_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo);518bcom_gen_bd_tx_release(lpbfifo.bcom_tx_task);519520/* Release the bestcomm receive task */521free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo);522bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task);523524free_irq(lpbfifo.irq, &lpbfifo);525iounmap(lpbfifo.regs);526lpbfifo.regs = NULL;527lpbfifo.dev = NULL;528529return 0;530}531532static struct of_device_id mpc52xx_lpbfifo_match[] __devinitconst = {533{ .compatible = "fsl,mpc5200-lpbfifo", },534{},535};536537static struct platform_driver mpc52xx_lpbfifo_driver = {538.driver = {539.name = "mpc52xx-lpbfifo",540.owner = THIS_MODULE,541.of_match_table = mpc52xx_lpbfifo_match,542},543.probe = mpc52xx_lpbfifo_probe,544.remove = __devexit_p(mpc52xx_lpbfifo_remove),545};546547/***********************************************************************548* Module init/exit549*/550static int __init mpc52xx_lpbfifo_init(void)551{552return platform_driver_register(&mpc52xx_lpbfifo_driver);553}554module_init(mpc52xx_lpbfifo_init);555556static void __exit mpc52xx_lpbfifo_exit(void)557{558platform_driver_unregister(&mpc52xx_lpbfifo_driver);559}560module_exit(mpc52xx_lpbfifo_exit);561562563