view target-utils/libbase/spidrv.c @ 408:f0e6dd5971f2

CHANGES: fcup-* minor fixes documented
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 11 Aug 2018 22:36:26 +0000
parents e7502631a0f9
children 3d73d4d3527f
line wrap: on
line source

/* Driver for SPI Master Controller inside TI Calypso */
/* lifted from OsmocomBB and ported to FreeCalypso target-utils environment */

/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
 *
 * All Rights Reserved
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#include "types.h"

#define	ASIC_CONF_REG	(*(volatile u16 *) 0xFFFEF008)

struct spi_regs {
	u16	reg_set1;
	u16	reg_set2;
	u16	reg_ctrl;
	u16	reg_status;
	u16	reg_tx_lsb;
	u16	reg_tx_msb;
	u16	reg_rx_lsb;
	u16	reg_rx_msb;
};

#define	SPI_REGS	(*(volatile struct spi_regs *) 0xFFFE3000)

#define BASE_ADDR_SPI	0xfffe3000
#define SPI_REG(n)	(BASE_ADDR_SPI+(n))

#define SPI_SET1_EN_CLK		(1 << 0)
#define SPI_SET1_WR_IRQ_DIS	(1 << 4)
#define SPI_SET1_RDWR_IRQ_DIS	(1 << 5)

#define SPI_CTRL_RDWR		(1 << 0)
#define SPI_CTRL_WR		(1 << 1)
#define SPI_CTRL_NB_SHIFT	2
#define SPI_CTRL_AD_SHIFT	7

#define SPI_STATUS_RE		(1 << 0)	/* Read End */
#define SPI_STATUS_WE		(1 << 1)	/* Write End */

spi_init()
{
	static int initdone;

	if (initdone)
		return(0);
	ASIC_CONF_REG |= 0x6000;
	SPI_REGS.reg_set1 = SPI_SET1_EN_CLK | SPI_SET1_WR_IRQ_DIS |
				SPI_SET1_RDWR_IRQ_DIS;
	SPI_REGS.reg_set2 = 0x0001;
	initdone = 1;
	return(1);
}

spi_xfer(dev_idx, bitlen, dout, din)
	void *dout, *din;
{
	int bytes_per_xfer;
	u16 reg_status, reg_ctrl = 0;
	u32 tmp;

	if (bitlen <= 0)
		return 0;

	if (bitlen > 32)
		return -1;

	if (dev_idx > 4)
		return -1;

	bytes_per_xfer = bitlen / 8;
	if (bitlen % 8)
		bytes_per_xfer ++;

	reg_ctrl |= (bitlen - 1) << SPI_CTRL_NB_SHIFT;
	reg_ctrl |= (dev_idx & 0x7) << SPI_CTRL_AD_SHIFT;

	if (bitlen <= 8) {
		tmp = *(u8 *)dout;
		tmp <<= 24 + (8-bitlen);	/* align to MSB */
	} else if (bitlen <= 16) {
		tmp = *(u16 *)dout;
		tmp <<= 16 + (16-bitlen);	/* align to MSB */
	} else {
		tmp = *(u32 *)dout;
		tmp <<= (32-bitlen);		/* align to MSB */
	}

	/* fill transmit registers */
	SPI_REGS.reg_tx_msb = tmp >> 16;
	SPI_REGS.reg_tx_lsb = tmp;

	/* initiate transfer */
	if (din)
		reg_ctrl |= SPI_CTRL_RDWR;
	else
		reg_ctrl |= SPI_CTRL_WR;
	SPI_REGS.reg_ctrl = reg_ctrl;

	/* wait until the transfer is complete */
	while (1) {
		reg_status = SPI_REGS.reg_status;
		if (din && (reg_status & SPI_STATUS_RE))
			break;
		else if (reg_status & SPI_STATUS_WE)
			break;
	}
	/* FIXME: calibrate how much delay we really need (seven 13MHz cycles) */
	osmo_delay_ms(1);

	if (din) {
		tmp = SPI_REGS.reg_rx_msb << 16;
		tmp |= SPI_REGS.reg_rx_lsb;

		if (bitlen <= 8)
			*(u8 *)din = tmp & 0xff;
		else if (bitlen <= 16)
			*(u16 *)din = tmp & 0xffff;
		else
			*(u32 *)din = tmp;
	}

	return 0;
}