diff options
author | Jonas Gorski <jogo@openwrt.org> | 2013-11-30 06:42:06 -0500 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-12-04 08:29:13 -0500 |
commit | 142168eba9dc5c20538a67049ad53c49bc6f8336 (patch) | |
tree | bf7db5d22abc27edbac18b5cd20e3c9c7316f15c /drivers | |
parent | 6ce4eac1f600b34f2f7f58f9cd8f0503d79e42ae (diff) |
spi: bcm63xx-hsspi: add bcm63xx HSSPI driver
Add a driver for the High Speed SPI controller found on newer BCM63XX SoCs.
It does feature some new modes like 3-wire or dual spi, but neither of it
is currently implemented.
Signed-off-by: Jonas Gorski <jogo@openwrt.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/Kconfig | 7 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx-hsspi.c | 484 |
3 files changed, 492 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index eb1f1ef5fa2e..62ce2a9ec612 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
@@ -118,6 +118,13 @@ config SPI_BCM63XX | |||
118 | help | 118 | help |
119 | Enable support for the SPI controller on the Broadcom BCM63xx SoCs. | 119 | Enable support for the SPI controller on the Broadcom BCM63xx SoCs. |
120 | 120 | ||
121 | config SPI_BCM63XX_HSSPI | ||
122 | tristate "Broadcom BCM63XX HS SPI controller driver" | ||
123 | depends on BCM63XX || COMPILE_TEST | ||
124 | help | ||
125 | This enables support for the High Speed SPI controller present on | ||
126 | newer Broadcom BCM63XX SoCs. | ||
127 | |||
121 | config SPI_BITBANG | 128 | config SPI_BITBANG |
122 | tristate "Utilities for Bitbanging SPI masters" | 129 | tristate "Utilities for Bitbanging SPI masters" |
123 | help | 130 | help |
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ab8d8644af0e..95af48d2d360 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_ATH79) += spi-ath79.o | |||
16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o | 16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o |
17 | obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o | 17 | obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o |
18 | obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o | 18 | obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o |
19 | obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o | ||
19 | obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o | 20 | obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o |
20 | obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o | 21 | obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o |
21 | obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o | 22 | obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o |
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c new file mode 100644 index 000000000000..bc8d848d33b7 --- /dev/null +++ b/drivers/spi/spi-bcm63xx-hsspi.c | |||
@@ -0,0 +1,484 @@ | |||
1 | /* | ||
2 | * Broadcom BCM63XX High Speed SPI Controller driver | ||
3 | * | ||
4 | * Copyright 2000-2010 Broadcom Corporation | ||
5 | * Copyright 2012-2013 Jonas Gorski <jogo@openwrt.org> | ||
6 | * | ||
7 | * Licensed under the GNU/GPL. See COPYING for details. | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/clk.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/dma-mapping.h> | ||
18 | #include <linux/err.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/spi/spi.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <linux/mutex.h> | ||
23 | |||
24 | #define HSSPI_GLOBAL_CTRL_REG 0x0 | ||
25 | #define GLOBAL_CTRL_CS_POLARITY_SHIFT 0 | ||
26 | #define GLOBAL_CTRL_CS_POLARITY_MASK 0x000000ff | ||
27 | #define GLOBAL_CTRL_PLL_CLK_CTRL_SHIFT 8 | ||
28 | #define GLOBAL_CTRL_PLL_CLK_CTRL_MASK 0x0000ff00 | ||
29 | #define GLOBAL_CTRL_CLK_GATE_SSOFF BIT(16) | ||
30 | #define GLOBAL_CTRL_CLK_POLARITY BIT(17) | ||
31 | #define GLOBAL_CTRL_MOSI_IDLE BIT(18) | ||
32 | |||
33 | #define HSSPI_GLOBAL_EXT_TRIGGER_REG 0x4 | ||
34 | |||
35 | #define HSSPI_INT_STATUS_REG 0x8 | ||
36 | #define HSSPI_INT_STATUS_MASKED_REG 0xc | ||
37 | #define HSSPI_INT_MASK_REG 0x10 | ||
38 | |||
39 | #define HSSPI_PINGx_CMD_DONE(i) BIT((i * 8) + 0) | ||
40 | #define HSSPI_PINGx_RX_OVER(i) BIT((i * 8) + 1) | ||
41 | #define HSSPI_PINGx_TX_UNDER(i) BIT((i * 8) + 2) | ||
42 | #define HSSPI_PINGx_POLL_TIMEOUT(i) BIT((i * 8) + 3) | ||
43 | #define HSSPI_PINGx_CTRL_INVAL(i) BIT((i * 8) + 4) | ||
44 | |||
45 | #define HSSPI_INT_CLEAR_ALL 0xff001f1f | ||
46 | |||
47 | #define HSSPI_PINGPONG_COMMAND_REG(x) (0x80 + (x) * 0x40) | ||
48 | #define PINGPONG_CMD_COMMAND_MASK 0xf | ||
49 | #define PINGPONG_COMMAND_NOOP 0 | ||
50 | #define PINGPONG_COMMAND_START_NOW 1 | ||
51 | #define PINGPONG_COMMAND_START_TRIGGER 2 | ||
52 | #define PINGPONG_COMMAND_HALT 3 | ||
53 | #define PINGPONG_COMMAND_FLUSH 4 | ||
54 | #define PINGPONG_CMD_PROFILE_SHIFT 8 | ||
55 | #define PINGPONG_CMD_SS_SHIFT 12 | ||
56 | |||
57 | #define HSSPI_PINGPONG_STATUS_REG(x) (0x84 + (x) * 0x40) | ||
58 | |||
59 | #define HSSPI_PROFILE_CLK_CTRL_REG(x) (0x100 + (x) * 0x20) | ||
60 | #define CLK_CTRL_FREQ_CTRL_MASK 0x0000ffff | ||
61 | #define CLK_CTRL_SPI_CLK_2X_SEL BIT(14) | ||
62 | #define CLK_CTRL_ACCUM_RST_ON_LOOP BIT(15) | ||
63 | |||
64 | #define HSSPI_PROFILE_SIGNAL_CTRL_REG(x) (0x104 + (x) * 0x20) | ||
65 | #define SIGNAL_CTRL_LATCH_RISING BIT(12) | ||
66 | #define SIGNAL_CTRL_LAUNCH_RISING BIT(13) | ||
67 | #define SIGNAL_CTRL_ASYNC_INPUT_PATH BIT(16) | ||
68 | |||
69 | #define HSSPI_PROFILE_MODE_CTRL_REG(x) (0x108 + (x) * 0x20) | ||
70 | #define MODE_CTRL_MULTIDATA_RD_STRT_SHIFT 8 | ||
71 | #define MODE_CTRL_MULTIDATA_WR_STRT_SHIFT 12 | ||
72 | #define MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT 16 | ||
73 | #define MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT 18 | ||
74 | #define MODE_CTRL_MODE_3WIRE BIT(20) | ||
75 | #define MODE_CTRL_PREPENDBYTE_CNT_SHIFT 24 | ||
76 | |||
77 | #define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200) | ||
78 | |||
79 | |||
80 | #define HSSPI_OP_CODE_SHIFT 13 | ||
81 | #define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT) | ||
82 | #define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT) | ||
83 | #define HSSPI_OP_WRITE (2 << HSSPI_OP_CODE_SHIFT) | ||
84 | #define HSSPI_OP_READ (3 << HSSPI_OP_CODE_SHIFT) | ||
85 | #define HSSPI_OP_SETIRQ (4 << HSSPI_OP_CODE_SHIFT) | ||
86 | |||
87 | #define HSSPI_BUFFER_LEN 512 | ||
88 | #define HSSPI_OPCODE_LEN 2 | ||
89 | |||
90 | #define HSSPI_MAX_PREPEND_LEN 15 | ||
91 | |||
92 | #define HSSPI_MAX_SYNC_CLOCK 30000000 | ||
93 | |||
94 | #define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */ | ||
95 | |||
96 | struct bcm63xx_hsspi { | ||
97 | struct completion done; | ||
98 | struct mutex bus_mutex; | ||
99 | |||
100 | struct platform_device *pdev; | ||
101 | struct clk *clk; | ||
102 | void __iomem *regs; | ||
103 | u8 __iomem *fifo; | ||
104 | |||
105 | u32 speed_hz; | ||
106 | u8 cs_polarity; | ||
107 | }; | ||
108 | |||
109 | static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs, | ||
110 | bool active) | ||
111 | { | ||
112 | u32 reg; | ||
113 | |||
114 | mutex_lock(&bs->bus_mutex); | ||
115 | reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
116 | |||
117 | reg &= ~BIT(cs); | ||
118 | if (active == !(bs->cs_polarity & BIT(cs))) | ||
119 | reg |= BIT(cs); | ||
120 | |||
121 | __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
122 | mutex_unlock(&bs->bus_mutex); | ||
123 | } | ||
124 | |||
125 | static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, | ||
126 | struct spi_device *spi, int hz) | ||
127 | { | ||
128 | unsigned profile = spi->chip_select; | ||
129 | u32 reg; | ||
130 | |||
131 | reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz)); | ||
132 | __raw_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | reg, | ||
133 | bs->regs + HSSPI_PROFILE_CLK_CTRL_REG(profile)); | ||
134 | |||
135 | reg = __raw_readl(bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); | ||
136 | if (hz > HSSPI_MAX_SYNC_CLOCK) | ||
137 | reg |= SIGNAL_CTRL_ASYNC_INPUT_PATH; | ||
138 | else | ||
139 | reg &= ~SIGNAL_CTRL_ASYNC_INPUT_PATH; | ||
140 | __raw_writel(reg, bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); | ||
141 | |||
142 | mutex_lock(&bs->bus_mutex); | ||
143 | /* setup clock polarity */ | ||
144 | reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
145 | reg &= ~GLOBAL_CTRL_CLK_POLARITY; | ||
146 | if (spi->mode & SPI_CPOL) | ||
147 | reg |= GLOBAL_CTRL_CLK_POLARITY; | ||
148 | __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
149 | mutex_unlock(&bs->bus_mutex); | ||
150 | } | ||
151 | |||
152 | static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t) | ||
153 | { | ||
154 | struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); | ||
155 | unsigned chip_select = spi->chip_select; | ||
156 | u16 opcode = 0; | ||
157 | int pending = t->len; | ||
158 | int step_size = HSSPI_BUFFER_LEN; | ||
159 | const u8 *tx = t->tx_buf; | ||
160 | u8 *rx = t->rx_buf; | ||
161 | |||
162 | bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz); | ||
163 | bcm63xx_hsspi_set_cs(bs, spi->chip_select, true); | ||
164 | |||
165 | if (tx && rx) | ||
166 | opcode = HSSPI_OP_READ_WRITE; | ||
167 | else if (tx) | ||
168 | opcode = HSSPI_OP_WRITE; | ||
169 | else if (rx) | ||
170 | opcode = HSSPI_OP_READ; | ||
171 | |||
172 | if (opcode != HSSPI_OP_READ) | ||
173 | step_size -= HSSPI_OPCODE_LEN; | ||
174 | |||
175 | __raw_writel(0 << MODE_CTRL_PREPENDBYTE_CNT_SHIFT | | ||
176 | 2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT | | ||
177 | 2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff, | ||
178 | bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); | ||
179 | |||
180 | while (pending > 0) { | ||
181 | int curr_step = min_t(int, step_size, pending); | ||
182 | |||
183 | init_completion(&bs->done); | ||
184 | if (tx) { | ||
185 | memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step); | ||
186 | tx += curr_step; | ||
187 | } | ||
188 | |||
189 | __raw_writew(opcode | curr_step, bs->fifo); | ||
190 | |||
191 | /* enable interrupt */ | ||
192 | __raw_writel(HSSPI_PINGx_CMD_DONE(0), | ||
193 | bs->regs + HSSPI_INT_MASK_REG); | ||
194 | |||
195 | /* start the transfer */ | ||
196 | __raw_writel(!chip_select << PINGPONG_CMD_SS_SHIFT | | ||
197 | chip_select << PINGPONG_CMD_PROFILE_SHIFT | | ||
198 | PINGPONG_COMMAND_START_NOW, | ||
199 | bs->regs + HSSPI_PINGPONG_COMMAND_REG(0)); | ||
200 | |||
201 | if (wait_for_completion_timeout(&bs->done, HZ) == 0) { | ||
202 | dev_err(&bs->pdev->dev, "transfer timed out!\n"); | ||
203 | return -ETIMEDOUT; | ||
204 | } | ||
205 | |||
206 | if (rx) { | ||
207 | memcpy_fromio(rx, bs->fifo, curr_step); | ||
208 | rx += curr_step; | ||
209 | } | ||
210 | |||
211 | pending -= curr_step; | ||
212 | } | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int bcm63xx_hsspi_setup(struct spi_device *spi) | ||
218 | { | ||
219 | struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); | ||
220 | u32 reg; | ||
221 | |||
222 | reg = __raw_readl(bs->regs + | ||
223 | HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select)); | ||
224 | reg &= ~(SIGNAL_CTRL_LAUNCH_RISING | SIGNAL_CTRL_LATCH_RISING); | ||
225 | if (spi->mode & SPI_CPHA) | ||
226 | reg |= SIGNAL_CTRL_LAUNCH_RISING; | ||
227 | else | ||
228 | reg |= SIGNAL_CTRL_LATCH_RISING; | ||
229 | __raw_writel(reg, bs->regs + | ||
230 | HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select)); | ||
231 | |||
232 | mutex_lock(&bs->bus_mutex); | ||
233 | reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
234 | |||
235 | /* only change actual polarities if there is no transfer */ | ||
236 | if ((reg & GLOBAL_CTRL_CS_POLARITY_MASK) == bs->cs_polarity) { | ||
237 | if (spi->mode & SPI_CS_HIGH) | ||
238 | reg |= BIT(spi->chip_select); | ||
239 | else | ||
240 | reg &= ~BIT(spi->chip_select); | ||
241 | __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
242 | } | ||
243 | |||
244 | if (spi->mode & SPI_CS_HIGH) | ||
245 | bs->cs_polarity |= BIT(spi->chip_select); | ||
246 | else | ||
247 | bs->cs_polarity &= ~BIT(spi->chip_select); | ||
248 | |||
249 | mutex_unlock(&bs->bus_mutex); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static int bcm63xx_hsspi_transfer_one(struct spi_master *master, | ||
255 | struct spi_message *msg) | ||
256 | { | ||
257 | struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); | ||
258 | struct spi_transfer *t; | ||
259 | struct spi_device *spi = msg->spi; | ||
260 | int status = -EINVAL; | ||
261 | int dummy_cs; | ||
262 | u32 reg; | ||
263 | |||
264 | /* This controller does not support keeping CS active during idle. | ||
265 | * To work around this, we use the following ugly hack: | ||
266 | * | ||
267 | * a. Invert the target chip select's polarity so it will be active. | ||
268 | * b. Select a "dummy" chip select to use as the hardware target. | ||
269 | * c. Invert the dummy chip select's polarity so it will be inactive | ||
270 | * during the actual transfers. | ||
271 | * d. Tell the hardware to send to the dummy chip select. Thanks to | ||
272 | * the multiplexed nature of SPI the actual target will receive | ||
273 | * the transfer and we see its response. | ||
274 | * | ||
275 | * e. At the end restore the polarities again to their default values. | ||
276 | */ | ||
277 | |||
278 | dummy_cs = !spi->chip_select; | ||
279 | bcm63xx_hsspi_set_cs(bs, dummy_cs, true); | ||
280 | |||
281 | list_for_each_entry(t, &msg->transfers, transfer_list) { | ||
282 | status = bcm63xx_hsspi_do_txrx(spi, t); | ||
283 | if (status) | ||
284 | break; | ||
285 | |||
286 | msg->actual_length += t->len; | ||
287 | |||
288 | if (t->delay_usecs) | ||
289 | udelay(t->delay_usecs); | ||
290 | |||
291 | if (t->cs_change) | ||
292 | bcm63xx_hsspi_set_cs(bs, spi->chip_select, false); | ||
293 | } | ||
294 | |||
295 | mutex_lock(&bs->bus_mutex); | ||
296 | reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
297 | reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK; | ||
298 | reg |= bs->cs_polarity; | ||
299 | __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
300 | mutex_unlock(&bs->bus_mutex); | ||
301 | |||
302 | msg->status = status; | ||
303 | spi_finalize_current_message(master); | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static irqreturn_t bcm63xx_hsspi_interrupt(int irq, void *dev_id) | ||
309 | { | ||
310 | struct bcm63xx_hsspi *bs = (struct bcm63xx_hsspi *)dev_id; | ||
311 | |||
312 | if (__raw_readl(bs->regs + HSSPI_INT_STATUS_MASKED_REG) == 0) | ||
313 | return IRQ_NONE; | ||
314 | |||
315 | __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); | ||
316 | __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); | ||
317 | |||
318 | complete(&bs->done); | ||
319 | |||
320 | return IRQ_HANDLED; | ||
321 | } | ||
322 | |||
323 | static int bcm63xx_hsspi_probe(struct platform_device *pdev) | ||
324 | { | ||
325 | struct spi_master *master; | ||
326 | struct bcm63xx_hsspi *bs; | ||
327 | struct resource *res_mem; | ||
328 | void __iomem *regs; | ||
329 | struct device *dev = &pdev->dev; | ||
330 | struct clk *clk; | ||
331 | int irq, ret; | ||
332 | u32 reg, rate; | ||
333 | |||
334 | irq = platform_get_irq(pdev, 0); | ||
335 | if (irq < 0) { | ||
336 | dev_err(dev, "no irq\n"); | ||
337 | return -ENXIO; | ||
338 | } | ||
339 | |||
340 | res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
341 | regs = devm_request_and_ioremap(dev, res_mem); | ||
342 | if (IS_ERR(regs)) | ||
343 | return PTR_ERR(regs); | ||
344 | |||
345 | clk = clk_get(dev, "hsspi"); | ||
346 | |||
347 | if (IS_ERR(clk)) | ||
348 | return PTR_ERR(clk); | ||
349 | |||
350 | rate = clk_get_rate(clk); | ||
351 | if (!rate) { | ||
352 | ret = -EINVAL; | ||
353 | goto out_put_clk; | ||
354 | } | ||
355 | |||
356 | clk_prepare_enable(clk); | ||
357 | |||
358 | master = spi_alloc_master(&pdev->dev, sizeof(*bs)); | ||
359 | if (!master) { | ||
360 | ret = -ENOMEM; | ||
361 | goto out_disable_clk; | ||
362 | } | ||
363 | |||
364 | bs = spi_master_get_devdata(master); | ||
365 | bs->pdev = pdev; | ||
366 | bs->clk = clk; | ||
367 | bs->regs = regs; | ||
368 | bs->speed_hz = rate; | ||
369 | bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0)); | ||
370 | |||
371 | mutex_init(&bs->bus_mutex); | ||
372 | |||
373 | master->bus_num = HSSPI_BUS_NUM; | ||
374 | master->num_chipselect = 8; | ||
375 | master->setup = bcm63xx_hsspi_setup; | ||
376 | master->transfer_one_message = bcm63xx_hsspi_transfer_one; | ||
377 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | ||
378 | master->bits_per_word_mask = SPI_BPW_MASK(8); | ||
379 | master->auto_runtime_pm = true; | ||
380 | |||
381 | platform_set_drvdata(pdev, master); | ||
382 | |||
383 | /* Initialize the hardware */ | ||
384 | __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); | ||
385 | |||
386 | /* clean up any pending interrupts */ | ||
387 | __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); | ||
388 | |||
389 | /* read out default CS polarities */ | ||
390 | reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
391 | bs->cs_polarity = reg & GLOBAL_CTRL_CS_POLARITY_MASK; | ||
392 | __raw_writel(reg | GLOBAL_CTRL_CLK_GATE_SSOFF, | ||
393 | bs->regs + HSSPI_GLOBAL_CTRL_REG); | ||
394 | |||
395 | ret = devm_request_irq(dev, irq, bcm63xx_hsspi_interrupt, IRQF_SHARED, | ||
396 | pdev->name, bs); | ||
397 | |||
398 | if (ret) | ||
399 | goto out_put_master; | ||
400 | |||
401 | /* register and we are done */ | ||
402 | ret = spi_register_master(master); | ||
403 | if (ret) | ||
404 | goto out_put_master; | ||
405 | |||
406 | return 0; | ||
407 | |||
408 | out_put_master: | ||
409 | spi_master_put(master); | ||
410 | out_disable_clk: | ||
411 | clk_disable_unprepare(clk); | ||
412 | out_put_clk: | ||
413 | clk_put(clk); | ||
414 | |||
415 | return ret; | ||
416 | } | ||
417 | |||
418 | |||
419 | static int bcm63xx_hsspi_remove(struct platform_device *pdev) | ||
420 | { | ||
421 | struct spi_master *master = platform_get_drvdata(pdev); | ||
422 | struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); | ||
423 | |||
424 | spi_unregister_master(master); | ||
425 | |||
426 | /* reset the hardware and block queue progress */ | ||
427 | __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); | ||
428 | clk_disable_unprepare(bs->clk); | ||
429 | clk_put(bs->clk); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | #ifdef CONFIG_PM | ||
435 | static int bcm63xx_hsspi_suspend(struct device *dev) | ||
436 | { | ||
437 | struct spi_master *master = dev_get_drvdata(dev); | ||
438 | struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); | ||
439 | |||
440 | spi_master_suspend(master); | ||
441 | clk_disable(bs->clk); | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static int bcm63xx_hsspi_resume(struct device *dev) | ||
447 | { | ||
448 | struct spi_master *master = dev_get_drvdata(dev); | ||
449 | struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); | ||
450 | |||
451 | clk_enable(bs->clk); | ||
452 | spi_master_resume(master); | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static const struct dev_pm_ops bcm63xx_hsspi_pm_ops = { | ||
458 | .suspend = bcm63xx_hsspi_suspend, | ||
459 | .resume = bcm63xx_hsspi_resume, | ||
460 | }; | ||
461 | |||
462 | #define BCM63XX_HSSPI_PM_OPS (&bcm63xx_hsspi_pm_ops) | ||
463 | #else | ||
464 | #define BCM63XX_HSSPI_PM_OPS NULL | ||
465 | #endif | ||
466 | |||
467 | |||
468 | |||
469 | static struct platform_driver bcm63xx_hsspi_driver = { | ||
470 | .driver = { | ||
471 | .name = "bcm63xx-hsspi", | ||
472 | .owner = THIS_MODULE, | ||
473 | .pm = BCM63XX_HSSPI_PM_OPS, | ||
474 | }, | ||
475 | .probe = bcm63xx_hsspi_probe, | ||
476 | .remove = bcm63xx_hsspi_remove, | ||
477 | }; | ||
478 | |||
479 | module_platform_driver(bcm63xx_hsspi_driver); | ||
480 | |||
481 | MODULE_ALIAS("platform:bcm63xx_hsspi"); | ||
482 | MODULE_DESCRIPTION("Broadcom BCM63xx High Speed SPI Controller driver"); | ||
483 | MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); | ||
484 | MODULE_LICENSE("GPL"); | ||