aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-sh-hspi.c
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2012-03-01 20:10:17 -0500
committerGrant Likely <grant.likely@secretlab.ca>2012-03-09 11:50:09 -0500
commitd1c8bbd793e4c2f346f8788ad312f5b5b530aff5 (patch)
tree136cf736f0a21f4414fa26ea480848e48e9aadbe /drivers/spi/spi-sh-hspi.c
parentffbbdd21329f3e15eeca6df2d4bc11c04d9d91c0 (diff)
spi: Add SuperH HSPI prototype driver
This patch adds SuperH HSPI driver. It is still prototype driver, but has enough function at this point. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/spi/spi-sh-hspi.c')
-rw-r--r--drivers/spi/spi-sh-hspi.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c
new file mode 100644
index 000000000000..8356ec8cfda2
--- /dev/null
+++ b/drivers/spi/spi-sh-hspi.c
@@ -0,0 +1,364 @@
1/*
2 * SuperH HSPI bus driver
3 *
4 * Copyright (C) 2011 Kuninori Morimoto
5 *
6 * Based on spi-sh.c:
7 * Based on pxa2xx_spi.c:
8 * Copyright (C) 2011 Renesas Solutions Corp.
9 * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2 of the License.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 *
24 */
25#include <linux/module.h>
26#include <linux/kernel.h>
27#include <linux/timer.h>
28#include <linux/delay.h>
29#include <linux/list.h>
30#include <linux/workqueue.h>
31#include <linux/interrupt.h>
32#include <linux/platform_device.h>
33#include <linux/pm_runtime.h>
34#include <linux/io.h>
35#include <linux/spi/spi.h>
36#include <linux/spi/sh_hspi.h>
37
38#define SPCR 0x00
39#define SPSR 0x04
40#define SPSCR 0x08
41#define SPTBR 0x0C
42#define SPRBR 0x10
43#define SPCR2 0x14
44
45/* SPSR */
46#define RXFL (1 << 2)
47
48#define hspi2info(h) (h->dev->platform_data)
49
50struct hspi_priv {
51 void __iomem *addr;
52 struct spi_master *master;
53 struct list_head queue;
54 struct workqueue_struct *workqueue;
55 struct work_struct ws;
56 struct device *dev;
57 spinlock_t lock;
58};
59
60/*
61 * basic function
62 */
63static void hspi_write(struct hspi_priv *hspi, int reg, u32 val)
64{
65 iowrite32(val, hspi->addr + reg);
66}
67
68static u32 hspi_read(struct hspi_priv *hspi, int reg)
69{
70 return ioread32(hspi->addr + reg);
71}
72
73/*
74 * transfer function
75 */
76static int hspi_status_check_timeout(struct hspi_priv *hspi, u32 mask, u32 val)
77{
78 int t = 256;
79
80 while (t--) {
81 if ((mask & hspi_read(hspi, SPSR)) == val)
82 return 0;
83
84 msleep(20);
85 }
86
87 dev_err(hspi->dev, "timeout\n");
88 return -ETIMEDOUT;
89}
90
91static int hspi_push(struct hspi_priv *hspi, struct spi_message *msg,
92 struct spi_transfer *t)
93{
94 int i, ret;
95 u8 *data = (u8 *)t->tx_buf;
96
97 /*
98 * FIXME
99 * very simple, but polling transfer
100 */
101 for (i = 0; i < t->len; i++) {
102 /* wait remains */
103 ret = hspi_status_check_timeout(hspi, 0x1, 0x0);
104 if (ret < 0)
105 return ret;
106
107 hspi_write(hspi, SPTBR, (u32)data[i]);
108
109 /* wait recive */
110 ret = hspi_status_check_timeout(hspi, 0x4, 0x4);
111 if (ret < 0)
112 return ret;
113
114 /* dummy read */
115 hspi_read(hspi, SPRBR);
116 }
117
118 return 0;
119}
120
121static int hspi_pop(struct hspi_priv *hspi, struct spi_message *msg,
122 struct spi_transfer *t)
123{
124 int i, ret;
125 u8 *data = (u8 *)t->rx_buf;
126
127 /*
128 * FIXME
129 * very simple, but polling receive
130 */
131 for (i = 0; i < t->len; i++) {
132 /* wait remains */
133 ret = hspi_status_check_timeout(hspi, 0x1, 0);
134 if (ret < 0)
135 return ret;
136
137 /* dummy write */
138 hspi_write(hspi, SPTBR, 0x0);
139
140 /* wait recive */
141 ret = hspi_status_check_timeout(hspi, 0x4, 0x4);
142 if (ret < 0)
143 return ret;
144
145 data[i] = (u8)hspi_read(hspi, SPRBR);
146 }
147
148 return 0;
149}
150
151static void hspi_work(struct work_struct *work)
152{
153 struct hspi_priv *hspi = container_of(work, struct hspi_priv, ws);
154 struct sh_hspi_info *info = hspi2info(hspi);
155 struct spi_message *msg;
156 struct spi_transfer *t;
157 unsigned long flags;
158 u32 data;
159 int ret;
160
161 dev_dbg(hspi->dev, "%s\n", __func__);
162
163 /************************ pm enable ************************/
164 pm_runtime_get_sync(hspi->dev);
165
166 /* setup first of all in under pm_runtime */
167 data = SH_HSPI_CLK_DIVC(info->flags);
168
169 if (info->flags & SH_HSPI_FBS)
170 data |= 1 << 7;
171 if (info->flags & SH_HSPI_CLKP_HIGH)
172 data |= 1 << 6;
173 if (info->flags & SH_HSPI_IDIV_DIV128)
174 data |= 1 << 5;
175
176 hspi_write(hspi, SPCR, data);
177 hspi_write(hspi, SPSR, 0x0);
178 hspi_write(hspi, SPSCR, 0x1); /* master mode */
179
180 while (1) {
181 msg = NULL;
182
183 /************************ spin lock ************************/
184 spin_lock_irqsave(&hspi->lock, flags);
185 if (!list_empty(&hspi->queue)) {
186 msg = list_entry(hspi->queue.next,
187 struct spi_message, queue);
188 list_del_init(&msg->queue);
189 }
190 spin_unlock_irqrestore(&hspi->lock, flags);
191 /************************ spin unlock ************************/
192 if (!msg)
193 break;
194
195 ret = 0;
196 list_for_each_entry(t, &msg->transfers, transfer_list) {
197 if (t->tx_buf) {
198 ret = hspi_push(hspi, msg, t);
199 if (ret < 0)
200 goto error;
201 }
202 if (t->rx_buf) {
203 ret = hspi_pop(hspi, msg, t);
204 if (ret < 0)
205 goto error;
206 }
207 msg->actual_length += t->len;
208 }
209error:
210 msg->status = ret;
211 msg->complete(msg->context);
212 }
213
214 pm_runtime_put_sync(hspi->dev);
215 /************************ pm disable ************************/
216
217 return;
218}
219
220/*
221 * spi master function
222 */
223static int hspi_setup(struct spi_device *spi)
224{
225 struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
226 struct device *dev = hspi->dev;
227
228 if (8 != spi->bits_per_word) {
229 dev_err(dev, "bits_per_word should be 8\n");
230 return -EIO;
231 }
232
233 dev_dbg(dev, "%s setup\n", spi->modalias);
234
235 return 0;
236}
237
238static void hspi_cleanup(struct spi_device *spi)
239{
240 struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
241 struct device *dev = hspi->dev;
242
243 dev_dbg(dev, "%s cleanup\n", spi->modalias);
244}
245
246static int hspi_transfer(struct spi_device *spi, struct spi_message *msg)
247{
248 struct hspi_priv *hspi = spi_master_get_devdata(spi->master);
249 unsigned long flags;
250
251 /************************ spin lock ************************/
252 spin_lock_irqsave(&hspi->lock, flags);
253
254 msg->actual_length = 0;
255 msg->status = -EINPROGRESS;
256 list_add_tail(&msg->queue, &hspi->queue);
257
258 spin_unlock_irqrestore(&hspi->lock, flags);
259 /************************ spin unlock ************************/
260
261 queue_work(hspi->workqueue, &hspi->ws);
262
263 return 0;
264}
265
266static int __devinit hspi_probe(struct platform_device *pdev)
267{
268 struct resource *res;
269 struct spi_master *master;
270 struct hspi_priv *hspi;
271 int ret;
272
273 /* get base addr */
274 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
275 if (!res) {
276 dev_err(&pdev->dev, "invalid resource\n");
277 return -EINVAL;
278 }
279
280 master = spi_alloc_master(&pdev->dev, sizeof(*hspi));
281 if (!master) {
282 dev_err(&pdev->dev, "spi_alloc_master error.\n");
283 return -ENOMEM;
284 }
285
286 hspi = spi_master_get_devdata(master);
287 dev_set_drvdata(&pdev->dev, hspi);
288
289 /* init hspi */
290 hspi->master = master;
291 hspi->dev = &pdev->dev;
292 hspi->addr = devm_ioremap(hspi->dev,
293 res->start, resource_size(res));
294 if (!hspi->addr) {
295 dev_err(&pdev->dev, "ioremap error.\n");
296 ret = -ENOMEM;
297 goto error1;
298 }
299 hspi->workqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
300 if (!hspi->workqueue) {
301 dev_err(&pdev->dev, "create workqueue error\n");
302 ret = -EBUSY;
303 goto error2;
304 }
305
306 spin_lock_init(&hspi->lock);
307 INIT_LIST_HEAD(&hspi->queue);
308 INIT_WORK(&hspi->ws, hspi_work);
309
310 master->num_chipselect = 1;
311 master->bus_num = pdev->id;
312 master->setup = hspi_setup;
313 master->transfer = hspi_transfer;
314 master->cleanup = hspi_cleanup;
315 master->mode_bits = SPI_CPOL | SPI_CPHA;
316 ret = spi_register_master(master);
317 if (ret < 0) {
318 dev_err(&pdev->dev, "spi_register_master error.\n");
319 goto error3;
320 }
321
322 pm_runtime_enable(&pdev->dev);
323
324 dev_info(&pdev->dev, "probed\n");
325
326 return 0;
327
328 error3:
329 destroy_workqueue(hspi->workqueue);
330 error2:
331 devm_iounmap(hspi->dev, hspi->addr);
332 error1:
333 spi_master_put(master);
334
335 return ret;
336}
337
338static int __devexit hspi_remove(struct platform_device *pdev)
339{
340 struct hspi_priv *hspi = dev_get_drvdata(&pdev->dev);
341
342 pm_runtime_disable(&pdev->dev);
343
344 spi_unregister_master(hspi->master);
345 destroy_workqueue(hspi->workqueue);
346 devm_iounmap(hspi->dev, hspi->addr);
347
348 return 0;
349}
350
351static struct platform_driver hspi_driver = {
352 .probe = hspi_probe,
353 .remove = __devexit_p(hspi_remove),
354 .driver = {
355 .name = "sh-hspi",
356 .owner = THIS_MODULE,
357 },
358};
359module_platform_driver(hspi_driver);
360
361MODULE_DESCRIPTION("SuperH HSPI bus driver");
362MODULE_LICENSE("GPL");
363MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
364MODULE_ALIAS("platform:sh_spi");