Root/
1 | /* |
2 | * Copyright (C) 2007 Google, Inc. |
3 | * Copyright (C) 2012 Intel, Inc. |
4 | * |
5 | * This software is licensed under the terms of the GNU General Public |
6 | * License version 2, as published by the Free Software Foundation, and |
7 | * may be copied, distributed, and modified under those terms. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | */ |
15 | |
16 | #include <linux/console.h> |
17 | #include <linux/init.h> |
18 | #include <linux/interrupt.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/tty.h> |
21 | #include <linux/tty_flip.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/io.h> |
24 | #include <linux/module.h> |
25 | |
26 | enum { |
27 | GOLDFISH_TTY_PUT_CHAR = 0x00, |
28 | GOLDFISH_TTY_BYTES_READY = 0x04, |
29 | GOLDFISH_TTY_CMD = 0x08, |
30 | |
31 | GOLDFISH_TTY_DATA_PTR = 0x10, |
32 | GOLDFISH_TTY_DATA_LEN = 0x14, |
33 | |
34 | GOLDFISH_TTY_CMD_INT_DISABLE = 0, |
35 | GOLDFISH_TTY_CMD_INT_ENABLE = 1, |
36 | GOLDFISH_TTY_CMD_WRITE_BUFFER = 2, |
37 | GOLDFISH_TTY_CMD_READ_BUFFER = 3, |
38 | }; |
39 | |
40 | struct goldfish_tty { |
41 | struct tty_port port; |
42 | spinlock_t lock; |
43 | void __iomem *base; |
44 | u32 irq; |
45 | int opencount; |
46 | struct console console; |
47 | }; |
48 | |
49 | static DEFINE_MUTEX(goldfish_tty_lock); |
50 | static struct tty_driver *goldfish_tty_driver; |
51 | static u32 goldfish_tty_line_count = 8; |
52 | static u32 goldfish_tty_current_line_count; |
53 | static struct goldfish_tty *goldfish_ttys; |
54 | |
55 | static void goldfish_tty_do_write(int line, const char *buf, unsigned count) |
56 | { |
57 | unsigned long irq_flags; |
58 | struct goldfish_tty *qtty = &goldfish_ttys[line]; |
59 | void __iomem *base = qtty->base; |
60 | spin_lock_irqsave(&qtty->lock, irq_flags); |
61 | writel((u32)buf, base + GOLDFISH_TTY_DATA_PTR); |
62 | writel(count, base + GOLDFISH_TTY_DATA_LEN); |
63 | writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD); |
64 | spin_unlock_irqrestore(&qtty->lock, irq_flags); |
65 | } |
66 | |
67 | static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) |
68 | { |
69 | struct platform_device *pdev = dev_id; |
70 | struct goldfish_tty *qtty = &goldfish_ttys[pdev->id]; |
71 | void __iomem *base = qtty->base; |
72 | unsigned long irq_flags; |
73 | unsigned char *buf; |
74 | u32 count; |
75 | |
76 | count = readl(base + GOLDFISH_TTY_BYTES_READY); |
77 | if(count == 0) |
78 | return IRQ_NONE; |
79 | |
80 | count = tty_prepare_flip_string(&qtty->port, &buf, count); |
81 | spin_lock_irqsave(&qtty->lock, irq_flags); |
82 | writel((u32)buf, base + GOLDFISH_TTY_DATA_PTR); |
83 | writel(count, base + GOLDFISH_TTY_DATA_LEN); |
84 | writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD); |
85 | spin_unlock_irqrestore(&qtty->lock, irq_flags); |
86 | tty_schedule_flip(&qtty->port); |
87 | return IRQ_HANDLED; |
88 | } |
89 | |
90 | static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty) |
91 | { |
92 | struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port); |
93 | writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD); |
94 | return 0; |
95 | } |
96 | |
97 | static void goldfish_tty_shutdown(struct tty_port *port) |
98 | { |
99 | struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port); |
100 | writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD); |
101 | } |
102 | |
103 | static int goldfish_tty_open(struct tty_struct * tty, struct file * filp) |
104 | { |
105 | struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; |
106 | return tty_port_open(&qtty->port, tty, filp); |
107 | } |
108 | |
109 | static void goldfish_tty_close(struct tty_struct * tty, struct file * filp) |
110 | { |
111 | tty_port_close(tty->port, tty, filp); |
112 | } |
113 | |
114 | static void goldfish_tty_hangup(struct tty_struct *tty) |
115 | { |
116 | tty_port_hangup(tty->port); |
117 | } |
118 | |
119 | static int goldfish_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) |
120 | { |
121 | goldfish_tty_do_write(tty->index, buf, count); |
122 | return count; |
123 | } |
124 | |
125 | static int goldfish_tty_write_room(struct tty_struct *tty) |
126 | { |
127 | return 0x10000; |
128 | } |
129 | |
130 | static int goldfish_tty_chars_in_buffer(struct tty_struct *tty) |
131 | { |
132 | struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; |
133 | void __iomem *base = qtty->base; |
134 | return readl(base + GOLDFISH_TTY_BYTES_READY); |
135 | } |
136 | |
137 | static void goldfish_tty_console_write(struct console *co, const char *b, unsigned count) |
138 | { |
139 | goldfish_tty_do_write(co->index, b, count); |
140 | } |
141 | |
142 | static struct tty_driver *goldfish_tty_console_device(struct console *c, int *index) |
143 | { |
144 | *index = c->index; |
145 | return goldfish_tty_driver; |
146 | } |
147 | |
148 | static int goldfish_tty_console_setup(struct console *co, char *options) |
149 | { |
150 | if((unsigned)co->index > goldfish_tty_line_count) |
151 | return -ENODEV; |
152 | if(goldfish_ttys[co->index].base == 0) |
153 | return -ENODEV; |
154 | return 0; |
155 | } |
156 | |
157 | static struct tty_port_operations goldfish_port_ops = { |
158 | .activate = goldfish_tty_activate, |
159 | .shutdown = goldfish_tty_shutdown |
160 | }; |
161 | |
162 | static struct tty_operations goldfish_tty_ops = { |
163 | .open = goldfish_tty_open, |
164 | .close = goldfish_tty_close, |
165 | .hangup = goldfish_tty_hangup, |
166 | .write = goldfish_tty_write, |
167 | .write_room = goldfish_tty_write_room, |
168 | .chars_in_buffer = goldfish_tty_chars_in_buffer, |
169 | }; |
170 | |
171 | static int goldfish_tty_create_driver(void) |
172 | { |
173 | int ret; |
174 | struct tty_driver *tty; |
175 | |
176 | goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * goldfish_tty_line_count, GFP_KERNEL); |
177 | if(goldfish_ttys == NULL) { |
178 | ret = -ENOMEM; |
179 | goto err_alloc_goldfish_ttys_failed; |
180 | } |
181 | tty = alloc_tty_driver(goldfish_tty_line_count); |
182 | if(tty == NULL) { |
183 | ret = -ENOMEM; |
184 | goto err_alloc_tty_driver_failed; |
185 | } |
186 | tty->driver_name = "goldfish"; |
187 | tty->name = "ttyGF"; |
188 | tty->type = TTY_DRIVER_TYPE_SERIAL; |
189 | tty->subtype = SERIAL_TYPE_NORMAL; |
190 | tty->init_termios = tty_std_termios; |
191 | tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; |
192 | tty_set_operations(tty, &goldfish_tty_ops); |
193 | ret = tty_register_driver(tty); |
194 | if(ret) |
195 | goto err_tty_register_driver_failed; |
196 | |
197 | goldfish_tty_driver = tty; |
198 | return 0; |
199 | |
200 | err_tty_register_driver_failed: |
201 | put_tty_driver(tty); |
202 | err_alloc_tty_driver_failed: |
203 | kfree(goldfish_ttys); |
204 | goldfish_ttys = NULL; |
205 | err_alloc_goldfish_ttys_failed: |
206 | return ret; |
207 | } |
208 | |
209 | static void goldfish_tty_delete_driver(void) |
210 | { |
211 | tty_unregister_driver(goldfish_tty_driver); |
212 | put_tty_driver(goldfish_tty_driver); |
213 | goldfish_tty_driver = NULL; |
214 | kfree(goldfish_ttys); |
215 | goldfish_ttys = NULL; |
216 | } |
217 | |
218 | static int goldfish_tty_probe(struct platform_device *pdev) |
219 | { |
220 | struct goldfish_tty *qtty; |
221 | int ret = -EINVAL; |
222 | int i; |
223 | struct resource *r; |
224 | struct device *ttydev; |
225 | void __iomem *base; |
226 | u32 irq; |
227 | |
228 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
229 | if(r == NULL) |
230 | return -EINVAL; |
231 | |
232 | base = ioremap(r->start, 0x1000); |
233 | if (base == NULL) |
234 | pr_err("goldfish_tty: unable to remap base\n"); |
235 | |
236 | r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
237 | if(r == NULL) |
238 | goto err_unmap; |
239 | |
240 | irq = r->start; |
241 | |
242 | if(pdev->id >= goldfish_tty_line_count) |
243 | goto err_unmap; |
244 | |
245 | mutex_lock(&goldfish_tty_lock); |
246 | if(goldfish_tty_current_line_count == 0) { |
247 | ret = goldfish_tty_create_driver(); |
248 | if(ret) |
249 | goto err_create_driver_failed; |
250 | } |
251 | goldfish_tty_current_line_count++; |
252 | |
253 | qtty = &goldfish_ttys[pdev->id]; |
254 | spin_lock_init(&qtty->lock); |
255 | tty_port_init(&qtty->port); |
256 | qtty->port.ops = &goldfish_port_ops; |
257 | qtty->base = base; |
258 | qtty->irq = irq; |
259 | |
260 | writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD); |
261 | |
262 | ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, "goldfish_tty", pdev); |
263 | if(ret) |
264 | goto err_request_irq_failed; |
265 | |
266 | |
267 | ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, |
268 | pdev->id, &pdev->dev); |
269 | if(IS_ERR(ttydev)) { |
270 | ret = PTR_ERR(ttydev); |
271 | goto err_tty_register_device_failed; |
272 | } |
273 | |
274 | strcpy(qtty->console.name, "ttyGF"); |
275 | qtty->console.write = goldfish_tty_console_write; |
276 | qtty->console.device = goldfish_tty_console_device; |
277 | qtty->console.setup = goldfish_tty_console_setup; |
278 | qtty->console.flags = CON_PRINTBUFFER; |
279 | qtty->console.index = pdev->id; |
280 | register_console(&qtty->console); |
281 | |
282 | mutex_unlock(&goldfish_tty_lock); |
283 | return 0; |
284 | |
285 | tty_unregister_device(goldfish_tty_driver, i); |
286 | err_tty_register_device_failed: |
287 | free_irq(irq, pdev); |
288 | err_request_irq_failed: |
289 | goldfish_tty_current_line_count--; |
290 | if(goldfish_tty_current_line_count == 0) |
291 | goldfish_tty_delete_driver(); |
292 | err_create_driver_failed: |
293 | mutex_unlock(&goldfish_tty_lock); |
294 | err_unmap: |
295 | iounmap(base); |
296 | return ret; |
297 | } |
298 | |
299 | static int goldfish_tty_remove(struct platform_device *pdev) |
300 | { |
301 | struct goldfish_tty *qtty; |
302 | |
303 | mutex_lock(&goldfish_tty_lock); |
304 | |
305 | qtty = &goldfish_ttys[pdev->id]; |
306 | unregister_console(&qtty->console); |
307 | tty_unregister_device(goldfish_tty_driver, pdev->id); |
308 | iounmap(qtty->base); |
309 | qtty->base = 0; |
310 | free_irq(qtty->irq, pdev); |
311 | goldfish_tty_current_line_count--; |
312 | if(goldfish_tty_current_line_count == 0) |
313 | goldfish_tty_delete_driver(); |
314 | mutex_unlock(&goldfish_tty_lock); |
315 | return 0; |
316 | } |
317 | |
318 | static struct platform_driver goldfish_tty_platform_driver = { |
319 | .probe = goldfish_tty_probe, |
320 | .remove = goldfish_tty_remove, |
321 | .driver = { |
322 | .name = "goldfish_tty" |
323 | } |
324 | }; |
325 | |
326 | module_platform_driver(goldfish_tty_platform_driver); |
327 | |
328 | MODULE_LICENSE("GPL v2"); |
329 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9