12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058 |
- /*
- * wcxb SPI library
- *
- * Copyright (C) 2013 Digium, Inc.
- *
- * All rights reserved.
- *
- */
- /*
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2 as published by the
- * Free Software Foundation. See the LICENSE file included with
- * this program for more details.
- */
- #include <linux/kernel.h>
- #include <linux/spinlock.h>
- #include <linux/errno.h>
- #include <linux/pci.h>
- #include <linux/interrupt.h>
- #include <linux/firmware.h>
- #include <linux/crc32.h>
- #include <linux/delay.h>
- #include <linux/version.h>
- #include <linux/slab.h>
- #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 26)
- #define HAVE_RATELIMIT
- #include <linux/ratelimit.h>
- #endif
- #include <dahdi/kernel.h>
- #include <stdbool.h>
- #include "wcxb.h"
- #include "wcxb_spi.h"
- #include "wcxb_flash.h"
- /* The definition for Surprise Down was added in Linux 3.6 in (a0dee2e PCI: misc
- * pci_reg additions). It may be backported though so we won't check for the
- * version. Same with PCI_EXP_SLTCTL_PDCE. */
- #ifndef PCI_ERR_UNC_SURPDN
- #define PCI_ERR_UNC_SURPDN 0x20
- #endif
- #ifndef PCI_EXP_SLTCTL_PDCE
- #define PCI_EXP_SLTCTL_PDCE 0x8
- #endif
- /* FPGA Status definitions */
- #define OCT_CPU_RESET (1 << 0)
- #define OCT_CPU_DRAM_CKE (1 << 1)
- #define STATUS_LED_GREEN (1 << 9)
- #define STATUS_LED_RED (1 << 10)
- #define FALC_CPU_RESET (1 << 11)
- /* Descriptor ring definitions */
- #define DRING_SIZE (1 << 7) /* Must be in multiples of 2 */
- #define DRING_SIZE_MASK (DRING_SIZE-1)
- #define DESC_EOR (1 << 0)
- #define DESC_INT (1 << 1)
- #define DESC_OWN (1 << 31)
- #define DESC_DEFAULT_STATUS 0xdeadbe00
- #define DMA_CHAN_SIZE 128
- /* Echocan definitions */
- #define OCT_OFFSET (xb->membase + 0x10000)
- #define OCT_CONTROL_REG (OCT_OFFSET + 0)
- #define OCT_DATA_REG (OCT_OFFSET + 0x4)
- #define OCT_ADDRESS_HIGH_REG (OCT_OFFSET + 0x8)
- #define OCT_ADDRESS_LOW_REG (OCT_OFFSET + 0xa)
- #define OCT_DIRECT_WRITE_MASK 0x3001
- #define OCT_INDIRECT_READ_MASK 0x0101
- #define OCT_INDIRECT_WRITE_MASK 0x3101
- /* DMA definitions */
- #define TDM_DRING_ADDR 0x2000
- #define TDM_CONTROL (TDM_DRING_ADDR + 0x4)
- #define ENABLE_ECHOCAN_TDM (1 << 0)
- #define TDM_RECOVER_CLOCK (1 << 1)
- #define ENABLE_DMA (1 << 2)
- #define DMA_RUNNING (1 << 3)
- #define DMA_LOOPBACK (1 << 4)
- #define AUTHENTICATED (1 << 5)
- #define TDM_VERSION (TDM_DRING_ADDR + 0x24)
- /* Interrupt definitions */
- #define INTERRUPT_CONTROL 0x300
- #define ISR (INTERRUPT_CONTROL + 0x0)
- #define IPR (INTERRUPT_CONTROL + 0x4)
- #define IER (INTERRUPT_CONTROL + 0x8)
- #define IAR (INTERRUPT_CONTROL + 0xc)
- #define SIE (INTERRUPT_CONTROL + 0x10)
- #define CIE (INTERRUPT_CONTROL + 0x14)
- #define IVR (INTERRUPT_CONTROL + 0x18)
- #define MER (INTERRUPT_CONTROL + 0x1c)
- #define MER_ME (1<<0)
- #define MER_HIE (1<<1)
- #define DESC_UNDERRUN (1<<0)
- #define DESC_COMPLETE (1<<1)
- #define OCT_INT (1<<2)
- #define FALC_INT (1<<3)
- #define SPI_INT (1<<4)
- #define FLASH_SPI_BASE 0x200
- struct wcxb_hw_desc {
- volatile __be32 status;
- __be32 tx_buf;
- __be32 rx_buf;
- volatile __be32 control;
- } __packed;
- struct wcxb_meta_desc {
- void *tx_buf_virt;
- void *rx_buf_virt;
- };
- static inline bool wcxb_is_pcie(const struct wcxb *xb)
- {
- #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
- return pci_is_pcie(xb->pdev);
- #else
- #ifndef WCXB_PCI_DEV_DOES_NOT_HAVE_IS_PCIE
- return (xb->pdev->is_pcie > 0);
- #else
- return (xb->flags.is_pcie > 0);
- #endif
- #endif
- }
- static const unsigned int CLK_SRC_MASK = ((1 << 13) | (1 << 12) | (1 << 1));
- enum wcxb_clock_sources wcxb_get_clksrc(struct wcxb *xb)
- {
- static const u32 SELF = 0x0;
- static const u32 RECOVER = (1 << 1);
- static const u32 SLAVE = (1 << 12) | (1 << 1);
- unsigned long flags;
- u32 reg;
- spin_lock_irqsave(&xb->lock, flags);
- reg = ioread32be(xb->membase + TDM_CONTROL) & CLK_SRC_MASK;
- spin_unlock_irqrestore(&xb->lock, flags);
- if (SELF == reg)
- return WCXB_CLOCK_SELF;
- else if (RECOVER == reg)
- return WCXB_CLOCK_RECOVER;
- else if (SLAVE == reg)
- return WCXB_CLOCK_SLAVE;
- else
- WARN_ON(1);
- return WCXB_CLOCK_SELF;
- }
- void wcxb_set_clksrc(struct wcxb *xb, enum wcxb_clock_sources clksrc)
- {
- unsigned long flags;
- u32 clkbits = 0;
- switch (clksrc) {
- case WCXB_CLOCK_RECOVER:
- if (xb->flags.drive_timing_cable)
- clkbits = (1<<13) | (1 << 1);
- else
- clkbits = (1 << 1);
- break;
- case WCXB_CLOCK_SELF:
- if (xb->flags.drive_timing_cable)
- clkbits = (1<<13);
- else
- clkbits = 0;
- break;
- case WCXB_CLOCK_SLAVE:
- /* When we're slave, do not ever drive the timing cable. */
- clkbits = (1<<12) | (1 << 1);
- break;
- };
- /* set new clock select */
- spin_lock_irqsave(&xb->lock, flags);
- if (!wcxb_is_stopped(xb)) {
- dev_err(&xb->pdev->dev, "ERROR: Cannot set clock source while DMA engine is running.\n");
- } else {
- u32 reg;
- reg = ioread32be(xb->membase + TDM_CONTROL);
- reg &= ~CLK_SRC_MASK;
- reg |= (clkbits & CLK_SRC_MASK);
- iowrite32be(reg, xb->membase + TDM_CONTROL);
- }
- spin_unlock_irqrestore(&xb->lock, flags);
- }
- void wcxb_enable_echocan(struct wcxb *xb)
- {
- u32 reg;
- unsigned long flags;
- spin_lock_irqsave(&xb->lock, flags);
- reg = ioread32be(xb->membase + TDM_CONTROL);
- reg |= ENABLE_ECHOCAN_TDM;
- iowrite32be(reg, xb->membase + TDM_CONTROL);
- spin_unlock_irqrestore(&xb->lock, flags);
- }
- void wcxb_disable_echocan(struct wcxb *xb)
- {
- u32 reg;
- unsigned long flags;
- spin_lock_irqsave(&xb->lock, flags);
- reg = ioread32be(xb->membase + TDM_CONTROL);
- reg &= ~ENABLE_ECHOCAN_TDM;
- iowrite32be(reg, xb->membase + TDM_CONTROL);
- spin_unlock_irqrestore(&xb->lock, flags);
- }
- void wcxb_reset_echocan(struct wcxb *xb)
- {
- unsigned long flags;
- int reg;
- spin_lock_irqsave(&xb->lock, flags);
- reg = ioread32be(xb->membase);
- iowrite32be((reg & ~OCT_CPU_RESET), xb->membase);
- spin_unlock_irqrestore(&xb->lock, flags);
- msleep_interruptible(1);
- spin_lock_irqsave(&xb->lock, flags);
- reg = ioread32be(xb->membase);
- iowrite32be((reg | OCT_CPU_RESET), xb->membase);
- spin_unlock_irqrestore(&xb->lock, flags);
- dev_dbg(&xb->pdev->dev, "Reset octasic\n");
- }
- bool wcxb_is_echocan_present(struct wcxb *xb)
- {
- return 0x1 == ioread16be(OCT_CONTROL_REG);
- }
- void wcxb_enable_echocan_dram(struct wcxb *xb)
- {
- unsigned long flags;
- int reg;
- spin_lock_irqsave(&xb->lock, flags);
- reg = ioread32be(xb->membase);
- iowrite32be((reg | OCT_CPU_DRAM_CKE), xb->membase);
- spin_unlock_irqrestore(&xb->lock, flags);
- }
- u16 wcxb_get_echocan_reg(struct wcxb *xb, u32 address)
- {
- uint16_t highaddress = ((address >> 20) & 0xfff);
- uint16_t lowaddress = ((address >> 4) & 0xfffff);
- unsigned long stop = jiffies + HZ/10;
- unsigned long flags;
- u16 ret;
- spin_lock_irqsave(&xb->lock, flags);
- iowrite16be(highaddress, OCT_ADDRESS_HIGH_REG);
- iowrite16be(lowaddress, OCT_ADDRESS_LOW_REG);
- iowrite16be(OCT_INDIRECT_READ_MASK | ((address & 0xe) << 8),
- OCT_CONTROL_REG);
- do {
- ret = ioread16be(OCT_CONTROL_REG);
- } while ((ret & (1<<8)) && time_before(jiffies, stop));
- WARN_ON_ONCE(time_after_eq(jiffies, stop));
- ret = ioread16be(OCT_DATA_REG);
- spin_unlock_irqrestore(&xb->lock, flags);
- return ret;
- }
- void wcxb_set_echocan_reg(struct wcxb *xb, u32 address, u16 val)
- {
- unsigned long flags;
- uint16_t ret;
- uint16_t highaddress = ((address >> 20) & 0xfff);
- uint16_t lowaddress = ((address >> 4) & 0xffff);
- unsigned long stop = jiffies + HZ/10;
- spin_lock_irqsave(&xb->lock, flags);
- iowrite16be(highaddress, OCT_ADDRESS_HIGH_REG);
- iowrite16be(lowaddress, OCT_ADDRESS_LOW_REG);
- iowrite16be(val, OCT_DATA_REG);
- iowrite16be(OCT_INDIRECT_WRITE_MASK | ((address & 0xe) << 8),
- OCT_CONTROL_REG);
- /* No write should take longer than 100ms */
- do {
- ret = ioread16be(OCT_CONTROL_REG);
- } while ((ret & (1<<8)) && time_before(jiffies, stop));
- spin_unlock_irqrestore(&xb->lock, flags);
- WARN_ON_ONCE(time_after_eq(jiffies, stop));
- }
- #ifdef HAVE_RATELIMIT
- static DEFINE_RATELIMIT_STATE(_underrun_rl, DEFAULT_RATELIMIT_INTERVAL,
- DEFAULT_RATELIMIT_BURST);
- #endif
- /* wcxb_reset_dring needs to be called with xb->lock held. */
- static void _wcxb_reset_dring(struct wcxb *xb)
- {
- int x;
- struct wcxb_meta_desc *mdesc;
- struct wcxb_hw_desc *hdesc = NULL;
- xb->dma_head = xb->dma_tail = 0;
- if (unlikely(xb->latency > DRING_SIZE)) {
- #ifdef HAVE_RATELIMIT
- if (__ratelimit(&_underrun_rl)) {
- #else
- if (printk_ratelimit()) {
- #endif
- dev_info(&xb->pdev->dev,
- "Oops! Tried to increase latency past buffer size.\n");
- }
- xb->latency = DRING_SIZE;
- }
- for (x = 0; x < xb->latency; x++) {
- dma_addr_t dma_tmp;
- mdesc = &xb->meta_dring[x];
- hdesc = &xb->hw_dring[x];
- hdesc->status = cpu_to_be32(DESC_DEFAULT_STATUS);
- if (!mdesc->tx_buf_virt) {
- mdesc->tx_buf_virt =
- dma_pool_alloc(xb->pool, GFP_ATOMIC, &dma_tmp);
- hdesc->tx_buf = cpu_to_be32(dma_tmp);
- mdesc->rx_buf_virt =
- dma_pool_alloc(xb->pool, GFP_ATOMIC, &dma_tmp);
- hdesc->rx_buf = cpu_to_be32(dma_tmp);
- }
- hdesc->control = cpu_to_be32(DESC_INT|DESC_OWN);
- BUG_ON(!mdesc->tx_buf_virt || !mdesc->rx_buf_virt);
- }
- BUG_ON(!hdesc);
- /* Set end of ring bit in last descriptor to force hw to loop around */
- hdesc->control |= cpu_to_be32(DESC_EOR);
- #ifdef DEBUG
- xb->last_retry_count = 0;
- xb->max_retry_count = 0;
- xb->last_dma_time = 0;
- xb->max_dma_time = 0;
- #endif
- iowrite32be(xb->hw_dring_phys, xb->membase + TDM_DRING_ADDR);
- }
- static void wcxb_handle_dma(struct wcxb *xb)
- {
- struct wcxb_meta_desc *mdesc;
- struct wcxb_hw_desc *tail = &(xb->hw_dring[xb->dma_tail]);
- while (!(tail->control & cpu_to_be32(DESC_OWN))) {
- u_char *frame;
- #ifdef DEBUG
- xb->last_retry_count =
- ((be32_to_cpu(tail->control) & 0x0000ff00) >> 8);
- xb->last_dma_time = (be32_to_cpu(tail->status));
- #endif
- mdesc = &xb->meta_dring[xb->dma_tail];
- frame = mdesc->rx_buf_virt;
- xb->ops->handle_receive(xb, frame);
- xb->dma_tail =
- (xb->dma_tail == xb->latency-1) ? 0 : xb->dma_tail + 1;
- tail = &(xb->hw_dring[xb->dma_tail]);
- mdesc = &xb->meta_dring[xb->dma_head];
- frame = mdesc->tx_buf_virt;
- xb->ops->handle_transmit(xb, frame);
- wmb();
- xb->hw_dring[xb->dma_head].control |= cpu_to_be32(DESC_OWN);
- xb->dma_head =
- (xb->dma_head == xb->latency-1) ? 0 : xb->dma_head + 1;
- }
- #ifdef DEBUG
- if (xb->last_retry_count > xb->max_retry_count) {
- xb->max_retry_count = xb->last_retry_count;
- dev_info(&xb->pdev->dev,
- "New DMA max retries detected: %d\n",
- xb->max_retry_count);
- }
- if (xb->last_dma_time > xb->max_dma_time) {
- xb->max_dma_time = xb->last_dma_time;
- dev_info(&xb->pdev->dev,
- "New DMA max transfer time detected: %d\n",
- xb->max_dma_time);
- }
- #endif
- }
- static irqreturn_t _wcxb_isr(int irq, void *dev_id)
- {
- struct wcxb *xb = dev_id;
- unsigned int limit = 8;
- u32 pending;
- pending = ioread32be(xb->membase + ISR);
- if (!pending)
- return IRQ_NONE;
- do {
- iowrite32be(pending, xb->membase + IAR);
- if (pending & DESC_UNDERRUN) {
- u32 reg;
- /* Report the error in case drivers have any custom
- * methods for indicating potential data corruption. An
- * underrun means data loss in the TDM channel. */
- if (xb->ops->handle_error)
- xb->ops->handle_error(xb);
- spin_lock(&xb->lock);
- if (!xb->flags.latency_locked) {
- /* bump latency */
- xb->latency = min(xb->latency + 1,
- xb->max_latency);
- #ifdef HAVE_RATELIMIT
- if (__ratelimit(&_underrun_rl)) {
- #else
- if (printk_ratelimit()) {
- #endif
- if (xb->latency != xb->max_latency) {
- dev_info(&xb->pdev->dev,
- "Underrun detected by hardware. Latency bumped to: %dms\n",
- xb->latency);
- } else {
- dev_info(&xb->pdev->dev,
- "Underrun detected by hardware. Latency at max of %dms.\n",
- xb->latency);
- }
- }
- }
- /* re-setup dma ring */
- _wcxb_reset_dring(xb);
- /* set dma enable bit */
- reg = ioread32be(xb->membase + TDM_CONTROL);
- reg |= ENABLE_DMA;
- iowrite32be(reg, xb->membase + TDM_CONTROL);
- spin_unlock(&xb->lock);
- }
- if (pending & DESC_COMPLETE) {
- xb->framecount++;
- wcxb_handle_dma(xb);
- }
- if (NULL != xb->ops->handle_interrupt)
- xb->ops->handle_interrupt(xb, pending);
- pending = ioread32be(xb->membase + ISR);
- } while (pending && --limit);
- return IRQ_HANDLED;
- }
- DAHDI_IRQ_HANDLER(wcxb_isr)
- {
- irqreturn_t ret;
- unsigned long flags;
- local_irq_save(flags);
- ret = _wcxb_isr(irq, dev_id);
- local_irq_restore(flags);
- return ret;
- }
- static int wcxb_alloc_dring(struct wcxb *xb, const char *board_name)
- {
- xb->meta_dring =
- kzalloc(sizeof(struct wcxb_meta_desc) * DRING_SIZE,
- GFP_KERNEL);
- if (!xb->meta_dring)
- return -ENOMEM;
- xb->hw_dring = dma_alloc_coherent(&xb->pdev->dev,
- sizeof(struct wcxb_hw_desc) * DRING_SIZE,
- &xb->hw_dring_phys,
- GFP_KERNEL);
- if (!xb->hw_dring) {
- kfree(xb->meta_dring);
- return -ENOMEM;
- }
- xb->pool = dma_pool_create(board_name, &xb->pdev->dev,
- PAGE_SIZE, PAGE_SIZE, 0);
- if (!xb->pool) {
- kfree(xb->meta_dring);
- dma_free_coherent(&xb->pdev->dev,
- sizeof(struct wcxb_hw_desc) * DRING_SIZE,
- xb->hw_dring,
- xb->hw_dring_phys);
- return -ENOMEM;
- }
- return 0;
- }
- /**
- * wcxb_soft_reset - Set interface registers back to known good values.
- *
- * This represents the normal default state after a reset of the FPGA. This
- * function is preferred over the hard reset function.
- *
- */
- static void wcxb_soft_reset(struct wcxb *xb)
- {
- /* digium_gpo */
- iowrite32be(0x0, xb->membase);
- /* xps_intc */
- iowrite32be(0x0, xb->membase + 0x300);
- iowrite32be(0x0, xb->membase + 0x308);
- iowrite32be(0x0, xb->membase + 0x310);
- iowrite32be(0x0, xb->membase + 0x31C);
- /* xps_spi_config_flash */
- iowrite32be(0xA, xb->membase + 0x200);
- /* tdm engine */
- iowrite32be(0x0, xb->membase + 0x2000);
- iowrite32be(0x0, xb->membase + 0x2004);
- }
- static void _wcxb_hard_reset(struct wcxb *xb)
- {
- struct pci_dev *const pdev = xb->pdev;
- u32 microblaze_version;
- unsigned long stop_time = jiffies + msecs_to_jiffies(2000);
- pci_save_state(pdev);
- iowrite32be(0xe00, xb->membase + TDM_CONTROL);
- /* This sleep is to give FPGA time to bring up the PCI/PCIe interface */
- msleep(200);
- pci_restore_state(pdev);
- /* Wait for the Microblaze CPU to complete it's startup */
- do {
- msleep(20);
- /* Can return either 0xffff or 0 before it's fully booted */
- microblaze_version = ioread32be(xb->membase + 0x2018) ?: 0xffff;
- } while (time_before(jiffies, stop_time)
- && 0xffff == microblaze_version);
- }
- /*
- * Since the FPGA hard reset drops the PCIe link we need to disable
- * error reporting on the upsteam link. Otherwise Surprise Down errors
- * may be reported in reponse to the link going away.
- *
- * NOTE: We cannot use pci_disable_pcie_error_reporting() because it will not
- * disable error reporting if the system firmware is attached to the advanced
- * error reporting mechanism.
- */
- static void _wcxb_pcie_hard_reset(struct wcxb *xb)
- {
- struct pci_dev *const parent = xb->pdev->bus->self;
- u32 aer_mask;
- u16 sltctl;
- int pos_err;
- int pos_exp;
- if (!wcxb_is_pcie(xb))
- return;
- pos_err = pci_find_ext_capability(parent, PCI_EXT_CAP_ID_ERR);
- if (pos_err) {
- pci_read_config_dword(parent, pos_err + PCI_ERR_UNCOR_MASK,
- &aer_mask);
- pci_write_config_dword(parent, pos_err + PCI_ERR_UNCOR_MASK,
- aer_mask | PCI_ERR_UNC_SURPDN);
- }
- /* Also disable any presence change reporting. */
- pos_exp = pci_find_capability(parent, PCI_CAP_ID_EXP);
- if (pos_exp) {
- pci_read_config_word(parent, pos_exp + PCI_EXP_SLTCTL,
- &sltctl);
- pci_write_config_word(parent, pos_exp + PCI_EXP_SLTCTL,
- sltctl & ~PCI_EXP_SLTCTL_PDCE);
- }
- _wcxb_hard_reset(xb);
- if (pos_exp)
- pci_write_config_word(parent, pos_exp + PCI_EXP_SLTCTL, sltctl);
- if (pos_err) {
- pci_write_config_dword(parent, pos_err + PCI_ERR_UNCOR_MASK,
- aer_mask);
- /* Clear the error as well from the status register. */
- pci_write_config_dword(parent, pos_err + PCI_ERR_UNCOR_STATUS,
- PCI_ERR_UNC_SURPDN);
- }
- return;
- }
- /**
- * wcxb_hard_reset - Reset FPGA and reload firmware.
- *
- * This may be called in the context of device probe and therefore the PCI
- * device may be locked.
- *
- */
- static void wcxb_hard_reset(struct wcxb *xb)
- {
- if (wcxb_is_pcie(xb))
- _wcxb_pcie_hard_reset(xb);
- else
- _wcxb_hard_reset(xb);
- }
- int wcxb_init(struct wcxb *xb, const char *board_name, u32 int_mode)
- {
- int res = 0;
- struct pci_dev *pdev = xb->pdev;
- u32 tdm_control;
- if (pci_enable_device(pdev))
- return -EIO;
- pci_set_master(pdev);
- #ifdef WCXB_PCI_DEV_DOES_NOT_HAVE_IS_PCIE
- xb->flags.is_pcie = pci_find_capability(pdev, PCI_CAP_ID_EXP) ? 1 : 0;
- #endif
- WARN_ON(!pdev);
- if (!pdev)
- return -EINVAL;
- xb->latency = WCXB_DEFAULT_LATENCY;
- xb->max_latency = WCXB_DEFAULT_MAXLATENCY;
- spin_lock_init(&xb->lock);
- xb->membase = pci_iomap(pdev, 0, 0);
- if (pci_request_regions(pdev, board_name))
- dev_info(&xb->pdev->dev, "Unable to request regions\n");
- wcxb_soft_reset(xb);
- res = wcxb_alloc_dring(xb, board_name);
- if (res) {
- dev_err(&xb->pdev->dev,
- "Failed to allocate descriptor rings.\n");
- goto fail_exit;
- }
- /* Enable writes to fpga status register */
- iowrite32be(0, xb->membase + 0x04);
- xb->flags.have_msi = (int_mode) ? 0 : (0 == pci_enable_msi(pdev));
- if (request_irq(pdev->irq, wcxb_isr,
- (xb->flags.have_msi) ? 0 : IRQF_SHARED,
- board_name, xb)) {
- dev_notice(&xb->pdev->dev, "Unable to request IRQ %d\n",
- pdev->irq);
- res = -EIO;
- goto fail_exit;
- }
- iowrite32be(0, xb->membase + TDM_CONTROL);
- tdm_control = ioread32be(xb->membase + TDM_CONTROL);
- if (!(tdm_control & 0x20)) {
- dev_err(&xb->pdev->dev,
- "This board is not authenticated and may not function properly.\n");
- msleep(1000);
- } else {
- dev_dbg(&xb->pdev->dev, "Authenticated. %08x\n", tdm_control);
- }
- return res;
- fail_exit:
- pci_release_regions(xb->pdev);
- return res;
- }
- void wcxb_stop_dma(struct wcxb *xb)
- {
- unsigned long flags;
- u32 reg;
- /* Quiesce DMA engine interrupts */
- spin_lock_irqsave(&xb->lock, flags);
- reg = ioread32be(xb->membase + TDM_CONTROL);
- reg &= ~ENABLE_DMA;
- iowrite32be(reg, xb->membase + TDM_CONTROL);
- spin_unlock_irqrestore(&xb->lock, flags);
- }
- int wcxb_wait_for_stop(struct wcxb *xb, unsigned long timeout_ms)
- {
- unsigned long stop;
- stop = jiffies + msecs_to_jiffies(timeout_ms);
- do {
- if (time_after(jiffies, stop))
- return -EIO;
- else
- cpu_relax();
- } while (!wcxb_is_stopped(xb));
- return 0;
- }
- void wcxb_disable_interrupts(struct wcxb *xb)
- {
- iowrite32be(0, xb->membase + IER);
- }
- void wcxb_stop(struct wcxb *xb)
- {
- unsigned long flags;
- spin_lock_irqsave(&xb->lock, flags);
- /* Stop everything */
- iowrite32be(0, xb->membase + TDM_CONTROL);
- iowrite32be(0, xb->membase + IER);
- iowrite32be(0, xb->membase + MER);
- iowrite32be(-1, xb->membase + IAR);
- /* Flush quiesce commands before exit */
- ioread32be(xb->membase);
- spin_unlock_irqrestore(&xb->lock, flags);
- synchronize_irq(xb->pdev->irq);
- }
- bool wcxb_is_stopped(struct wcxb *xb)
- {
- return !(ioread32be(xb->membase + TDM_CONTROL) & DMA_RUNNING);
- }
- static void wcxb_free_dring(struct wcxb *xb)
- {
- struct wcxb_meta_desc *mdesc;
- struct wcxb_hw_desc *hdesc;
- int i;
- /* Free tx/rx buffs */
- for (i = 0; i < DRING_SIZE; i++) {
- mdesc = &xb->meta_dring[i];
- hdesc = &xb->hw_dring[i];
- if (mdesc->tx_buf_virt) {
- dma_pool_free(xb->pool,
- mdesc->tx_buf_virt,
- be32_to_cpu(hdesc->tx_buf));
- dma_pool_free(xb->pool,
- mdesc->rx_buf_virt,
- be32_to_cpu(hdesc->rx_buf));
- }
- }
- dma_pool_destroy(xb->pool);
- dma_free_coherent(&xb->pdev->dev,
- sizeof(struct wcxb_hw_desc) * DRING_SIZE,
- xb->hw_dring,
- xb->hw_dring_phys);
- kfree(xb->meta_dring);
- }
- void wcxb_release(struct wcxb *xb)
- {
- wcxb_stop(xb);
- synchronize_irq(xb->pdev->irq);
- free_irq(xb->pdev->irq, xb);
- if (xb->flags.have_msi)
- pci_disable_msi(xb->pdev);
- if (xb->membase)
- pci_iounmap(xb->pdev, xb->membase);
- wcxb_free_dring(xb);
- pci_release_regions(xb->pdev);
- pci_disable_device(xb->pdev);
- return;
- }
- int wcxb_start(struct wcxb *xb)
- {
- u32 reg;
- unsigned long flags;
- spin_lock_irqsave(&xb->lock, flags);
- _wcxb_reset_dring(xb);
- /* Enable hardware interrupts */
- iowrite32be(-1, xb->membase + IAR);
- iowrite32be(DESC_UNDERRUN|DESC_COMPLETE, xb->membase + IER);
- /* iowrite32be(0x3f7, xb->membase + IER); */
- iowrite32be(MER_ME|MER_HIE, xb->membase + MER);
- /* Start the DMA engine processing. */
- reg = ioread32be(xb->membase + TDM_CONTROL);
- reg |= ENABLE_DMA;
- iowrite32be(reg, xb->membase + TDM_CONTROL);
- spin_unlock_irqrestore(&xb->lock, flags);
- return 0;
- }
- struct wcxb_meta_block {
- __le32 chksum;
- __le32 version;
- __le32 size;
- } __packed;
- struct wcxb_firm_header {
- u8 header[6];
- __le32 chksum;
- u8 pad[18];
- __le32 version;
- } __packed;
- u32 wcxb_get_firmware_version(struct wcxb *xb)
- {
- u32 version = 0;
- /* Two version registers are read and catenated into one */
- /* Firmware version goes in bits upper byte */
- version = ((ioread32be(xb->membase + 0x400) & 0xffff)<<16);
- /* Microblaze version goes in lower word */
- version += ioread32be(xb->membase + 0x2018);
- return version;
- }
- static int wcxb_update_firmware(struct wcxb *xb, const struct firmware *fw,
- const char *filename,
- enum wcxb_reset_option reset)
- {
- u32 tdm_control;
- static const int APPLICATION_ADDRESS = 0x200000;
- static const int META_BLOCK_OFFSET = 0x170000;
- static const int ERASE_BLOCK_SIZE = 0x010000;
- static const int END_OFFSET = APPLICATION_ADDRESS + META_BLOCK_OFFSET +
- ERASE_BLOCK_SIZE;
- struct wcxb_spi_master *flash_spi_master;
- struct wcxb_spi_device *flash_spi_device;
- struct wcxb_meta_block meta;
- int offset;
- struct wcxb_firm_header *head = (struct wcxb_firm_header *)(fw->data);
- if (fw->size > (META_BLOCK_OFFSET + sizeof(*head))) {
- dev_err(&xb->pdev->dev,
- "Firmware is too large to fit in available space.\n");
- return -EINVAL;
- }
- meta.size = cpu_to_le32(fw->size);
- meta.version = head->version;
- meta.chksum = head->chksum;
- flash_spi_master = wcxb_spi_master_create(&xb->pdev->dev,
- xb->membase + FLASH_SPI_BASE,
- false);
- flash_spi_device = wcxb_spi_device_create(flash_spi_master, 0);
- dev_info(&xb->pdev->dev,
- "Uploading %s. This can take up to 30 seconds.\n", filename);
- /* First erase all the blocks in the application area. */
- offset = APPLICATION_ADDRESS;
- while (offset < END_OFFSET) {
- wcxb_flash_sector_erase(flash_spi_device, offset);
- offset += ERASE_BLOCK_SIZE;
- }
- /* Then write the new firmware file. */
- wcxb_flash_write(flash_spi_device, APPLICATION_ADDRESS,
- &fw->data[sizeof(struct wcxb_firm_header)],
- fw->size - sizeof(struct wcxb_firm_header));
- /* Finally, update the meta block. */
- wcxb_flash_write(flash_spi_device,
- APPLICATION_ADDRESS + META_BLOCK_OFFSET,
- &meta, sizeof(meta));
- if (WCXB_RESET_NOW == reset) {
- /* Reset fpga after loading firmware */
- dev_info(&xb->pdev->dev,
- "Firmware load complete. Reseting device.\n");
- tdm_control = ioread32be(xb->membase + TDM_CONTROL);
- wcxb_hard_reset(xb);
- iowrite32be(0, xb->membase + 0x04);
- iowrite32be(tdm_control, xb->membase + TDM_CONTROL);
- } else {
- dev_info(&xb->pdev->dev,
- "Delaying reset. Firmware load requires a power cycle\n");
- }
- wcxb_spi_device_destroy(flash_spi_device);
- wcxb_spi_master_destroy(flash_spi_master);
- return 0;
- }
- int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
- const char *firmware_filename, bool force_firmware,
- enum wcxb_reset_option reset)
- {
- const struct firmware *fw;
- const struct wcxb_firm_header *header;
- static const int APPLICATION_ADDRESS = 0x200000;
- static const int META_BLOCK_OFFSET = 0x170000;
- struct wcxb_spi_master *flash_spi_master;
- struct wcxb_spi_device *flash_spi_device;
- struct wcxb_meta_block meta;
- int res = 0;
- u32 crc;
- u32 version = 0;
- version = wcxb_get_firmware_version(xb);
- if (0xff000000 == (version & 0xff000000)) {
- dev_info(&xb->pdev->dev,
- "Invalid firmware %x. Please check your hardware.\n",
- version);
- return -EIO;
- }
- if ((expected_version == version) && !force_firmware) {
- dev_info(&xb->pdev->dev, "Firmware version: %x\n", version);
- return 0;
- }
- /* Check meta firmware version for a not-booted application image */
- flash_spi_master = wcxb_spi_master_create(&xb->pdev->dev,
- xb->membase + FLASH_SPI_BASE,
- false);
- flash_spi_device = wcxb_spi_device_create(flash_spi_master, 0);
- res = wcxb_flash_read(flash_spi_device,
- APPLICATION_ADDRESS + META_BLOCK_OFFSET,
- &meta, sizeof(meta));
- if (res) {
- dev_info(&xb->pdev->dev, "Unable to read flash\n");
- return -EIO;
- }
- if ((meta.version == cpu_to_le32(expected_version))
- && !force_firmware) {
- dev_info(&xb->pdev->dev,
- "Detected previous firmware updated to current version %x, but %x is currently running on card. You likely need to power cycle your system.\n",
- expected_version, version);
- return 0;
- }
- if (force_firmware) {
- dev_info(&xb->pdev->dev,
- "force_firmware module parameter is set. Forcing firmware load, regardless of version\n");
- } else {
- dev_info(&xb->pdev->dev,
- "Firmware version %x is running, but we require version %x.\n",
- version, expected_version);
- }
- res = request_firmware(&fw, firmware_filename, &xb->pdev->dev);
- if (res) {
- dev_info(&xb->pdev->dev,
- "Firmware '%s' not available from userspace.\n",
- firmware_filename);
- goto cleanup;
- }
- header = (const struct wcxb_firm_header *)fw->data;
- /* Check the crc */
- crc = crc32(~0, &fw->data[10], fw->size - 10) ^ ~0;
- if (memcmp("DIGIUM", header->header, sizeof(header->header)) ||
- (le32_to_cpu(header->chksum) != crc)) {
- dev_info(&xb->pdev->dev,
- "%s is invalid. Please reinstall.\n",
- firmware_filename);
- goto cleanup;
- }
- /* Check the file vs required firmware versions */
- if (le32_to_cpu(header->version) != expected_version) {
- dev_err(&xb->pdev->dev,
- "Existing firmware file %s is version %x, but we require %x. Please install the correct firmware file.\n",
- firmware_filename, le32_to_cpu(header->version),
- expected_version);
- res = -EIO;
- goto cleanup;
- }
- dev_info(&xb->pdev->dev, "Found %s (version: %x) Preparing for flash\n",
- firmware_filename, header->version);
- res = wcxb_update_firmware(xb, fw, firmware_filename, reset);
- version = wcxb_get_firmware_version(xb);
- if (WCXB_RESET_NOW == reset) {
- dev_info(&xb->pdev->dev,
- "Reset into firmware version: %x\n", version);
- } else {
- dev_info(&xb->pdev->dev,
- "Running firmware version: %x\n", version);
- dev_info(&xb->pdev->dev,
- "Loaded firmware version: %x (Will load after next power cycle)\n",
- header->version);
- }
- if ((WCXB_RESET_NOW == reset) && (expected_version != version)
- && !force_firmware) {
- /* On the off chance that the interface is in a state where it
- * cannot boot into the updated firmware image, power cycling
- * the card can recover. A simple "reset" of the computer is not
- * sufficient, power has to be removed completely. */
- dev_err(&xb->pdev->dev,
- "The wrong firmware is running after update. Please power cycle and try again.\n");
- res = -EIO;
- goto cleanup;
- }
- if (res) {
- dev_info(&xb->pdev->dev,
- "Failed to load firmware %s\n", firmware_filename);
- }
- cleanup:
- release_firmware(fw);
- return res;
- }
|