| /* | 
 |  * Analog Devices SPI3 controller driver | 
 |  * | 
 |  * Copyright (c) 2011 Analog Devices Inc. | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License version 2 as | 
 |  * published by the Free Software Foundation. | 
 |  * | 
 |  * 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., 675 Mass Ave, Cambridge, MA 02139, USA. | 
 |  */ | 
 |  | 
 | #include <common.h> | 
 | #include <malloc.h> | 
 | #include <spi.h> | 
 |  | 
 | #include <asm/blackfin.h> | 
 | #include <asm/gpio.h> | 
 | #include <asm/portmux.h> | 
 | #include <asm/mach-common/bits/spi6xx.h> | 
 |  | 
 | struct bfin_spi_slave { | 
 | 	struct spi_slave slave; | 
 | 	u32 control, clock; | 
 | 	struct bfin_spi_regs *regs; | 
 | 	int cs_pol; | 
 | }; | 
 |  | 
 | #define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave) | 
 |  | 
 | #define gpio_cs(cs) ((cs) - MAX_CTRL_CS) | 
 | #ifdef CONFIG_BFIN_SPI_GPIO_CS | 
 | # define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS) | 
 | #else | 
 | # define is_gpio_cs(cs) 0 | 
 | #endif | 
 |  | 
 | int spi_cs_is_valid(unsigned int bus, unsigned int cs) | 
 | { | 
 | 	if (is_gpio_cs(cs)) | 
 | 		return gpio_is_valid(gpio_cs(cs)); | 
 | 	else | 
 | 		return (cs >= 1 && cs <= MAX_CTRL_CS); | 
 | } | 
 |  | 
 | void spi_cs_activate(struct spi_slave *slave) | 
 | { | 
 | 	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | 
 |  | 
 | 	if (is_gpio_cs(slave->cs)) { | 
 | 		unsigned int cs = gpio_cs(slave->cs); | 
 | 		gpio_set_value(cs, bss->cs_pol); | 
 | 	} else { | 
 | 		u32 ssel; | 
 | 		ssel = bfin_read32(&bss->regs->ssel); | 
 | 		ssel |= 1 << slave->cs; | 
 | 		if (bss->cs_pol) | 
 | 			ssel |= (1 << 8) << slave->cs; | 
 | 		else | 
 | 			ssel &= ~((1 << 8) << slave->cs); | 
 | 		bfin_write32(&bss->regs->ssel, ssel); | 
 | 	} | 
 |  | 
 | 	SSYNC(); | 
 | } | 
 |  | 
 | void spi_cs_deactivate(struct spi_slave *slave) | 
 | { | 
 | 	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | 
 |  | 
 | 	if (is_gpio_cs(slave->cs)) { | 
 | 		unsigned int cs = gpio_cs(slave->cs); | 
 | 		gpio_set_value(cs, !bss->cs_pol); | 
 | 	} else { | 
 | 		u32 ssel; | 
 | 		ssel = bfin_read32(&bss->regs->ssel); | 
 | 		if (bss->cs_pol) | 
 | 			ssel &= ~((1 << 8) << slave->cs); | 
 | 		else | 
 | 			ssel |= (1 << 8) << slave->cs; | 
 | 		/* deassert cs */ | 
 | 		bfin_write32(&bss->regs->ssel, ssel); | 
 | 		SSYNC(); | 
 | 		/* disable cs */ | 
 | 		ssel &= ~(1 << slave->cs); | 
 | 		bfin_write32(&bss->regs->ssel, ssel); | 
 | 	} | 
 |  | 
 | 	SSYNC(); | 
 | } | 
 |  | 
 | void spi_init() | 
 | { | 
 | } | 
 |  | 
 | #define SPI_PINS(n) \ | 
 | 	{ 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 } | 
 | static unsigned short pins[][5] = { | 
 | #ifdef SPI0_REGBASE | 
 | 	[0] = SPI_PINS(0), | 
 | #endif | 
 | #ifdef SPI1_REGBASE | 
 | 	[1] = SPI_PINS(1), | 
 | #endif | 
 | #ifdef SPI2_REGBASE | 
 | 	[2] = SPI_PINS(2), | 
 | #endif | 
 | }; | 
 |  | 
 | #define SPI_CS_PINS(n) \ | 
 | 	{ \ | 
 | 		P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \ | 
 | 		P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \ | 
 | 		P_SPI##n##_SSEL7, \ | 
 | 	} | 
 | static const unsigned short cs_pins[][7] = { | 
 | #ifdef SPI0_REGBASE | 
 | 	[0] = SPI_CS_PINS(0), | 
 | #endif | 
 | #ifdef SPI1_REGBASE | 
 | 	[1] = SPI_CS_PINS(1), | 
 | #endif | 
 | #ifdef SPI2_REGBASE | 
 | 	[2] = SPI_CS_PINS(2), | 
 | #endif | 
 | }; | 
 |  | 
 | void spi_set_speed(struct spi_slave *slave, uint hz) | 
 | { | 
 | 	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | 
 | 	ulong sclk; | 
 | 	u32 clock; | 
 |  | 
 | 	sclk = get_sclk1(); | 
 | 	clock = sclk / hz; | 
 | 	if (clock) | 
 | 		clock--; | 
 | 	bss->clock = clock; | 
 | } | 
 |  | 
 | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | 
 | 		unsigned int max_hz, unsigned int mode) | 
 | { | 
 | 	struct bfin_spi_slave *bss; | 
 | 	u32 reg_base; | 
 |  | 
 | 	if (!spi_cs_is_valid(bus, cs)) | 
 | 		return NULL; | 
 |  | 
 | 	if (bus >= ARRAY_SIZE(pins) || pins[bus] == NULL) { | 
 | 		debug("%s: invalid bus %u\n", __func__, bus); | 
 | 		return NULL; | 
 | 	} | 
 | 	switch (bus) { | 
 | #ifdef SPI0_REGBASE | 
 | 	case 0: | 
 | 		reg_base = SPI0_REGBASE; | 
 | 		break; | 
 | #endif | 
 | #ifdef SPI1_REGBASE | 
 | 	case 1: | 
 | 		reg_base = SPI1_REGBASE; | 
 | 		break; | 
 | #endif | 
 | #ifdef SPI2_REGBASE | 
 | 	case 2: | 
 | 		reg_base = SPI2_REGBASE; | 
 | 		break; | 
 | #endif | 
 | 	default: | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs); | 
 | 	if (!bss) | 
 | 		return NULL; | 
 |  | 
 | 	bss->regs = (struct bfin_spi_regs *)reg_base; | 
 | 	bss->control = SPI_CTL_EN | SPI_CTL_MSTR; | 
 | 	if (mode & SPI_CPHA) | 
 | 		bss->control |= SPI_CTL_CPHA; | 
 | 	if (mode & SPI_CPOL) | 
 | 		bss->control |= SPI_CTL_CPOL; | 
 | 	if (mode & SPI_LSB_FIRST) | 
 | 		bss->control |= SPI_CTL_LSBF; | 
 | 	bss->control &= ~SPI_CTL_ASSEL; | 
 | 	bss->cs_pol = mode & SPI_CS_HIGH ? 1 : 0; | 
 | 	spi_set_speed(&bss->slave, max_hz); | 
 |  | 
 | 	return &bss->slave; | 
 | } | 
 |  | 
 | void spi_free_slave(struct spi_slave *slave) | 
 | { | 
 | 	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | 
 | 	free(bss); | 
 | } | 
 |  | 
 | int spi_claim_bus(struct spi_slave *slave) | 
 | { | 
 | 	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | 
 |  | 
 | 	debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); | 
 |  | 
 | 	if (is_gpio_cs(slave->cs)) { | 
 | 		unsigned int cs = gpio_cs(slave->cs); | 
 | 		gpio_request(cs, "bfin-spi"); | 
 | 		gpio_direction_output(cs, !bss->cs_pol); | 
 | 		pins[slave->bus][0] = P_DONTCARE; | 
 | 	} else | 
 | 		pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1]; | 
 | 	peripheral_request_list(pins[slave->bus], "bfin-spi"); | 
 |  | 
 | 	bfin_write32(&bss->regs->control, bss->control); | 
 | 	bfin_write32(&bss->regs->clock, bss->clock); | 
 | 	bfin_write32(&bss->regs->delay, 0x0); | 
 | 	bfin_write32(&bss->regs->rx_control, SPI_RXCTL_REN); | 
 | 	bfin_write32(&bss->regs->tx_control, SPI_TXCTL_TEN | SPI_TXCTL_TTI); | 
 | 	SSYNC(); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | void spi_release_bus(struct spi_slave *slave) | 
 | { | 
 | 	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | 
 |  | 
 | 	debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); | 
 |  | 
 | 	peripheral_free_list(pins[slave->bus]); | 
 | 	if (is_gpio_cs(slave->cs)) | 
 | 		gpio_free(gpio_cs(slave->cs)); | 
 |  | 
 | 	bfin_write32(&bss->regs->rx_control, 0x0); | 
 | 	bfin_write32(&bss->regs->tx_control, 0x0); | 
 | 	bfin_write32(&bss->regs->control, 0x0); | 
 | 	SSYNC(); | 
 | } | 
 |  | 
 | #ifndef CONFIG_BFIN_SPI_IDLE_VAL | 
 | # define CONFIG_BFIN_SPI_IDLE_VAL 0xff | 
 | #endif | 
 |  | 
 | static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, | 
 | 			uint bytes) | 
 | { | 
 | 	/* discard invalid rx data and empty rfifo */ | 
 | 	while (!(bfin_read32(&bss->regs->status) & SPI_STAT_RFE)) | 
 | 		bfin_read32(&bss->regs->rfifo); | 
 |  | 
 | 	while (bytes--) { | 
 | 		u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL); | 
 | 		debug("%s: tx:%x ", __func__, value); | 
 | 		bfin_write32(&bss->regs->tfifo, value); | 
 | 		SSYNC(); | 
 | 		while (bfin_read32(&bss->regs->status) & SPI_STAT_RFE) | 
 | 			if (ctrlc()) | 
 | 				return -1; | 
 | 		value = bfin_read32(&bss->regs->rfifo); | 
 | 		if (rx) | 
 | 			*rx++ = value; | 
 | 		debug("rx:%x\n", value); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, | 
 | 		void *din, unsigned long flags) | 
 | { | 
 | 	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); | 
 | 	const u8 *tx = dout; | 
 | 	u8 *rx = din; | 
 | 	uint bytes = bitlen / 8; | 
 | 	int ret = 0; | 
 |  | 
 | 	debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, | 
 | 		slave->bus, slave->cs, bitlen, bytes, flags); | 
 |  | 
 | 	if (bitlen == 0) | 
 | 		goto done; | 
 |  | 
 | 	/* we can only do 8 bit transfers */ | 
 | 	if (bitlen % 8) { | 
 | 		flags |= SPI_XFER_END; | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	if (flags & SPI_XFER_BEGIN) | 
 | 		spi_cs_activate(slave); | 
 |  | 
 | 	ret = spi_pio_xfer(bss, tx, rx, bytes); | 
 |  | 
 |  done: | 
 | 	if (flags & SPI_XFER_END) | 
 | 		spi_cs_deactivate(slave); | 
 |  | 
 | 	return ret; | 
 | } |