diff options
author | Mingkai Hu <Mingkai.hu@freescale.com> | 2010-10-12 06:18:31 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-10-12 23:38:12 -0400 |
commit | b36ece832512c1a0afa54ff0a56d63492a1caf08 (patch) | |
tree | 1c8a35d3dd485eb80a56355b5d5cb3bc4b4b2699 /drivers/spi/spi_fsl_lib.c | |
parent | 3272029fb33a88873b9b02224ebeb23bf3a4668e (diff) |
spi/mpc8xxx: refactor the common code for SPI/eSPI controller
Refactor the common code in file spi_fsl_spi.c to spi_fsl_lib.c used
by SPI/eSPI controller driver as a library, and leave the QE/CPM SPI
controller code in the SPI controller driver spi_fsl_spi.c.
Because the register map of the SPI controller and eSPI controller
is so different, also leave the code operated the register to the
driver code, not the common code.
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/spi/spi_fsl_lib.c')
-rw-r--r-- | drivers/spi/spi_fsl_lib.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c new file mode 100644 index 000000000000..5cd741fdb5c3 --- /dev/null +++ b/drivers/spi/spi_fsl_lib.c | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * Freescale SPI/eSPI controller driver library. | ||
3 | * | ||
4 | * Maintainer: Kumar Gala | ||
5 | * | ||
6 | * Copyright (C) 2006 Polycom, Inc. | ||
7 | * | ||
8 | * CPM SPI and QE buffer descriptors mode support: | ||
9 | * Copyright (c) 2009 MontaVista Software, Inc. | ||
10 | * Author: Anton Vorontsov <avorontsov@ru.mvista.com> | ||
11 | * | ||
12 | * Copyright 2010 Freescale Semiconductor, Inc. | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify it | ||
15 | * under the terms of the GNU General Public License as published by the | ||
16 | * Free Software Foundation; either version 2 of the License, or (at your | ||
17 | * option) any later version. | ||
18 | */ | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/fsl_devices.h> | ||
22 | #include <linux/dma-mapping.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/of_platform.h> | ||
25 | #include <linux/of_spi.h> | ||
26 | #include <sysdev/fsl_soc.h> | ||
27 | |||
28 | #include "spi_fsl_lib.h" | ||
29 | |||
30 | #define MPC8XXX_SPI_RX_BUF(type) \ | ||
31 | void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ | ||
32 | { \ | ||
33 | type *rx = mpc8xxx_spi->rx; \ | ||
34 | *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ | ||
35 | mpc8xxx_spi->rx = rx; \ | ||
36 | } | ||
37 | |||
38 | #define MPC8XXX_SPI_TX_BUF(type) \ | ||
39 | u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ | ||
40 | { \ | ||
41 | u32 data; \ | ||
42 | const type *tx = mpc8xxx_spi->tx; \ | ||
43 | if (!tx) \ | ||
44 | return 0; \ | ||
45 | data = *tx++ << mpc8xxx_spi->tx_shift; \ | ||
46 | mpc8xxx_spi->tx = tx; \ | ||
47 | return data; \ | ||
48 | } | ||
49 | |||
50 | MPC8XXX_SPI_RX_BUF(u8) | ||
51 | MPC8XXX_SPI_RX_BUF(u16) | ||
52 | MPC8XXX_SPI_RX_BUF(u32) | ||
53 | MPC8XXX_SPI_TX_BUF(u8) | ||
54 | MPC8XXX_SPI_TX_BUF(u16) | ||
55 | MPC8XXX_SPI_TX_BUF(u32) | ||
56 | |||
57 | struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata) | ||
58 | { | ||
59 | return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); | ||
60 | } | ||
61 | |||
62 | void mpc8xxx_spi_work(struct work_struct *work) | ||
63 | { | ||
64 | struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi, | ||
65 | work); | ||
66 | |||
67 | spin_lock_irq(&mpc8xxx_spi->lock); | ||
68 | while (!list_empty(&mpc8xxx_spi->queue)) { | ||
69 | struct spi_message *m = container_of(mpc8xxx_spi->queue.next, | ||
70 | struct spi_message, queue); | ||
71 | |||
72 | list_del_init(&m->queue); | ||
73 | spin_unlock_irq(&mpc8xxx_spi->lock); | ||
74 | |||
75 | if (mpc8xxx_spi->spi_do_one_msg) | ||
76 | mpc8xxx_spi->spi_do_one_msg(m); | ||
77 | |||
78 | spin_lock_irq(&mpc8xxx_spi->lock); | ||
79 | } | ||
80 | spin_unlock_irq(&mpc8xxx_spi->lock); | ||
81 | } | ||
82 | |||
83 | int mpc8xxx_spi_transfer(struct spi_device *spi, | ||
84 | struct spi_message *m) | ||
85 | { | ||
86 | struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); | ||
87 | unsigned long flags; | ||
88 | |||
89 | m->actual_length = 0; | ||
90 | m->status = -EINPROGRESS; | ||
91 | |||
92 | spin_lock_irqsave(&mpc8xxx_spi->lock, flags); | ||
93 | list_add_tail(&m->queue, &mpc8xxx_spi->queue); | ||
94 | queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work); | ||
95 | spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | void mpc8xxx_spi_cleanup(struct spi_device *spi) | ||
101 | { | ||
102 | kfree(spi->controller_state); | ||
103 | } | ||
104 | |||
105 | const char *mpc8xxx_spi_strmode(unsigned int flags) | ||
106 | { | ||
107 | if (flags & SPI_QE_CPU_MODE) { | ||
108 | return "QE CPU"; | ||
109 | } else if (flags & SPI_CPM_MODE) { | ||
110 | if (flags & SPI_QE) | ||
111 | return "QE"; | ||
112 | else if (flags & SPI_CPM2) | ||
113 | return "CPM2"; | ||
114 | else | ||
115 | return "CPM1"; | ||
116 | } | ||
117 | return "CPU"; | ||
118 | } | ||
119 | |||
120 | int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, | ||
121 | unsigned int irq) | ||
122 | { | ||
123 | struct fsl_spi_platform_data *pdata = dev->platform_data; | ||
124 | struct spi_master *master; | ||
125 | struct mpc8xxx_spi *mpc8xxx_spi; | ||
126 | int ret = 0; | ||
127 | |||
128 | master = dev_get_drvdata(dev); | ||
129 | |||
130 | /* the spi->mode bits understood by this driver: */ | ||
131 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | ||
132 | | SPI_LSB_FIRST | SPI_LOOP; | ||
133 | |||
134 | master->transfer = mpc8xxx_spi_transfer; | ||
135 | master->cleanup = mpc8xxx_spi_cleanup; | ||
136 | master->dev.of_node = dev->of_node; | ||
137 | |||
138 | mpc8xxx_spi = spi_master_get_devdata(master); | ||
139 | mpc8xxx_spi->dev = dev; | ||
140 | mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8; | ||
141 | mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8; | ||
142 | mpc8xxx_spi->flags = pdata->flags; | ||
143 | mpc8xxx_spi->spibrg = pdata->sysclk; | ||
144 | mpc8xxx_spi->irq = irq; | ||
145 | |||
146 | mpc8xxx_spi->rx_shift = 0; | ||
147 | mpc8xxx_spi->tx_shift = 0; | ||
148 | |||
149 | init_completion(&mpc8xxx_spi->done); | ||
150 | |||
151 | master->bus_num = pdata->bus_num; | ||
152 | master->num_chipselect = pdata->max_chipselect; | ||
153 | |||
154 | spin_lock_init(&mpc8xxx_spi->lock); | ||
155 | init_completion(&mpc8xxx_spi->done); | ||
156 | INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work); | ||
157 | INIT_LIST_HEAD(&mpc8xxx_spi->queue); | ||
158 | |||
159 | mpc8xxx_spi->workqueue = create_singlethread_workqueue( | ||
160 | dev_name(master->dev.parent)); | ||
161 | if (mpc8xxx_spi->workqueue == NULL) { | ||
162 | ret = -EBUSY; | ||
163 | goto err; | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | |||
168 | err: | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | int __devexit mpc8xxx_spi_remove(struct device *dev) | ||
173 | { | ||
174 | struct mpc8xxx_spi *mpc8xxx_spi; | ||
175 | struct spi_master *master; | ||
176 | |||
177 | master = dev_get_drvdata(dev); | ||
178 | mpc8xxx_spi = spi_master_get_devdata(master); | ||
179 | |||
180 | flush_workqueue(mpc8xxx_spi->workqueue); | ||
181 | destroy_workqueue(mpc8xxx_spi->workqueue); | ||
182 | spi_unregister_master(master); | ||
183 | |||
184 | free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); | ||
185 | |||
186 | if (mpc8xxx_spi->spi_remove) | ||
187 | mpc8xxx_spi->spi_remove(mpc8xxx_spi); | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev, | ||
193 | const struct of_device_id *ofid) | ||
194 | { | ||
195 | struct device *dev = &ofdev->dev; | ||
196 | struct device_node *np = ofdev->dev.of_node; | ||
197 | struct mpc8xxx_spi_probe_info *pinfo; | ||
198 | struct fsl_spi_platform_data *pdata; | ||
199 | const void *prop; | ||
200 | int ret = -ENOMEM; | ||
201 | |||
202 | pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); | ||
203 | if (!pinfo) | ||
204 | return -ENOMEM; | ||
205 | |||
206 | pdata = &pinfo->pdata; | ||
207 | dev->platform_data = pdata; | ||
208 | |||
209 | /* Allocate bus num dynamically. */ | ||
210 | pdata->bus_num = -1; | ||
211 | |||
212 | /* SPI controller is either clocked from QE or SoC clock. */ | ||
213 | pdata->sysclk = get_brgfreq(); | ||
214 | if (pdata->sysclk == -1) { | ||
215 | pdata->sysclk = fsl_get_sys_freq(); | ||
216 | if (pdata->sysclk == -1) { | ||
217 | ret = -ENODEV; | ||
218 | goto err; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | prop = of_get_property(np, "mode", NULL); | ||
223 | if (prop && !strcmp(prop, "cpu-qe")) | ||
224 | pdata->flags = SPI_QE_CPU_MODE; | ||
225 | else if (prop && !strcmp(prop, "qe")) | ||
226 | pdata->flags = SPI_CPM_MODE | SPI_QE; | ||
227 | else if (of_device_is_compatible(np, "fsl,cpm2-spi")) | ||
228 | pdata->flags = SPI_CPM_MODE | SPI_CPM2; | ||
229 | else if (of_device_is_compatible(np, "fsl,cpm1-spi")) | ||
230 | pdata->flags = SPI_CPM_MODE | SPI_CPM1; | ||
231 | |||
232 | return 0; | ||
233 | |||
234 | err: | ||
235 | kfree(pinfo); | ||
236 | return ret; | ||
237 | } | ||