Daily Archives: 2019-10-16

VoCore2: develop SPI driver 3

Now in the deep zone.

From /proc/interrupts, I know esw, gdma, sdhci etc have interrupts.

Check gdma source code, we can add similar irq code to spi.

static irqreturn_t mt7621_spi_irq(int irq, void *devid)
{
	struct mt7621_spi *rs = (struct mt7621_spi *)devid;
	printk(KERN_INFO "mt7621_spi_irq %d trigger\n", irq);
	return IRQ_HANDLED;
}

...

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) { dev_err(&pdev->dev, "failed to get irq\n");
		return -EINVAL;
	}
	ret = devm_request_irq(&pdev->dev, irq, mt7621_spi_irq,
			0, dev_name(&pdev->dev), rs);
	if (ret) {
		dev_err(&pdev->dev, "failed to request irq\n");
		return ret;
	}

and in vocore2 dts(device tree, mt7628an.dtsi), we also need to add spi interrupt.

                spi0: spi@b00 {
                        compatible = "ralink,mt7621-spi";
                        reg = <0xb00 0x100>;

                        resets = <&rstctrl 18>;
                        reset-names = "spi";

                        #address-cells = <1>;
                        #size-cells = <0>;

                        pinctrl-names = "default";
                        pinctrl-0 = <&spi_pins>;

                        interrupt-parent = <&intc>;
                        interrupts = <11>;

                        status = "disabled";
                };

OK, that all, we can compile and check if this works.

After VoCore2 upgrade ready and flash init ready, we can call mem command(I write this command for easy debug, source code can be downloaded at vocore.io/v2.html) to enable spi interrupt.

mem 0x10000b28 0xe0128a84 

Then, write something through spi, like ‘touch /root/x’, this will create a new file under /root/, so OS will try to use spi write to the flash.

Ouch… I just get endless messages “mt7621_spi_irq 19 trigger” from console, after a while, the system dead.

I guess there must be something wrong in the interrupt code. As I remember, once interrupt is triggered, we must write to some register to clean the interrupt, or it will keep in trigger status, that will cause a deadloop, system will hang up too.

Checking esw(ethernet switch) source code, I find esw 0x00 register is Interrupt Status Register, once you write same thing into the register, it will clean up the interrupt.

So SPI must has something similar.

Finally I find it is 0x10000b34 register, its name is SPI Status. The final bit of it:

Once the register is read by system, the interrupt will be clear, easy 😉

Update IRQ code to the following code

static irqreturn_t mt7621_spi_irq(int irq, void *devid)
{
	struct mt7621_spi *rs = (struct mt7621_spi *)devid;
	u32 reg;

	reg = mt7621_spi_read(rs, MT7621_SPI_STATUS);
	printk(KERN_INFO "mt7621_spi_irq %d trigger, status 0x%08x\n", irq, reg);
	return IRQ_HANDLED;
}

Now once we touch new file into flash, we just get around ten lines of the kernel interrupt log, that means, it works! Check system, spi interrupt is not zero anymore.

root@OpenWrt:~# cat /proc/interrupts 
           CPU0       
  5:        895      MIPS   5  10100000.ethernet
  6:      50378      MIPS   6  ra0
  7:     266055      MIPS   7  timer
 15:          0      INTC   7  10002800.gdma
 19:        138      INTC  11  10000b00.spi
 22:      79014      INTC  14  10130000.sdhci
 25:          2      INTC  17  esw
 26:          1      INTC  18  ehci_hcd:usb1, ohci_hcd:usb2
 30:        440      INTC  22  ttyS2
ERR:          0

Next I will try to combine the interrupt into the source code, this is pretty risk, because once Linux boot up, it is using this driver read from flash.
Next next, if I have any luck go that far, maybe try GDMA with SPI, then we have some fast speed spi devices.