Path: blob/master/arch/powerpc/platforms/pasemi/dma_lib.c
10819 views
/*1* Copyright (C) 2006-2007 PA Semi, Inc2*3* Common functions for DMA access on PA Semi PWRficient4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License version 2 as7* published by the Free Software Foundation.8*9* This program is distributed in the hope that it will be useful,10* but WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12* GNU General Public License for more details.13*14* You should have received a copy of the GNU General Public License15* along with this program; if not, write to the Free Software16* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA17*/1819#include <linux/kernel.h>20#include <linux/init.h>21#include <linux/module.h>22#include <linux/pci.h>23#include <linux/slab.h>24#include <linux/of.h>2526#include <asm/pasemi_dma.h>2728#define MAX_TXCH 6429#define MAX_RXCH 6430#define MAX_FLAGS 6431#define MAX_FUN 83233static struct pasdma_status *dma_status;3435static void __iomem *iob_regs;36static void __iomem *mac_regs[6];37static void __iomem *dma_regs;3839static int base_hw_irq;4041static int num_txch, num_rxch;4243static struct pci_dev *dma_pdev;4445/* Bitmaps to handle allocation of channels */4647static DECLARE_BITMAP(txch_free, MAX_TXCH);48static DECLARE_BITMAP(rxch_free, MAX_RXCH);49static DECLARE_BITMAP(flags_free, MAX_FLAGS);50static DECLARE_BITMAP(fun_free, MAX_FUN);5152/* pasemi_read_iob_reg - read IOB register53* @reg: Register to read (offset into PCI CFG space)54*/55unsigned int pasemi_read_iob_reg(unsigned int reg)56{57return in_le32(iob_regs+reg);58}59EXPORT_SYMBOL(pasemi_read_iob_reg);6061/* pasemi_write_iob_reg - write IOB register62* @reg: Register to write to (offset into PCI CFG space)63* @val: Value to write64*/65void pasemi_write_iob_reg(unsigned int reg, unsigned int val)66{67out_le32(iob_regs+reg, val);68}69EXPORT_SYMBOL(pasemi_write_iob_reg);7071/* pasemi_read_mac_reg - read MAC register72* @intf: MAC interface73* @reg: Register to read (offset into PCI CFG space)74*/75unsigned int pasemi_read_mac_reg(int intf, unsigned int reg)76{77return in_le32(mac_regs[intf]+reg);78}79EXPORT_SYMBOL(pasemi_read_mac_reg);8081/* pasemi_write_mac_reg - write MAC register82* @intf: MAC interface83* @reg: Register to write to (offset into PCI CFG space)84* @val: Value to write85*/86void pasemi_write_mac_reg(int intf, unsigned int reg, unsigned int val)87{88out_le32(mac_regs[intf]+reg, val);89}90EXPORT_SYMBOL(pasemi_write_mac_reg);9192/* pasemi_read_dma_reg - read DMA register93* @reg: Register to read (offset into PCI CFG space)94*/95unsigned int pasemi_read_dma_reg(unsigned int reg)96{97return in_le32(dma_regs+reg);98}99EXPORT_SYMBOL(pasemi_read_dma_reg);100101/* pasemi_write_dma_reg - write DMA register102* @reg: Register to write to (offset into PCI CFG space)103* @val: Value to write104*/105void pasemi_write_dma_reg(unsigned int reg, unsigned int val)106{107out_le32(dma_regs+reg, val);108}109EXPORT_SYMBOL(pasemi_write_dma_reg);110111static int pasemi_alloc_tx_chan(enum pasemi_dmachan_type type)112{113int bit;114int start, limit;115116switch (type & (TXCHAN_EVT0|TXCHAN_EVT1)) {117case TXCHAN_EVT0:118start = 0;119limit = 10;120break;121case TXCHAN_EVT1:122start = 10;123limit = MAX_TXCH;124break;125default:126start = 0;127limit = MAX_TXCH;128break;129}130retry:131bit = find_next_bit(txch_free, MAX_TXCH, start);132if (bit >= limit)133return -ENOSPC;134if (!test_and_clear_bit(bit, txch_free))135goto retry;136137return bit;138}139140static void pasemi_free_tx_chan(int chan)141{142BUG_ON(test_bit(chan, txch_free));143set_bit(chan, txch_free);144}145146static int pasemi_alloc_rx_chan(void)147{148int bit;149retry:150bit = find_first_bit(rxch_free, MAX_RXCH);151if (bit >= MAX_TXCH)152return -ENOSPC;153if (!test_and_clear_bit(bit, rxch_free))154goto retry;155156return bit;157}158159static void pasemi_free_rx_chan(int chan)160{161BUG_ON(test_bit(chan, rxch_free));162set_bit(chan, rxch_free);163}164165/* pasemi_dma_alloc_chan - Allocate a DMA channel166* @type: Type of channel to allocate167* @total_size: Total size of structure to allocate (to allow for more168* room behind the structure to be used by the client)169* @offset: Offset in bytes from start of the total structure to the beginning170* of struct pasemi_dmachan. Needed when struct pasemi_dmachan is171* not the first member of the client structure.172*173* pasemi_dma_alloc_chan allocates a DMA channel for use by a client. The174* type argument specifies whether it's a RX or TX channel, and in the case175* of TX channels which group it needs to belong to (if any).176*177* Returns a pointer to the total structure allocated on success, NULL178* on failure.179*/180void *pasemi_dma_alloc_chan(enum pasemi_dmachan_type type,181int total_size, int offset)182{183void *buf;184struct pasemi_dmachan *chan;185int chno;186187BUG_ON(total_size < sizeof(struct pasemi_dmachan));188189buf = kzalloc(total_size, GFP_KERNEL);190191if (!buf)192return NULL;193chan = buf + offset;194195chan->priv = buf;196197switch (type & (TXCHAN|RXCHAN)) {198case RXCHAN:199chno = pasemi_alloc_rx_chan();200chan->chno = chno;201chan->irq = irq_create_mapping(NULL,202base_hw_irq + num_txch + chno);203chan->status = &dma_status->rx_sta[chno];204break;205case TXCHAN:206chno = pasemi_alloc_tx_chan(type);207chan->chno = chno;208chan->irq = irq_create_mapping(NULL, base_hw_irq + chno);209chan->status = &dma_status->tx_sta[chno];210break;211}212213chan->chan_type = type;214215return chan;216}217EXPORT_SYMBOL(pasemi_dma_alloc_chan);218219/* pasemi_dma_free_chan - Free a previously allocated channel220* @chan: Channel to free221*222* Frees a previously allocated channel. It will also deallocate any223* descriptor ring associated with the channel, if allocated.224*/225void pasemi_dma_free_chan(struct pasemi_dmachan *chan)226{227if (chan->ring_virt)228pasemi_dma_free_ring(chan);229230switch (chan->chan_type & (RXCHAN|TXCHAN)) {231case RXCHAN:232pasemi_free_rx_chan(chan->chno);233break;234case TXCHAN:235pasemi_free_tx_chan(chan->chno);236break;237}238239kfree(chan->priv);240}241EXPORT_SYMBOL(pasemi_dma_free_chan);242243/* pasemi_dma_alloc_ring - Allocate descriptor ring for a channel244* @chan: Channel for which to allocate245* @ring_size: Ring size in 64-bit (8-byte) words246*247* Allocate a descriptor ring for a channel. Returns 0 on success, errno248* on failure. The passed in struct pasemi_dmachan is updated with the249* virtual and DMA addresses of the ring.250*/251int pasemi_dma_alloc_ring(struct pasemi_dmachan *chan, int ring_size)252{253BUG_ON(chan->ring_virt);254255chan->ring_size = ring_size;256257chan->ring_virt = dma_alloc_coherent(&dma_pdev->dev,258ring_size * sizeof(u64),259&chan->ring_dma, GFP_KERNEL);260261if (!chan->ring_virt)262return -ENOMEM;263264memset(chan->ring_virt, 0, ring_size * sizeof(u64));265266return 0;267}268EXPORT_SYMBOL(pasemi_dma_alloc_ring);269270/* pasemi_dma_free_ring - Free an allocated descriptor ring for a channel271* @chan: Channel for which to free the descriptor ring272*273* Frees a previously allocated descriptor ring for a channel.274*/275void pasemi_dma_free_ring(struct pasemi_dmachan *chan)276{277BUG_ON(!chan->ring_virt);278279dma_free_coherent(&dma_pdev->dev, chan->ring_size * sizeof(u64),280chan->ring_virt, chan->ring_dma);281chan->ring_virt = NULL;282chan->ring_size = 0;283chan->ring_dma = 0;284}285EXPORT_SYMBOL(pasemi_dma_free_ring);286287/* pasemi_dma_start_chan - Start a DMA channel288* @chan: Channel to start289* @cmdsta: Additional CCMDSTA/TCMDSTA bits to write290*291* Enables (starts) a DMA channel with optional additional arguments.292*/293void pasemi_dma_start_chan(const struct pasemi_dmachan *chan, const u32 cmdsta)294{295if (chan->chan_type == RXCHAN)296pasemi_write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(chan->chno),297cmdsta | PAS_DMA_RXCHAN_CCMDSTA_EN);298else299pasemi_write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(chan->chno),300cmdsta | PAS_DMA_TXCHAN_TCMDSTA_EN);301}302EXPORT_SYMBOL(pasemi_dma_start_chan);303304/* pasemi_dma_stop_chan - Stop a DMA channel305* @chan: Channel to stop306*307* Stops (disables) a DMA channel. This is done by setting the ST bit in the308* CMDSTA register and waiting on the ACT (active) bit to clear, then309* finally disabling the whole channel.310*311* This function will only try for a short while for the channel to stop, if312* it doesn't it will return failure.313*314* Returns 1 on success, 0 on failure.315*/316#define MAX_RETRIES 5000317int pasemi_dma_stop_chan(const struct pasemi_dmachan *chan)318{319int reg, retries;320u32 sta;321322if (chan->chan_type == RXCHAN) {323reg = PAS_DMA_RXCHAN_CCMDSTA(chan->chno);324pasemi_write_dma_reg(reg, PAS_DMA_RXCHAN_CCMDSTA_ST);325for (retries = 0; retries < MAX_RETRIES; retries++) {326sta = pasemi_read_dma_reg(reg);327if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) {328pasemi_write_dma_reg(reg, 0);329return 1;330}331cond_resched();332}333} else {334reg = PAS_DMA_TXCHAN_TCMDSTA(chan->chno);335pasemi_write_dma_reg(reg, PAS_DMA_TXCHAN_TCMDSTA_ST);336for (retries = 0; retries < MAX_RETRIES; retries++) {337sta = pasemi_read_dma_reg(reg);338if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) {339pasemi_write_dma_reg(reg, 0);340return 1;341}342cond_resched();343}344}345346return 0;347}348EXPORT_SYMBOL(pasemi_dma_stop_chan);349350/* pasemi_dma_alloc_buf - Allocate a buffer to use for DMA351* @chan: Channel to allocate for352* @size: Size of buffer in bytes353* @handle: DMA handle354*355* Allocate a buffer to be used by the DMA engine for read/write,356* similar to dma_alloc_coherent().357*358* Returns the virtual address of the buffer, or NULL in case of failure.359*/360void *pasemi_dma_alloc_buf(struct pasemi_dmachan *chan, int size,361dma_addr_t *handle)362{363return dma_alloc_coherent(&dma_pdev->dev, size, handle, GFP_KERNEL);364}365EXPORT_SYMBOL(pasemi_dma_alloc_buf);366367/* pasemi_dma_free_buf - Free a buffer used for DMA368* @chan: Channel the buffer was allocated for369* @size: Size of buffer in bytes370* @handle: DMA handle371*372* Frees a previously allocated buffer.373*/374void pasemi_dma_free_buf(struct pasemi_dmachan *chan, int size,375dma_addr_t *handle)376{377dma_free_coherent(&dma_pdev->dev, size, handle, GFP_KERNEL);378}379EXPORT_SYMBOL(pasemi_dma_free_buf);380381/* pasemi_dma_alloc_flag - Allocate a flag (event) for channel synchronization382*383* Allocates a flag for use with channel synchronization (event descriptors).384* Returns allocated flag (0-63), < 0 on error.385*/386int pasemi_dma_alloc_flag(void)387{388int bit;389390retry:391bit = find_next_bit(flags_free, MAX_FLAGS, 0);392if (bit >= MAX_FLAGS)393return -ENOSPC;394if (!test_and_clear_bit(bit, flags_free))395goto retry;396397return bit;398}399EXPORT_SYMBOL(pasemi_dma_alloc_flag);400401402/* pasemi_dma_free_flag - Deallocates a flag (event)403* @flag: Flag number to deallocate404*405* Frees up a flag so it can be reused for other purposes.406*/407void pasemi_dma_free_flag(int flag)408{409BUG_ON(test_bit(flag, flags_free));410BUG_ON(flag >= MAX_FLAGS);411set_bit(flag, flags_free);412}413EXPORT_SYMBOL(pasemi_dma_free_flag);414415416/* pasemi_dma_set_flag - Sets a flag (event) to 1417* @flag: Flag number to set active418*419* Sets the flag provided to 1.420*/421void pasemi_dma_set_flag(int flag)422{423BUG_ON(flag >= MAX_FLAGS);424if (flag < 32)425pasemi_write_dma_reg(PAS_DMA_TXF_SFLG0, 1 << flag);426else427pasemi_write_dma_reg(PAS_DMA_TXF_SFLG1, 1 << flag);428}429EXPORT_SYMBOL(pasemi_dma_set_flag);430431/* pasemi_dma_clear_flag - Sets a flag (event) to 0432* @flag: Flag number to set inactive433*434* Sets the flag provided to 0.435*/436void pasemi_dma_clear_flag(int flag)437{438BUG_ON(flag >= MAX_FLAGS);439if (flag < 32)440pasemi_write_dma_reg(PAS_DMA_TXF_CFLG0, 1 << flag);441else442pasemi_write_dma_reg(PAS_DMA_TXF_CFLG1, 1 << flag);443}444EXPORT_SYMBOL(pasemi_dma_clear_flag);445446/* pasemi_dma_alloc_fun - Allocate a function engine447*448* Allocates a function engine to use for crypto/checksum offload449* Returns allocated engine (0-8), < 0 on error.450*/451int pasemi_dma_alloc_fun(void)452{453int bit;454455retry:456bit = find_next_bit(fun_free, MAX_FLAGS, 0);457if (bit >= MAX_FLAGS)458return -ENOSPC;459if (!test_and_clear_bit(bit, fun_free))460goto retry;461462return bit;463}464EXPORT_SYMBOL(pasemi_dma_alloc_fun);465466467/* pasemi_dma_free_fun - Deallocates a function engine468* @flag: Engine number to deallocate469*470* Frees up a function engine so it can be used for other purposes.471*/472void pasemi_dma_free_fun(int fun)473{474BUG_ON(test_bit(fun, fun_free));475BUG_ON(fun >= MAX_FLAGS);476set_bit(fun, fun_free);477}478EXPORT_SYMBOL(pasemi_dma_free_fun);479480481static void *map_onedev(struct pci_dev *p, int index)482{483struct device_node *dn;484void __iomem *ret;485486dn = pci_device_to_OF_node(p);487if (!dn)488goto fallback;489490ret = of_iomap(dn, index);491if (!ret)492goto fallback;493494return ret;495fallback:496/* This is hardcoded and ugly, but we have some firmware versions497* that don't provide the register space in the device tree. Luckily498* they are at well-known locations so we can just do the math here.499*/500return ioremap(0xe0000000 + (p->devfn << 12), 0x2000);501}502503/* pasemi_dma_init - Initialize the PA Semi DMA library504*505* This function initializes the DMA library. It must be called before506* any other function in the library.507*508* Returns 0 on success, errno on failure.509*/510int pasemi_dma_init(void)511{512static DEFINE_SPINLOCK(init_lock);513struct pci_dev *iob_pdev;514struct pci_dev *pdev;515struct resource res;516struct device_node *dn;517int i, intf, err = 0;518unsigned long timeout;519u32 tmp;520521if (!machine_is(pasemi))522return -ENODEV;523524spin_lock(&init_lock);525526/* Make sure we haven't already initialized */527if (dma_pdev)528goto out;529530iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL);531if (!iob_pdev) {532BUG();533printk(KERN_WARNING "Can't find I/O Bridge\n");534err = -ENODEV;535goto out;536}537iob_regs = map_onedev(iob_pdev, 0);538539dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL);540if (!dma_pdev) {541BUG();542printk(KERN_WARNING "Can't find DMA controller\n");543err = -ENODEV;544goto out;545}546dma_regs = map_onedev(dma_pdev, 0);547base_hw_irq = virq_to_hw(dma_pdev->irq);548549pci_read_config_dword(dma_pdev, PAS_DMA_CAP_TXCH, &tmp);550num_txch = (tmp & PAS_DMA_CAP_TXCH_TCHN_M) >> PAS_DMA_CAP_TXCH_TCHN_S;551552pci_read_config_dword(dma_pdev, PAS_DMA_CAP_RXCH, &tmp);553num_rxch = (tmp & PAS_DMA_CAP_RXCH_RCHN_M) >> PAS_DMA_CAP_RXCH_RCHN_S;554555intf = 0;556for (pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa006, NULL);557pdev;558pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa006, pdev))559mac_regs[intf++] = map_onedev(pdev, 0);560561pci_dev_put(pdev);562563for (pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa005, NULL);564pdev;565pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa005, pdev))566mac_regs[intf++] = map_onedev(pdev, 0);567568pci_dev_put(pdev);569570dn = pci_device_to_OF_node(iob_pdev);571if (dn)572err = of_address_to_resource(dn, 1, &res);573if (!dn || err) {574/* Fallback for old firmware */575res.start = 0xfd800000;576res.end = res.start + 0x1000;577}578dma_status = __ioremap(res.start, res.end-res.start, 0);579pci_dev_put(iob_pdev);580581for (i = 0; i < MAX_TXCH; i++)582__set_bit(i, txch_free);583584for (i = 0; i < MAX_RXCH; i++)585__set_bit(i, rxch_free);586587timeout = jiffies + HZ;588pasemi_write_dma_reg(PAS_DMA_COM_RXCMD, 0);589while (pasemi_read_dma_reg(PAS_DMA_COM_RXSTA) & 1) {590if (time_after(jiffies, timeout)) {591pr_warning("Warning: Could not disable RX section\n");592break;593}594}595596timeout = jiffies + HZ;597pasemi_write_dma_reg(PAS_DMA_COM_TXCMD, 0);598while (pasemi_read_dma_reg(PAS_DMA_COM_TXSTA) & 1) {599if (time_after(jiffies, timeout)) {600pr_warning("Warning: Could not disable TX section\n");601break;602}603}604605/* setup resource allocations for the different DMA sections */606tmp = pasemi_read_dma_reg(PAS_DMA_COM_CFG);607pasemi_write_dma_reg(PAS_DMA_COM_CFG, tmp | 0x18000000);608609/* enable tx section */610pasemi_write_dma_reg(PAS_DMA_COM_TXCMD, PAS_DMA_COM_TXCMD_EN);611612/* enable rx section */613pasemi_write_dma_reg(PAS_DMA_COM_RXCMD, PAS_DMA_COM_RXCMD_EN);614615for (i = 0; i < MAX_FLAGS; i++)616__set_bit(i, flags_free);617618for (i = 0; i < MAX_FUN; i++)619__set_bit(i, fun_free);620621/* clear all status flags */622pasemi_write_dma_reg(PAS_DMA_TXF_CFLG0, 0xffffffff);623pasemi_write_dma_reg(PAS_DMA_TXF_CFLG1, 0xffffffff);624625printk(KERN_INFO "PA Semi PWRficient DMA library initialized "626"(%d tx, %d rx channels)\n", num_txch, num_rxch);627628out:629spin_unlock(&init_lock);630return err;631}632EXPORT_SYMBOL(pasemi_dma_init);633634635