1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
|
/*
* SPI Driver for EP93xx
*
* Copyright (C) 2013 Sergey Kostanabev <sergey.kostanbaev <at> fairwaves.ru>
*
* Inspired form linux kernel driver and atmel uboot driver
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/ep93xx.h>
#define SSPBASE SPI_BASE
#define SSPCR0 0x0000
#define SSPCR0_MODE_SHIFT 6
#define SSPCR0_SCR_SHIFT 8
#define SSPCR0_SPH BIT(7)
#define SSPCR0_SPO BIT(6)
#define SSPCR0_FRF_SPI 0
#define SSPCR0_DSS_8BIT 7
#define SSPCR1 0x0004
#define SSPCR1_RIE BIT(0)
#define SSPCR1_TIE BIT(1)
#define SSPCR1_RORIE BIT(2)
#define SSPCR1_LBM BIT(3)
#define SSPCR1_SSE BIT(4)
#define SSPCR1_MS BIT(5)
#define SSPCR1_SOD BIT(6)
#define SSPDR 0x0008
#define SSPSR 0x000c
#define SSPSR_TFE BIT(0)
#define SSPSR_TNF BIT(1)
#define SSPSR_RNE BIT(2)
#define SSPSR_RFF BIT(3)
#define SSPSR_BSY BIT(4)
#define SSPCPSR 0x0010
#define SSPIIR 0x0014
#define SSPIIR_RIS BIT(0)
#define SSPIIR_TIS BIT(1)
#define SSPIIR_RORIS BIT(2)
#define SSPICR SSPIIR
#define SSPCLOCK 14745600
#define SSP_MAX_RATE (SSPCLOCK / 2)
#define SSP_MIN_RATE (SSPCLOCK / (254 * 256))
/* timeout in milliseconds */
#define SPI_TIMEOUT 5
/* maximum depth of RX/TX FIFO */
#define SPI_FIFO_SIZE 8
struct ep93xx_spi_slave {
struct spi_slave slave;
unsigned sspcr0;
unsigned sspcpsr;
};
static inline struct ep93xx_spi_slave *to_ep93xx_spi(struct spi_slave *slave)
{
return container_of(slave, struct ep93xx_spi_slave, slave);
}
void spi_init()
{
}
static inline void ep93xx_spi_write_u8(u16 reg, u8 value)
{
writel(value, (unsigned int *)(SSPBASE + reg));
}
static inline u8 ep93xx_spi_read_u8(u16 reg)
{
return readl((unsigned int *)(SSPBASE + reg));
}
static inline void ep93xx_spi_write_u16(u16 reg, u16 value)
{
writel(value, (unsigned int *)(SSPBASE + reg));
}
static inline u16 ep93xx_spi_read_u16(u16 reg)
{
return (u16)readl((unsigned int *)(SSPBASE + reg));
}
static int ep93xx_spi_init_hw(unsigned int rate, unsigned int mode,
struct ep93xx_spi_slave *slave)
{
unsigned cpsr, scr;
if (rate > SSP_MAX_RATE)
rate = SSP_MAX_RATE;
if (rate < SSP_MIN_RATE)
return -1;
/* Calculate divisors so that we can get speed according the
* following formula:
* rate = spi_clock_rate / (cpsr * (1 + scr))
*
* cpsr must be even number and starts from 2, scr can be any number
* between 0 and 255.
*/
for (cpsr = 2; cpsr <= 254; cpsr += 2) {
for (scr = 0; scr <= 255; scr++) {
if ((SSPCLOCK / (cpsr * (scr + 1))) <= rate) {
/* Set CHPA and CPOL, SPI format and 8bit */
unsigned sspcr0 = (scr << SSPCR0_SCR_SHIFT) |
SSPCR0_FRF_SPI | SSPCR0_DSS_8BIT;
if (mode & SPI_CPHA)
sspcr0 |= SSPCR0_SPH;
if (mode & SPI_CPOL)
sspcr0 |= SSPCR0_SPO;
slave->sspcr0 = sspcr0;
slave->sspcpsr = cpsr;
return 0;
}
}
}
return -1;
}
void spi_set_speed(struct spi_slave *slave, unsigned int hz)
{
struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
unsigned int mode = 0;
if (as->sspcr0 & SSPCR0_SPH)
mode |= SPI_CPHA;
if (as->sspcr0 & SSPCR0_SPO)
mode |= SPI_CPOL;
ep93xx_spi_init_hw(hz, mode, as);
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct ep93xx_spi_slave *as;
if (!spi_cs_is_valid(bus, cs))
return NULL;
as = spi_alloc_slave(struct ep93xx_spi_slave, bus, cs);
if (!as)
return NULL;
if (ep93xx_spi_init_hw(max_hz, mode, as)) {
free(as);
return NULL;
}
return &as->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
free(as);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
/* Enable the SPI hardware */
ep93xx_spi_write_u8(SSPCR1, SSPCR1_SSE);
ep93xx_spi_write_u8(SSPCPSR, as->sspcpsr);
ep93xx_spi_write_u16(SSPCR0, as->sspcr0);
debug("Select CS:%d SSPCPSR=%02x SSPCR0=%04x\n",
slave->cs, as->sspcpsr, as->sspcr0);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
/* Disable the SPI hardware */
ep93xx_spi_write_u8(SSPCR1, 0);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
unsigned int len_tx;
unsigned int len_rx;
unsigned int len;
u32 status;
const u8 *txp = dout;
u8 *rxp = din;
u8 value;
debug("spi_xfer: slave %u:%u dout %p din %p bitlen %u\n",
slave->bus, slave->cs, (uint *)dout, (uint *)din, bitlen);
if (bitlen == 0)
/* Finish any previously submitted transfers */
goto out;
if (bitlen % 8) {
/* Errors always terminate an ongoing transfer */
flags |= SPI_XFER_END;
goto out;
}
len = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* Empty RX FIFO */
while ((ep93xx_spi_read_u8(SSPSR) & SSPSR_RNE))
ep93xx_spi_read_u8(SSPDR);
spi_cs_activate(slave);
}
for (len_tx = 0, len_rx = 0; len_rx < len; ) {
status = ep93xx_spi_read_u8(SSPSR);
if ((len_tx < len) && (status & SSPSR_TNF)) {
if (txp)
value = *txp++;
else
value = 0xff;
ep93xx_spi_write_u8(SSPDR, value);
len_tx++;
}
if (status & SSPSR_RNE) {
value = ep93xx_spi_read_u8(SSPDR);
if (rxp)
*rxp++ = value;
len_rx++;
}
}
out:
if (flags & SPI_XFER_END) {
/*
* Wait until the transfer is completely done before
* we deactivate CS.
*/
do {
status = ep93xx_spi_read_u8(SSPSR);
} while (status & SSPSR_BSY);
spi_cs_deactivate(slave);
}
return 0;
}
|