|
I'm writing a SPI Slave kernel module for the at91sam9g20 that will respond to NSSR and RDRF interrupts. The problem I'm having is after I have configured the SPI port, assigned an Interrupt Service Routine and enable the port, my Service Routine gets called constantly. When checking the Interrupt Mask and Status Register I get: IMR = 0x0 and SR = 0x103FB. As soon as I leave the ISR it is called again. I have tried reading the SR during the open function but it always is set to 0x103FB even when no SPI traffic occurs.
static irqreturn_t spi_slave_isr (int irq, void *dev_id) { struct spi_slave_dev *dev = dev_id; u16 val; u32 status, pending, imr; int ret = IRQ_NONE;
spi_writel(dev, IDR, SPI_BIT(RDRF)); spi_writel(dev, PTCR, SPI_BIT(RXTDIS));
imr = spi_readl(dev, IMR); status = spi_readl(dev, SR); pending = status & imr;
val = spi_readl(dev, RDR) & 0xFFFF;
CDBG("imr (0x%X) sr(0x%X) val(0x%X)\n", imr, status, val);
if (pending & SPI_BIT(RDRF)) ret = IRQ_HANDLED;
sr = spi_readl(dev, SR);
spi_writel(dev, IER, SPI_BIT(RDRF) | SPI_BIT(NSSR)); spi_writel(dev, PTCR, SPI_BIT(RXTEN));
return ret; }
My setup of the Port / IRQ: static int spi_slave_open (struct inode *inode, struct file *filp) { u32 mr, csr; int ret; struct spi_slave_dev *dev;
/* Find Device */ dev = container_of(inode->i_cdev, struct spi_slave_dev, cdev);
dev->regs = ioremap_wc(AT91C_BASE_SPI1, 0x4000); if (!dev->regs) { printk(KERN_INFO "Failed ioremap_wc\n"); return -ENOMEM; } /* Disable SPI */ spi_writel(dev, CR, SPI_BIT(SPIDIS));
/* Setup ISR */ ret = request_irq(AT91C_ID_SPI1), // IRQ Number spi_slave_isr, // ISR 0, // Flags "spi_slave", // /proc/interrupts dev);
if (ret) { printk(KERN_INFO "SPI Slave Unable to register IRQ\n"); return ret; }
/* Execute software reset twice per Atmel's AT91Lib*/ spi_writel(dev, CR, SPI_BIT(SWRST)); spi_writel(dev, CR, SPI_BIT(SWRST));
/* Configure MR */ spi_writel(dev, MR, (SPI_BIT(MODFDIS)));
/* Configure PTCR */ spi_writel(dev, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
/* Configure CS */ spi_writel(dev, CSR0, (SPI_BIT(CPOL) | SPI_BF(BITS, 0)));
/* Clearing everything else */ spi_writel(dev, TDR, 0x00BC); spi_readl(dev, RDR);
/* Enable SPI */ spi_writel(dev, CR, SPI_BIT(SPIEN));
/* Enable IER & PTCR */ spi_writel(dev, IER, SPI_BIT(RDRF) | SPI_BIT(NSSR)); spi_writel(dev, PTCR, SPI_BIT(RXTEN));
filp->private_data = dev;
return 0; }
|