diff target-utils/libbase/spidrv.c @ 991:5cff3579814c

target-utils: libbase factored out of libcommon The library dependency order is now strictly unidirectional
author Mychaela Falconia <falcon@ivan.Harhan.ORG>
date Wed, 30 Dec 2015 20:48:12 +0000
parents target-utils/libcommon/spidrv.c@e60aecf23970
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libbase/spidrv.c	Wed Dec 30 20:48:12 2015 +0000
@@ -0,0 +1,139 @@
+/* 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;
+}