160445cb5SHans-Christian Egtvedt /* 260445cb5SHans-Christian Egtvedt * Copyright (C) 2007 Atmel Corporation 360445cb5SHans-Christian Egtvedt * 460445cb5SHans-Christian Egtvedt * See file CREDITS for list of people who contributed to this 560445cb5SHans-Christian Egtvedt * project. 660445cb5SHans-Christian Egtvedt * 760445cb5SHans-Christian Egtvedt * This program is free software; you can redistribute it and/or 860445cb5SHans-Christian Egtvedt * modify it under the terms of the GNU General Public License as 960445cb5SHans-Christian Egtvedt * published by the Free Software Foundation; either version 2 of 1060445cb5SHans-Christian Egtvedt * the License, or (at your option) any later version. 1160445cb5SHans-Christian Egtvedt * 1260445cb5SHans-Christian Egtvedt * This program is distributed in the hope that it will be useful, 1360445cb5SHans-Christian Egtvedt * but WITHOUT ANY WARRANTY; without even the implied warranty of 1460445cb5SHans-Christian Egtvedt * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1560445cb5SHans-Christian Egtvedt * GNU General Public License for more details. 1660445cb5SHans-Christian Egtvedt * 1760445cb5SHans-Christian Egtvedt * You should have received a copy of the GNU General Public License 1860445cb5SHans-Christian Egtvedt * along with this program; if not, write to the Free Software 1960445cb5SHans-Christian Egtvedt * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 2060445cb5SHans-Christian Egtvedt * MA 02111-1307 USA 2160445cb5SHans-Christian Egtvedt */ 2260445cb5SHans-Christian Egtvedt #include <common.h> 2360445cb5SHans-Christian Egtvedt #include <spi.h> 2460445cb5SHans-Christian Egtvedt #include <malloc.h> 2560445cb5SHans-Christian Egtvedt 2660445cb5SHans-Christian Egtvedt #include <asm/io.h> 2760445cb5SHans-Christian Egtvedt 2860445cb5SHans-Christian Egtvedt #include <asm/arch/clk.h> 29*329f0f52SReinhard Meyer #include <asm/arch/hardware.h> 3060445cb5SHans-Christian Egtvedt 3160445cb5SHans-Christian Egtvedt #include "atmel_spi.h" 3260445cb5SHans-Christian Egtvedt 3360445cb5SHans-Christian Egtvedt void spi_init() 3460445cb5SHans-Christian Egtvedt { 3560445cb5SHans-Christian Egtvedt 3660445cb5SHans-Christian Egtvedt } 3760445cb5SHans-Christian Egtvedt 3860445cb5SHans-Christian Egtvedt struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, 3960445cb5SHans-Christian Egtvedt unsigned int max_hz, unsigned int mode) 4060445cb5SHans-Christian Egtvedt { 4160445cb5SHans-Christian Egtvedt struct atmel_spi_slave *as; 4260445cb5SHans-Christian Egtvedt unsigned int scbr; 4360445cb5SHans-Christian Egtvedt u32 csrx; 4460445cb5SHans-Christian Egtvedt void *regs; 4560445cb5SHans-Christian Egtvedt 46f09d3b28SReinhard Meyer if (!spi_cs_is_valid(bus, cs)) 4760445cb5SHans-Christian Egtvedt return NULL; 4860445cb5SHans-Christian Egtvedt 4960445cb5SHans-Christian Egtvedt switch (bus) { 5060445cb5SHans-Christian Egtvedt case 0: 51*329f0f52SReinhard Meyer regs = (void *)ATMEL_BASE_SPI0; 5260445cb5SHans-Christian Egtvedt break; 53*329f0f52SReinhard Meyer #ifdef ATMEL_BASE_SPI1 5460445cb5SHans-Christian Egtvedt case 1: 55*329f0f52SReinhard Meyer regs = (void *)ATMEL_BASE_SPI1; 5660445cb5SHans-Christian Egtvedt break; 5760445cb5SHans-Christian Egtvedt #endif 58*329f0f52SReinhard Meyer #ifdef ATMEL_BASE_SPI2 5960445cb5SHans-Christian Egtvedt case 2: 60*329f0f52SReinhard Meyer regs = (void *)ATMEL_BASE_SPI2; 6160445cb5SHans-Christian Egtvedt break; 6260445cb5SHans-Christian Egtvedt #endif 63*329f0f52SReinhard Meyer #ifdef ATMEL_BASE_SPI3 6460445cb5SHans-Christian Egtvedt case 3: 65*329f0f52SReinhard Meyer regs = (void *)ATMEL_BASE_SPI3; 6660445cb5SHans-Christian Egtvedt break; 6760445cb5SHans-Christian Egtvedt #endif 6860445cb5SHans-Christian Egtvedt default: 6960445cb5SHans-Christian Egtvedt return NULL; 7060445cb5SHans-Christian Egtvedt } 7160445cb5SHans-Christian Egtvedt 7260445cb5SHans-Christian Egtvedt 7360445cb5SHans-Christian Egtvedt scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz; 7460445cb5SHans-Christian Egtvedt if (scbr > ATMEL_SPI_CSRx_SCBR_MAX) 7560445cb5SHans-Christian Egtvedt /* Too low max SCK rate */ 7660445cb5SHans-Christian Egtvedt return NULL; 7760445cb5SHans-Christian Egtvedt if (scbr < 1) 7860445cb5SHans-Christian Egtvedt scbr = 1; 7960445cb5SHans-Christian Egtvedt 8060445cb5SHans-Christian Egtvedt csrx = ATMEL_SPI_CSRx_SCBR(scbr); 8160445cb5SHans-Christian Egtvedt csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8); 8260445cb5SHans-Christian Egtvedt if (!(mode & SPI_CPHA)) 8360445cb5SHans-Christian Egtvedt csrx |= ATMEL_SPI_CSRx_NCPHA; 8460445cb5SHans-Christian Egtvedt if (mode & SPI_CPOL) 8560445cb5SHans-Christian Egtvedt csrx |= ATMEL_SPI_CSRx_CPOL; 8660445cb5SHans-Christian Egtvedt 8760445cb5SHans-Christian Egtvedt as = malloc(sizeof(struct atmel_spi_slave)); 8860445cb5SHans-Christian Egtvedt if (!as) 8960445cb5SHans-Christian Egtvedt return NULL; 9060445cb5SHans-Christian Egtvedt 9160445cb5SHans-Christian Egtvedt as->slave.bus = bus; 9260445cb5SHans-Christian Egtvedt as->slave.cs = cs; 9360445cb5SHans-Christian Egtvedt as->regs = regs; 9460445cb5SHans-Christian Egtvedt as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS 9560445cb5SHans-Christian Egtvedt | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); 9660445cb5SHans-Christian Egtvedt spi_writel(as, CSR(cs), csrx); 9760445cb5SHans-Christian Egtvedt 9860445cb5SHans-Christian Egtvedt return &as->slave; 9960445cb5SHans-Christian Egtvedt } 10060445cb5SHans-Christian Egtvedt 10160445cb5SHans-Christian Egtvedt void spi_free_slave(struct spi_slave *slave) 10260445cb5SHans-Christian Egtvedt { 10360445cb5SHans-Christian Egtvedt struct atmel_spi_slave *as = to_atmel_spi(slave); 10460445cb5SHans-Christian Egtvedt 10560445cb5SHans-Christian Egtvedt free(as); 10660445cb5SHans-Christian Egtvedt } 10760445cb5SHans-Christian Egtvedt 10860445cb5SHans-Christian Egtvedt int spi_claim_bus(struct spi_slave *slave) 10960445cb5SHans-Christian Egtvedt { 11060445cb5SHans-Christian Egtvedt struct atmel_spi_slave *as = to_atmel_spi(slave); 11160445cb5SHans-Christian Egtvedt 11260445cb5SHans-Christian Egtvedt /* Enable the SPI hardware */ 11360445cb5SHans-Christian Egtvedt spi_writel(as, CR, ATMEL_SPI_CR_SPIEN); 11460445cb5SHans-Christian Egtvedt 11560445cb5SHans-Christian Egtvedt /* 11660445cb5SHans-Christian Egtvedt * Select the slave. This should set SCK to the correct 11760445cb5SHans-Christian Egtvedt * initial state, etc. 11860445cb5SHans-Christian Egtvedt */ 11960445cb5SHans-Christian Egtvedt spi_writel(as, MR, as->mr); 12060445cb5SHans-Christian Egtvedt 12160445cb5SHans-Christian Egtvedt return 0; 12260445cb5SHans-Christian Egtvedt } 12360445cb5SHans-Christian Egtvedt 12460445cb5SHans-Christian Egtvedt void spi_release_bus(struct spi_slave *slave) 12560445cb5SHans-Christian Egtvedt { 12660445cb5SHans-Christian Egtvedt struct atmel_spi_slave *as = to_atmel_spi(slave); 12760445cb5SHans-Christian Egtvedt 12860445cb5SHans-Christian Egtvedt /* Disable the SPI hardware */ 12960445cb5SHans-Christian Egtvedt spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS); 13060445cb5SHans-Christian Egtvedt } 13160445cb5SHans-Christian Egtvedt 13260445cb5SHans-Christian Egtvedt int spi_xfer(struct spi_slave *slave, unsigned int bitlen, 13360445cb5SHans-Christian Egtvedt const void *dout, void *din, unsigned long flags) 13460445cb5SHans-Christian Egtvedt { 13560445cb5SHans-Christian Egtvedt struct atmel_spi_slave *as = to_atmel_spi(slave); 13660445cb5SHans-Christian Egtvedt unsigned int len_tx; 13760445cb5SHans-Christian Egtvedt unsigned int len_rx; 13860445cb5SHans-Christian Egtvedt unsigned int len; 13960445cb5SHans-Christian Egtvedt int ret; 14060445cb5SHans-Christian Egtvedt u32 status; 14160445cb5SHans-Christian Egtvedt const u8 *txp = dout; 14260445cb5SHans-Christian Egtvedt u8 *rxp = din; 14360445cb5SHans-Christian Egtvedt u8 value; 14460445cb5SHans-Christian Egtvedt 14560445cb5SHans-Christian Egtvedt ret = 0; 14660445cb5SHans-Christian Egtvedt if (bitlen == 0) 14760445cb5SHans-Christian Egtvedt /* Finish any previously submitted transfers */ 14860445cb5SHans-Christian Egtvedt goto out; 14960445cb5SHans-Christian Egtvedt 15060445cb5SHans-Christian Egtvedt /* 15160445cb5SHans-Christian Egtvedt * TODO: The controller can do non-multiple-of-8 bit 15260445cb5SHans-Christian Egtvedt * transfers, but this driver currently doesn't support it. 15360445cb5SHans-Christian Egtvedt * 15460445cb5SHans-Christian Egtvedt * It's also not clear how such transfers are supposed to be 15560445cb5SHans-Christian Egtvedt * represented as a stream of bytes...this is a limitation of 15660445cb5SHans-Christian Egtvedt * the current SPI interface. 15760445cb5SHans-Christian Egtvedt */ 15860445cb5SHans-Christian Egtvedt if (bitlen % 8) { 15960445cb5SHans-Christian Egtvedt /* Errors always terminate an ongoing transfer */ 16060445cb5SHans-Christian Egtvedt flags |= SPI_XFER_END; 16160445cb5SHans-Christian Egtvedt goto out; 16260445cb5SHans-Christian Egtvedt } 16360445cb5SHans-Christian Egtvedt 16460445cb5SHans-Christian Egtvedt len = bitlen / 8; 16560445cb5SHans-Christian Egtvedt 16660445cb5SHans-Christian Egtvedt /* 16760445cb5SHans-Christian Egtvedt * The controller can do automatic CS control, but it is 16860445cb5SHans-Christian Egtvedt * somewhat quirky, and it doesn't really buy us much anyway 16960445cb5SHans-Christian Egtvedt * in the context of U-Boot. 17060445cb5SHans-Christian Egtvedt */ 171f09d3b28SReinhard Meyer if (flags & SPI_XFER_BEGIN) { 17260445cb5SHans-Christian Egtvedt spi_cs_activate(slave); 173f09d3b28SReinhard Meyer /* 174f09d3b28SReinhard Meyer * sometimes the RDR is not empty when we get here, 175f09d3b28SReinhard Meyer * in theory that should not happen, but it DOES happen. 176f09d3b28SReinhard Meyer * Read it here to be on the safe side. 177f09d3b28SReinhard Meyer * That also clears the OVRES flag. Required if the 178f09d3b28SReinhard Meyer * following loop exits due to OVRES! 179f09d3b28SReinhard Meyer */ 180f09d3b28SReinhard Meyer spi_readl(as, RDR); 181f09d3b28SReinhard Meyer } 18260445cb5SHans-Christian Egtvedt 18360445cb5SHans-Christian Egtvedt for (len_tx = 0, len_rx = 0; len_rx < len; ) { 18460445cb5SHans-Christian Egtvedt status = spi_readl(as, SR); 18560445cb5SHans-Christian Egtvedt 18660445cb5SHans-Christian Egtvedt if (status & ATMEL_SPI_SR_OVRES) 18760445cb5SHans-Christian Egtvedt return -1; 18860445cb5SHans-Christian Egtvedt 18960445cb5SHans-Christian Egtvedt if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) { 19060445cb5SHans-Christian Egtvedt if (txp) 19160445cb5SHans-Christian Egtvedt value = *txp++; 19260445cb5SHans-Christian Egtvedt else 19360445cb5SHans-Christian Egtvedt value = 0; 19460445cb5SHans-Christian Egtvedt spi_writel(as, TDR, value); 19560445cb5SHans-Christian Egtvedt len_tx++; 19660445cb5SHans-Christian Egtvedt } 19760445cb5SHans-Christian Egtvedt if (status & ATMEL_SPI_SR_RDRF) { 19860445cb5SHans-Christian Egtvedt value = spi_readl(as, RDR); 19960445cb5SHans-Christian Egtvedt if (rxp) 20060445cb5SHans-Christian Egtvedt *rxp++ = value; 20160445cb5SHans-Christian Egtvedt len_rx++; 20260445cb5SHans-Christian Egtvedt } 20360445cb5SHans-Christian Egtvedt } 20460445cb5SHans-Christian Egtvedt 20560445cb5SHans-Christian Egtvedt out: 20660445cb5SHans-Christian Egtvedt if (flags & SPI_XFER_END) { 20760445cb5SHans-Christian Egtvedt /* 20860445cb5SHans-Christian Egtvedt * Wait until the transfer is completely done before 20960445cb5SHans-Christian Egtvedt * we deactivate CS. 21060445cb5SHans-Christian Egtvedt */ 21160445cb5SHans-Christian Egtvedt do { 21260445cb5SHans-Christian Egtvedt status = spi_readl(as, SR); 21360445cb5SHans-Christian Egtvedt } while (!(status & ATMEL_SPI_SR_TXEMPTY)); 21460445cb5SHans-Christian Egtvedt 21560445cb5SHans-Christian Egtvedt spi_cs_deactivate(slave); 21660445cb5SHans-Christian Egtvedt } 21760445cb5SHans-Christian Egtvedt 21860445cb5SHans-Christian Egtvedt return 0; 21960445cb5SHans-Christian Egtvedt } 220