Skip to content
Snippets Groups Projects
soft_spi.c 4.56 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*
     * (C) Copyright 2002
     * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
     *
     * Influenced by code from:
     * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
     *
     * 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>
    
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    #include <malloc.h>
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*-----------------------------------------------------------------------
     * Definitions
     */
    
    #ifdef DEBUG_SPI
    #define PRINTD(fmt,args...)	printf (fmt ,##args)
    #else
    #define PRINTD(fmt,args...)
    #endif
    
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    struct soft_spi_slave {
    	struct spi_slave slave;
    	unsigned int mode;
    };
    
    static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave)
    {
    	return container_of(slave, struct soft_spi_slave, slave);
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    /*=====================================================================*/
    /*                         Public Functions                            */
    /*=====================================================================*/
    
    /*-----------------------------------------------------------------------
     * Initialization
     */
    void spi_init (void)
    {
    #ifdef	SPI_INIT
    
    	volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	SPI_INIT;
    #endif
    }
    
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
    		unsigned int max_hz, unsigned int mode)
    {
    	struct soft_spi_slave *ss;
    
    	if (!spi_cs_is_valid(bus, cs))
    		return NULL;
    
    	ss = malloc(sizeof(struct soft_spi_slave));
    	if (!ss)
    		return NULL;
    
    	ss->slave.bus = bus;
    	ss->slave.cs = cs;
    	ss->mode = mode;
    
    	/* TODO: Use max_hz to limit the SCK rate */
    
    	return &ss->slave;
    }
    
    void spi_free_slave(struct spi_slave *slave)
    {
    	struct soft_spi_slave *ss = to_soft_spi(slave);
    
    	free(ss);
    }
    
    int spi_claim_bus(struct spi_slave *slave)
    {
    
    #ifdef CONFIG_SYS_IMMR
    	volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    #endif
    	struct soft_spi_slave *ss = to_soft_spi(slave);
    
    	/*
    	 * Make sure the SPI clock is in idle state as defined for
    	 * this slave.
    	 */
    	if (ss->mode & SPI_CPOL)
    		SPI_SCL(1);
    	else
    		SPI_SCL(0);
    
    	return 0;
    }
    
    void spi_release_bus(struct spi_slave *slave)
    {
    	/* Nothing to do */
    }
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    /*-----------------------------------------------------------------------
     * SPI transfer
     *
     * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
     * "bitlen" bits in the SPI MISO port.  That's just the way SPI works.
     *
     * The source of the outgoing bits is the "dout" parameter and the
     * destination of the input bits is the "din" parameter.  Note that "dout"
     * and "din" can point to the same memory location, in which case the
     * input data overwrites the output data (since both are buffered by
     * temporary variables, this is OK).
     */
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    int  spi_xfer(struct spi_slave *slave, unsigned int bitlen,
    		const void *dout, void *din, unsigned long flags)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    #ifdef CONFIG_SYS_IMMR
    	volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    	struct soft_spi_slave *ss = to_soft_spi(slave);
    	uchar		tmpdin  = 0;
    	uchar		tmpdout = 0;
    	const u8	*txd = dout;
    	u8		*rxd = din;
    	int		cpol = ss->mode & SPI_CPOL;
    	int		cpha = ss->mode & SPI_CPHA;
    	unsigned int	j;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    	PRINTD("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
    		slave->bus, slave->cs, *(uint *)txd, *(uint *)rxd, bitlen);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    	if (flags & SPI_XFER_BEGIN)
    		spi_cs_activate(slave);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	for(j = 0; j < bitlen; j++) {
    		/*
    		 * Check if it is time to work on a new byte.
    		 */
    		if((j % 8) == 0) {
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    			tmpdout = *txd++;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			if(j != 0) {
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    				*rxd++ = tmpdin;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			}
    			tmpdin  = 0;
    		}
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    
    		if (!cpha)
    			SPI_SCL(!cpol);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		SPI_SDA(tmpdout & 0x80);
    		SPI_DELAY;
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    		if (cpha)
    			SPI_SCL(!cpol);
    		else
    			SPI_SCL(cpol);
    		tmpdin	<<= 1;
    		tmpdin	|= SPI_READ;
    		tmpdout	<<= 1;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		SPI_DELAY;
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    		if (cpha)
    			SPI_SCL(cpol);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    	/*
    	 * If the number of bits isn't a multiple of 8, shift the last
    	 * bits over to left-justify them.  Then store the last byte
    	 * read in.
    	 */
    	if((bitlen % 8) != 0)
    		tmpdin <<= 8 - (bitlen % 8);
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    	*rxd++ = tmpdin;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Haavard Skinnemoen's avatar
    Haavard Skinnemoen committed
    	if (flags & SPI_XFER_END)
    		spi_cs_deactivate(slave);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	return(0);
    }