]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
b85dc460 WW |
2 | /* |
3 | * Copyright (C) 2015-2016 Wills Wang <[email protected]> | |
b85dc460 WW |
4 | */ |
5 | ||
6 | #include <common.h> | |
d96c2604 | 7 | #include <clock_legacy.h> |
b85dc460 WW |
8 | #include <spi.h> |
9 | #include <dm.h> | |
10 | #include <div64.h> | |
11 | #include <errno.h> | |
1045315d | 12 | #include <time.h> |
b85dc460 WW |
13 | #include <asm/io.h> |
14 | #include <asm/addrspace.h> | |
15 | #include <asm/types.h> | |
16 | #include <dm/pinctrl.h> | |
17 | #include <mach/ar71xx_regs.h> | |
18 | ||
19 | /* CLOCK_DIVIDER = 3 (SPI clock = 200 / 8 ~ 25 MHz) */ | |
20 | #define ATH79_SPI_CLK_DIV(x) (((x) >> 1) - 1) | |
21 | #define ATH79_SPI_RRW_DELAY_FACTOR 12000 | |
22 | #define ATH79_SPI_MHZ (1000 * 1000) | |
23 | ||
24 | struct ath79_spi_priv { | |
25 | void __iomem *regs; | |
26 | u32 rrw_delay; | |
27 | }; | |
28 | ||
29 | static void spi_cs_activate(struct udevice *dev) | |
30 | { | |
31 | struct udevice *bus = dev_get_parent(dev); | |
32 | struct ath79_spi_priv *priv = dev_get_priv(bus); | |
33 | ||
34 | writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS); | |
35 | writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC); | |
36 | } | |
37 | ||
38 | static void spi_cs_deactivate(struct udevice *dev) | |
39 | { | |
40 | struct udevice *bus = dev_get_parent(dev); | |
41 | struct ath79_spi_priv *priv = dev_get_priv(bus); | |
42 | ||
43 | writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC); | |
44 | writel(0, priv->regs + AR71XX_SPI_REG_FS); | |
45 | } | |
46 | ||
47 | static int ath79_spi_claim_bus(struct udevice *dev) | |
48 | { | |
49 | return 0; | |
50 | } | |
51 | ||
52 | static int ath79_spi_release_bus(struct udevice *dev) | |
53 | { | |
54 | return 0; | |
55 | } | |
56 | ||
57 | static int ath79_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
58 | const void *dout, void *din, unsigned long flags) | |
59 | { | |
60 | struct udevice *bus = dev_get_parent(dev); | |
61 | struct ath79_spi_priv *priv = dev_get_priv(bus); | |
62 | struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev); | |
63 | u8 *rx = din; | |
64 | const u8 *tx = dout; | |
65 | u8 curbyte, curbitlen, restbits; | |
66 | u32 bytes = bitlen / 8; | |
67 | u32 out, in; | |
68 | u64 tick; | |
69 | ||
70 | if (flags & SPI_XFER_BEGIN) | |
71 | spi_cs_activate(dev); | |
72 | ||
73 | restbits = (bitlen % 8); | |
74 | if (restbits) | |
75 | bytes++; | |
76 | ||
77 | out = AR71XX_SPI_IOC_CS_ALL & ~(AR71XX_SPI_IOC_CS(slave->cs)); | |
78 | while (bytes > 0) { | |
79 | bytes--; | |
80 | curbyte = 0; | |
81 | if (tx) | |
82 | curbyte = *tx++; | |
83 | ||
84 | if (restbits && !bytes) { | |
85 | curbitlen = restbits; | |
86 | curbyte <<= 8 - restbits; | |
87 | } else { | |
88 | curbitlen = 8; | |
89 | } | |
90 | ||
91 | for (curbyte <<= (8 - curbitlen); curbitlen; curbitlen--) { | |
92 | if (curbyte & 0x80) | |
93 | out |= AR71XX_SPI_IOC_DO; | |
94 | else | |
95 | out &= ~(AR71XX_SPI_IOC_DO); | |
96 | ||
97 | writel(out, priv->regs + AR71XX_SPI_REG_IOC); | |
98 | ||
99 | /* delay for low level */ | |
100 | if (priv->rrw_delay) { | |
101 | tick = get_ticks() + priv->rrw_delay; | |
102 | while (get_ticks() < tick) | |
103 | /*NOP*/; | |
104 | } | |
105 | ||
106 | writel(out | AR71XX_SPI_IOC_CLK, | |
107 | priv->regs + AR71XX_SPI_REG_IOC); | |
108 | ||
109 | /* delay for high level */ | |
110 | if (priv->rrw_delay) { | |
111 | tick = get_ticks() + priv->rrw_delay; | |
112 | while (get_ticks() < tick) | |
113 | /*NOP*/; | |
114 | } | |
115 | ||
116 | curbyte <<= 1; | |
117 | } | |
118 | ||
119 | if (!bytes) | |
120 | writel(out, priv->regs + AR71XX_SPI_REG_IOC); | |
121 | ||
122 | in = readl(priv->regs + AR71XX_SPI_REG_RDS); | |
123 | if (rx) { | |
124 | if (restbits && !bytes) | |
125 | *rx++ = (in << (8 - restbits)); | |
126 | else | |
127 | *rx++ = in; | |
128 | } | |
129 | } | |
130 | ||
131 | if (flags & SPI_XFER_END) | |
132 | spi_cs_deactivate(dev); | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | ||
138 | static int ath79_spi_set_speed(struct udevice *bus, uint speed) | |
139 | { | |
140 | struct ath79_spi_priv *priv = dev_get_priv(bus); | |
141 | u32 val, div = 0; | |
142 | u64 time; | |
143 | ||
144 | if (speed) | |
145 | div = get_bus_freq(0) / speed; | |
146 | ||
147 | if (div > 63) | |
148 | div = 63; | |
149 | ||
150 | if (div < 5) | |
151 | div = 5; | |
152 | ||
153 | /* calculate delay */ | |
154 | time = get_tbclk(); | |
155 | do_div(time, speed / 2); | |
156 | val = get_bus_freq(0) / ATH79_SPI_MHZ; | |
157 | val = ATH79_SPI_RRW_DELAY_FACTOR / val; | |
158 | if (time > val) | |
159 | priv->rrw_delay = time - val + 1; | |
160 | else | |
161 | priv->rrw_delay = 0; | |
162 | ||
163 | writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS); | |
164 | clrsetbits_be32(priv->regs + AR71XX_SPI_REG_CTRL, | |
165 | AR71XX_SPI_CTRL_DIV_MASK, | |
166 | ATH79_SPI_CLK_DIV(div)); | |
167 | writel(0, priv->regs + AR71XX_SPI_REG_FS); | |
168 | return 0; | |
169 | } | |
170 | ||
171 | static int ath79_spi_set_mode(struct udevice *bus, uint mode) | |
172 | { | |
173 | return 0; | |
174 | } | |
175 | ||
176 | static int ath79_spi_probe(struct udevice *bus) | |
177 | { | |
178 | struct ath79_spi_priv *priv = dev_get_priv(bus); | |
b85dc460 | 179 | fdt_addr_t addr; |
b85dc460 | 180 | |
a821c4af | 181 | addr = devfdt_get_addr(bus); |
b85dc460 WW |
182 | if (addr == FDT_ADDR_T_NONE) |
183 | return -EINVAL; | |
184 | ||
185 | priv->regs = map_physmem(addr, | |
186 | AR71XX_SPI_SIZE, | |
187 | MAP_NOCACHE); | |
188 | ||
189 | /* Init SPI Hardware, disable remap, set clock */ | |
190 | writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS); | |
191 | writel(AR71XX_SPI_CTRL_RD | ATH79_SPI_CLK_DIV(8), | |
192 | priv->regs + AR71XX_SPI_REG_CTRL); | |
193 | writel(0, priv->regs + AR71XX_SPI_REG_FS); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static int ath79_cs_info(struct udevice *bus, uint cs, | |
199 | struct spi_cs_info *info) | |
200 | { | |
201 | /* Always allow activity on CS 0/1/2 */ | |
202 | if (cs >= 3) | |
4b060003 | 203 | return -EINVAL; |
b85dc460 WW |
204 | |
205 | return 0; | |
206 | } | |
207 | ||
208 | static const struct dm_spi_ops ath79_spi_ops = { | |
209 | .claim_bus = ath79_spi_claim_bus, | |
210 | .release_bus = ath79_spi_release_bus, | |
211 | .xfer = ath79_spi_xfer, | |
212 | .set_speed = ath79_spi_set_speed, | |
213 | .set_mode = ath79_spi_set_mode, | |
214 | .cs_info = ath79_cs_info, | |
215 | }; | |
216 | ||
217 | static const struct udevice_id ath79_spi_ids[] = { | |
218 | { .compatible = "qca,ar7100-spi" }, | |
219 | {} | |
220 | }; | |
221 | ||
222 | U_BOOT_DRIVER(ath79_spi) = { | |
223 | .name = "ath79_spi", | |
224 | .id = UCLASS_SPI, | |
225 | .of_match = ath79_spi_ids, | |
226 | .ops = &ath79_spi_ops, | |
227 | .priv_auto_alloc_size = sizeof(struct ath79_spi_priv), | |
228 | .probe = ath79_spi_probe, | |
229 | }; |