145051539SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28ee9d857SOlof Johansson /*
38ee9d857SOlof Johansson * Copyright (C) 2006-2007 PA Semi, Inc
48ee9d857SOlof Johansson *
58ee9d857SOlof Johansson * Common functions for DMA access on PA Semi PWRficient
68ee9d857SOlof Johansson */
78ee9d857SOlof Johansson
8afea3278SOlof Johansson #include <linux/kernel.h>
94b16f8e2SPaul Gortmaker #include <linux/export.h>
108ee9d857SOlof Johansson #include <linux/pci.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
128ee9d857SOlof Johansson #include <linux/of.h>
13*e6f6390aSChristophe Leroy #include <linux/of_address.h>
14*e6f6390aSChristophe Leroy #include <linux/of_irq.h>
1562fe91bbSPaul Gortmaker #include <linux/sched.h>
168ee9d857SOlof Johansson
178ee9d857SOlof Johansson #include <asm/pasemi_dma.h>
188ee9d857SOlof Johansson
198ee9d857SOlof Johansson #define MAX_TXCH 64
208ee9d857SOlof Johansson #define MAX_RXCH 64
21f37203b5SOlof Johansson #define MAX_FLAGS 64
22dda56df0SOlof Johansson #define MAX_FUN 8
238ee9d857SOlof Johansson
248ee9d857SOlof Johansson static struct pasdma_status *dma_status;
258ee9d857SOlof Johansson
268ee9d857SOlof Johansson static void __iomem *iob_regs;
278ee9d857SOlof Johansson static void __iomem *mac_regs[6];
288ee9d857SOlof Johansson static void __iomem *dma_regs;
298ee9d857SOlof Johansson
308ee9d857SOlof Johansson static int base_hw_irq;
318ee9d857SOlof Johansson
328ee9d857SOlof Johansson static int num_txch, num_rxch;
338ee9d857SOlof Johansson
348ee9d857SOlof Johansson static struct pci_dev *dma_pdev;
358ee9d857SOlof Johansson
368ee9d857SOlof Johansson /* Bitmaps to handle allocation of channels */
378ee9d857SOlof Johansson
388ee9d857SOlof Johansson static DECLARE_BITMAP(txch_free, MAX_TXCH);
398ee9d857SOlof Johansson static DECLARE_BITMAP(rxch_free, MAX_RXCH);
40f37203b5SOlof Johansson static DECLARE_BITMAP(flags_free, MAX_FLAGS);
41dda56df0SOlof Johansson static DECLARE_BITMAP(fun_free, MAX_FUN);
428ee9d857SOlof Johansson
438ee9d857SOlof Johansson /* pasemi_read_iob_reg - read IOB register
448ee9d857SOlof Johansson * @reg: Register to read (offset into PCI CFG space)
458ee9d857SOlof Johansson */
pasemi_read_iob_reg(unsigned int reg)468ee9d857SOlof Johansson unsigned int pasemi_read_iob_reg(unsigned int reg)
478ee9d857SOlof Johansson {
488ee9d857SOlof Johansson return in_le32(iob_regs+reg);
498ee9d857SOlof Johansson }
508ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_read_iob_reg);
518ee9d857SOlof Johansson
528ee9d857SOlof Johansson /* pasemi_write_iob_reg - write IOB register
538ee9d857SOlof Johansson * @reg: Register to write to (offset into PCI CFG space)
548ee9d857SOlof Johansson * @val: Value to write
558ee9d857SOlof Johansson */
pasemi_write_iob_reg(unsigned int reg,unsigned int val)568ee9d857SOlof Johansson void pasemi_write_iob_reg(unsigned int reg, unsigned int val)
578ee9d857SOlof Johansson {
588ee9d857SOlof Johansson out_le32(iob_regs+reg, val);
598ee9d857SOlof Johansson }
608ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_write_iob_reg);
618ee9d857SOlof Johansson
628ee9d857SOlof Johansson /* pasemi_read_mac_reg - read MAC register
638ee9d857SOlof Johansson * @intf: MAC interface
648ee9d857SOlof Johansson * @reg: Register to read (offset into PCI CFG space)
658ee9d857SOlof Johansson */
pasemi_read_mac_reg(int intf,unsigned int reg)668ee9d857SOlof Johansson unsigned int pasemi_read_mac_reg(int intf, unsigned int reg)
678ee9d857SOlof Johansson {
688ee9d857SOlof Johansson return in_le32(mac_regs[intf]+reg);
698ee9d857SOlof Johansson }
708ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_read_mac_reg);
718ee9d857SOlof Johansson
728ee9d857SOlof Johansson /* pasemi_write_mac_reg - write MAC register
738ee9d857SOlof Johansson * @intf: MAC interface
748ee9d857SOlof Johansson * @reg: Register to write to (offset into PCI CFG space)
758ee9d857SOlof Johansson * @val: Value to write
768ee9d857SOlof Johansson */
pasemi_write_mac_reg(int intf,unsigned int reg,unsigned int val)778ee9d857SOlof Johansson void pasemi_write_mac_reg(int intf, unsigned int reg, unsigned int val)
788ee9d857SOlof Johansson {
798ee9d857SOlof Johansson out_le32(mac_regs[intf]+reg, val);
808ee9d857SOlof Johansson }
818ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_write_mac_reg);
828ee9d857SOlof Johansson
838ee9d857SOlof Johansson /* pasemi_read_dma_reg - read DMA register
848ee9d857SOlof Johansson * @reg: Register to read (offset into PCI CFG space)
858ee9d857SOlof Johansson */
pasemi_read_dma_reg(unsigned int reg)868ee9d857SOlof Johansson unsigned int pasemi_read_dma_reg(unsigned int reg)
878ee9d857SOlof Johansson {
888ee9d857SOlof Johansson return in_le32(dma_regs+reg);
898ee9d857SOlof Johansson }
908ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_read_dma_reg);
918ee9d857SOlof Johansson
928ee9d857SOlof Johansson /* pasemi_write_dma_reg - write DMA register
938ee9d857SOlof Johansson * @reg: Register to write to (offset into PCI CFG space)
948ee9d857SOlof Johansson * @val: Value to write
958ee9d857SOlof Johansson */
pasemi_write_dma_reg(unsigned int reg,unsigned int val)968ee9d857SOlof Johansson void pasemi_write_dma_reg(unsigned int reg, unsigned int val)
978ee9d857SOlof Johansson {
988ee9d857SOlof Johansson out_le32(dma_regs+reg, val);
998ee9d857SOlof Johansson }
1008ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_write_dma_reg);
1018ee9d857SOlof Johansson
pasemi_alloc_tx_chan(enum pasemi_dmachan_type type)1028ee9d857SOlof Johansson static int pasemi_alloc_tx_chan(enum pasemi_dmachan_type type)
1038ee9d857SOlof Johansson {
1048ee9d857SOlof Johansson int bit;
1058ee9d857SOlof Johansson int start, limit;
1068ee9d857SOlof Johansson
1078ee9d857SOlof Johansson switch (type & (TXCHAN_EVT0|TXCHAN_EVT1)) {
1088ee9d857SOlof Johansson case TXCHAN_EVT0:
1098ee9d857SOlof Johansson start = 0;
1108ee9d857SOlof Johansson limit = 10;
1118ee9d857SOlof Johansson break;
1128ee9d857SOlof Johansson case TXCHAN_EVT1:
1138ee9d857SOlof Johansson start = 10;
1148ee9d857SOlof Johansson limit = MAX_TXCH;
1158ee9d857SOlof Johansson break;
1168ee9d857SOlof Johansson default:
1178ee9d857SOlof Johansson start = 0;
1188ee9d857SOlof Johansson limit = MAX_TXCH;
1198ee9d857SOlof Johansson break;
1208ee9d857SOlof Johansson }
1218ee9d857SOlof Johansson retry:
1228ee9d857SOlof Johansson bit = find_next_bit(txch_free, MAX_TXCH, start);
1238ee9d857SOlof Johansson if (bit >= limit)
1248ee9d857SOlof Johansson return -ENOSPC;
1258ee9d857SOlof Johansson if (!test_and_clear_bit(bit, txch_free))
1268ee9d857SOlof Johansson goto retry;
1278ee9d857SOlof Johansson
1288ee9d857SOlof Johansson return bit;
1298ee9d857SOlof Johansson }
1308ee9d857SOlof Johansson
pasemi_free_tx_chan(int chan)1318ee9d857SOlof Johansson static void pasemi_free_tx_chan(int chan)
1328ee9d857SOlof Johansson {
1338ee9d857SOlof Johansson BUG_ON(test_bit(chan, txch_free));
1348ee9d857SOlof Johansson set_bit(chan, txch_free);
1358ee9d857SOlof Johansson }
1368ee9d857SOlof Johansson
pasemi_alloc_rx_chan(void)1378ee9d857SOlof Johansson static int pasemi_alloc_rx_chan(void)
1388ee9d857SOlof Johansson {
1398ee9d857SOlof Johansson int bit;
1408ee9d857SOlof Johansson retry:
1418ee9d857SOlof Johansson bit = find_first_bit(rxch_free, MAX_RXCH);
1428ee9d857SOlof Johansson if (bit >= MAX_TXCH)
1438ee9d857SOlof Johansson return -ENOSPC;
1448ee9d857SOlof Johansson if (!test_and_clear_bit(bit, rxch_free))
1458ee9d857SOlof Johansson goto retry;
1468ee9d857SOlof Johansson
1478ee9d857SOlof Johansson return bit;
1488ee9d857SOlof Johansson }
1498ee9d857SOlof Johansson
pasemi_free_rx_chan(int chan)1508ee9d857SOlof Johansson static void pasemi_free_rx_chan(int chan)
1518ee9d857SOlof Johansson {
1528ee9d857SOlof Johansson BUG_ON(test_bit(chan, rxch_free));
1538ee9d857SOlof Johansson set_bit(chan, rxch_free);
1548ee9d857SOlof Johansson }
1558ee9d857SOlof Johansson
1568ee9d857SOlof Johansson /* pasemi_dma_alloc_chan - Allocate a DMA channel
1578ee9d857SOlof Johansson * @type: Type of channel to allocate
1588ee9d857SOlof Johansson * @total_size: Total size of structure to allocate (to allow for more
1598ee9d857SOlof Johansson * room behind the structure to be used by the client)
1608ee9d857SOlof Johansson * @offset: Offset in bytes from start of the total structure to the beginning
1618ee9d857SOlof Johansson * of struct pasemi_dmachan. Needed when struct pasemi_dmachan is
1628ee9d857SOlof Johansson * not the first member of the client structure.
1638ee9d857SOlof Johansson *
1648ee9d857SOlof Johansson * pasemi_dma_alloc_chan allocates a DMA channel for use by a client. The
1658ee9d857SOlof Johansson * type argument specifies whether it's a RX or TX channel, and in the case
1668ee9d857SOlof Johansson * of TX channels which group it needs to belong to (if any).
1678ee9d857SOlof Johansson *
1688ee9d857SOlof Johansson * Returns a pointer to the total structure allocated on success, NULL
1698ee9d857SOlof Johansson * on failure.
1708ee9d857SOlof Johansson */
pasemi_dma_alloc_chan(enum pasemi_dmachan_type type,int total_size,int offset)1718ee9d857SOlof Johansson void *pasemi_dma_alloc_chan(enum pasemi_dmachan_type type,
1728ee9d857SOlof Johansson int total_size, int offset)
1738ee9d857SOlof Johansson {
1748ee9d857SOlof Johansson void *buf;
1758ee9d857SOlof Johansson struct pasemi_dmachan *chan;
1768ee9d857SOlof Johansson int chno;
1778ee9d857SOlof Johansson
1788ee9d857SOlof Johansson BUG_ON(total_size < sizeof(struct pasemi_dmachan));
1798ee9d857SOlof Johansson
1808ee9d857SOlof Johansson buf = kzalloc(total_size, GFP_KERNEL);
1818ee9d857SOlof Johansson
1828ee9d857SOlof Johansson if (!buf)
1838ee9d857SOlof Johansson return NULL;
1848ee9d857SOlof Johansson chan = buf + offset;
1858ee9d857SOlof Johansson
1868ee9d857SOlof Johansson chan->priv = buf;
1878ee9d857SOlof Johansson
1888ee9d857SOlof Johansson switch (type & (TXCHAN|RXCHAN)) {
1898ee9d857SOlof Johansson case RXCHAN:
1908ee9d857SOlof Johansson chno = pasemi_alloc_rx_chan();
1918ee9d857SOlof Johansson chan->chno = chno;
1928ee9d857SOlof Johansson chan->irq = irq_create_mapping(NULL,
1938ee9d857SOlof Johansson base_hw_irq + num_txch + chno);
1948ee9d857SOlof Johansson chan->status = &dma_status->rx_sta[chno];
1958ee9d857SOlof Johansson break;
1968ee9d857SOlof Johansson case TXCHAN:
1978ee9d857SOlof Johansson chno = pasemi_alloc_tx_chan(type);
1988ee9d857SOlof Johansson chan->chno = chno;
1998ee9d857SOlof Johansson chan->irq = irq_create_mapping(NULL, base_hw_irq + chno);
2008ee9d857SOlof Johansson chan->status = &dma_status->tx_sta[chno];
2018ee9d857SOlof Johansson break;
2028ee9d857SOlof Johansson }
2038ee9d857SOlof Johansson
2048ee9d857SOlof Johansson chan->chan_type = type;
2058ee9d857SOlof Johansson
2068ee9d857SOlof Johansson return chan;
2078ee9d857SOlof Johansson }
2088ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_alloc_chan);
2098ee9d857SOlof Johansson
2108ee9d857SOlof Johansson /* pasemi_dma_free_chan - Free a previously allocated channel
2118ee9d857SOlof Johansson * @chan: Channel to free
2128ee9d857SOlof Johansson *
2138ee9d857SOlof Johansson * Frees a previously allocated channel. It will also deallocate any
2148ee9d857SOlof Johansson * descriptor ring associated with the channel, if allocated.
2158ee9d857SOlof Johansson */
pasemi_dma_free_chan(struct pasemi_dmachan * chan)2168ee9d857SOlof Johansson void pasemi_dma_free_chan(struct pasemi_dmachan *chan)
2178ee9d857SOlof Johansson {
2188ee9d857SOlof Johansson if (chan->ring_virt)
2198ee9d857SOlof Johansson pasemi_dma_free_ring(chan);
2208ee9d857SOlof Johansson
2218ee9d857SOlof Johansson switch (chan->chan_type & (RXCHAN|TXCHAN)) {
2228ee9d857SOlof Johansson case RXCHAN:
2238ee9d857SOlof Johansson pasemi_free_rx_chan(chan->chno);
2248ee9d857SOlof Johansson break;
2258ee9d857SOlof Johansson case TXCHAN:
2268ee9d857SOlof Johansson pasemi_free_tx_chan(chan->chno);
2278ee9d857SOlof Johansson break;
2288ee9d857SOlof Johansson }
2298ee9d857SOlof Johansson
2308ee9d857SOlof Johansson kfree(chan->priv);
2318ee9d857SOlof Johansson }
2328ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_free_chan);
2338ee9d857SOlof Johansson
2348ee9d857SOlof Johansson /* pasemi_dma_alloc_ring - Allocate descriptor ring for a channel
2358ee9d857SOlof Johansson * @chan: Channel for which to allocate
2368ee9d857SOlof Johansson * @ring_size: Ring size in 64-bit (8-byte) words
2378ee9d857SOlof Johansson *
2388ee9d857SOlof Johansson * Allocate a descriptor ring for a channel. Returns 0 on success, errno
2398ee9d857SOlof Johansson * on failure. The passed in struct pasemi_dmachan is updated with the
2408ee9d857SOlof Johansson * virtual and DMA addresses of the ring.
2418ee9d857SOlof Johansson */
pasemi_dma_alloc_ring(struct pasemi_dmachan * chan,int ring_size)2428ee9d857SOlof Johansson int pasemi_dma_alloc_ring(struct pasemi_dmachan *chan, int ring_size)
2438ee9d857SOlof Johansson {
2448ee9d857SOlof Johansson BUG_ON(chan->ring_virt);
2458ee9d857SOlof Johansson
2468ee9d857SOlof Johansson chan->ring_size = ring_size;
2478ee9d857SOlof Johansson
248750afb08SLuis Chamberlain chan->ring_virt = dma_alloc_coherent(&dma_pdev->dev,
2498ee9d857SOlof Johansson ring_size * sizeof(u64),
2508ee9d857SOlof Johansson &chan->ring_dma, GFP_KERNEL);
2518ee9d857SOlof Johansson
2528ee9d857SOlof Johansson if (!chan->ring_virt)
2538ee9d857SOlof Johansson return -ENOMEM;
2548ee9d857SOlof Johansson
2558ee9d857SOlof Johansson return 0;
2568ee9d857SOlof Johansson }
2578ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_alloc_ring);
2588ee9d857SOlof Johansson
2598ee9d857SOlof Johansson /* pasemi_dma_free_ring - Free an allocated descriptor ring for a channel
2608ee9d857SOlof Johansson * @chan: Channel for which to free the descriptor ring
2618ee9d857SOlof Johansson *
2628ee9d857SOlof Johansson * Frees a previously allocated descriptor ring for a channel.
2638ee9d857SOlof Johansson */
pasemi_dma_free_ring(struct pasemi_dmachan * chan)2648ee9d857SOlof Johansson void pasemi_dma_free_ring(struct pasemi_dmachan *chan)
2658ee9d857SOlof Johansson {
2668ee9d857SOlof Johansson BUG_ON(!chan->ring_virt);
2678ee9d857SOlof Johansson
2688ee9d857SOlof Johansson dma_free_coherent(&dma_pdev->dev, chan->ring_size * sizeof(u64),
2698ee9d857SOlof Johansson chan->ring_virt, chan->ring_dma);
2708ee9d857SOlof Johansson chan->ring_virt = NULL;
2718ee9d857SOlof Johansson chan->ring_size = 0;
2728ee9d857SOlof Johansson chan->ring_dma = 0;
2738ee9d857SOlof Johansson }
2748ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_free_ring);
2758ee9d857SOlof Johansson
2768ee9d857SOlof Johansson /* pasemi_dma_start_chan - Start a DMA channel
2778ee9d857SOlof Johansson * @chan: Channel to start
2788ee9d857SOlof Johansson * @cmdsta: Additional CCMDSTA/TCMDSTA bits to write
2798ee9d857SOlof Johansson *
2808ee9d857SOlof Johansson * Enables (starts) a DMA channel with optional additional arguments.
2818ee9d857SOlof Johansson */
pasemi_dma_start_chan(const struct pasemi_dmachan * chan,const u32 cmdsta)2828ee9d857SOlof Johansson void pasemi_dma_start_chan(const struct pasemi_dmachan *chan, const u32 cmdsta)
2838ee9d857SOlof Johansson {
2848ee9d857SOlof Johansson if (chan->chan_type == RXCHAN)
2858ee9d857SOlof Johansson pasemi_write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(chan->chno),
2868ee9d857SOlof Johansson cmdsta | PAS_DMA_RXCHAN_CCMDSTA_EN);
2878ee9d857SOlof Johansson else
2888ee9d857SOlof Johansson pasemi_write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(chan->chno),
2898ee9d857SOlof Johansson cmdsta | PAS_DMA_TXCHAN_TCMDSTA_EN);
2908ee9d857SOlof Johansson }
2918ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_start_chan);
2928ee9d857SOlof Johansson
2938ee9d857SOlof Johansson /* pasemi_dma_stop_chan - Stop a DMA channel
2948ee9d857SOlof Johansson * @chan: Channel to stop
2958ee9d857SOlof Johansson *
2968ee9d857SOlof Johansson * Stops (disables) a DMA channel. This is done by setting the ST bit in the
2978ee9d857SOlof Johansson * CMDSTA register and waiting on the ACT (active) bit to clear, then
2988ee9d857SOlof Johansson * finally disabling the whole channel.
2998ee9d857SOlof Johansson *
3008ee9d857SOlof Johansson * This function will only try for a short while for the channel to stop, if
3018ee9d857SOlof Johansson * it doesn't it will return failure.
3028ee9d857SOlof Johansson *
3038ee9d857SOlof Johansson * Returns 1 on success, 0 on failure.
3048ee9d857SOlof Johansson */
3058ee9d857SOlof Johansson #define MAX_RETRIES 5000
pasemi_dma_stop_chan(const struct pasemi_dmachan * chan)3068ee9d857SOlof Johansson int pasemi_dma_stop_chan(const struct pasemi_dmachan *chan)
3078ee9d857SOlof Johansson {
3088ee9d857SOlof Johansson int reg, retries;
3098ee9d857SOlof Johansson u32 sta;
3108ee9d857SOlof Johansson
3118ee9d857SOlof Johansson if (chan->chan_type == RXCHAN) {
3128ee9d857SOlof Johansson reg = PAS_DMA_RXCHAN_CCMDSTA(chan->chno);
3138ee9d857SOlof Johansson pasemi_write_dma_reg(reg, PAS_DMA_RXCHAN_CCMDSTA_ST);
3148ee9d857SOlof Johansson for (retries = 0; retries < MAX_RETRIES; retries++) {
3158ee9d857SOlof Johansson sta = pasemi_read_dma_reg(reg);
3168ee9d857SOlof Johansson if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) {
3178ee9d857SOlof Johansson pasemi_write_dma_reg(reg, 0);
3188ee9d857SOlof Johansson return 1;
3198ee9d857SOlof Johansson }
3208ee9d857SOlof Johansson cond_resched();
3218ee9d857SOlof Johansson }
3228ee9d857SOlof Johansson } else {
3238ee9d857SOlof Johansson reg = PAS_DMA_TXCHAN_TCMDSTA(chan->chno);
3248ee9d857SOlof Johansson pasemi_write_dma_reg(reg, PAS_DMA_TXCHAN_TCMDSTA_ST);
3258ee9d857SOlof Johansson for (retries = 0; retries < MAX_RETRIES; retries++) {
3268ee9d857SOlof Johansson sta = pasemi_read_dma_reg(reg);
3278ee9d857SOlof Johansson if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) {
3288ee9d857SOlof Johansson pasemi_write_dma_reg(reg, 0);
3298ee9d857SOlof Johansson return 1;
3308ee9d857SOlof Johansson }
3318ee9d857SOlof Johansson cond_resched();
3328ee9d857SOlof Johansson }
3338ee9d857SOlof Johansson }
3348ee9d857SOlof Johansson
3358ee9d857SOlof Johansson return 0;
3368ee9d857SOlof Johansson }
3378ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_stop_chan);
3388ee9d857SOlof Johansson
3398ee9d857SOlof Johansson /* pasemi_dma_alloc_buf - Allocate a buffer to use for DMA
3408ee9d857SOlof Johansson * @chan: Channel to allocate for
3418ee9d857SOlof Johansson * @size: Size of buffer in bytes
3428ee9d857SOlof Johansson * @handle: DMA handle
3438ee9d857SOlof Johansson *
3448ee9d857SOlof Johansson * Allocate a buffer to be used by the DMA engine for read/write,
3458ee9d857SOlof Johansson * similar to dma_alloc_coherent().
3468ee9d857SOlof Johansson *
3478ee9d857SOlof Johansson * Returns the virtual address of the buffer, or NULL in case of failure.
3488ee9d857SOlof Johansson */
pasemi_dma_alloc_buf(struct pasemi_dmachan * chan,int size,dma_addr_t * handle)3498ee9d857SOlof Johansson void *pasemi_dma_alloc_buf(struct pasemi_dmachan *chan, int size,
3508ee9d857SOlof Johansson dma_addr_t *handle)
3518ee9d857SOlof Johansson {
3528ee9d857SOlof Johansson return dma_alloc_coherent(&dma_pdev->dev, size, handle, GFP_KERNEL);
3538ee9d857SOlof Johansson }
3548ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_alloc_buf);
3558ee9d857SOlof Johansson
3568ee9d857SOlof Johansson /* pasemi_dma_free_buf - Free a buffer used for DMA
3578ee9d857SOlof Johansson * @chan: Channel the buffer was allocated for
3588ee9d857SOlof Johansson * @size: Size of buffer in bytes
3598ee9d857SOlof Johansson * @handle: DMA handle
3608ee9d857SOlof Johansson *
3618ee9d857SOlof Johansson * Frees a previously allocated buffer.
3628ee9d857SOlof Johansson */
pasemi_dma_free_buf(struct pasemi_dmachan * chan,int size,dma_addr_t * handle)3638ee9d857SOlof Johansson void pasemi_dma_free_buf(struct pasemi_dmachan *chan, int size,
3648ee9d857SOlof Johansson dma_addr_t *handle)
3658ee9d857SOlof Johansson {
3668ee9d857SOlof Johansson dma_free_coherent(&dma_pdev->dev, size, handle, GFP_KERNEL);
3678ee9d857SOlof Johansson }
3688ee9d857SOlof Johansson EXPORT_SYMBOL(pasemi_dma_free_buf);
3698ee9d857SOlof Johansson
37025985edcSLucas De Marchi /* pasemi_dma_alloc_flag - Allocate a flag (event) for channel synchronization
371f37203b5SOlof Johansson *
37225985edcSLucas De Marchi * Allocates a flag for use with channel synchronization (event descriptors).
373f37203b5SOlof Johansson * Returns allocated flag (0-63), < 0 on error.
374f37203b5SOlof Johansson */
pasemi_dma_alloc_flag(void)375f37203b5SOlof Johansson int pasemi_dma_alloc_flag(void)
376f37203b5SOlof Johansson {
377f37203b5SOlof Johansson int bit;
378f37203b5SOlof Johansson
379f37203b5SOlof Johansson retry:
380b5c7e7ecSYury Norov bit = find_first_bit(flags_free, MAX_FLAGS);
381f37203b5SOlof Johansson if (bit >= MAX_FLAGS)
382f37203b5SOlof Johansson return -ENOSPC;
383f37203b5SOlof Johansson if (!test_and_clear_bit(bit, flags_free))
384f37203b5SOlof Johansson goto retry;
385f37203b5SOlof Johansson
386f37203b5SOlof Johansson return bit;
387f37203b5SOlof Johansson }
388f37203b5SOlof Johansson EXPORT_SYMBOL(pasemi_dma_alloc_flag);
389f37203b5SOlof Johansson
390f37203b5SOlof Johansson
391f37203b5SOlof Johansson /* pasemi_dma_free_flag - Deallocates a flag (event)
392f37203b5SOlof Johansson * @flag: Flag number to deallocate
393f37203b5SOlof Johansson *
394f37203b5SOlof Johansson * Frees up a flag so it can be reused for other purposes.
395f37203b5SOlof Johansson */
pasemi_dma_free_flag(int flag)396f37203b5SOlof Johansson void pasemi_dma_free_flag(int flag)
397f37203b5SOlof Johansson {
398f37203b5SOlof Johansson BUG_ON(test_bit(flag, flags_free));
399f37203b5SOlof Johansson BUG_ON(flag >= MAX_FLAGS);
400f37203b5SOlof Johansson set_bit(flag, flags_free);
401f37203b5SOlof Johansson }
402f37203b5SOlof Johansson EXPORT_SYMBOL(pasemi_dma_free_flag);
403f37203b5SOlof Johansson
404f37203b5SOlof Johansson
405f37203b5SOlof Johansson /* pasemi_dma_set_flag - Sets a flag (event) to 1
406f37203b5SOlof Johansson * @flag: Flag number to set active
407f37203b5SOlof Johansson *
408f37203b5SOlof Johansson * Sets the flag provided to 1.
409f37203b5SOlof Johansson */
pasemi_dma_set_flag(int flag)410f37203b5SOlof Johansson void pasemi_dma_set_flag(int flag)
411f37203b5SOlof Johansson {
412f37203b5SOlof Johansson BUG_ON(flag >= MAX_FLAGS);
413f37203b5SOlof Johansson if (flag < 32)
414f37203b5SOlof Johansson pasemi_write_dma_reg(PAS_DMA_TXF_SFLG0, 1 << flag);
415f37203b5SOlof Johansson else
416f37203b5SOlof Johansson pasemi_write_dma_reg(PAS_DMA_TXF_SFLG1, 1 << flag);
417f37203b5SOlof Johansson }
418f37203b5SOlof Johansson EXPORT_SYMBOL(pasemi_dma_set_flag);
419f37203b5SOlof Johansson
420f37203b5SOlof Johansson /* pasemi_dma_clear_flag - Sets a flag (event) to 0
421f37203b5SOlof Johansson * @flag: Flag number to set inactive
422f37203b5SOlof Johansson *
423f37203b5SOlof Johansson * Sets the flag provided to 0.
424f37203b5SOlof Johansson */
pasemi_dma_clear_flag(int flag)425f37203b5SOlof Johansson void pasemi_dma_clear_flag(int flag)
426f37203b5SOlof Johansson {
427f37203b5SOlof Johansson BUG_ON(flag >= MAX_FLAGS);
428f37203b5SOlof Johansson if (flag < 32)
429f37203b5SOlof Johansson pasemi_write_dma_reg(PAS_DMA_TXF_CFLG0, 1 << flag);
430f37203b5SOlof Johansson else
431f37203b5SOlof Johansson pasemi_write_dma_reg(PAS_DMA_TXF_CFLG1, 1 << flag);
432f37203b5SOlof Johansson }
433f37203b5SOlof Johansson EXPORT_SYMBOL(pasemi_dma_clear_flag);
434f37203b5SOlof Johansson
435dda56df0SOlof Johansson /* pasemi_dma_alloc_fun - Allocate a function engine
436dda56df0SOlof Johansson *
437dda56df0SOlof Johansson * Allocates a function engine to use for crypto/checksum offload
438dda56df0SOlof Johansson * Returns allocated engine (0-8), < 0 on error.
439dda56df0SOlof Johansson */
pasemi_dma_alloc_fun(void)440dda56df0SOlof Johansson int pasemi_dma_alloc_fun(void)
441dda56df0SOlof Johansson {
442dda56df0SOlof Johansson int bit;
443dda56df0SOlof Johansson
444dda56df0SOlof Johansson retry:
445b5c7e7ecSYury Norov bit = find_first_bit(fun_free, MAX_FLAGS);
446dda56df0SOlof Johansson if (bit >= MAX_FLAGS)
447dda56df0SOlof Johansson return -ENOSPC;
448dda56df0SOlof Johansson if (!test_and_clear_bit(bit, fun_free))
449dda56df0SOlof Johansson goto retry;
450dda56df0SOlof Johansson
451dda56df0SOlof Johansson return bit;
452dda56df0SOlof Johansson }
453dda56df0SOlof Johansson EXPORT_SYMBOL(pasemi_dma_alloc_fun);
454dda56df0SOlof Johansson
455dda56df0SOlof Johansson
456dda56df0SOlof Johansson /* pasemi_dma_free_fun - Deallocates a function engine
457dda56df0SOlof Johansson * @flag: Engine number to deallocate
458dda56df0SOlof Johansson *
459dda56df0SOlof Johansson * Frees up a function engine so it can be used for other purposes.
460dda56df0SOlof Johansson */
pasemi_dma_free_fun(int fun)461dda56df0SOlof Johansson void pasemi_dma_free_fun(int fun)
462dda56df0SOlof Johansson {
463dda56df0SOlof Johansson BUG_ON(test_bit(fun, fun_free));
464dda56df0SOlof Johansson BUG_ON(fun >= MAX_FLAGS);
465dda56df0SOlof Johansson set_bit(fun, fun_free);
466dda56df0SOlof Johansson }
467dda56df0SOlof Johansson EXPORT_SYMBOL(pasemi_dma_free_fun);
468dda56df0SOlof Johansson
469dda56df0SOlof Johansson
map_onedev(struct pci_dev * p,int index)4708ee9d857SOlof Johansson static void *map_onedev(struct pci_dev *p, int index)
4718ee9d857SOlof Johansson {
4728ee9d857SOlof Johansson struct device_node *dn;
4738ee9d857SOlof Johansson void __iomem *ret;
4748ee9d857SOlof Johansson
4758ee9d857SOlof Johansson dn = pci_device_to_OF_node(p);
4768ee9d857SOlof Johansson if (!dn)
4778ee9d857SOlof Johansson goto fallback;
4788ee9d857SOlof Johansson
4798ee9d857SOlof Johansson ret = of_iomap(dn, index);
4808ee9d857SOlof Johansson if (!ret)
4818ee9d857SOlof Johansson goto fallback;
4828ee9d857SOlof Johansson
4838ee9d857SOlof Johansson return ret;
4848ee9d857SOlof Johansson fallback:
4858ee9d857SOlof Johansson /* This is hardcoded and ugly, but we have some firmware versions
4868ee9d857SOlof Johansson * that don't provide the register space in the device tree. Luckily
4878ee9d857SOlof Johansson * they are at well-known locations so we can just do the math here.
4888ee9d857SOlof Johansson */
4898ee9d857SOlof Johansson return ioremap(0xe0000000 + (p->devfn << 12), 0x2000);
4908ee9d857SOlof Johansson }
4918ee9d857SOlof Johansson
4928ee9d857SOlof Johansson /* pasemi_dma_init - Initialize the PA Semi DMA library
4938ee9d857SOlof Johansson *
4948ee9d857SOlof Johansson * This function initializes the DMA library. It must be called before
4958ee9d857SOlof Johansson * any other function in the library.
4968ee9d857SOlof Johansson *
4978ee9d857SOlof Johansson * Returns 0 on success, errno on failure.
4988ee9d857SOlof Johansson */
pasemi_dma_init(void)4998ee9d857SOlof Johansson int pasemi_dma_init(void)
5008ee9d857SOlof Johansson {
5011d5bc03aSJulia Lawall static DEFINE_SPINLOCK(init_lock);
5028ee9d857SOlof Johansson struct pci_dev *iob_pdev;
5038ee9d857SOlof Johansson struct pci_dev *pdev;
5048ee9d857SOlof Johansson struct resource res;
5058ee9d857SOlof Johansson struct device_node *dn;
5068ee9d857SOlof Johansson int i, intf, err = 0;
507afea3278SOlof Johansson unsigned long timeout;
5088ee9d857SOlof Johansson u32 tmp;
5098ee9d857SOlof Johansson
5108ee9d857SOlof Johansson if (!machine_is(pasemi))
5118ee9d857SOlof Johansson return -ENODEV;
5128ee9d857SOlof Johansson
5138ee9d857SOlof Johansson spin_lock(&init_lock);
5148ee9d857SOlof Johansson
5158ee9d857SOlof Johansson /* Make sure we haven't already initialized */
5168ee9d857SOlof Johansson if (dma_pdev)
5178ee9d857SOlof Johansson goto out;
5188ee9d857SOlof Johansson
5198ee9d857SOlof Johansson iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL);
5208ee9d857SOlof Johansson if (!iob_pdev) {
5218ee9d857SOlof Johansson BUG();
522e13606d7SDarren Stevens pr_warn("Can't find I/O Bridge\n");
5238ee9d857SOlof Johansson err = -ENODEV;
5248ee9d857SOlof Johansson goto out;
5258ee9d857SOlof Johansson }
5268ee9d857SOlof Johansson iob_regs = map_onedev(iob_pdev, 0);
5278ee9d857SOlof Johansson
5288ee9d857SOlof Johansson dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL);
5298ee9d857SOlof Johansson if (!dma_pdev) {
5308ee9d857SOlof Johansson BUG();
531e13606d7SDarren Stevens pr_warn("Can't find DMA controller\n");
5328ee9d857SOlof Johansson err = -ENODEV;
5338ee9d857SOlof Johansson goto out;
5348ee9d857SOlof Johansson }
5358ee9d857SOlof Johansson dma_regs = map_onedev(dma_pdev, 0);
5368ee9d857SOlof Johansson base_hw_irq = virq_to_hw(dma_pdev->irq);
5378ee9d857SOlof Johansson
5388ee9d857SOlof Johansson pci_read_config_dword(dma_pdev, PAS_DMA_CAP_TXCH, &tmp);
5398ee9d857SOlof Johansson num_txch = (tmp & PAS_DMA_CAP_TXCH_TCHN_M) >> PAS_DMA_CAP_TXCH_TCHN_S;
5408ee9d857SOlof Johansson
5418ee9d857SOlof Johansson pci_read_config_dword(dma_pdev, PAS_DMA_CAP_RXCH, &tmp);
5428ee9d857SOlof Johansson num_rxch = (tmp & PAS_DMA_CAP_RXCH_RCHN_M) >> PAS_DMA_CAP_RXCH_RCHN_S;
5438ee9d857SOlof Johansson
5448ee9d857SOlof Johansson intf = 0;
5458ee9d857SOlof Johansson for (pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa006, NULL);
5468ee9d857SOlof Johansson pdev;
5478ee9d857SOlof Johansson pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa006, pdev))
5488ee9d857SOlof Johansson mac_regs[intf++] = map_onedev(pdev, 0);
5498ee9d857SOlof Johansson
5508ee9d857SOlof Johansson pci_dev_put(pdev);
5518ee9d857SOlof Johansson
5528ee9d857SOlof Johansson for (pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa005, NULL);
5538ee9d857SOlof Johansson pdev;
5548ee9d857SOlof Johansson pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa005, pdev))
5558ee9d857SOlof Johansson mac_regs[intf++] = map_onedev(pdev, 0);
5568ee9d857SOlof Johansson
5578ee9d857SOlof Johansson pci_dev_put(pdev);
5588ee9d857SOlof Johansson
5598ee9d857SOlof Johansson dn = pci_device_to_OF_node(iob_pdev);
5608ee9d857SOlof Johansson if (dn)
5618ee9d857SOlof Johansson err = of_address_to_resource(dn, 1, &res);
5628ee9d857SOlof Johansson if (!dn || err) {
5638ee9d857SOlof Johansson /* Fallback for old firmware */
5648ee9d857SOlof Johansson res.start = 0xfd800000;
5658ee9d857SOlof Johansson res.end = res.start + 0x1000;
5668ee9d857SOlof Johansson }
567aa91796eSChristophe Leroy dma_status = ioremap_cache(res.start, resource_size(&res));
5688ee9d857SOlof Johansson pci_dev_put(iob_pdev);
5698ee9d857SOlof Johansson
5708ee9d857SOlof Johansson for (i = 0; i < MAX_TXCH; i++)
5718ee9d857SOlof Johansson __set_bit(i, txch_free);
5728ee9d857SOlof Johansson
5738ee9d857SOlof Johansson for (i = 0; i < MAX_RXCH; i++)
5748ee9d857SOlof Johansson __set_bit(i, rxch_free);
5758ee9d857SOlof Johansson
576afea3278SOlof Johansson timeout = jiffies + HZ;
577afea3278SOlof Johansson pasemi_write_dma_reg(PAS_DMA_COM_RXCMD, 0);
578afea3278SOlof Johansson while (pasemi_read_dma_reg(PAS_DMA_COM_RXSTA) & 1) {
579afea3278SOlof Johansson if (time_after(jiffies, timeout)) {
580f2c2cbccSJoe Perches pr_warn("Warning: Could not disable RX section\n");
581afea3278SOlof Johansson break;
582afea3278SOlof Johansson }
583afea3278SOlof Johansson }
584afea3278SOlof Johansson
585afea3278SOlof Johansson timeout = jiffies + HZ;
586afea3278SOlof Johansson pasemi_write_dma_reg(PAS_DMA_COM_TXCMD, 0);
587afea3278SOlof Johansson while (pasemi_read_dma_reg(PAS_DMA_COM_TXSTA) & 1) {
588afea3278SOlof Johansson if (time_after(jiffies, timeout)) {
589f2c2cbccSJoe Perches pr_warn("Warning: Could not disable TX section\n");
590afea3278SOlof Johansson break;
591afea3278SOlof Johansson }
592afea3278SOlof Johansson }
593afea3278SOlof Johansson
594afea3278SOlof Johansson /* setup resource allocations for the different DMA sections */
595afea3278SOlof Johansson tmp = pasemi_read_dma_reg(PAS_DMA_COM_CFG);
596afea3278SOlof Johansson pasemi_write_dma_reg(PAS_DMA_COM_CFG, tmp | 0x18000000);
597afea3278SOlof Johansson
598afea3278SOlof Johansson /* enable tx section */
599afea3278SOlof Johansson pasemi_write_dma_reg(PAS_DMA_COM_TXCMD, PAS_DMA_COM_TXCMD_EN);
600afea3278SOlof Johansson
601afea3278SOlof Johansson /* enable rx section */
602afea3278SOlof Johansson pasemi_write_dma_reg(PAS_DMA_COM_RXCMD, PAS_DMA_COM_RXCMD_EN);
603afea3278SOlof Johansson
604f37203b5SOlof Johansson for (i = 0; i < MAX_FLAGS; i++)
605f37203b5SOlof Johansson __set_bit(i, flags_free);
606f37203b5SOlof Johansson
607dda56df0SOlof Johansson for (i = 0; i < MAX_FUN; i++)
608dda56df0SOlof Johansson __set_bit(i, fun_free);
609dda56df0SOlof Johansson
610f37203b5SOlof Johansson /* clear all status flags */
611f37203b5SOlof Johansson pasemi_write_dma_reg(PAS_DMA_TXF_CFLG0, 0xffffffff);
612f37203b5SOlof Johansson pasemi_write_dma_reg(PAS_DMA_TXF_CFLG1, 0xffffffff);
613f37203b5SOlof Johansson
614e13606d7SDarren Stevens pr_info("PA Semi PWRficient DMA library initialized "
6158ee9d857SOlof Johansson "(%d tx, %d rx channels)\n", num_txch, num_rxch);
6168ee9d857SOlof Johansson
6178ee9d857SOlof Johansson out:
6188ee9d857SOlof Johansson spin_unlock(&init_lock);
6198ee9d857SOlof Johansson return err;
6208ee9d857SOlof Johansson }
62106daa168SOlof Johansson EXPORT_SYMBOL(pasemi_dma_init);
622