.. | .. |
---|
9 | 9 | |
---|
10 | 10 | #include <linux/bitfield.h> |
---|
11 | 11 | #include <linux/clk.h> |
---|
| 12 | +#include <linux/clk-provider.h> |
---|
12 | 13 | #include <linux/device.h> |
---|
13 | 14 | #include <linux/io.h> |
---|
14 | 15 | #include <linux/kernel.h> |
---|
15 | 16 | #include <linux/module.h> |
---|
16 | 17 | #include <linux/of.h> |
---|
| 18 | +#include <linux/of_device.h> |
---|
17 | 19 | #include <linux/platform_device.h> |
---|
18 | 20 | #include <linux/spi/spi.h> |
---|
19 | 21 | #include <linux/types.h> |
---|
20 | 22 | #include <linux/interrupt.h> |
---|
21 | 23 | #include <linux/reset.h> |
---|
22 | | -#include <linux/gpio.h> |
---|
23 | 24 | |
---|
24 | 25 | /* |
---|
25 | 26 | * The Meson SPICC controller could support DMA based transfers, but is not |
---|
.. | .. |
---|
34 | 35 | * to have a CS go down over the full transfer |
---|
35 | 36 | */ |
---|
36 | 37 | |
---|
37 | | -#define SPICC_MAX_FREQ 30000000 |
---|
38 | 38 | #define SPICC_MAX_BURST 128 |
---|
39 | 39 | |
---|
40 | 40 | /* Register Map */ |
---|
.. | .. |
---|
106 | 106 | #define SPICC_SWAP_RO BIT(14) /* RX FIFO Data Swap Read-Only */ |
---|
107 | 107 | #define SPICC_SWAP_W1 BIT(15) /* RX FIFO Data Swap Write-Only */ |
---|
108 | 108 | #define SPICC_DLYCTL_RO_MASK GENMASK(20, 15) /* Delay Control Read-Only */ |
---|
109 | | -#define SPICC_DLYCTL_W1_MASK GENMASK(21, 16) /* Delay Control Write-Only */ |
---|
| 109 | +#define SPICC_MO_DELAY_MASK GENMASK(17, 16) /* Master Output Delay */ |
---|
| 110 | +#define SPICC_MO_NO_DELAY 0 |
---|
| 111 | +#define SPICC_MO_DELAY_1_CYCLE 1 |
---|
| 112 | +#define SPICC_MO_DELAY_2_CYCLE 2 |
---|
| 113 | +#define SPICC_MO_DELAY_3_CYCLE 3 |
---|
| 114 | +#define SPICC_MI_DELAY_MASK GENMASK(19, 18) /* Master Input Delay */ |
---|
| 115 | +#define SPICC_MI_NO_DELAY 0 |
---|
| 116 | +#define SPICC_MI_DELAY_1_CYCLE 1 |
---|
| 117 | +#define SPICC_MI_DELAY_2_CYCLE 2 |
---|
| 118 | +#define SPICC_MI_DELAY_3_CYCLE 3 |
---|
| 119 | +#define SPICC_MI_CAP_DELAY_MASK GENMASK(21, 20) /* Master Capture Delay */ |
---|
| 120 | +#define SPICC_CAP_AHEAD_2_CYCLE 0 |
---|
| 121 | +#define SPICC_CAP_AHEAD_1_CYCLE 1 |
---|
| 122 | +#define SPICC_CAP_NO_DELAY 2 |
---|
| 123 | +#define SPICC_CAP_DELAY_1_CYCLE 3 |
---|
110 | 124 | #define SPICC_FIFORST_RO_MASK GENMASK(22, 21) /* FIFO Softreset Read-Only */ |
---|
111 | 125 | #define SPICC_FIFORST_W1_MASK GENMASK(23, 22) /* FIFO Softreset Write-Only */ |
---|
112 | 126 | |
---|
.. | .. |
---|
114 | 128 | |
---|
115 | 129 | #define SPICC_DWADDR 0x24 /* Write Address of DMA */ |
---|
116 | 130 | |
---|
| 131 | +#define SPICC_ENH_CTL0 0x38 /* Enhanced Feature */ |
---|
| 132 | +#define SPICC_ENH_CLK_CS_DELAY_MASK GENMASK(15, 0) |
---|
| 133 | +#define SPICC_ENH_DATARATE_MASK GENMASK(23, 16) |
---|
| 134 | +#define SPICC_ENH_DATARATE_EN BIT(24) |
---|
| 135 | +#define SPICC_ENH_MOSI_OEN BIT(25) |
---|
| 136 | +#define SPICC_ENH_CLK_OEN BIT(26) |
---|
| 137 | +#define SPICC_ENH_CS_OEN BIT(27) |
---|
| 138 | +#define SPICC_ENH_CLK_CS_DELAY_EN BIT(28) |
---|
| 139 | +#define SPICC_ENH_MAIN_CLK_AO BIT(29) |
---|
| 140 | + |
---|
117 | 141 | #define writel_bits_relaxed(mask, val, addr) \ |
---|
118 | 142 | writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) |
---|
119 | 143 | |
---|
120 | | -#define SPICC_BURST_MAX 16 |
---|
121 | | -#define SPICC_FIFO_HALF 10 |
---|
| 144 | +struct meson_spicc_data { |
---|
| 145 | + unsigned int max_speed_hz; |
---|
| 146 | + unsigned int min_speed_hz; |
---|
| 147 | + unsigned int fifo_size; |
---|
| 148 | + bool has_oen; |
---|
| 149 | + bool has_enhance_clk_div; |
---|
| 150 | + bool has_pclk; |
---|
| 151 | +}; |
---|
122 | 152 | |
---|
123 | 153 | struct meson_spicc_device { |
---|
124 | 154 | struct spi_master *master; |
---|
125 | 155 | struct platform_device *pdev; |
---|
126 | 156 | void __iomem *base; |
---|
127 | 157 | struct clk *core; |
---|
| 158 | + struct clk *pclk; |
---|
| 159 | + struct clk_divider pow2_div; |
---|
| 160 | + struct clk *clk; |
---|
128 | 161 | struct spi_message *message; |
---|
129 | 162 | struct spi_transfer *xfer; |
---|
| 163 | + const struct meson_spicc_data *data; |
---|
130 | 164 | u8 *tx_buf; |
---|
131 | 165 | u8 *rx_buf; |
---|
132 | 166 | unsigned int bytes_per_word; |
---|
133 | 167 | unsigned long tx_remain; |
---|
134 | | - unsigned long txb_remain; |
---|
135 | 168 | unsigned long rx_remain; |
---|
136 | | - unsigned long rxb_remain; |
---|
137 | 169 | unsigned long xfer_remain; |
---|
138 | | - bool is_burst_end; |
---|
139 | | - bool is_last_burst; |
---|
140 | 170 | }; |
---|
| 171 | + |
---|
| 172 | +#define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div) |
---|
| 173 | + |
---|
| 174 | +static void meson_spicc_oen_enable(struct meson_spicc_device *spicc) |
---|
| 175 | +{ |
---|
| 176 | + u32 conf; |
---|
| 177 | + |
---|
| 178 | + if (!spicc->data->has_oen) |
---|
| 179 | + return; |
---|
| 180 | + |
---|
| 181 | + conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) | |
---|
| 182 | + SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN; |
---|
| 183 | + |
---|
| 184 | + writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0); |
---|
| 185 | +} |
---|
141 | 186 | |
---|
142 | 187 | static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc) |
---|
143 | 188 | { |
---|
.. | .. |
---|
147 | 192 | |
---|
148 | 193 | static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc) |
---|
149 | 194 | { |
---|
150 | | - return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF_EN, |
---|
| 195 | + return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF, |
---|
151 | 196 | readl_relaxed(spicc->base + SPICC_STATREG)); |
---|
152 | 197 | } |
---|
153 | 198 | |
---|
.. | .. |
---|
202 | 247 | spicc->base + SPICC_TXDATA); |
---|
203 | 248 | } |
---|
204 | 249 | |
---|
205 | | -static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc, |
---|
206 | | - u32 irq_ctrl) |
---|
| 250 | +static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc) |
---|
207 | 251 | { |
---|
208 | | - if (spicc->rx_remain > SPICC_FIFO_HALF) |
---|
209 | | - irq_ctrl |= SPICC_RH_EN; |
---|
210 | | - else |
---|
211 | | - irq_ctrl |= SPICC_RR_EN; |
---|
212 | 252 | |
---|
213 | | - return irq_ctrl; |
---|
214 | | -} |
---|
215 | | - |
---|
216 | | -static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc, |
---|
217 | | - unsigned int burst_len) |
---|
218 | | -{ |
---|
| 253 | + unsigned int burst_len = min_t(unsigned int, |
---|
| 254 | + spicc->xfer_remain / |
---|
| 255 | + spicc->bytes_per_word, |
---|
| 256 | + spicc->data->fifo_size); |
---|
219 | 257 | /* Setup Xfer variables */ |
---|
220 | 258 | spicc->tx_remain = burst_len; |
---|
221 | 259 | spicc->rx_remain = burst_len; |
---|
222 | 260 | spicc->xfer_remain -= burst_len * spicc->bytes_per_word; |
---|
223 | | - spicc->is_burst_end = false; |
---|
224 | | - if (burst_len < SPICC_BURST_MAX || !spicc->xfer_remain) |
---|
225 | | - spicc->is_last_burst = true; |
---|
226 | | - else |
---|
227 | | - spicc->is_last_burst = false; |
---|
228 | 261 | |
---|
229 | 262 | /* Setup burst length */ |
---|
230 | 263 | writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, |
---|
231 | 264 | FIELD_PREP(SPICC_BURSTLENGTH_MASK, |
---|
232 | | - burst_len), |
---|
| 265 | + burst_len - 1), |
---|
233 | 266 | spicc->base + SPICC_CONREG); |
---|
234 | 267 | |
---|
235 | 268 | /* Fill TX FIFO */ |
---|
.. | .. |
---|
239 | 272 | static irqreturn_t meson_spicc_irq(int irq, void *data) |
---|
240 | 273 | { |
---|
241 | 274 | struct meson_spicc_device *spicc = (void *) data; |
---|
242 | | - u32 ctrl = readl_relaxed(spicc->base + SPICC_INTREG); |
---|
243 | | - u32 stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl; |
---|
244 | 275 | |
---|
245 | | - ctrl &= ~(SPICC_RH_EN | SPICC_RR_EN); |
---|
| 276 | + writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG); |
---|
246 | 277 | |
---|
247 | 278 | /* Empty RX FIFO */ |
---|
248 | 279 | meson_spicc_rx(spicc); |
---|
249 | 280 | |
---|
250 | | - /* Enable TC interrupt since we transferred everything */ |
---|
251 | | - if (!spicc->tx_remain && !spicc->rx_remain) { |
---|
252 | | - spicc->is_burst_end = true; |
---|
| 281 | + if (!spicc->xfer_remain) { |
---|
| 282 | + /* Disable all IRQs */ |
---|
| 283 | + writel(0, spicc->base + SPICC_INTREG); |
---|
253 | 284 | |
---|
254 | | - /* Enable TC interrupt */ |
---|
255 | | - ctrl |= SPICC_TC_EN; |
---|
| 285 | + spi_finalize_current_transfer(spicc->master); |
---|
256 | 286 | |
---|
257 | | - /* Reload IRQ status */ |
---|
258 | | - stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl; |
---|
| 287 | + return IRQ_HANDLED; |
---|
259 | 288 | } |
---|
260 | 289 | |
---|
261 | | - /* Check transfer complete */ |
---|
262 | | - if ((stat & SPICC_TC) && spicc->is_burst_end) { |
---|
263 | | - unsigned int burst_len; |
---|
| 290 | + /* Setup burst */ |
---|
| 291 | + meson_spicc_setup_burst(spicc); |
---|
264 | 292 | |
---|
265 | | - /* Clear TC bit */ |
---|
266 | | - writel_relaxed(SPICC_TC, spicc->base + SPICC_STATREG); |
---|
267 | | - |
---|
268 | | - /* Disable TC interrupt */ |
---|
269 | | - ctrl &= ~SPICC_TC_EN; |
---|
270 | | - |
---|
271 | | - if (spicc->is_last_burst) { |
---|
272 | | - /* Disable all IRQs */ |
---|
273 | | - writel(0, spicc->base + SPICC_INTREG); |
---|
274 | | - |
---|
275 | | - spi_finalize_current_transfer(spicc->master); |
---|
276 | | - |
---|
277 | | - return IRQ_HANDLED; |
---|
278 | | - } |
---|
279 | | - |
---|
280 | | - burst_len = min_t(unsigned int, |
---|
281 | | - spicc->xfer_remain / spicc->bytes_per_word, |
---|
282 | | - SPICC_BURST_MAX); |
---|
283 | | - |
---|
284 | | - /* Setup burst */ |
---|
285 | | - meson_spicc_setup_burst(spicc, burst_len); |
---|
286 | | - |
---|
287 | | - /* Restart burst */ |
---|
288 | | - writel_bits_relaxed(SPICC_XCH, SPICC_XCH, |
---|
289 | | - spicc->base + SPICC_CONREG); |
---|
290 | | - } |
---|
291 | | - |
---|
292 | | - /* Setup RX interrupt trigger */ |
---|
293 | | - ctrl = meson_spicc_setup_rx_irq(spicc, ctrl); |
---|
294 | | - |
---|
295 | | - /* Reconfigure interrupts */ |
---|
296 | | - writel(ctrl, spicc->base + SPICC_INTREG); |
---|
| 293 | + /* Start burst */ |
---|
| 294 | + writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); |
---|
297 | 295 | |
---|
298 | 296 | return IRQ_HANDLED; |
---|
299 | 297 | } |
---|
300 | 298 | |
---|
301 | | -static u32 meson_spicc_setup_speed(struct meson_spicc_device *spicc, u32 conf, |
---|
302 | | - u32 speed) |
---|
| 299 | +static void meson_spicc_auto_io_delay(struct meson_spicc_device *spicc) |
---|
303 | 300 | { |
---|
304 | | - unsigned long parent, value; |
---|
305 | | - unsigned int i, div; |
---|
| 301 | + u32 div, hz; |
---|
| 302 | + u32 mi_delay, cap_delay; |
---|
| 303 | + u32 conf; |
---|
306 | 304 | |
---|
307 | | - parent = clk_get_rate(spicc->core); |
---|
308 | | - |
---|
309 | | - /* Find closest inferior/equal possible speed */ |
---|
310 | | - for (i = 0 ; i < 7 ; ++i) { |
---|
311 | | - /* 2^(data_rate+2) */ |
---|
312 | | - value = parent >> (i + 2); |
---|
313 | | - |
---|
314 | | - if (value <= speed) |
---|
315 | | - break; |
---|
| 305 | + if (spicc->data->has_enhance_clk_div) { |
---|
| 306 | + div = FIELD_GET(SPICC_ENH_DATARATE_MASK, |
---|
| 307 | + readl_relaxed(spicc->base + SPICC_ENH_CTL0)); |
---|
| 308 | + div++; |
---|
| 309 | + div <<= 1; |
---|
| 310 | + } else { |
---|
| 311 | + div = FIELD_GET(SPICC_DATARATE_MASK, |
---|
| 312 | + readl_relaxed(spicc->base + SPICC_CONREG)); |
---|
| 313 | + div += 2; |
---|
| 314 | + div = 1 << div; |
---|
316 | 315 | } |
---|
317 | 316 | |
---|
318 | | - /* If provided speed it lower than max divider, use max divider */ |
---|
319 | | - if (i > 7) { |
---|
320 | | - div = 7; |
---|
321 | | - dev_warn_once(&spicc->pdev->dev, "unable to get close to speed %u\n", |
---|
322 | | - speed); |
---|
323 | | - } else |
---|
324 | | - div = i; |
---|
| 317 | + mi_delay = SPICC_MI_NO_DELAY; |
---|
| 318 | + cap_delay = SPICC_CAP_AHEAD_2_CYCLE; |
---|
| 319 | + hz = clk_get_rate(spicc->clk); |
---|
325 | 320 | |
---|
326 | | - dev_dbg(&spicc->pdev->dev, "parent %lu, speed %u -> %lu (%u)\n", |
---|
327 | | - parent, speed, value, div); |
---|
| 321 | + if (hz >= 100000000) |
---|
| 322 | + cap_delay = SPICC_CAP_DELAY_1_CYCLE; |
---|
| 323 | + else if (hz >= 80000000) |
---|
| 324 | + cap_delay = SPICC_CAP_NO_DELAY; |
---|
| 325 | + else if (hz >= 40000000) |
---|
| 326 | + cap_delay = SPICC_CAP_AHEAD_1_CYCLE; |
---|
| 327 | + else if (div >= 16) |
---|
| 328 | + mi_delay = SPICC_MI_DELAY_3_CYCLE; |
---|
| 329 | + else if (div >= 8) |
---|
| 330 | + mi_delay = SPICC_MI_DELAY_2_CYCLE; |
---|
| 331 | + else if (div >= 6) |
---|
| 332 | + mi_delay = SPICC_MI_DELAY_1_CYCLE; |
---|
328 | 333 | |
---|
329 | | - conf &= ~SPICC_DATARATE_MASK; |
---|
330 | | - conf |= FIELD_PREP(SPICC_DATARATE_MASK, div); |
---|
331 | | - |
---|
332 | | - return conf; |
---|
| 334 | + conf = readl_relaxed(spicc->base + SPICC_TESTREG); |
---|
| 335 | + conf &= ~(SPICC_MO_DELAY_MASK | SPICC_MI_DELAY_MASK |
---|
| 336 | + | SPICC_MI_CAP_DELAY_MASK); |
---|
| 337 | + conf |= FIELD_PREP(SPICC_MI_DELAY_MASK, mi_delay); |
---|
| 338 | + conf |= FIELD_PREP(SPICC_MI_CAP_DELAY_MASK, cap_delay); |
---|
| 339 | + writel_relaxed(conf, spicc->base + SPICC_TESTREG); |
---|
333 | 340 | } |
---|
334 | 341 | |
---|
335 | 342 | static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc, |
---|
.. | .. |
---|
340 | 347 | /* Read original configuration */ |
---|
341 | 348 | conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG); |
---|
342 | 349 | |
---|
343 | | - /* Select closest divider */ |
---|
344 | | - conf = meson_spicc_setup_speed(spicc, conf, xfer->speed_hz); |
---|
345 | | - |
---|
346 | 350 | /* Setup word width */ |
---|
347 | 351 | conf &= ~SPICC_BITLENGTH_MASK; |
---|
348 | 352 | conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, |
---|
.. | .. |
---|
351 | 355 | /* Ignore if unchanged */ |
---|
352 | 356 | if (conf != conf_orig) |
---|
353 | 357 | writel_relaxed(conf, spicc->base + SPICC_CONREG); |
---|
| 358 | + |
---|
| 359 | + clk_set_rate(spicc->clk, xfer->speed_hz); |
---|
| 360 | + |
---|
| 361 | + meson_spicc_auto_io_delay(spicc); |
---|
| 362 | + |
---|
| 363 | + writel_relaxed(0, spicc->base + SPICC_DMAREG); |
---|
| 364 | +} |
---|
| 365 | + |
---|
| 366 | +static void meson_spicc_reset_fifo(struct meson_spicc_device *spicc) |
---|
| 367 | +{ |
---|
| 368 | + if (spicc->data->has_oen) |
---|
| 369 | + writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, |
---|
| 370 | + SPICC_ENH_MAIN_CLK_AO, |
---|
| 371 | + spicc->base + SPICC_ENH_CTL0); |
---|
| 372 | + |
---|
| 373 | + writel_bits_relaxed(SPICC_FIFORST_W1_MASK, SPICC_FIFORST_W1_MASK, |
---|
| 374 | + spicc->base + SPICC_TESTREG); |
---|
| 375 | + |
---|
| 376 | + while (meson_spicc_rxready(spicc)) |
---|
| 377 | + readl_relaxed(spicc->base + SPICC_RXDATA); |
---|
| 378 | + |
---|
| 379 | + if (spicc->data->has_oen) |
---|
| 380 | + writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0, |
---|
| 381 | + spicc->base + SPICC_ENH_CTL0); |
---|
354 | 382 | } |
---|
355 | 383 | |
---|
356 | 384 | static int meson_spicc_transfer_one(struct spi_master *master, |
---|
.. | .. |
---|
358 | 386 | struct spi_transfer *xfer) |
---|
359 | 387 | { |
---|
360 | 388 | struct meson_spicc_device *spicc = spi_master_get_devdata(master); |
---|
361 | | - unsigned int burst_len; |
---|
362 | | - u32 irq = 0; |
---|
363 | 389 | |
---|
364 | 390 | /* Store current transfer */ |
---|
365 | 391 | spicc->xfer = xfer; |
---|
.. | .. |
---|
373 | 399 | spicc->bytes_per_word = |
---|
374 | 400 | DIV_ROUND_UP(spicc->xfer->bits_per_word, 8); |
---|
375 | 401 | |
---|
| 402 | + if (xfer->len % spicc->bytes_per_word) |
---|
| 403 | + return -EINVAL; |
---|
| 404 | + |
---|
376 | 405 | /* Setup transfer parameters */ |
---|
377 | 406 | meson_spicc_setup_xfer(spicc, xfer); |
---|
378 | 407 | |
---|
379 | | - burst_len = min_t(unsigned int, |
---|
380 | | - spicc->xfer_remain / spicc->bytes_per_word, |
---|
381 | | - SPICC_BURST_MAX); |
---|
| 408 | + meson_spicc_reset_fifo(spicc); |
---|
382 | 409 | |
---|
383 | | - meson_spicc_setup_burst(spicc, burst_len); |
---|
384 | | - |
---|
385 | | - irq = meson_spicc_setup_rx_irq(spicc, irq); |
---|
| 410 | + /* Setup burst */ |
---|
| 411 | + meson_spicc_setup_burst(spicc); |
---|
386 | 412 | |
---|
387 | 413 | /* Start burst */ |
---|
388 | 414 | writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); |
---|
389 | 415 | |
---|
390 | 416 | /* Enable interrupts */ |
---|
391 | | - writel_relaxed(irq, spicc->base + SPICC_INTREG); |
---|
| 417 | + writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG); |
---|
392 | 418 | |
---|
393 | 419 | return 1; |
---|
394 | 420 | } |
---|
.. | .. |
---|
398 | 424 | { |
---|
399 | 425 | struct meson_spicc_device *spicc = spi_master_get_devdata(master); |
---|
400 | 426 | struct spi_device *spi = message->spi; |
---|
401 | | - u32 conf = 0; |
---|
| 427 | + u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK; |
---|
402 | 428 | |
---|
403 | 429 | /* Store current message */ |
---|
404 | 430 | spicc->message = message; |
---|
.. | .. |
---|
435 | 461 | /* Select CS */ |
---|
436 | 462 | conf |= FIELD_PREP(SPICC_CS_MASK, spi->chip_select); |
---|
437 | 463 | |
---|
438 | | - /* Default Clock rate core/4 */ |
---|
439 | | - |
---|
440 | 464 | /* Default 8bit word */ |
---|
441 | 465 | conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1); |
---|
442 | 466 | |
---|
.. | .. |
---|
445 | 469 | /* Setup no wait cycles by default */ |
---|
446 | 470 | writel_relaxed(0, spicc->base + SPICC_PERIODREG); |
---|
447 | 471 | |
---|
448 | | - writel_bits_relaxed(BIT(24), BIT(24), spicc->base + SPICC_TESTREG); |
---|
| 472 | + writel_bits_relaxed(SPICC_LBC_W1, 0, spicc->base + SPICC_TESTREG); |
---|
449 | 473 | |
---|
450 | 474 | return 0; |
---|
451 | 475 | } |
---|
.. | .. |
---|
453 | 477 | static int meson_spicc_unprepare_transfer(struct spi_master *master) |
---|
454 | 478 | { |
---|
455 | 479 | struct meson_spicc_device *spicc = spi_master_get_devdata(master); |
---|
| 480 | + u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK; |
---|
456 | 481 | |
---|
457 | 482 | /* Disable all IRQs */ |
---|
458 | 483 | writel(0, spicc->base + SPICC_INTREG); |
---|
459 | 484 | |
---|
460 | | - /* Disable controller */ |
---|
461 | | - writel_bits_relaxed(SPICC_ENABLE, 0, spicc->base + SPICC_CONREG); |
---|
462 | | - |
---|
463 | 485 | device_reset_optional(&spicc->pdev->dev); |
---|
| 486 | + |
---|
| 487 | + /* Set default configuration, keeping datarate field */ |
---|
| 488 | + writel_relaxed(conf, spicc->base + SPICC_CONREG); |
---|
464 | 489 | |
---|
465 | 490 | return 0; |
---|
466 | 491 | } |
---|
467 | 492 | |
---|
468 | 493 | static int meson_spicc_setup(struct spi_device *spi) |
---|
469 | 494 | { |
---|
470 | | - int ret = 0; |
---|
471 | | - |
---|
472 | 495 | if (!spi->controller_state) |
---|
473 | 496 | spi->controller_state = spi_master_get_devdata(spi->master); |
---|
474 | | - else if (gpio_is_valid(spi->cs_gpio)) |
---|
475 | | - goto out_gpio; |
---|
476 | | - else if (spi->cs_gpio == -ENOENT) |
---|
477 | | - return 0; |
---|
478 | 497 | |
---|
479 | | - if (gpio_is_valid(spi->cs_gpio)) { |
---|
480 | | - ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); |
---|
481 | | - if (ret) { |
---|
482 | | - dev_err(&spi->dev, "failed to request cs gpio\n"); |
---|
483 | | - return ret; |
---|
484 | | - } |
---|
485 | | - } |
---|
486 | | - |
---|
487 | | -out_gpio: |
---|
488 | | - ret = gpio_direction_output(spi->cs_gpio, |
---|
489 | | - !(spi->mode & SPI_CS_HIGH)); |
---|
490 | | - |
---|
491 | | - return ret; |
---|
| 498 | + return 0; |
---|
492 | 499 | } |
---|
493 | 500 | |
---|
494 | 501 | static void meson_spicc_cleanup(struct spi_device *spi) |
---|
495 | 502 | { |
---|
496 | | - if (gpio_is_valid(spi->cs_gpio)) |
---|
497 | | - gpio_free(spi->cs_gpio); |
---|
498 | | - |
---|
499 | 503 | spi->controller_state = NULL; |
---|
| 504 | +} |
---|
| 505 | + |
---|
| 506 | +/* |
---|
| 507 | + * The Clock Mux |
---|
| 508 | + * x-----------------x x------------x x------\ |
---|
| 509 | + * |---| pow2 fixed div |---| pow2 div |----| | |
---|
| 510 | + * | x-----------------x x------------x | | |
---|
| 511 | + * src ---| | mux |-- out |
---|
| 512 | + * | x-----------------x x------------x | | |
---|
| 513 | + * |---| enh fixed div |---| enh div |0---| | |
---|
| 514 | + * x-----------------x x------------x x------/ |
---|
| 515 | + * |
---|
| 516 | + * Clk path for GX series: |
---|
| 517 | + * src -> pow2 fixed div -> pow2 div -> out |
---|
| 518 | + * |
---|
| 519 | + * Clk path for AXG series: |
---|
| 520 | + * src -> pow2 fixed div -> pow2 div -> mux -> out |
---|
| 521 | + * src -> enh fixed div -> enh div -> mux -> out |
---|
| 522 | + * |
---|
| 523 | + * Clk path for G12A series: |
---|
| 524 | + * pclk -> pow2 fixed div -> pow2 div -> mux -> out |
---|
| 525 | + * pclk -> enh fixed div -> enh div -> mux -> out |
---|
| 526 | + * |
---|
| 527 | + * The pow2 divider is tied to the controller HW state, and the |
---|
| 528 | + * divider is only valid when the controller is initialized. |
---|
| 529 | + * |
---|
| 530 | + * A set of clock ops is added to make sure we don't read/set this |
---|
| 531 | + * clock rate while the controller is in an unknown state. |
---|
| 532 | + */ |
---|
| 533 | + |
---|
| 534 | +static unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw, |
---|
| 535 | + unsigned long parent_rate) |
---|
| 536 | +{ |
---|
| 537 | + struct clk_divider *divider = to_clk_divider(hw); |
---|
| 538 | + struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); |
---|
| 539 | + |
---|
| 540 | + if (!spicc->master->cur_msg) |
---|
| 541 | + return 0; |
---|
| 542 | + |
---|
| 543 | + return clk_divider_ops.recalc_rate(hw, parent_rate); |
---|
| 544 | +} |
---|
| 545 | + |
---|
| 546 | +static int meson_spicc_pow2_determine_rate(struct clk_hw *hw, |
---|
| 547 | + struct clk_rate_request *req) |
---|
| 548 | +{ |
---|
| 549 | + struct clk_divider *divider = to_clk_divider(hw); |
---|
| 550 | + struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); |
---|
| 551 | + |
---|
| 552 | + if (!spicc->master->cur_msg) |
---|
| 553 | + return -EINVAL; |
---|
| 554 | + |
---|
| 555 | + return clk_divider_ops.determine_rate(hw, req); |
---|
| 556 | +} |
---|
| 557 | + |
---|
| 558 | +static int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate, |
---|
| 559 | + unsigned long parent_rate) |
---|
| 560 | +{ |
---|
| 561 | + struct clk_divider *divider = to_clk_divider(hw); |
---|
| 562 | + struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); |
---|
| 563 | + |
---|
| 564 | + if (!spicc->master->cur_msg) |
---|
| 565 | + return -EINVAL; |
---|
| 566 | + |
---|
| 567 | + return clk_divider_ops.set_rate(hw, rate, parent_rate); |
---|
| 568 | +} |
---|
| 569 | + |
---|
| 570 | +const struct clk_ops meson_spicc_pow2_clk_ops = { |
---|
| 571 | + .recalc_rate = meson_spicc_pow2_recalc_rate, |
---|
| 572 | + .determine_rate = meson_spicc_pow2_determine_rate, |
---|
| 573 | + .set_rate = meson_spicc_pow2_set_rate, |
---|
| 574 | +}; |
---|
| 575 | + |
---|
| 576 | +static int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc) |
---|
| 577 | +{ |
---|
| 578 | + struct device *dev = &spicc->pdev->dev; |
---|
| 579 | + struct clk_fixed_factor *pow2_fixed_div; |
---|
| 580 | + struct clk_init_data init; |
---|
| 581 | + struct clk *clk; |
---|
| 582 | + struct clk_parent_data parent_data[2]; |
---|
| 583 | + char name[64]; |
---|
| 584 | + |
---|
| 585 | + memset(&init, 0, sizeof(init)); |
---|
| 586 | + memset(&parent_data, 0, sizeof(parent_data)); |
---|
| 587 | + |
---|
| 588 | + init.parent_data = parent_data; |
---|
| 589 | + |
---|
| 590 | + /* algorithm for pow2 div: rate = freq / 4 / (2 ^ N) */ |
---|
| 591 | + |
---|
| 592 | + pow2_fixed_div = devm_kzalloc(dev, sizeof(*pow2_fixed_div), GFP_KERNEL); |
---|
| 593 | + if (!pow2_fixed_div) |
---|
| 594 | + return -ENOMEM; |
---|
| 595 | + |
---|
| 596 | + snprintf(name, sizeof(name), "%s#pow2_fixed_div", dev_name(dev)); |
---|
| 597 | + init.name = name; |
---|
| 598 | + init.ops = &clk_fixed_factor_ops; |
---|
| 599 | + init.flags = 0; |
---|
| 600 | + if (spicc->data->has_pclk) |
---|
| 601 | + parent_data[0].hw = __clk_get_hw(spicc->pclk); |
---|
| 602 | + else |
---|
| 603 | + parent_data[0].hw = __clk_get_hw(spicc->core); |
---|
| 604 | + init.num_parents = 1; |
---|
| 605 | + |
---|
| 606 | + pow2_fixed_div->mult = 1, |
---|
| 607 | + pow2_fixed_div->div = 4, |
---|
| 608 | + pow2_fixed_div->hw.init = &init; |
---|
| 609 | + |
---|
| 610 | + clk = devm_clk_register(dev, &pow2_fixed_div->hw); |
---|
| 611 | + if (WARN_ON(IS_ERR(clk))) |
---|
| 612 | + return PTR_ERR(clk); |
---|
| 613 | + |
---|
| 614 | + snprintf(name, sizeof(name), "%s#pow2_div", dev_name(dev)); |
---|
| 615 | + init.name = name; |
---|
| 616 | + init.ops = &meson_spicc_pow2_clk_ops; |
---|
| 617 | + /* |
---|
| 618 | + * Set NOCACHE here to make sure we read the actual HW value |
---|
| 619 | + * since we reset the HW after each transfer. |
---|
| 620 | + */ |
---|
| 621 | + init.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE; |
---|
| 622 | + parent_data[0].hw = &pow2_fixed_div->hw; |
---|
| 623 | + init.num_parents = 1; |
---|
| 624 | + |
---|
| 625 | + spicc->pow2_div.shift = 16, |
---|
| 626 | + spicc->pow2_div.width = 3, |
---|
| 627 | + spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO, |
---|
| 628 | + spicc->pow2_div.reg = spicc->base + SPICC_CONREG; |
---|
| 629 | + spicc->pow2_div.hw.init = &init; |
---|
| 630 | + |
---|
| 631 | + spicc->clk = devm_clk_register(dev, &spicc->pow2_div.hw); |
---|
| 632 | + if (WARN_ON(IS_ERR(spicc->clk))) |
---|
| 633 | + return PTR_ERR(spicc->clk); |
---|
| 634 | + |
---|
| 635 | + return 0; |
---|
| 636 | +} |
---|
| 637 | + |
---|
| 638 | +static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc) |
---|
| 639 | +{ |
---|
| 640 | + struct device *dev = &spicc->pdev->dev; |
---|
| 641 | + struct clk_fixed_factor *enh_fixed_div; |
---|
| 642 | + struct clk_divider *enh_div; |
---|
| 643 | + struct clk_mux *mux; |
---|
| 644 | + struct clk_init_data init; |
---|
| 645 | + struct clk *clk; |
---|
| 646 | + struct clk_parent_data parent_data[2]; |
---|
| 647 | + char name[64]; |
---|
| 648 | + |
---|
| 649 | + memset(&init, 0, sizeof(init)); |
---|
| 650 | + memset(&parent_data, 0, sizeof(parent_data)); |
---|
| 651 | + |
---|
| 652 | + init.parent_data = parent_data; |
---|
| 653 | + |
---|
| 654 | + /* algorithm for enh div: rate = freq / 2 / (N + 1) */ |
---|
| 655 | + |
---|
| 656 | + enh_fixed_div = devm_kzalloc(dev, sizeof(*enh_fixed_div), GFP_KERNEL); |
---|
| 657 | + if (!enh_fixed_div) |
---|
| 658 | + return -ENOMEM; |
---|
| 659 | + |
---|
| 660 | + snprintf(name, sizeof(name), "%s#enh_fixed_div", dev_name(dev)); |
---|
| 661 | + init.name = name; |
---|
| 662 | + init.ops = &clk_fixed_factor_ops; |
---|
| 663 | + init.flags = 0; |
---|
| 664 | + if (spicc->data->has_pclk) |
---|
| 665 | + parent_data[0].hw = __clk_get_hw(spicc->pclk); |
---|
| 666 | + else |
---|
| 667 | + parent_data[0].hw = __clk_get_hw(spicc->core); |
---|
| 668 | + init.num_parents = 1; |
---|
| 669 | + |
---|
| 670 | + enh_fixed_div->mult = 1, |
---|
| 671 | + enh_fixed_div->div = 2, |
---|
| 672 | + enh_fixed_div->hw.init = &init; |
---|
| 673 | + |
---|
| 674 | + clk = devm_clk_register(dev, &enh_fixed_div->hw); |
---|
| 675 | + if (WARN_ON(IS_ERR(clk))) |
---|
| 676 | + return PTR_ERR(clk); |
---|
| 677 | + |
---|
| 678 | + enh_div = devm_kzalloc(dev, sizeof(*enh_div), GFP_KERNEL); |
---|
| 679 | + if (!enh_div) |
---|
| 680 | + return -ENOMEM; |
---|
| 681 | + |
---|
| 682 | + snprintf(name, sizeof(name), "%s#enh_div", dev_name(dev)); |
---|
| 683 | + init.name = name; |
---|
| 684 | + init.ops = &clk_divider_ops; |
---|
| 685 | + init.flags = CLK_SET_RATE_PARENT; |
---|
| 686 | + parent_data[0].hw = &enh_fixed_div->hw; |
---|
| 687 | + init.num_parents = 1; |
---|
| 688 | + |
---|
| 689 | + enh_div->shift = 16, |
---|
| 690 | + enh_div->width = 8, |
---|
| 691 | + enh_div->reg = spicc->base + SPICC_ENH_CTL0; |
---|
| 692 | + enh_div->hw.init = &init; |
---|
| 693 | + |
---|
| 694 | + clk = devm_clk_register(dev, &enh_div->hw); |
---|
| 695 | + if (WARN_ON(IS_ERR(clk))) |
---|
| 696 | + return PTR_ERR(clk); |
---|
| 697 | + |
---|
| 698 | + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); |
---|
| 699 | + if (!mux) |
---|
| 700 | + return -ENOMEM; |
---|
| 701 | + |
---|
| 702 | + snprintf(name, sizeof(name), "%s#sel", dev_name(dev)); |
---|
| 703 | + init.name = name; |
---|
| 704 | + init.ops = &clk_mux_ops; |
---|
| 705 | + parent_data[0].hw = &spicc->pow2_div.hw; |
---|
| 706 | + parent_data[1].hw = &enh_div->hw; |
---|
| 707 | + init.num_parents = 2; |
---|
| 708 | + init.flags = CLK_SET_RATE_PARENT; |
---|
| 709 | + |
---|
| 710 | + mux->mask = 0x1, |
---|
| 711 | + mux->shift = 24, |
---|
| 712 | + mux->reg = spicc->base + SPICC_ENH_CTL0; |
---|
| 713 | + mux->hw.init = &init; |
---|
| 714 | + |
---|
| 715 | + spicc->clk = devm_clk_register(dev, &mux->hw); |
---|
| 716 | + if (WARN_ON(IS_ERR(spicc->clk))) |
---|
| 717 | + return PTR_ERR(spicc->clk); |
---|
| 718 | + |
---|
| 719 | + return 0; |
---|
500 | 720 | } |
---|
501 | 721 | |
---|
502 | 722 | static int meson_spicc_probe(struct platform_device *pdev) |
---|
503 | 723 | { |
---|
504 | 724 | struct spi_master *master; |
---|
505 | 725 | struct meson_spicc_device *spicc; |
---|
506 | | - struct resource *res; |
---|
507 | | - int ret, irq, rate; |
---|
| 726 | + int ret, irq; |
---|
508 | 727 | |
---|
509 | 728 | master = spi_alloc_master(&pdev->dev, sizeof(*spicc)); |
---|
510 | 729 | if (!master) { |
---|
.. | .. |
---|
514 | 733 | spicc = spi_master_get_devdata(master); |
---|
515 | 734 | spicc->master = master; |
---|
516 | 735 | |
---|
| 736 | + spicc->data = of_device_get_match_data(&pdev->dev); |
---|
| 737 | + if (!spicc->data) { |
---|
| 738 | + dev_err(&pdev->dev, "failed to get match data\n"); |
---|
| 739 | + ret = -EINVAL; |
---|
| 740 | + goto out_master; |
---|
| 741 | + } |
---|
| 742 | + |
---|
517 | 743 | spicc->pdev = pdev; |
---|
518 | 744 | platform_set_drvdata(pdev, spicc); |
---|
519 | 745 | |
---|
520 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
521 | | - spicc->base = devm_ioremap_resource(&pdev->dev, res); |
---|
| 746 | + spicc->base = devm_platform_ioremap_resource(pdev, 0); |
---|
522 | 747 | if (IS_ERR(spicc->base)) { |
---|
523 | 748 | dev_err(&pdev->dev, "io resource mapping failed\n"); |
---|
524 | 749 | ret = PTR_ERR(spicc->base); |
---|
525 | 750 | goto out_master; |
---|
526 | 751 | } |
---|
| 752 | + |
---|
| 753 | + /* Set master mode and enable controller */ |
---|
| 754 | + writel_relaxed(SPICC_ENABLE | SPICC_MODE_MASTER, |
---|
| 755 | + spicc->base + SPICC_CONREG); |
---|
527 | 756 | |
---|
528 | 757 | /* Disable all IRQs */ |
---|
529 | 758 | writel_relaxed(0, spicc->base + SPICC_INTREG); |
---|
.. | .. |
---|
548 | 777 | goto out_master; |
---|
549 | 778 | } |
---|
550 | 779 | |
---|
| 780 | + if (spicc->data->has_pclk) { |
---|
| 781 | + spicc->pclk = devm_clk_get(&pdev->dev, "pclk"); |
---|
| 782 | + if (IS_ERR(spicc->pclk)) { |
---|
| 783 | + dev_err(&pdev->dev, "pclk clock request failed\n"); |
---|
| 784 | + ret = PTR_ERR(spicc->pclk); |
---|
| 785 | + goto out_master; |
---|
| 786 | + } |
---|
| 787 | + } |
---|
| 788 | + |
---|
551 | 789 | ret = clk_prepare_enable(spicc->core); |
---|
552 | 790 | if (ret) { |
---|
553 | 791 | dev_err(&pdev->dev, "core clock enable failed\n"); |
---|
554 | 792 | goto out_master; |
---|
555 | 793 | } |
---|
556 | | - rate = clk_get_rate(spicc->core); |
---|
| 794 | + |
---|
| 795 | + ret = clk_prepare_enable(spicc->pclk); |
---|
| 796 | + if (ret) { |
---|
| 797 | + dev_err(&pdev->dev, "pclk clock enable failed\n"); |
---|
| 798 | + goto out_core_clk; |
---|
| 799 | + } |
---|
557 | 800 | |
---|
558 | 801 | device_reset_optional(&pdev->dev); |
---|
559 | 802 | |
---|
.. | .. |
---|
565 | 808 | SPI_BPW_MASK(16) | |
---|
566 | 809 | SPI_BPW_MASK(8); |
---|
567 | 810 | master->flags = (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX); |
---|
568 | | - master->min_speed_hz = rate >> 9; |
---|
| 811 | + master->min_speed_hz = spicc->data->min_speed_hz; |
---|
| 812 | + master->max_speed_hz = spicc->data->max_speed_hz; |
---|
569 | 813 | master->setup = meson_spicc_setup; |
---|
570 | 814 | master->cleanup = meson_spicc_cleanup; |
---|
571 | 815 | master->prepare_message = meson_spicc_prepare_message; |
---|
572 | 816 | master->unprepare_transfer_hardware = meson_spicc_unprepare_transfer; |
---|
573 | 817 | master->transfer_one = meson_spicc_transfer_one; |
---|
| 818 | + master->use_gpio_descriptors = true; |
---|
574 | 819 | |
---|
575 | | - /* Setup max rate according to the Meson GX datasheet */ |
---|
576 | | - if ((rate >> 2) > SPICC_MAX_FREQ) |
---|
577 | | - master->max_speed_hz = SPICC_MAX_FREQ; |
---|
578 | | - else |
---|
579 | | - master->max_speed_hz = rate >> 2; |
---|
| 820 | + meson_spicc_oen_enable(spicc); |
---|
| 821 | + |
---|
| 822 | + ret = meson_spicc_pow2_clk_init(spicc); |
---|
| 823 | + if (ret) { |
---|
| 824 | + dev_err(&pdev->dev, "pow2 clock registration failed\n"); |
---|
| 825 | + goto out_clk; |
---|
| 826 | + } |
---|
| 827 | + |
---|
| 828 | + if (spicc->data->has_enhance_clk_div) { |
---|
| 829 | + ret = meson_spicc_enh_clk_init(spicc); |
---|
| 830 | + if (ret) { |
---|
| 831 | + dev_err(&pdev->dev, "clock registration failed\n"); |
---|
| 832 | + goto out_clk; |
---|
| 833 | + } |
---|
| 834 | + } |
---|
580 | 835 | |
---|
581 | 836 | ret = devm_spi_register_master(&pdev->dev, master); |
---|
582 | 837 | if (ret) { |
---|
.. | .. |
---|
587 | 842 | return 0; |
---|
588 | 843 | |
---|
589 | 844 | out_clk: |
---|
| 845 | + clk_disable_unprepare(spicc->pclk); |
---|
| 846 | + |
---|
| 847 | +out_core_clk: |
---|
590 | 848 | clk_disable_unprepare(spicc->core); |
---|
591 | 849 | |
---|
592 | 850 | out_master: |
---|
.. | .. |
---|
603 | 861 | writel(0, spicc->base + SPICC_CONREG); |
---|
604 | 862 | |
---|
605 | 863 | clk_disable_unprepare(spicc->core); |
---|
| 864 | + clk_disable_unprepare(spicc->pclk); |
---|
606 | 865 | |
---|
607 | 866 | spi_master_put(spicc->master); |
---|
608 | 867 | |
---|
609 | 868 | return 0; |
---|
610 | 869 | } |
---|
611 | 870 | |
---|
| 871 | +static const struct meson_spicc_data meson_spicc_gx_data = { |
---|
| 872 | + .max_speed_hz = 30000000, |
---|
| 873 | + .min_speed_hz = 325000, |
---|
| 874 | + .fifo_size = 16, |
---|
| 875 | +}; |
---|
| 876 | + |
---|
| 877 | +static const struct meson_spicc_data meson_spicc_axg_data = { |
---|
| 878 | + .max_speed_hz = 80000000, |
---|
| 879 | + .min_speed_hz = 325000, |
---|
| 880 | + .fifo_size = 16, |
---|
| 881 | + .has_oen = true, |
---|
| 882 | + .has_enhance_clk_div = true, |
---|
| 883 | +}; |
---|
| 884 | + |
---|
| 885 | +static const struct meson_spicc_data meson_spicc_g12a_data = { |
---|
| 886 | + .max_speed_hz = 166666666, |
---|
| 887 | + .min_speed_hz = 50000, |
---|
| 888 | + .fifo_size = 15, |
---|
| 889 | + .has_oen = true, |
---|
| 890 | + .has_enhance_clk_div = true, |
---|
| 891 | + .has_pclk = true, |
---|
| 892 | +}; |
---|
| 893 | + |
---|
612 | 894 | static const struct of_device_id meson_spicc_of_match[] = { |
---|
613 | | - { .compatible = "amlogic,meson-gx-spicc", }, |
---|
614 | | - { .compatible = "amlogic,meson-axg-spicc", }, |
---|
| 895 | + { |
---|
| 896 | + .compatible = "amlogic,meson-gx-spicc", |
---|
| 897 | + .data = &meson_spicc_gx_data, |
---|
| 898 | + }, |
---|
| 899 | + { |
---|
| 900 | + .compatible = "amlogic,meson-axg-spicc", |
---|
| 901 | + .data = &meson_spicc_axg_data, |
---|
| 902 | + }, |
---|
| 903 | + { |
---|
| 904 | + .compatible = "amlogic,meson-g12a-spicc", |
---|
| 905 | + .data = &meson_spicc_g12a_data, |
---|
| 906 | + }, |
---|
615 | 907 | { /* sentinel */ } |
---|
616 | 908 | }; |
---|
617 | 909 | MODULE_DEVICE_TABLE(of, meson_spicc_of_match); |
---|