1 /* 2 * Copyright (C) 2007 Atmel Corporation 3 * 4 * See file CREDITS for list of people who contributed to this 5 * project. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 20 * MA 02111-1307 USA 21 */ 22 #include <common.h> 23 #include <spi.h> 24 #include <malloc.h> 25 26 #include <asm/io.h> 27 28 #include <asm/arch/clk.h> 29 #include <asm/arch/hardware.h> 30 31 #include "atmel_spi.h" 32 33 static int spi_has_wdrbt(struct atmel_spi_slave *slave) 34 { 35 unsigned int ver; 36 37 ver = spi_readl(slave, VERSION); 38 39 return (ATMEL_SPI_VERSION_REV(ver) >= 0x210); 40 } 41 42 void spi_init() 43 { 44 45 } 46 47 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, 48 unsigned int max_hz, unsigned int mode) 49 { 50 struct atmel_spi_slave *as; 51 unsigned int scbr; 52 u32 csrx; 53 void *regs; 54 55 if (!spi_cs_is_valid(bus, cs)) 56 return NULL; 57 58 switch (bus) { 59 case 0: 60 regs = (void *)ATMEL_BASE_SPI0; 61 break; 62 #ifdef ATMEL_BASE_SPI1 63 case 1: 64 regs = (void *)ATMEL_BASE_SPI1; 65 break; 66 #endif 67 #ifdef ATMEL_BASE_SPI2 68 case 2: 69 regs = (void *)ATMEL_BASE_SPI2; 70 break; 71 #endif 72 #ifdef ATMEL_BASE_SPI3 73 case 3: 74 regs = (void *)ATMEL_BASE_SPI3; 75 break; 76 #endif 77 default: 78 return NULL; 79 } 80 81 82 scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz; 83 if (scbr > ATMEL_SPI_CSRx_SCBR_MAX) 84 /* Too low max SCK rate */ 85 return NULL; 86 if (scbr < 1) 87 scbr = 1; 88 89 csrx = ATMEL_SPI_CSRx_SCBR(scbr); 90 csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8); 91 if (!(mode & SPI_CPHA)) 92 csrx |= ATMEL_SPI_CSRx_NCPHA; 93 if (mode & SPI_CPOL) 94 csrx |= ATMEL_SPI_CSRx_CPOL; 95 96 as = spi_alloc_slave(struct atmel_spi_slave, bus, cs); 97 if (!as) 98 return NULL; 99 100 as->regs = regs; 101 as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS 102 | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf); 103 if (spi_has_wdrbt(as)) 104 as->mr |= ATMEL_SPI_MR_WDRBT; 105 106 spi_writel(as, CSR(cs), csrx); 107 108 return &as->slave; 109 } 110 111 void spi_free_slave(struct spi_slave *slave) 112 { 113 struct atmel_spi_slave *as = to_atmel_spi(slave); 114 115 free(as); 116 } 117 118 int spi_claim_bus(struct spi_slave *slave) 119 { 120 struct atmel_spi_slave *as = to_atmel_spi(slave); 121 122 /* Enable the SPI hardware */ 123 spi_writel(as, CR, ATMEL_SPI_CR_SPIEN); 124 125 /* 126 * Select the slave. This should set SCK to the correct 127 * initial state, etc. 128 */ 129 spi_writel(as, MR, as->mr); 130 131 return 0; 132 } 133 134 void spi_release_bus(struct spi_slave *slave) 135 { 136 struct atmel_spi_slave *as = to_atmel_spi(slave); 137 138 /* Disable the SPI hardware */ 139 spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS); 140 } 141 142 int spi_xfer(struct spi_slave *slave, unsigned int bitlen, 143 const void *dout, void *din, unsigned long flags) 144 { 145 struct atmel_spi_slave *as = to_atmel_spi(slave); 146 unsigned int len_tx; 147 unsigned int len_rx; 148 unsigned int len; 149 u32 status; 150 const u8 *txp = dout; 151 u8 *rxp = din; 152 u8 value; 153 154 if (bitlen == 0) 155 /* Finish any previously submitted transfers */ 156 goto out; 157 158 /* 159 * TODO: The controller can do non-multiple-of-8 bit 160 * transfers, but this driver currently doesn't support it. 161 * 162 * It's also not clear how such transfers are supposed to be 163 * represented as a stream of bytes...this is a limitation of 164 * the current SPI interface. 165 */ 166 if (bitlen % 8) { 167 /* Errors always terminate an ongoing transfer */ 168 flags |= SPI_XFER_END; 169 goto out; 170 } 171 172 len = bitlen / 8; 173 174 /* 175 * The controller can do automatic CS control, but it is 176 * somewhat quirky, and it doesn't really buy us much anyway 177 * in the context of U-Boot. 178 */ 179 if (flags & SPI_XFER_BEGIN) { 180 spi_cs_activate(slave); 181 /* 182 * sometimes the RDR is not empty when we get here, 183 * in theory that should not happen, but it DOES happen. 184 * Read it here to be on the safe side. 185 * That also clears the OVRES flag. Required if the 186 * following loop exits due to OVRES! 187 */ 188 spi_readl(as, RDR); 189 } 190 191 for (len_tx = 0, len_rx = 0; len_rx < len; ) { 192 status = spi_readl(as, SR); 193 194 if (status & ATMEL_SPI_SR_OVRES) 195 return -1; 196 197 if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) { 198 if (txp) 199 value = *txp++; 200 else 201 value = 0; 202 spi_writel(as, TDR, value); 203 len_tx++; 204 } 205 if (status & ATMEL_SPI_SR_RDRF) { 206 value = spi_readl(as, RDR); 207 if (rxp) 208 *rxp++ = value; 209 len_rx++; 210 } 211 } 212 213 out: 214 if (flags & SPI_XFER_END) { 215 /* 216 * Wait until the transfer is completely done before 217 * we deactivate CS. 218 */ 219 do { 220 status = spi_readl(as, SR); 221 } while (!(status & ATMEL_SPI_SR_TXEMPTY)); 222 223 spi_cs_deactivate(slave); 224 } 225 226 return 0; 227 } 228