diff options
author | Hemanth V <hemanthv@ti.com> | 2009-09-22 19:46:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:44 -0400 |
commit | a41ae1ad907655b2efbb9b1a97736ab1451e1649 (patch) | |
tree | 2b74a410c9108bee0ff1db47ffe9a30a15bce1be /drivers | |
parent | 0644c48672a3146c01bf7314d440edecf8742d62 (diff) |
spi: McSPI off-mode support
Add context save/restore feature to McSPI driver.
Signed-off-by: Hemanth V <hemanthv@ti.com>
Reviewed-by: Aaro Koskinen <Aaro.Koskinen@nokia.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/omap2_mcspi.c | 134 |
1 files changed, 100 insertions, 34 deletions
diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index ae785cc627b6..e96ad2709565 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c | |||
@@ -41,6 +41,9 @@ | |||
41 | 41 | ||
42 | #define OMAP2_MCSPI_MAX_FREQ 48000000 | 42 | #define OMAP2_MCSPI_MAX_FREQ 48000000 |
43 | 43 | ||
44 | /* OMAP2 has 3 SPI controllers, while OMAP3 has 4 */ | ||
45 | #define OMAP2_MCSPI_MAX_CTRL 4 | ||
46 | |||
44 | #define OMAP2_MCSPI_REVISION 0x00 | 47 | #define OMAP2_MCSPI_REVISION 0x00 |
45 | #define OMAP2_MCSPI_SYSCONFIG 0x10 | 48 | #define OMAP2_MCSPI_SYSCONFIG 0x10 |
46 | #define OMAP2_MCSPI_SYSSTATUS 0x14 | 49 | #define OMAP2_MCSPI_SYSSTATUS 0x14 |
@@ -131,8 +134,21 @@ struct omap2_mcspi_cs { | |||
131 | void __iomem *base; | 134 | void __iomem *base; |
132 | unsigned long phys; | 135 | unsigned long phys; |
133 | int word_len; | 136 | int word_len; |
137 | /* Context save and restore shadow register */ | ||
138 | u32 chconf0; | ||
139 | }; | ||
140 | |||
141 | /* used for context save and restore, structure members to be updated whenever | ||
142 | * corresponding registers are modified. | ||
143 | */ | ||
144 | struct omap2_mcspi_regs { | ||
145 | u32 sysconfig; | ||
146 | u32 modulctrl; | ||
147 | u32 wakeupenable; | ||
134 | }; | 148 | }; |
135 | 149 | ||
150 | static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL]; | ||
151 | |||
136 | static struct workqueue_struct *omap2_mcspi_wq; | 152 | static struct workqueue_struct *omap2_mcspi_wq; |
137 | 153 | ||
138 | #define MOD_REG_BIT(val, mask, set) do { \ | 154 | #define MOD_REG_BIT(val, mask, set) do { \ |
@@ -172,12 +188,27 @@ static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, int idx) | |||
172 | return __raw_readl(cs->base + idx); | 188 | return __raw_readl(cs->base + idx); |
173 | } | 189 | } |
174 | 190 | ||
191 | static inline u32 mcspi_cached_chconf0(const struct spi_device *spi) | ||
192 | { | ||
193 | struct omap2_mcspi_cs *cs = spi->controller_state; | ||
194 | |||
195 | return cs->chconf0; | ||
196 | } | ||
197 | |||
198 | static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val) | ||
199 | { | ||
200 | struct omap2_mcspi_cs *cs = spi->controller_state; | ||
201 | |||
202 | cs->chconf0 = val; | ||
203 | mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, val); | ||
204 | } | ||
205 | |||
175 | static void omap2_mcspi_set_dma_req(const struct spi_device *spi, | 206 | static void omap2_mcspi_set_dma_req(const struct spi_device *spi, |
176 | int is_read, int enable) | 207 | int is_read, int enable) |
177 | { | 208 | { |
178 | u32 l, rw; | 209 | u32 l, rw; |
179 | 210 | ||
180 | l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); | 211 | l = mcspi_cached_chconf0(spi); |
181 | 212 | ||
182 | if (is_read) /* 1 is read, 0 write */ | 213 | if (is_read) /* 1 is read, 0 write */ |
183 | rw = OMAP2_MCSPI_CHCONF_DMAR; | 214 | rw = OMAP2_MCSPI_CHCONF_DMAR; |
@@ -185,7 +216,7 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi, | |||
185 | rw = OMAP2_MCSPI_CHCONF_DMAW; | 216 | rw = OMAP2_MCSPI_CHCONF_DMAW; |
186 | 217 | ||
187 | MOD_REG_BIT(l, rw, enable); | 218 | MOD_REG_BIT(l, rw, enable); |
188 | mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); | 219 | mcspi_write_chconf0(spi, l); |
189 | } | 220 | } |
190 | 221 | ||
191 | static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) | 222 | static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) |
@@ -200,9 +231,9 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) | |||
200 | { | 231 | { |
201 | u32 l; | 232 | u32 l; |
202 | 233 | ||
203 | l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); | 234 | l = mcspi_cached_chconf0(spi); |
204 | MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); | 235 | MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); |
205 | mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); | 236 | mcspi_write_chconf0(spi, l); |
206 | } | 237 | } |
207 | 238 | ||
208 | static void omap2_mcspi_set_master_mode(struct spi_master *master) | 239 | static void omap2_mcspi_set_master_mode(struct spi_master *master) |
@@ -217,6 +248,41 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) | |||
217 | MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); | 248 | MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); |
218 | MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); | 249 | MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); |
219 | mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); | 250 | mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); |
251 | |||
252 | omap2_mcspi_ctx[master->bus_num - 1].modulctrl = l; | ||
253 | } | ||
254 | |||
255 | static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) | ||
256 | { | ||
257 | struct spi_master *spi_cntrl; | ||
258 | spi_cntrl = mcspi->master; | ||
259 | |||
260 | /* McSPI: context restore */ | ||
261 | mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, | ||
262 | omap2_mcspi_ctx[spi_cntrl->bus_num - 1].modulctrl); | ||
263 | |||
264 | mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_SYSCONFIG, | ||
265 | omap2_mcspi_ctx[spi_cntrl->bus_num - 1].sysconfig); | ||
266 | |||
267 | mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, | ||
268 | omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable); | ||
269 | } | ||
270 | static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) | ||
271 | { | ||
272 | clk_disable(mcspi->ick); | ||
273 | clk_disable(mcspi->fck); | ||
274 | } | ||
275 | |||
276 | static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) | ||
277 | { | ||
278 | if (clk_enable(mcspi->ick)) | ||
279 | return -ENODEV; | ||
280 | if (clk_enable(mcspi->fck)) | ||
281 | return -ENODEV; | ||
282 | |||
283 | omap2_mcspi_restore_ctx(mcspi); | ||
284 | |||
285 | return 0; | ||
220 | } | 286 | } |
221 | 287 | ||
222 | static unsigned | 288 | static unsigned |
@@ -357,7 +423,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
357 | c = count; | 423 | c = count; |
358 | word_len = cs->word_len; | 424 | word_len = cs->word_len; |
359 | 425 | ||
360 | l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); | 426 | l = mcspi_cached_chconf0(spi); |
361 | l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; | 427 | l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; |
362 | 428 | ||
363 | /* We store the pre-calculated register addresses on stack to speed | 429 | /* We store the pre-calculated register addresses on stack to speed |
@@ -397,8 +463,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
397 | * more word i/o: switch to rx+tx | 463 | * more word i/o: switch to rx+tx |
398 | */ | 464 | */ |
399 | if (c == 0 && tx == NULL) | 465 | if (c == 0 && tx == NULL) |
400 | mcspi_write_cs_reg(spi, | 466 | mcspi_write_chconf0(spi, l); |
401 | OMAP2_MCSPI_CHCONF0, l); | ||
402 | *rx++ = __raw_readl(rx_reg); | 467 | *rx++ = __raw_readl(rx_reg); |
403 | #ifdef VERBOSE | 468 | #ifdef VERBOSE |
404 | dev_dbg(&spi->dev, "read-%d %02x\n", | 469 | dev_dbg(&spi->dev, "read-%d %02x\n", |
@@ -436,8 +501,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
436 | * more word i/o: switch to rx+tx | 501 | * more word i/o: switch to rx+tx |
437 | */ | 502 | */ |
438 | if (c == 0 && tx == NULL) | 503 | if (c == 0 && tx == NULL) |
439 | mcspi_write_cs_reg(spi, | 504 | mcspi_write_chconf0(spi, l); |
440 | OMAP2_MCSPI_CHCONF0, l); | ||
441 | *rx++ = __raw_readl(rx_reg); | 505 | *rx++ = __raw_readl(rx_reg); |
442 | #ifdef VERBOSE | 506 | #ifdef VERBOSE |
443 | dev_dbg(&spi->dev, "read-%d %04x\n", | 507 | dev_dbg(&spi->dev, "read-%d %04x\n", |
@@ -475,8 +539,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
475 | * more word i/o: switch to rx+tx | 539 | * more word i/o: switch to rx+tx |
476 | */ | 540 | */ |
477 | if (c == 0 && tx == NULL) | 541 | if (c == 0 && tx == NULL) |
478 | mcspi_write_cs_reg(spi, | 542 | mcspi_write_chconf0(spi, l); |
479 | OMAP2_MCSPI_CHCONF0, l); | ||
480 | *rx++ = __raw_readl(rx_reg); | 543 | *rx++ = __raw_readl(rx_reg); |
481 | #ifdef VERBOSE | 544 | #ifdef VERBOSE |
482 | dev_dbg(&spi->dev, "read-%d %04x\n", | 545 | dev_dbg(&spi->dev, "read-%d %04x\n", |
@@ -505,10 +568,12 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, | |||
505 | { | 568 | { |
506 | struct omap2_mcspi_cs *cs = spi->controller_state; | 569 | struct omap2_mcspi_cs *cs = spi->controller_state; |
507 | struct omap2_mcspi *mcspi; | 570 | struct omap2_mcspi *mcspi; |
571 | struct spi_master *spi_cntrl; | ||
508 | u32 l = 0, div = 0; | 572 | u32 l = 0, div = 0; |
509 | u8 word_len = spi->bits_per_word; | 573 | u8 word_len = spi->bits_per_word; |
510 | 574 | ||
511 | mcspi = spi_master_get_devdata(spi->master); | 575 | mcspi = spi_master_get_devdata(spi->master); |
576 | spi_cntrl = mcspi->master; | ||
512 | 577 | ||
513 | if (t != NULL && t->bits_per_word) | 578 | if (t != NULL && t->bits_per_word) |
514 | word_len = t->bits_per_word; | 579 | word_len = t->bits_per_word; |
@@ -522,7 +587,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, | |||
522 | } else | 587 | } else |
523 | div = 15; | 588 | div = 15; |
524 | 589 | ||
525 | l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); | 590 | l = mcspi_cached_chconf0(spi); |
526 | 591 | ||
527 | /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS | 592 | /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS |
528 | * REVISIT: this controller could support SPI_3WIRE mode. | 593 | * REVISIT: this controller could support SPI_3WIRE mode. |
@@ -554,7 +619,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, | |||
554 | else | 619 | else |
555 | l &= ~OMAP2_MCSPI_CHCONF_PHA; | 620 | l &= ~OMAP2_MCSPI_CHCONF_PHA; |
556 | 621 | ||
557 | mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); | 622 | mcspi_write_chconf0(spi, l); |
558 | 623 | ||
559 | dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", | 624 | dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", |
560 | OMAP2_MCSPI_MAX_FREQ / (1 << div), | 625 | OMAP2_MCSPI_MAX_FREQ / (1 << div), |
@@ -647,6 +712,7 @@ static int omap2_mcspi_setup(struct spi_device *spi) | |||
647 | return -ENOMEM; | 712 | return -ENOMEM; |
648 | cs->base = mcspi->base + spi->chip_select * 0x14; | 713 | cs->base = mcspi->base + spi->chip_select * 0x14; |
649 | cs->phys = mcspi->phys + spi->chip_select * 0x14; | 714 | cs->phys = mcspi->phys + spi->chip_select * 0x14; |
715 | cs->chconf0 = 0; | ||
650 | spi->controller_state = cs; | 716 | spi->controller_state = cs; |
651 | } | 717 | } |
652 | 718 | ||
@@ -657,11 +723,11 @@ static int omap2_mcspi_setup(struct spi_device *spi) | |||
657 | return ret; | 723 | return ret; |
658 | } | 724 | } |
659 | 725 | ||
660 | clk_enable(mcspi->ick); | 726 | if (omap2_mcspi_enable_clocks(mcspi)) |
661 | clk_enable(mcspi->fck); | 727 | return -ENODEV; |
728 | |||
662 | ret = omap2_mcspi_setup_transfer(spi, NULL); | 729 | ret = omap2_mcspi_setup_transfer(spi, NULL); |
663 | clk_disable(mcspi->fck); | 730 | omap2_mcspi_disable_clocks(mcspi); |
664 | clk_disable(mcspi->ick); | ||
665 | 731 | ||
666 | return ret; | 732 | return ret; |
667 | } | 733 | } |
@@ -693,8 +759,8 @@ static void omap2_mcspi_work(struct work_struct *work) | |||
693 | mcspi = container_of(work, struct omap2_mcspi, work); | 759 | mcspi = container_of(work, struct omap2_mcspi, work); |
694 | spin_lock_irq(&mcspi->lock); | 760 | spin_lock_irq(&mcspi->lock); |
695 | 761 | ||
696 | clk_enable(mcspi->ick); | 762 | if (omap2_mcspi_enable_clocks(mcspi)) |
697 | clk_enable(mcspi->fck); | 763 | goto out; |
698 | 764 | ||
699 | /* We only enable one channel at a time -- the one whose message is | 765 | /* We only enable one channel at a time -- the one whose message is |
700 | * at the head of the queue -- although this controller would gladly | 766 | * at the head of the queue -- although this controller would gladly |
@@ -741,13 +807,13 @@ static void omap2_mcspi_work(struct work_struct *work) | |||
741 | cs_active = 1; | 807 | cs_active = 1; |
742 | } | 808 | } |
743 | 809 | ||
744 | chconf = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); | 810 | chconf = mcspi_cached_chconf0(spi); |
745 | chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; | 811 | chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; |
746 | if (t->tx_buf == NULL) | 812 | if (t->tx_buf == NULL) |
747 | chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; | 813 | chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; |
748 | else if (t->rx_buf == NULL) | 814 | else if (t->rx_buf == NULL) |
749 | chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; | 815 | chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; |
750 | mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, chconf); | 816 | mcspi_write_chconf0(spi, chconf); |
751 | 817 | ||
752 | if (t->len) { | 818 | if (t->len) { |
753 | unsigned count; | 819 | unsigned count; |
@@ -796,9 +862,9 @@ static void omap2_mcspi_work(struct work_struct *work) | |||
796 | spin_lock_irq(&mcspi->lock); | 862 | spin_lock_irq(&mcspi->lock); |
797 | } | 863 | } |
798 | 864 | ||
799 | clk_disable(mcspi->fck); | 865 | omap2_mcspi_disable_clocks(mcspi); |
800 | clk_disable(mcspi->ick); | ||
801 | 866 | ||
867 | out: | ||
802 | spin_unlock_irq(&mcspi->lock); | 868 | spin_unlock_irq(&mcspi->lock); |
803 | } | 869 | } |
804 | 870 | ||
@@ -885,8 +951,8 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) | |||
885 | struct spi_master *master = mcspi->master; | 951 | struct spi_master *master = mcspi->master; |
886 | u32 tmp; | 952 | u32 tmp; |
887 | 953 | ||
888 | clk_enable(mcspi->ick); | 954 | if (omap2_mcspi_enable_clocks(mcspi)) |
889 | clk_enable(mcspi->fck); | 955 | return -1; |
890 | 956 | ||
891 | mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, | 957 | mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, |
892 | OMAP2_MCSPI_SYSCONFIG_SOFTRESET); | 958 | OMAP2_MCSPI_SYSCONFIG_SOFTRESET); |
@@ -894,18 +960,18 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi) | |||
894 | tmp = mcspi_read_reg(master, OMAP2_MCSPI_SYSSTATUS); | 960 | tmp = mcspi_read_reg(master, OMAP2_MCSPI_SYSSTATUS); |
895 | } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); | 961 | } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); |
896 | 962 | ||
897 | mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, | 963 | tmp = OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | |
898 | OMAP2_MCSPI_SYSCONFIG_AUTOIDLE | | 964 | OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | |
899 | OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP | | 965 | OMAP2_MCSPI_SYSCONFIG_SMARTIDLE; |
900 | OMAP2_MCSPI_SYSCONFIG_SMARTIDLE); | 966 | mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, tmp); |
967 | omap2_mcspi_ctx[master->bus_num - 1].sysconfig = tmp; | ||
901 | 968 | ||
902 | mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, | 969 | tmp = OMAP2_MCSPI_WAKEUPENABLE_WKEN; |
903 | OMAP2_MCSPI_WAKEUPENABLE_WKEN); | 970 | mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, tmp); |
971 | omap2_mcspi_ctx[master->bus_num - 1].wakeupenable = tmp; | ||
904 | 972 | ||
905 | omap2_mcspi_set_master_mode(master); | 973 | omap2_mcspi_set_master_mode(master); |
906 | 974 | omap2_mcspi_disable_clocks(mcspi); | |
907 | clk_disable(mcspi->fck); | ||
908 | clk_disable(mcspi->ick); | ||
909 | return 0; | 975 | return 0; |
910 | } | 976 | } |
911 | 977 | ||