This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

66AK2H12 K2H EVM SPI Communications

I have a rev 1.1 EVM and I am running MCSDK 3_00_03_15. I see /dev/spidev32766.3 as the only spi device listed by the kernel and I have a couple of questions related to this:

  1. What Bus/Device does this correspond to?
  2. When I try to open the device and write to it I don't see any of the SPI wires active on an oscilloscope.
  3. I compiled and ran the spidev test provided here https://www.kernel.org/doc/Documentation/spi/spidev_test.c and I see no active SPI comms on the oscilloscope. (FYI I have confirmed setup because I see the SPI NOR comms at power up)
  4. I took the kernel out of the picture and compiled in the "sspi" U-Boot command and at the U-Boot prompt I tried to communicate on every bus/device and the only one that I see communication on is the 0.0 SPI NOR. Do I need to enable the other buses and devices somehow?

Ultimately I would like to be able to communicate over any of the SPI buses and devices from Linux running on the ARM.

Thank you.

  • Was playing with the U-boot sspi command some more and noticed a couple of things. Besides the fact that davinci_spi.c is hardcoded to only work with bus 0 and cs 0, which seems weird, it also is hardcoded to always use CONFIG_SYS_SPI_BASE (which is TCI6638_SPI0_BASE). In other words, if a call is placed to spi_setup_slave with bus = 1, it will use bus 0's registers. This might have happened from reusing this code for keystone. Might want to have someone look at that.

  • Hi, Justin,

    That device is to expose SPI to user space, and you can see from the k2hk-evm.dts where spi0 is defined. The default is chip select 0. There is a test partition defined and you can access to it.

    Rex

  • Rex, thanks for the reply. I don't know what you are trying to say. This is the situation: I cannot get any spi communications to work either in the kernel or in U-boot. And in Uboot it is only 0:0 that works.

  • Here's an example of the kind of stuff I am seeing:

    If I do a sf probe 0:2 I see the chip select for slave 0 toggle, not slave 2. But if I do a sspi 0:2 8 f I see nothing toggle at all on the oscope.

  • Justin,

    U-boot only supports bus 0 and cs 0. During spi slave setup, u-boot rejects the setup if bus or cs is non-zero. In Kernel, if you take a look at the dts file as I mentioned earlier, it supports 3 devices, but only the first device is hooked up to NOR. The other 2 are not hooked up to anything. From user space, the NOR device should be access through mtd, not spi. 

    Rex

  • I'm taking the kernel out of the picture here, and just trying to verify SPI communications from U-boot. At first I recompiled U-boot to allow any bus and cs value, as well as assigned the proper SPI base address for each (0x400, 0x600, or 0x800). When that still did not produce results I took a look at exactly what davinci_spi was R/Wing and I am just hand coding that. Please take a look at the following:

    Transmit a word out of 0:0:
    mw 0x21000400 0x00000001 
    mw 0x21000414 0x00000E01 
    mw 0x21000404 0x01000003 
    mw 0x2100043C 0x10000000 
    mw 0x2100043C 0x00000000

    Transmit a word out of 0:2:
    mw 0x21000400 0x00000001
    mw 0x21000414 0x00000E01
    mw 0x21000404 0x01000003
    mw 0x2100043C 0x10020000
    mw 0x2100043C 0x00020000
    When I do the first section for 0:0, I properly see a CS pulse on pin F25 (SPI0CS0). When I do the second section, for 0:2, I also see a CS pulse on F25 not E26. Something is fundamentally wrong it seems.
  • Has anyone had a chance to look at this?  I hope that as opposed to just telling me this is wrong one would explain what the correct sequence of register hits would be to write a simple word across a keystone SPI bus that isn't 0:0.

    Thanks.

  • Justin,

    In both cases, you have 0x0e01 for offset 0x14, why?

    Rex

  • Rex,

    It was a quick hack at seeing the peeks and pokes Uboot did, but I have to admit I'm not even quite sure what all the registers are. I could have overlooked it but the Keystone datasheet doesn't have details. So to answer your question, because that's the value Uboot wrote to it. Can you explain in more detail what I should be doing?

    Thanks,

    Justin

  • Hi, Justin,

    In the datasheet, http://www.ti.com/product/66ak2h06,  there is a link to the SPI user's guide.

    bit n-0 is SCSFUN filed which is SPI chip select pin function. The bit position i in this field determines whether the SPISCS[i] pin is Reserved or a SPI functional pin. Please see the device-specific data manual for supported number of the chip select pins.

    Bit position i in this field:

       0 = The corresponding SPISCS[i] pin is Reserved
       1 = The corresponding SPISCS[i] pin is a SPI functional pin.

    Isn't 0x01 only for cs 0?

     

  • Rex,

    Thanks for pointing that out. I've attached a working uboot davinci driver source file for future reference. 

    Now to the kernel.. In order to see bus 1 slave 2, to give a specific example, in the kernel devices, do I need to actually modify the .dts file? I understand the overall concept of device tree .dts but I've never worked with it.

    /*
     * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
     *
     * Driver for SPI controller on DaVinci. Based on atmel_spi.c
     * by Atmel Corporation
     *
     * Copyright (C) 2007 Atmel Corporation
     *
     * See file CREDITS for list of people who contributed to this
     * project.
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License as
     * published by the Free Software Foundation; either version 2 of
     * the License, or (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
     * MA 02111-1307 USA
     */
    #include <common.h>
    #include <spi.h>
    #include <malloc.h>
    #include <asm/io.h>
    #include <asm/arch/hardware.h>
    #include "davinci_spi.h"
    
    void spi_init()
    {
    	/* do nothing */
    }
    
    struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
    			unsigned int max_hz, unsigned int mode)
    {
        struct davinci_spi_slave        *ds;
    
        if (!spi_cs_is_valid(bus, cs))
            return NULL;
    
        ds = malloc(sizeof(*ds));
        if (!ds)
            return NULL;
    
        ds->slave.bus = bus;
        ds->slave.cs = cs;
    
        // This is a hack to get Keystone EVM working. This can be done better.
        #ifdef TCI6638_SPI0_BASE
        if( bus == 0) {
            ds->regs = (struct davinci_spi_regs *)TCI6638_SPI0_BASE;
        }
        else if( bus == 1) {
            ds->regs = (struct davinci_spi_regs *)TCI6638_SPI1_BASE;
        }
        else if( bus == 2) {
            ds->regs = (struct davinci_spi_regs *)TCI6638_SPI2_BASE;
        }
        else {
            ds->regs = (struct davinci_spi_regs *)TCI6638_SPI0_BASE;
        }
        #else
        ds->regs = (struct davinci_spi_regs *)CONFIG_SYS_SPI_BASE;
        #endif
        ds->freq = max_hz;
    
        return &ds->slave;
    }
    
    void spi_free_slave(struct spi_slave *slave)
    {
    	struct davinci_spi_slave *ds = to_davinci_spi(slave);
    
    	free(ds);
    }
    
    int spi_claim_bus(struct spi_slave *slave)
    {
    	struct davinci_spi_slave *ds = to_davinci_spi(slave);
    	unsigned int scalar;
    
    	/* Enable the SPI hardware */
    	writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0);
    	udelay(1000);
    	writel(SPIGCR0_SPIENA_MASK, &ds->regs->gcr0);
    
    	/* Set master mode, powered up and not activated */
    	writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK, &ds->regs->gcr1);
    
    	/* CS, CLK, SIMO and SOMI are functional pins */
    	writel(( (1 << slave->cs ) | SPIPC0_CLKFUN_MASK |
    		SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK), &ds->regs->pc0);
    
    	/* setup format */
    	scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) - 1) & 0xFF;
    
    	/*
    	 * Use following format:
    	 *   character length = 8,
    	 *   clock signal delayed by half clk cycle,
    	 *   clock low in idle state - Mode 0,
    	 *   MSB shifted out first
    	 */
    	writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) |
    		(1 << SPIFMT_PHASE_SHIFT), &ds->regs->fmt0);
    
    	/*
    	 * Including a minor delay. No science here. Should be good even with
    	 * no delay
    	 */
    	writel((50 << SPI_C2TDELAY_SHIFT) |
    		(50 << SPI_T2CDELAY_SHIFT), &ds->regs->delay);
    
    	/* default chip select register */
    	writel(SPIDEF_CSDEF0_MASK, &ds->regs->def);
    
    	/* no interrupts */
    	writel(0, &ds->regs->int0);
    	writel(0, &ds->regs->lvl);
    
    	/* enable SPI */
    	writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1);
    
    	return 0;
    }
    
    void spi_release_bus(struct spi_slave *slave)
    {
    	struct davinci_spi_slave *ds = to_davinci_spi(slave);
    
    	/* Disable the SPI hardware */
    	writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0);
    }
    
    /*
     * This functions needs to act like a macro to avoid pipeline reloads in the
     * loops below. Use always_inline. This gains us about 160KiB/s and the bloat
     * appears to be zero bytes (da830).
     */
    __attribute__((always_inline))
    static inline u32 davinci_spi_xfer_data(struct davinci_spi_slave *ds, u32 data)
    {
    	u32	buf_reg_val;
    
    	/* send out data */
    	writel(data, &ds->regs->dat1);
    
    	/* wait for the data to clock in/out */
    	while ((buf_reg_val = readl(&ds->regs->buf)) & SPIBUF_RXEMPTY_MASK)
    		;
    
    	return buf_reg_val;
    }
    
    static int davinci_spi_read(struct spi_slave *slave, unsigned int len,
    			    u8 *rxp, unsigned long flags)
    {
    	struct davinci_spi_slave *ds = to_davinci_spi(slave);
    	unsigned int data1_reg_val;
    
    	/* enable CS hold, CS[n] and clear the data bits */
    	data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) |
    			 (slave->cs << SPIDAT1_CSNR_SHIFT));
    
    	/* wait till TXFULL is deasserted */
    	while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK)
    		;
    
    	/* preload the TX buffer to avoid clock starvation */
    	writel(data1_reg_val, &ds->regs->dat1);
    
    	/* keep reading 1 byte until only 1 byte left */
    	while ((len--) > 1)
    		*rxp++ = davinci_spi_xfer_data(ds, data1_reg_val);
    
    	/* clear CS hold when we reach the end */
    	if (flags & SPI_XFER_END)
    		data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT);
    
    	/* read the last byte */
    	*rxp = davinci_spi_xfer_data(ds, data1_reg_val);
    
    	return 0;
    }
    
    static int davinci_spi_write(struct spi_slave *slave, unsigned int len,
    			     const u8 *txp, unsigned long flags)
    {
    	struct davinci_spi_slave *ds = to_davinci_spi(slave);
    	unsigned int data1_reg_val;
    
    	/* enable CS hold and clear the data bits */
    	data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) |
    			 (slave->cs << SPIDAT1_CSNR_SHIFT));
    
    	/* wait till TXFULL is deasserted */
    	while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK)
    		;
    
    	/* preload the TX buffer to avoid clock starvation */
    	if (len > 2) {
    		writel(data1_reg_val | *txp++, &ds->regs->dat1);
    		len--;
    	}
    
    	/* keep writing 1 byte until only 1 byte left */
    	while ((len--) > 1)
    		davinci_spi_xfer_data(ds, data1_reg_val | *txp++);
    
    	/* clear CS hold when we reach the end */
    	if (flags & SPI_XFER_END)
    		data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT);
    
    	/* write the last byte */
    	davinci_spi_xfer_data(ds, data1_reg_val | *txp);
    
    	return 0;
    }
    
    #ifndef CONFIG_SPI_HALF_DUPLEX
    static int davinci_spi_read_write(struct spi_slave *slave, unsigned int len,
    				  u8 *rxp, const u8 *txp, unsigned long flags)
    {
    	struct davinci_spi_slave *ds = to_davinci_spi(slave);
    	unsigned int data1_reg_val;
    
    	/* enable CS hold and clear the data bits */
    	data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) |
    			 (slave->cs << SPIDAT1_CSNR_SHIFT));
    
    	/* wait till TXFULL is deasserted */
    	while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK)
    		;
    
    	/* keep reading and writing 1 byte until only 1 byte left */
    	while ((len--) > 1)
    		*rxp++ = davinci_spi_xfer_data(ds, data1_reg_val | *txp++);
    
    	/* clear CS hold when we reach the end */
    	if (flags & SPI_XFER_END)
    		data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT);
    
    	/* read and write the last byte */
    	*rxp = davinci_spi_xfer_data(ds, data1_reg_val | *txp);
    
    	return 0;
    }
    #endif
    
    int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
    	     const void *dout, void *din, unsigned long flags)
    {
    	unsigned int len;
    
    	if (bitlen == 0)
    		/* Finish any previously submitted transfers */
    		goto out;
    
    	/*
    	 * It's not clear how non-8-bit-aligned transfers are supposed to be
    	 * represented as a stream of bytes...this is a limitation of
    	 * the current SPI interface - here we terminate on receiving such a
    	 * transfer request.
    	 */
    	if (bitlen % 8) {
    		/* Errors always terminate an ongoing transfer */
    		flags |= SPI_XFER_END;
    		goto out;
    	}
    
    	len = bitlen / 8;
    
    	if (!dout)
    		return davinci_spi_read(slave, len, din, flags);
    	else if (!din)
    		return davinci_spi_write(slave, len, dout, flags);
    #ifndef CONFIG_SPI_HALF_DUPLEX
    	else
    		return davinci_spi_read_write(slave, len, din, dout, flags);
    #else
    	printf("SPI full duplex transaction requested with "
    	       "CONFIG_SPI_HALF_DUPLEX defined.\n");
    	flags |= SPI_XFER_END;
    #endif
    
    out:
    	if (flags & SPI_XFER_END) {
    		u8 dummy = 0;
    		davinci_spi_write(slave, 1, &dummy, flags);
    	}
    	return 0;
    }
    
    int spi_cs_is_valid(unsigned int bus, unsigned int cs)
    {
    	//return bus == 0 && cs == 0;
    	return (bus == 0 || bus == 1 || bus == 2) && (cs == 0 || cs == 1 || cs == 2 || cs == 3);
    }
    
    void spi_cs_activate(struct spi_slave *slave)
    {
    	/* do nothing */
    }
    
    void spi_cs_deactivate(struct spi_slave *slave)
    {
    	/* do nothing */
    }
    
    
    

  • Hi, Justin,

    Good to know that u-boot is working now. I'll modify it further so that it works for other TI platforms and we can push it upstream.

    Yes, it is in dts file. In MCSDK 3.0.3.15, it has cs 0 and 1 defined. Check spi0 definition as an example and it has flash and spidev for cs 0 and 1 respectively

    Rex