]>
Commit | Line | Data |
---|---|---|
661ba140 TC |
1 | /* |
2 | * Altera SPI driver | |
3 | * | |
4 | * based on bfin_spi.c | |
5 | * Copyright (c) 2005-2008 Analog Devices Inc. | |
6 | * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> | |
7 | * | |
e7b1e452 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
661ba140 TC |
9 | */ |
10 | #include <common.h> | |
15a56f9c TC |
11 | #include <dm.h> |
12 | #include <errno.h> | |
661ba140 | 13 | #include <malloc.h> |
15a56f9c | 14 | #include <fdtdec.h> |
bef87adf | 15 | #include <spi.h> |
15a56f9c TC |
16 | #include <asm/io.h> |
17 | ||
18 | DECLARE_GLOBAL_DATA_PTR; | |
661ba140 | 19 | |
bef87adf JT |
20 | #define ALTERA_SPI_STATUS_RRDY_MSK BIT(7) |
21 | #define ALTERA_SPI_CONTROL_SSO_MSK BIT(10) | |
22 | ||
cdcdad85 | 23 | #ifndef CONFIG_ALTERA_SPI_IDLE_VAL |
bef87adf | 24 | #define CONFIG_ALTERA_SPI_IDLE_VAL 0xff |
cdcdad85 MV |
25 | #endif |
26 | ||
eef67029 MV |
27 | struct altera_spi_regs { |
28 | u32 rxdata; | |
29 | u32 txdata; | |
30 | u32 status; | |
31 | u32 control; | |
32 | u32 _reserved; | |
33 | u32 slave_sel; | |
34 | }; | |
661ba140 | 35 | |
15a56f9c TC |
36 | struct altera_spi_platdata { |
37 | struct altera_spi_regs *regs; | |
38 | }; | |
661ba140 | 39 | |
15a56f9c TC |
40 | struct altera_spi_priv { |
41 | struct altera_spi_regs *regs; | |
661ba140 | 42 | }; |
661ba140 | 43 | |
15a56f9c | 44 | static void spi_cs_activate(struct udevice *dev, uint cs) |
661ba140 | 45 | { |
15a56f9c TC |
46 | struct udevice *bus = dev->parent; |
47 | struct altera_spi_priv *priv = dev_get_priv(bus); | |
48 | struct altera_spi_regs *const regs = priv->regs; | |
661ba140 | 49 | |
15a56f9c TC |
50 | writel(1 << cs, ®s->slave_sel); |
51 | writel(ALTERA_SPI_CONTROL_SSO_MSK, ®s->control); | |
661ba140 TC |
52 | } |
53 | ||
15a56f9c | 54 | static void spi_cs_deactivate(struct udevice *dev) |
661ba140 | 55 | { |
15a56f9c TC |
56 | struct udevice *bus = dev->parent; |
57 | struct altera_spi_priv *priv = dev_get_priv(bus); | |
58 | struct altera_spi_regs *const regs = priv->regs; | |
661ba140 | 59 | |
15a56f9c TC |
60 | writel(0, ®s->control); |
61 | writel(0, ®s->slave_sel); | |
df8f1252 TC |
62 | } |
63 | ||
15a56f9c | 64 | static int altera_spi_claim_bus(struct udevice *dev) |
661ba140 | 65 | { |
15a56f9c TC |
66 | struct udevice *bus = dev->parent; |
67 | struct altera_spi_priv *priv = dev_get_priv(bus); | |
68 | struct altera_spi_regs *const regs = priv->regs; | |
661ba140 | 69 | |
15a56f9c TC |
70 | writel(0, ®s->control); |
71 | writel(0, ®s->slave_sel); | |
661ba140 | 72 | |
15a56f9c | 73 | return 0; |
661ba140 TC |
74 | } |
75 | ||
15a56f9c | 76 | static int altera_spi_release_bus(struct udevice *dev) |
661ba140 | 77 | { |
15a56f9c TC |
78 | struct udevice *bus = dev->parent; |
79 | struct altera_spi_priv *priv = dev_get_priv(bus); | |
80 | struct altera_spi_regs *const regs = priv->regs; | |
661ba140 | 81 | |
15a56f9c | 82 | writel(0, ®s->slave_sel); |
661ba140 | 83 | |
661ba140 TC |
84 | return 0; |
85 | } | |
86 | ||
15a56f9c TC |
87 | static int altera_spi_xfer(struct udevice *dev, unsigned int bitlen, |
88 | const void *dout, void *din, unsigned long flags) | |
661ba140 | 89 | { |
15a56f9c TC |
90 | struct udevice *bus = dev->parent; |
91 | struct altera_spi_priv *priv = dev_get_priv(bus); | |
92 | struct altera_spi_regs *const regs = priv->regs; | |
93 | struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); | |
661ba140 | 94 | |
661ba140 | 95 | /* assume spi core configured to do 8 bit transfers */ |
bc76b821 MV |
96 | unsigned int bytes = bitlen / 8; |
97 | const unsigned char *txp = dout; | |
98 | unsigned char *rxp = din; | |
99 | uint32_t reg, data, start; | |
661ba140 TC |
100 | |
101 | debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, | |
15a56f9c | 102 | bus->seq, slave_plat->cs, bitlen, bytes, flags); |
37dcc130 | 103 | |
661ba140 TC |
104 | if (bitlen == 0) |
105 | goto done; | |
106 | ||
107 | if (bitlen % 8) { | |
108 | flags |= SPI_XFER_END; | |
109 | goto done; | |
110 | } | |
111 | ||
112 | /* empty read buffer */ | |
15a56f9c TC |
113 | if (readl(®s->status) & ALTERA_SPI_STATUS_RRDY_MSK) |
114 | readl(®s->rxdata); | |
37dcc130 | 115 | |
661ba140 | 116 | if (flags & SPI_XFER_BEGIN) |
15a56f9c | 117 | spi_cs_activate(dev, slave_plat->cs); |
661ba140 TC |
118 | |
119 | while (bytes--) { | |
bc76b821 MV |
120 | if (txp) |
121 | data = *txp++; | |
122 | else | |
123 | data = CONFIG_ALTERA_SPI_IDLE_VAL; | |
37dcc130 | 124 | |
bc76b821 | 125 | debug("%s: tx:%x ", __func__, data); |
15a56f9c | 126 | writel(data, ®s->txdata); |
37dcc130 | 127 | |
80d73338 MV |
128 | start = get_timer(0); |
129 | while (1) { | |
15a56f9c | 130 | reg = readl(®s->status); |
80d73338 MV |
131 | if (reg & ALTERA_SPI_STATUS_RRDY_MSK) |
132 | break; | |
133 | if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { | |
15a56f9c TC |
134 | debug("%s: Transmission timed out!\n", __func__); |
135 | return -1; | |
80d73338 MV |
136 | } |
137 | } | |
37dcc130 | 138 | |
15a56f9c | 139 | data = readl(®s->rxdata); |
661ba140 | 140 | if (rxp) |
bc76b821 | 141 | *rxp++ = data & 0xff; |
37dcc130 | 142 | |
bc76b821 | 143 | debug("rx:%x\n", data); |
661ba140 | 144 | } |
37dcc130 MV |
145 | |
146 | done: | |
661ba140 | 147 | if (flags & SPI_XFER_END) |
15a56f9c | 148 | spi_cs_deactivate(dev); |
661ba140 TC |
149 | |
150 | return 0; | |
151 | } | |
15a56f9c TC |
152 | |
153 | static int altera_spi_set_speed(struct udevice *bus, uint speed) | |
154 | { | |
155 | return 0; | |
156 | } | |
157 | ||
158 | static int altera_spi_set_mode(struct udevice *bus, uint mode) | |
159 | { | |
160 | return 0; | |
161 | } | |
162 | ||
163 | static int altera_spi_probe(struct udevice *bus) | |
164 | { | |
165 | struct altera_spi_platdata *plat = dev_get_platdata(bus); | |
166 | struct altera_spi_priv *priv = dev_get_priv(bus); | |
167 | ||
168 | priv->regs = plat->regs; | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | static int altera_spi_ofdata_to_platdata(struct udevice *bus) | |
174 | { | |
175 | struct altera_spi_platdata *plat = dev_get_platdata(bus); | |
176 | ||
a821c4af | 177 | plat->regs = map_physmem(devfdt_get_addr(bus), |
7313e21a TC |
178 | sizeof(struct altera_spi_regs), |
179 | MAP_NOCACHE); | |
15a56f9c TC |
180 | |
181 | return 0; | |
182 | } | |
183 | ||
184 | static const struct dm_spi_ops altera_spi_ops = { | |
185 | .claim_bus = altera_spi_claim_bus, | |
186 | .release_bus = altera_spi_release_bus, | |
187 | .xfer = altera_spi_xfer, | |
188 | .set_speed = altera_spi_set_speed, | |
189 | .set_mode = altera_spi_set_mode, | |
190 | /* | |
191 | * cs_info is not needed, since we require all chip selects to be | |
192 | * in the device tree explicitly | |
193 | */ | |
194 | }; | |
195 | ||
196 | static const struct udevice_id altera_spi_ids[] = { | |
ddf34c26 TC |
197 | { .compatible = "altr,spi-1.0" }, |
198 | {} | |
15a56f9c TC |
199 | }; |
200 | ||
201 | U_BOOT_DRIVER(altera_spi) = { | |
202 | .name = "altera_spi", | |
203 | .id = UCLASS_SPI, | |
204 | .of_match = altera_spi_ids, | |
205 | .ops = &altera_spi_ops, | |
206 | .ofdata_to_platdata = altera_spi_ofdata_to_platdata, | |
207 | .platdata_auto_alloc_size = sizeof(struct altera_spi_platdata), | |
208 | .priv_auto_alloc_size = sizeof(struct altera_spi_priv), | |
209 | .probe = altera_spi_probe, | |
210 | }; |