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