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
|
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id:
*
* Copyright © 2009 Michael Sparmann
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include "config.h"
#include "s5l87xx.h"
#include "clocking-s5l8702.h"
#include "spi-s5l8702.h"
#define SPI_N_PORT 3
static int clkdiv[SPI_N_PORT] = {4, 4, 4};
/* configure SPI clock, speed is PClk/(div+1) (TBC) */
void spi_clkdiv(int port, int div)
{
clkdiv[port] = div;
}
/* state: 1 -> route GPIO ports to SPI controller
* 0 -> set GPIO to lowest power consumption
*/
void spi_init(int port, bool state)
{
uint32_t val = state ? 0x2222 : 0xEEEF;
switch (port)
{
case 0:
PCON0 = (PCON0 & ~0xffff) | val;
break;
#if CONFIG_CPU == S5L8702
case 1:
PCON6 = (PCON6 & ~0xffff0000) | (val << 16);
break;
case 2:
PCON14 = (PCON14 & ~0xff000000) | (val << 24);
PCON15 = (PCON15 & ~0xff) | (val >> 8);
break;
#elif CONFIG_CPU == S5L8720
case 1:
PCON4 = (PCON4 & ~0xff000000) | (val << 24);
PCON5 = (PCON5 & ~0xff) | (val >> 8);
break;
case 2:
/* unknown */
break;
#endif
}
}
/* nSS pin: output LOW -> external device SLAVE
* output HIGH -> deactivate external SLAVE device
* input LOW -> SLAVE condition (external MASTER device)
*/
void spi_ce(int port, bool state)
{
uint32_t level = state ? 0 : 1;
switch (port)
{
case 0: GPIOCMD = 0x0000e | level; break;
#if CONFIG_CPU == S5L8702
case 1: GPIOCMD = 0x6040e | level; break;
case 2: GPIOCMD = 0xe060e | level; break;
#elif CONFIG_CPU == S5L8720
case 1: GPIOCMD = 0x4060e | level; break;
case 2: /* unknown */ break;
#endif
}
}
void spi_prepare(int port)
{
clockgate_enable(SPICLKGATE(port), true);
#if CONFIG_CPU == S5L8720
clockgate_enable(SPICLKGATE_2(port), true);
#endif
SPISTATUS(port) = 0xf;
SPICTRL(port) |= 0xc;
SPICLKDIV(port) = clkdiv[port];
SPIPIN(port) = 6;
#if CONFIG_CPU == S5L8702
SPISETUP(port) = 0x10618;
#elif CONFIG_CPU == S5L8720
SPIUNK40(port) = 0xffffffff;
SPIUNK3C(port) = 0xffffffff;
SPISETUP(port) = 0x40618;
#endif
SPICTRL(port) |= 0xc;
SPICTRL(port) = 1;
}
void spi_release(int port)
{
clockgate_enable(SPICLKGATE(port), false);
#if CONFIG_CPU == S5L8720
clockgate_enable(SPICLKGATE_2(port), false);
#endif
}
static inline void spi_wait_ready(int port)
{
#if CONFIG_CPU == S5L8702
while (!(SPISTATUS(port) & 0x3e00));
#elif CONFIG_CPU == S5L8720
while (!(SPISTATUS(port) & 0xf800));
#endif
}
uint32_t spi_write(int port, uint32_t data)
{
#if CONFIG_CPU == S5L8702
SPIRXLIMIT(port) = 1;
while ((SPISTATUS(port) & 0x1f0) == 0x100);
#elif CONFIG_CPU == S5L8720
SPIUNK4C(port) = 1;
SPIRXLIMIT(port) = 1;
// TODO: Wouldn't this be 0x7c0? so that the rule of <<2 is fulfilled in this register?
while ((SPISTATUS(port) & 0x7f0) == 0x400);
#endif
SPITXDATA(port) = data;
spi_wait_ready(port);
return SPIRXDATA(port);
}
void spi_read(int port, uint32_t size, void* buf)
{
uint8_t* buffer = (uint8_t*)buf;
#if CONFIG_CPU == S5L8720
SPIUNK4C(port) = size;
#endif
SPIRXLIMIT(port) = size;
SPISETUP(port) |= 1;
while (size--)
{
spi_wait_ready(port);
*buffer++ = SPIRXDATA(port);
}
SPISETUP(port) &= ~1;
}
|