aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiodrag Dinic <miodrag.dinic@imgtec.com>2017-08-29 09:53:19 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-08-31 12:58:45 -0400
commit7157d2be23da9f8860c69e2b79184a4e02701dad (patch)
tree2f51051b9f35a0c14ab95cb67cd29ab3b942868c
parent2296eee704e70dac7fef8ac13f2716e4896dd13e (diff)
tty: goldfish: Use streaming DMA for r/w operations on Ranchu platforms
Implement tty r/w operations using streaming DMA. Goldfish tty for Ranchu platforms has been modified to use streaming DMA mappings for read/write operations. This change eliminates the need for snooping through the TLB in QEMU using cpu_get_phys_page_debug() which does not guarantee that it will return the valid va -> pa mapping. The streaming DMA mapping is implemented using dma_map_single() per transfer, while dma_unmap_single() is used for unmapping right after the DMA transfer. Using DMA API is the proper way for handling r/w transfers and makes this driver more portable, thus effectively eliminating the need for virt_to_page() and page_to_phys() conversions. This change does not affect the old style Goldfish tty behaviour which is still used by the Goldfish emulator. Version register has been added and probed to see which platform is running this driver. Reading from the new register GOLDFISH_TTY_REG_VERSION using the Goldfish emulator will return 0 and driver will work with virtual addresses. Whereas if run on Ranchu it returns 1, and thus DMA is used. (Goldfish and Ranchu are code names for the first and the second generation of virtual boards used by Android emulator.) Signed-off-by: Miodrag Dinic <miodrag.dinic@imgtec.com> Signed-off-by: Goran Ferenc <goran.ferenc@imgtec.com> Signed-off-by: Aleksandar Markovic <aleksandar.markovic@imgtec.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/goldfish.c166
1 files changed, 139 insertions, 27 deletions
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c
index 011eb5322077..757e17ff7aa4 100644
--- a/drivers/tty/goldfish.c
+++ b/drivers/tty/goldfish.c
@@ -22,6 +22,8 @@
22#include <linux/io.h> 22#include <linux/io.h>
23#include <linux/module.h> 23#include <linux/module.h>
24#include <linux/goldfish.h> 24#include <linux/goldfish.h>
25#include <linux/mm.h>
26#include <linux/dma-mapping.h>
25 27
26/* Goldfish tty register's offsets */ 28/* Goldfish tty register's offsets */
27#define GOLDFISH_TTY_REG_BYTES_READY 0x04 29#define GOLDFISH_TTY_REG_BYTES_READY 0x04
@@ -29,6 +31,7 @@
29#define GOLDFISH_TTY_REG_DATA_PTR 0x10 31#define GOLDFISH_TTY_REG_DATA_PTR 0x10
30#define GOLDFISH_TTY_REG_DATA_LEN 0x14 32#define GOLDFISH_TTY_REG_DATA_LEN 0x14
31#define GOLDFISH_TTY_REG_DATA_PTR_HIGH 0x18 33#define GOLDFISH_TTY_REG_DATA_PTR_HIGH 0x18
34#define GOLDFISH_TTY_REG_VERSION 0x20
32 35
33/* Goldfish tty commands */ 36/* Goldfish tty commands */
34#define GOLDFISH_TTY_CMD_INT_DISABLE 0 37#define GOLDFISH_TTY_CMD_INT_DISABLE 0
@@ -43,6 +46,8 @@ struct goldfish_tty {
43 u32 irq; 46 u32 irq;
44 int opencount; 47 int opencount;
45 struct console console; 48 struct console console;
49 u32 version;
50 struct device *dev;
46}; 51};
47 52
48static DEFINE_MUTEX(goldfish_tty_lock); 53static DEFINE_MUTEX(goldfish_tty_lock);
@@ -51,24 +56,95 @@ static u32 goldfish_tty_line_count = 8;
51static u32 goldfish_tty_current_line_count; 56static u32 goldfish_tty_current_line_count;
52static struct goldfish_tty *goldfish_ttys; 57static struct goldfish_tty *goldfish_ttys;
53 58
54static void goldfish_tty_do_write(int line, const char *buf, unsigned count) 59static void do_rw_io(struct goldfish_tty *qtty,
60 unsigned long address,
61 unsigned int count,
62 int is_write)
55{ 63{
56 unsigned long irq_flags; 64 unsigned long irq_flags;
57 struct goldfish_tty *qtty = &goldfish_ttys[line];
58 void __iomem *base = qtty->base; 65 void __iomem *base = qtty->base;
66
59 spin_lock_irqsave(&qtty->lock, irq_flags); 67 spin_lock_irqsave(&qtty->lock, irq_flags);
60 gf_write_ptr(buf, base + GOLDFISH_TTY_REG_DATA_PTR, 68 gf_write_ptr((void *)address, base + GOLDFISH_TTY_REG_DATA_PTR,
61 base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); 69 base + GOLDFISH_TTY_REG_DATA_PTR_HIGH);
62 writel(count, base + GOLDFISH_TTY_REG_DATA_LEN); 70 writel(count, base + GOLDFISH_TTY_REG_DATA_LEN);
63 writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_REG_CMD); 71
72 if (is_write)
73 writel(GOLDFISH_TTY_CMD_WRITE_BUFFER,
74 base + GOLDFISH_TTY_REG_CMD);
75 else
76 writel(GOLDFISH_TTY_CMD_READ_BUFFER,
77 base + GOLDFISH_TTY_REG_CMD);
78
64 spin_unlock_irqrestore(&qtty->lock, irq_flags); 79 spin_unlock_irqrestore(&qtty->lock, irq_flags);
65} 80}
66 81
82static void goldfish_tty_rw(struct goldfish_tty *qtty,
83 unsigned long addr,
84 unsigned int count,
85 int is_write)
86{
87 dma_addr_t dma_handle;
88 enum dma_data_direction dma_dir;
89
90 dma_dir = (is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
91 if (qtty->version > 0) {
92 /*
93 * Goldfish TTY for Ranchu platform uses
94 * physical addresses and DMA for read/write operations
95 */
96 unsigned long addr_end = addr + count;
97
98 while (addr < addr_end) {
99 unsigned long pg_end = (addr & PAGE_MASK) + PAGE_SIZE;
100 unsigned long next =
101 pg_end < addr_end ? pg_end : addr_end;
102 unsigned long avail = next - addr;
103
104 /*
105 * Map the buffer's virtual address to the DMA address
106 * so the buffer can be accessed by the device.
107 */
108 dma_handle = dma_map_single(qtty->dev, (void *)addr,
109 avail, dma_dir);
110
111 if (dma_mapping_error(qtty->dev, dma_handle)) {
112 dev_err(qtty->dev, "tty: DMA mapping error.\n");
113 return;
114 }
115 do_rw_io(qtty, dma_handle, avail, is_write);
116
117 /*
118 * Unmap the previously mapped region after
119 * the completion of the read/write operation.
120 */
121 dma_unmap_single(qtty->dev, dma_handle, avail, dma_dir);
122
123 addr += avail;
124 }
125 } else {
126 /*
127 * Old style Goldfish TTY used on the Goldfish platform
128 * uses virtual addresses.
129 */
130 do_rw_io(qtty, addr, count, is_write);
131 }
132}
133
134static void goldfish_tty_do_write(int line, const char *buf,
135 unsigned int count)
136{
137 struct goldfish_tty *qtty = &goldfish_ttys[line];
138 unsigned long address = (unsigned long)(void *)buf;
139
140 goldfish_tty_rw(qtty, address, count, 1);
141}
142
67static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) 143static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
68{ 144{
69 struct goldfish_tty *qtty = dev_id; 145 struct goldfish_tty *qtty = dev_id;
70 void __iomem *base = qtty->base; 146 void __iomem *base = qtty->base;
71 unsigned long irq_flags; 147 unsigned long address;
72 unsigned char *buf; 148 unsigned char *buf;
73 u32 count; 149 u32 count;
74 150
@@ -77,12 +153,10 @@ static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
77 return IRQ_NONE; 153 return IRQ_NONE;
78 154
79 count = tty_prepare_flip_string(&qtty->port, &buf, count); 155 count = tty_prepare_flip_string(&qtty->port, &buf, count);
80 spin_lock_irqsave(&qtty->lock, irq_flags); 156
81 gf_write_ptr(buf, base + GOLDFISH_TTY_REG_DATA_PTR, 157 address = (unsigned long)(void *)buf;
82 base + GOLDFISH_TTY_REG_DATA_PTR_HIGH); 158 goldfish_tty_rw(qtty, address, count, 0);
83 writel(count, base + GOLDFISH_TTY_REG_DATA_LEN); 159
84 writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_REG_CMD);
85 spin_unlock_irqrestore(&qtty->lock, irq_flags);
86 tty_schedule_flip(&qtty->port); 160 tty_schedule_flip(&qtty->port);
87 return IRQ_HANDLED; 161 return IRQ_HANDLED;
88} 162}
@@ -225,7 +299,7 @@ static void goldfish_tty_delete_driver(void)
225static int goldfish_tty_probe(struct platform_device *pdev) 299static int goldfish_tty_probe(struct platform_device *pdev)
226{ 300{
227 struct goldfish_tty *qtty; 301 struct goldfish_tty *qtty;
228 int ret = -EINVAL; 302 int ret = -ENODEV;
229 struct resource *r; 303 struct resource *r;
230 struct device *ttydev; 304 struct device *ttydev;
231 void __iomem *base; 305 void __iomem *base;
@@ -233,16 +307,22 @@ static int goldfish_tty_probe(struct platform_device *pdev)
233 unsigned int line; 307 unsigned int line;
234 308
235 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 309 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
236 if (r == NULL) 310 if (!r) {
237 return -EINVAL; 311 pr_err("goldfish_tty: No MEM resource available!\n");
312 return -ENOMEM;
313 }
238 314
239 base = ioremap(r->start, 0x1000); 315 base = ioremap(r->start, 0x1000);
240 if (base == NULL) 316 if (!base) {
241 pr_err("goldfish_tty: unable to remap base\n"); 317 pr_err("goldfish_tty: Unable to ioremap base!\n");
318 return -ENOMEM;
319 }
242 320
243 r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 321 r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
244 if (r == NULL) 322 if (!r) {
323 pr_err("goldfish_tty: No IRQ resource available!\n");
245 goto err_unmap; 324 goto err_unmap;
325 }
246 326
247 irq = r->start; 327 irq = r->start;
248 328
@@ -253,13 +333,17 @@ static int goldfish_tty_probe(struct platform_device *pdev)
253 else 333 else
254 line = pdev->id; 334 line = pdev->id;
255 335
256 if (line >= goldfish_tty_line_count) 336 if (line >= goldfish_tty_line_count) {
257 goto err_create_driver_failed; 337 pr_err("goldfish_tty: Reached maximum tty number of %d.\n",
338 goldfish_tty_current_line_count);
339 ret = -ENOMEM;
340 goto err_unlock;
341 }
258 342
259 if (goldfish_tty_current_line_count == 0) { 343 if (goldfish_tty_current_line_count == 0) {
260 ret = goldfish_tty_create_driver(); 344 ret = goldfish_tty_create_driver();
261 if (ret) 345 if (ret)
262 goto err_create_driver_failed; 346 goto err_unlock;
263 } 347 }
264 goldfish_tty_current_line_count++; 348 goldfish_tty_current_line_count++;
265 349
@@ -269,17 +353,45 @@ static int goldfish_tty_probe(struct platform_device *pdev)
269 qtty->port.ops = &goldfish_port_ops; 353 qtty->port.ops = &goldfish_port_ops;
270 qtty->base = base; 354 qtty->base = base;
271 qtty->irq = irq; 355 qtty->irq = irq;
356 qtty->dev = &pdev->dev;
357
358 /*
359 * Goldfish TTY device used by the Goldfish emulator
360 * should identify itself with 0, forcing the driver
361 * to use virtual addresses. Goldfish TTY device
362 * on Ranchu emulator (qemu2) returns 1 here and
363 * driver will use physical addresses.
364 */
365 qtty->version = readl(base + GOLDFISH_TTY_REG_VERSION);
366
367 /*
368 * Goldfish TTY device on Ranchu emulator (qemu2)
369 * will use DMA for read/write IO operations.
370 */
371 if (qtty->version > 0) {
372 /*
373 * Initialize dma_mask to 32-bits.
374 */
375 if (!pdev->dev.dma_mask)
376 pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
377 ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
378 if (ret) {
379 dev_err(&pdev->dev, "No suitable DMA available.\n");
380 goto err_dec_line_count;
381 }
382 }
272 383
273 writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD); 384 writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_REG_CMD);
274 385
275 ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, 386 ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED,
276 "goldfish_tty", qtty); 387 "goldfish_tty", qtty);
277 if (ret) 388 if (ret) {
278 goto err_request_irq_failed; 389 pr_err("goldfish_tty: No IRQ available!\n");
279 390 goto err_dec_line_count;
391 }
280 392
281 ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, 393 ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver,
282 line, &pdev->dev); 394 line, &pdev->dev);
283 if (IS_ERR(ttydev)) { 395 if (IS_ERR(ttydev)) {
284 ret = PTR_ERR(ttydev); 396 ret = PTR_ERR(ttydev);
285 goto err_tty_register_device_failed; 397 goto err_tty_register_device_failed;
@@ -299,11 +411,11 @@ static int goldfish_tty_probe(struct platform_device *pdev)
299 411
300err_tty_register_device_failed: 412err_tty_register_device_failed:
301 free_irq(irq, qtty); 413 free_irq(irq, qtty);
302err_request_irq_failed: 414err_dec_line_count:
303 goldfish_tty_current_line_count--; 415 goldfish_tty_current_line_count--;
304 if (goldfish_tty_current_line_count == 0) 416 if (goldfish_tty_current_line_count == 0)
305 goldfish_tty_delete_driver(); 417 goldfish_tty_delete_driver();
306err_create_driver_failed: 418err_unlock:
307 mutex_unlock(&goldfish_tty_lock); 419 mutex_unlock(&goldfish_tty_lock);
308err_unmap: 420err_unmap:
309 iounmap(base); 421 iounmap(base);