summaryrefslogtreecommitdiff
path: root/patches.tizen/0715-spi-spi-s3c64xx-Add-coherent-buffers-for-dma-transfe.patch
blob: 0ec6c57a29f8ae75915e4a95066b6cbeba63b8c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
From 307d4697ea7f0eda0fa02508fd8d0b3a4f219216 Mon Sep 17 00:00:00 2001
From: Lukasz Czerwinski <l.czerwinski@samsung.com>
Date: Mon, 26 Aug 2013 14:20:44 +0200
Subject: [PATCH 0715/1302] spi: spi-s3c64xx: Add coherent buffers for dma
 transfers

The spi-s3c64xx currently doesn't support transfers from non-contiguous
client buffers.

This patch adds two coherent buffers which allow transfers from
non-contiguous client buffers without extra coherent memory allocation
in the client driver.

Buffer size is hardcoded to 16kB for Tx/Rx. Client drivers shouldn't
exceed that value.

Signed-off-by: Lukasz Czerwinski <l.czerwinski@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
---
 drivers/spi/spi-s3c64xx.c | 81 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 65 insertions(+), 16 deletions(-)

diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index f358c16..6219907 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -112,6 +112,7 @@
 #define S3C64XX_SPI_SWAP_TX_EN			(1<<0)
 
 #define S3C64XX_SPI_FBCLK_MSK		(3<<0)
+#define S3C64XX_SPI_DMA_BUF_SIZE	(16 * 1024)
 
 #define FIFO_LVL_MASK(i) ((i)->port_conf->fifo_lvl_mask[i->port_id])
 #define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & \
@@ -131,6 +132,8 @@
 #define TXBUSY    (1<<3)
 
 struct s3c64xx_spi_dma_data {
+	u32 *vbuf;
+	dma_addr_t dma_phys;
 	struct dma_chan *ch;
 	enum dma_transfer_direction direction;
 	unsigned int dmach;
@@ -176,6 +179,7 @@ struct s3c64xx_spi_port_config {
  * @xfer_completion: To indicate completion of xfer task.
  * @cur_mode: Stores the active configuration of the controller.
  * @cur_bpw: Stores the active bits per word settings.
+ * @dma_buf_size: Stores buffer size for Rx/Tx.
  * @cur_speed: Stores the active xfer clock speed.
  */
 struct s3c64xx_spi_driver_data {
@@ -193,6 +197,7 @@ struct s3c64xx_spi_driver_data {
 	unsigned                        state;
 	unsigned                        cur_mode, cur_bpw;
 	unsigned                        cur_speed;
+	unsigned                        dma_buf_size;
 	struct s3c64xx_spi_dma_data	rx_dma;
 	struct s3c64xx_spi_dma_data	tx_dma;
 
@@ -286,6 +291,8 @@ static int s3c64xx_dma_init_param(struct s3c64xx_spi_driver_data *sdd,
 	struct s3c64xx_spi_dma_data *dma_data = NULL;
 	dma_filter_fn filter = sdd->cntrlr_info->filter;
 	dma_cap_mask_t mask;
+	dma_addr_t dma_phys;
+	u32 *dma_vbuf;
 	int ret;
 
 	memset(&config, 0, sizeof(struct dma_slave_config));
@@ -307,7 +314,7 @@ static int s3c64xx_dma_init_param(struct s3c64xx_spi_driver_data *sdd,
 		config.dst_maxburst = 1;
 	}
 
-	if (dma_data->ch)
+	if (dma_data->ch && dma_data->vbuf)
 		return 0;
 
 	dma_chan = dma_request_slave_channel_compat(mask,
@@ -318,6 +325,13 @@ static int s3c64xx_dma_init_param(struct s3c64xx_spi_driver_data *sdd,
 		return -EBUSY;
 	}
 
+	dma_vbuf = dma_alloc_coherent(dev, sdd->dma_buf_size,
+			&dma_phys, GFP_KERNEL);
+	if (!dma_vbuf) {
+		dev_err(dev, "Not able to allocate the dma buffer\n");
+		return -ENOMEM;
+	}
+
 	ret = dmaengine_slave_config(dma_chan, &config);
 	if (ret)
 		goto release;
@@ -329,26 +343,31 @@ static int s3c64xx_dma_init_param(struct s3c64xx_spi_driver_data *sdd,
 	}
 
 	dma_data->ch = dma_chan;
+	dma_data->vbuf = dma_vbuf;
+	dma_data->dma_phys = dma_phys;
 
 	return 0;
 
 release:
 	dma_release_channel(dma_chan);
-
+	dma_free_coherent(dev, sdd->dma_buf_size, dma_vbuf, dma_phys);
 	return ret;
 }
 
 static void s3c64xx_dma_deinit_param(struct s3c64xx_spi_driver_data *sdd,
 					bool dma_to_memory)
 {
-	struct dma_chan *dma_chan;
+	struct s3c64xx_spi_dma_data *dma_data;
+	struct device *dev = &sdd->pdev->dev;
 
 	if (dma_to_memory)
-		dma_chan = sdd->rx_dma.ch;
+		dma_data = &sdd->rx_dma;
 	else
-		dma_chan = sdd->tx_dma.ch;
+		dma_data = &sdd->tx_dma;
 
-	dma_release_channel(dma_chan);
+	dma_free_coherent(dev, sdd->dma_buf_size, dma_data->vbuf,
+			dma_data->dma_phys);
+	dma_release_channel(dma_data->ch);
 	pm_runtime_put(&sdd->pdev->dev);
 }
 
@@ -406,6 +425,35 @@ static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd,
 	dmaengine_terminate_all(dma->ch);
 }
 
+
+static void s3c64xx_copy_txbuf_to_spi(struct s3c64xx_spi_driver_data *sdd,
+				    struct spi_transfer *xfer)
+{
+	struct device *dev = &sdd->pdev->dev;
+
+	dma_sync_single_for_cpu(dev, sdd->tx_dma.dma_phys,
+			sdd->dma_buf_size, DMA_TO_DEVICE);
+
+	memcpy(sdd->tx_dma.vbuf, xfer->tx_buf, xfer->len);
+
+	dma_sync_single_for_device(dev, sdd->tx_dma.dma_phys,
+			sdd->dma_buf_size, DMA_TO_DEVICE);
+}
+
+static void s3c64xx_copy_spi_to_rxbuf(struct s3c64xx_spi_driver_data *sdd,
+				    struct spi_transfer *xfer)
+{
+	struct device *dev = &sdd->pdev->dev;
+
+	dma_sync_single_for_cpu(dev, sdd->rx_dma.dma_phys,
+			sdd->dma_buf_size, DMA_FROM_DEVICE);
+
+	memcpy(xfer->rx_buf, sdd->rx_dma.vbuf, xfer->len);
+
+	dma_sync_single_for_device(dev, sdd->rx_dma.dma_phys,
+			sdd->dma_buf_size, DMA_FROM_DEVICE);
+}
+
 static void s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd,
 				struct spi_device *spi,
 				struct spi_transfer *xfer, int dma_mode)
@@ -437,6 +485,7 @@ static void s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd,
 		chcfg |= S3C64XX_SPI_CH_TXCH_ON;
 		if (dma_mode) {
 			modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
+			s3c64xx_copy_txbuf_to_spi(sdd, xfer);
 			s3c64xx_prepare_dma(&sdd->tx_dma,
 					xfer->len, xfer->tx_dma);
 		} else {
@@ -470,6 +519,7 @@ static void s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd,
 			writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
 					| S3C64XX_SPI_PACKET_CNT_EN,
 					regs + S3C64XX_SPI_PACKET_CNT);
+			s3c64xx_copy_spi_to_rxbuf(sdd, xfer);
 			s3c64xx_prepare_dma(&sdd->rx_dma,
 					xfer->len, xfer->rx_dma);
 		}
@@ -763,14 +813,6 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
 			goto out;
 	}
 
-	/* Map all the transfers if needed */
-	if (s3c64xx_spi_map_mssg(sdd, msg)) {
-		dev_err(&spi->dev,
-			"Xfer: Unable to map message buffers!\n");
-		status = -ENOMEM;
-		goto out;
-	}
-
 	/* Configure feedback delay */
 	writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
 
@@ -785,6 +827,14 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
 		bpw = xfer->bits_per_word;
 		speed = xfer->speed_hz ? : spi->max_speed_hz;
 
+		if (xfer->len > sdd->dma_buf_size) {
+			dev_err(&spi->dev,
+				"Message length exceeds dma buffer size %d>%d\n",
+				xfer->len, sdd->dma_buf_size);
+			status = -EIO;
+			goto out;
+		}
+
 		if (xfer->len % (bpw / 8)) {
 			dev_err(&spi->dev,
 				"Xfer length(%u) not a multiple of word size(%u)\n",
@@ -868,8 +918,6 @@ out:
 	else
 		sdd->tgl_spi = spi;
 
-	s3c64xx_spi_unmap_mssg(sdd, msg);
-
 	msg->status = status;
 
 	spi_finalize_current_message(master);
@@ -1244,6 +1292,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
 
 	sdd->tx_dma.direction = DMA_MEM_TO_DEV;
 	sdd->rx_dma.direction = DMA_DEV_TO_MEM;
+	sdd->dma_buf_size = S3C64XX_SPI_DMA_BUF_SIZE;
 
 	master->dev.of_node = pdev->dev.of_node;
 	master->bus_num = sdd->port_id;
-- 
1.8.3.2