1/*
2 * Copyright (c) 2009 Nuvoton technology.
3 * Wan ZongShun <mcuos.com@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 */
10
11#include <linux/module.h>
12#include <linux/spinlock.h>
13#include <linux/interrupt.h>
14#include <linux/delay.h>
15#include <linux/errno.h>
16#include <linux/err.h>
17#include <linux/clk.h>
18#include <linux/device.h>
19#include <linux/platform_device.h>
20#include <linux/gpio.h>
21#include <linux/io.h>
22#include <linux/slab.h>
23
24#include <linux/spi/spi.h>
25#include <linux/spi/spi_bitbang.h>
26
27#include <linux/platform_data/spi-nuc900.h>
28
29/* usi registers offset */
30#define USI_CNT		0x00
31#define USI_DIV		0x04
32#define USI_SSR		0x08
33#define USI_RX0		0x10
34#define USI_TX0		0x10
35
36/* usi register bit */
37#define ENINT		(0x01 << 17)
38#define ENFLG		(0x01 << 16)
39#define SLEEP		(0x0f << 12)
40#define TXNUM		(0x03 << 8)
41#define TXBITLEN	(0x1f << 3)
42#define TXNEG		(0x01 << 2)
43#define RXNEG		(0x01 << 1)
44#define LSB		(0x01 << 10)
45#define SELECTLEV	(0x01 << 2)
46#define SELECTPOL	(0x01 << 31)
47#define SELECTSLAVE	0x01
48#define GOBUSY		0x01
49
50struct nuc900_spi {
51	struct spi_bitbang	 bitbang;
52	struct completion	 done;
53	void __iomem		*regs;
54	int			 irq;
55	int			 len;
56	int			 count;
57	const unsigned char	*tx;
58	unsigned char		*rx;
59	struct clk		*clk;
60	struct spi_master	*master;
61	struct nuc900_spi_info *pdata;
62	spinlock_t		lock;
63};
64
65static inline struct nuc900_spi *to_hw(struct spi_device *sdev)
66{
67	return spi_master_get_devdata(sdev->master);
68}
69
70static void nuc900_slave_select(struct spi_device *spi, unsigned int ssr)
71{
72	struct nuc900_spi *hw = to_hw(spi);
73	unsigned int val;
74	unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0;
75	unsigned int cpol = spi->mode & SPI_CPOL ? 1 : 0;
76	unsigned long flags;
77
78	spin_lock_irqsave(&hw->lock, flags);
79
80	val = __raw_readl(hw->regs + USI_SSR);
81
82	if (!cs)
83		val &= ~SELECTLEV;
84	else
85		val |= SELECTLEV;
86
87	if (!ssr)
88		val &= ~SELECTSLAVE;
89	else
90		val |= SELECTSLAVE;
91
92	__raw_writel(val, hw->regs + USI_SSR);
93
94	val = __raw_readl(hw->regs + USI_CNT);
95
96	if (!cpol)
97		val &= ~SELECTPOL;
98	else
99		val |= SELECTPOL;
100
101	__raw_writel(val, hw->regs + USI_CNT);
102
103	spin_unlock_irqrestore(&hw->lock, flags);
104}
105
106static void nuc900_spi_chipsel(struct spi_device *spi, int value)
107{
108	switch (value) {
109	case BITBANG_CS_INACTIVE:
110		nuc900_slave_select(spi, 0);
111		break;
112
113	case BITBANG_CS_ACTIVE:
114		nuc900_slave_select(spi, 1);
115		break;
116	}
117}
118
119static void nuc900_spi_setup_txnum(struct nuc900_spi *hw, unsigned int txnum)
120{
121	unsigned int val;
122	unsigned long flags;
123
124	spin_lock_irqsave(&hw->lock, flags);
125
126	val = __raw_readl(hw->regs + USI_CNT) & ~TXNUM;
127
128	if (txnum)
129		val |= txnum << 0x08;
130
131	__raw_writel(val, hw->regs + USI_CNT);
132
133	spin_unlock_irqrestore(&hw->lock, flags);
134
135}
136
137static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw,
138							unsigned int txbitlen)
139{
140	unsigned int val;
141	unsigned long flags;
142
143	spin_lock_irqsave(&hw->lock, flags);
144
145	val = __raw_readl(hw->regs + USI_CNT) & ~TXBITLEN;
146
147	val |= (txbitlen << 0x03);
148
149	__raw_writel(val, hw->regs + USI_CNT);
150
151	spin_unlock_irqrestore(&hw->lock, flags);
152}
153
154static void nuc900_spi_gobusy(struct nuc900_spi *hw)
155{
156	unsigned int val;
157	unsigned long flags;
158
159	spin_lock_irqsave(&hw->lock, flags);
160
161	val = __raw_readl(hw->regs + USI_CNT);
162
163	val |= GOBUSY;
164
165	__raw_writel(val, hw->regs + USI_CNT);
166
167	spin_unlock_irqrestore(&hw->lock, flags);
168}
169
170static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count)
171{
172	return hw->tx ? hw->tx[count] : 0;
173}
174
175static int nuc900_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
176{
177	struct nuc900_spi *hw = to_hw(spi);
178
179	hw->tx = t->tx_buf;
180	hw->rx = t->rx_buf;
181	hw->len = t->len;
182	hw->count = 0;
183
184	__raw_writel(hw_txbyte(hw, 0x0), hw->regs + USI_TX0);
185
186	nuc900_spi_gobusy(hw);
187
188	wait_for_completion(&hw->done);
189
190	return hw->count;
191}
192
193static irqreturn_t nuc900_spi_irq(int irq, void *dev)
194{
195	struct nuc900_spi *hw = dev;
196	unsigned int status;
197	unsigned int count = hw->count;
198
199	status = __raw_readl(hw->regs + USI_CNT);
200	__raw_writel(status, hw->regs + USI_CNT);
201
202	if (status & ENFLG) {
203		hw->count++;
204
205		if (hw->rx)
206			hw->rx[count] = __raw_readl(hw->regs + USI_RX0);
207		count++;
208
209		if (count < hw->len) {
210			__raw_writel(hw_txbyte(hw, count), hw->regs + USI_TX0);
211			nuc900_spi_gobusy(hw);
212		} else {
213			complete(&hw->done);
214		}
215
216		return IRQ_HANDLED;
217	}
218
219	complete(&hw->done);
220	return IRQ_HANDLED;
221}
222
223static void nuc900_tx_edge(struct nuc900_spi *hw, unsigned int edge)
224{
225	unsigned int val;
226	unsigned long flags;
227
228	spin_lock_irqsave(&hw->lock, flags);
229
230	val = __raw_readl(hw->regs + USI_CNT);
231
232	if (edge)
233		val |= TXNEG;
234	else
235		val &= ~TXNEG;
236	__raw_writel(val, hw->regs + USI_CNT);
237
238	spin_unlock_irqrestore(&hw->lock, flags);
239}
240
241static void nuc900_rx_edge(struct nuc900_spi *hw, unsigned int edge)
242{
243	unsigned int val;
244	unsigned long flags;
245
246	spin_lock_irqsave(&hw->lock, flags);
247
248	val = __raw_readl(hw->regs + USI_CNT);
249
250	if (edge)
251		val |= RXNEG;
252	else
253		val &= ~RXNEG;
254	__raw_writel(val, hw->regs + USI_CNT);
255
256	spin_unlock_irqrestore(&hw->lock, flags);
257}
258
259static void nuc900_send_first(struct nuc900_spi *hw, unsigned int lsb)
260{
261	unsigned int val;
262	unsigned long flags;
263
264	spin_lock_irqsave(&hw->lock, flags);
265
266	val = __raw_readl(hw->regs + USI_CNT);
267
268	if (lsb)
269		val |= LSB;
270	else
271		val &= ~LSB;
272	__raw_writel(val, hw->regs + USI_CNT);
273
274	spin_unlock_irqrestore(&hw->lock, flags);
275}
276
277static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep)
278{
279	unsigned int val;
280	unsigned long flags;
281
282	spin_lock_irqsave(&hw->lock, flags);
283
284	val = __raw_readl(hw->regs + USI_CNT) & ~SLEEP;
285
286	if (sleep)
287		val |= (sleep << 12);
288
289	__raw_writel(val, hw->regs + USI_CNT);
290
291	spin_unlock_irqrestore(&hw->lock, flags);
292}
293
294static void nuc900_enable_int(struct nuc900_spi *hw)
295{
296	unsigned int val;
297	unsigned long flags;
298
299	spin_lock_irqsave(&hw->lock, flags);
300
301	val = __raw_readl(hw->regs + USI_CNT);
302
303	val |= ENINT;
304
305	__raw_writel(val, hw->regs + USI_CNT);
306
307	spin_unlock_irqrestore(&hw->lock, flags);
308}
309
310static void nuc900_set_divider(struct nuc900_spi *hw)
311{
312	__raw_writel(hw->pdata->divider, hw->regs + USI_DIV);
313}
314
315static void nuc900_init_spi(struct nuc900_spi *hw)
316{
317	clk_enable(hw->clk);
318	spin_lock_init(&hw->lock);
319
320	nuc900_tx_edge(hw, hw->pdata->txneg);
321	nuc900_rx_edge(hw, hw->pdata->rxneg);
322	nuc900_send_first(hw, hw->pdata->lsb);
323	nuc900_set_sleep(hw, hw->pdata->sleep);
324	nuc900_spi_setup_txbitlen(hw, hw->pdata->txbitlen);
325	nuc900_spi_setup_txnum(hw, hw->pdata->txnum);
326	nuc900_set_divider(hw);
327	nuc900_enable_int(hw);
328}
329
330static int nuc900_spi_probe(struct platform_device *pdev)
331{
332	struct nuc900_spi *hw;
333	struct spi_master *master;
334	struct resource *res;
335	int err = 0;
336
337	master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi));
338	if (master == NULL) {
339		dev_err(&pdev->dev, "No memory for spi_master\n");
340		return -ENOMEM;
341	}
342
343	hw = spi_master_get_devdata(master);
344	hw->master = master;
345	hw->pdata  = dev_get_platdata(&pdev->dev);
346
347	if (hw->pdata == NULL) {
348		dev_err(&pdev->dev, "No platform data supplied\n");
349		err = -ENOENT;
350		goto err_pdata;
351	}
352
353	platform_set_drvdata(pdev, hw);
354	init_completion(&hw->done);
355
356	master->mode_bits          = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
357	if (hw->pdata->lsb)
358		master->mode_bits |= SPI_LSB_FIRST;
359	master->num_chipselect     = hw->pdata->num_cs;
360	master->bus_num            = hw->pdata->bus_num;
361	hw->bitbang.master         = hw->master;
362	hw->bitbang.chipselect     = nuc900_spi_chipsel;
363	hw->bitbang.txrx_bufs      = nuc900_spi_txrx;
364
365	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
366	hw->regs = devm_ioremap_resource(&pdev->dev, res);
367	if (IS_ERR(hw->regs)) {
368		err = PTR_ERR(hw->regs);
369		goto err_pdata;
370	}
371
372	hw->irq = platform_get_irq(pdev, 0);
373	if (hw->irq < 0) {
374		dev_err(&pdev->dev, "No IRQ specified\n");
375		err = -ENOENT;
376		goto err_pdata;
377	}
378
379	err = devm_request_irq(&pdev->dev, hw->irq, nuc900_spi_irq, 0,
380				pdev->name, hw);
381	if (err) {
382		dev_err(&pdev->dev, "Cannot claim IRQ\n");
383		goto err_pdata;
384	}
385
386	hw->clk = devm_clk_get(&pdev->dev, "spi");
387	if (IS_ERR(hw->clk)) {
388		dev_err(&pdev->dev, "No clock for device\n");
389		err = PTR_ERR(hw->clk);
390		goto err_pdata;
391	}
392
393	mfp_set_groupg(&pdev->dev, NULL);
394	nuc900_init_spi(hw);
395
396	err = spi_bitbang_start(&hw->bitbang);
397	if (err) {
398		dev_err(&pdev->dev, "Failed to register SPI master\n");
399		goto err_register;
400	}
401
402	return 0;
403
404err_register:
405	clk_disable(hw->clk);
406err_pdata:
407	spi_master_put(hw->master);
408	return err;
409}
410
411static int nuc900_spi_remove(struct platform_device *dev)
412{
413	struct nuc900_spi *hw = platform_get_drvdata(dev);
414
415	spi_bitbang_stop(&hw->bitbang);
416	clk_disable(hw->clk);
417	spi_master_put(hw->master);
418	return 0;
419}
420
421static struct platform_driver nuc900_spi_driver = {
422	.probe		= nuc900_spi_probe,
423	.remove		= nuc900_spi_remove,
424	.driver		= {
425		.name	= "nuc900-spi",
426	},
427};
428module_platform_driver(nuc900_spi_driver);
429
430MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
431MODULE_DESCRIPTION("nuc900 spi driver!");
432MODULE_LICENSE("GPL");
433MODULE_ALIAS("platform:nuc900-spi");
434