This source file includes following definitions.
- boston_update
- malta_update
- sead3_wait_sm_idle
- sead3_wait_lcd_idle
- sead3_update
- img_ascii_lcd_scroll
- img_ascii_lcd_display
- message_show
- message_store
- img_ascii_lcd_probe
- img_ascii_lcd_remove
1
2
3
4
5
6
7 #include <generated/utsrelease.h>
8 #include <linux/kernel.h>
9 #include <linux/io.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/module.h>
12 #include <linux/of_address.h>
13 #include <linux/of_platform.h>
14 #include <linux/platform_device.h>
15 #include <linux/regmap.h>
16 #include <linux/slab.h>
17 #include <linux/sysfs.h>
18
19 struct img_ascii_lcd_ctx;
20
21
22
23
24
25
26
27 struct img_ascii_lcd_config {
28 unsigned int num_chars;
29 bool external_regmap;
30 void (*update)(struct img_ascii_lcd_ctx *ctx);
31 };
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 struct img_ascii_lcd_ctx {
48 struct platform_device *pdev;
49 union {
50 void __iomem *base;
51 struct regmap *regmap;
52 };
53 u32 offset;
54 const struct img_ascii_lcd_config *cfg;
55 char *message;
56 unsigned int message_len;
57 unsigned int scroll_pos;
58 unsigned int scroll_rate;
59 struct timer_list timer;
60 char curr[] __aligned(8);
61 };
62
63
64
65
66
67 static void boston_update(struct img_ascii_lcd_ctx *ctx)
68 {
69 ulong val;
70
71 #if BITS_PER_LONG == 64
72 val = *((u64 *)&ctx->curr[0]);
73 __raw_writeq(val, ctx->base);
74 #elif BITS_PER_LONG == 32
75 val = *((u32 *)&ctx->curr[0]);
76 __raw_writel(val, ctx->base);
77 val = *((u32 *)&ctx->curr[4]);
78 __raw_writel(val, ctx->base + 4);
79 #else
80 # error Not 32 or 64 bit
81 #endif
82 }
83
84 static struct img_ascii_lcd_config boston_config = {
85 .num_chars = 8,
86 .update = boston_update,
87 };
88
89
90
91
92
93 static void malta_update(struct img_ascii_lcd_ctx *ctx)
94 {
95 unsigned int i;
96 int err = 0;
97
98 for (i = 0; i < ctx->cfg->num_chars; i++) {
99 err = regmap_write(ctx->regmap,
100 ctx->offset + (i * 8), ctx->curr[i]);
101 if (err)
102 break;
103 }
104
105 if (unlikely(err))
106 pr_err_ratelimited("Failed to update LCD display: %d\n", err);
107 }
108
109 static struct img_ascii_lcd_config malta_config = {
110 .num_chars = 8,
111 .external_regmap = true,
112 .update = malta_update,
113 };
114
115
116
117
118
119 enum {
120 SEAD3_REG_LCD_CTRL = 0x00,
121 #define SEAD3_REG_LCD_CTRL_SETDRAM BIT(7)
122 SEAD3_REG_LCD_DATA = 0x08,
123 SEAD3_REG_CPLD_STATUS = 0x10,
124 #define SEAD3_REG_CPLD_STATUS_BUSY BIT(0)
125 SEAD3_REG_CPLD_DATA = 0x18,
126 #define SEAD3_REG_CPLD_DATA_BUSY BIT(7)
127 };
128
129 static int sead3_wait_sm_idle(struct img_ascii_lcd_ctx *ctx)
130 {
131 unsigned int status;
132 int err;
133
134 do {
135 err = regmap_read(ctx->regmap,
136 ctx->offset + SEAD3_REG_CPLD_STATUS,
137 &status);
138 if (err)
139 return err;
140 } while (status & SEAD3_REG_CPLD_STATUS_BUSY);
141
142 return 0;
143
144 }
145
146 static int sead3_wait_lcd_idle(struct img_ascii_lcd_ctx *ctx)
147 {
148 unsigned int cpld_data;
149 int err;
150
151 err = sead3_wait_sm_idle(ctx);
152 if (err)
153 return err;
154
155 do {
156 err = regmap_read(ctx->regmap,
157 ctx->offset + SEAD3_REG_LCD_CTRL,
158 &cpld_data);
159 if (err)
160 return err;
161
162 err = sead3_wait_sm_idle(ctx);
163 if (err)
164 return err;
165
166 err = regmap_read(ctx->regmap,
167 ctx->offset + SEAD3_REG_CPLD_DATA,
168 &cpld_data);
169 if (err)
170 return err;
171 } while (cpld_data & SEAD3_REG_CPLD_DATA_BUSY);
172
173 return 0;
174 }
175
176 static void sead3_update(struct img_ascii_lcd_ctx *ctx)
177 {
178 unsigned int i;
179 int err = 0;
180
181 for (i = 0; i < ctx->cfg->num_chars; i++) {
182 err = sead3_wait_lcd_idle(ctx);
183 if (err)
184 break;
185
186 err = regmap_write(ctx->regmap,
187 ctx->offset + SEAD3_REG_LCD_CTRL,
188 SEAD3_REG_LCD_CTRL_SETDRAM | i);
189 if (err)
190 break;
191
192 err = sead3_wait_lcd_idle(ctx);
193 if (err)
194 break;
195
196 err = regmap_write(ctx->regmap,
197 ctx->offset + SEAD3_REG_LCD_DATA,
198 ctx->curr[i]);
199 if (err)
200 break;
201 }
202
203 if (unlikely(err))
204 pr_err_ratelimited("Failed to update LCD display: %d\n", err);
205 }
206
207 static struct img_ascii_lcd_config sead3_config = {
208 .num_chars = 16,
209 .external_regmap = true,
210 .update = sead3_update,
211 };
212
213 static const struct of_device_id img_ascii_lcd_matches[] = {
214 { .compatible = "img,boston-lcd", .data = &boston_config },
215 { .compatible = "mti,malta-lcd", .data = &malta_config },
216 { .compatible = "mti,sead3-lcd", .data = &sead3_config },
217 { }
218 };
219 MODULE_DEVICE_TABLE(of, img_ascii_lcd_matches);
220
221
222
223
224
225
226
227
228 static void img_ascii_lcd_scroll(struct timer_list *t)
229 {
230 struct img_ascii_lcd_ctx *ctx = from_timer(ctx, t, timer);
231 unsigned int i, ch = ctx->scroll_pos;
232 unsigned int num_chars = ctx->cfg->num_chars;
233
234
235 for (i = 0; i < num_chars;) {
236
237 for (; i < num_chars && ch < ctx->message_len; i++, ch++)
238 ctx->curr[i] = ctx->message[ch];
239
240
241 ch = 0;
242 }
243
244
245 ctx->cfg->update(ctx);
246
247
248 ctx->scroll_pos++;
249 ctx->scroll_pos %= ctx->message_len;
250
251
252 if (ctx->message_len > ctx->cfg->num_chars)
253 mod_timer(&ctx->timer, jiffies + ctx->scroll_rate);
254 }
255
256
257
258
259
260
261
262
263
264
265
266
267
268 static int img_ascii_lcd_display(struct img_ascii_lcd_ctx *ctx,
269 const char *msg, ssize_t count)
270 {
271 char *new_msg;
272
273
274 del_timer_sync(&ctx->timer);
275
276 if (count == -1)
277 count = strlen(msg);
278
279
280 if (msg[count - 1] == '\n')
281 count--;
282
283 new_msg = devm_kmalloc(&ctx->pdev->dev, count + 1, GFP_KERNEL);
284 if (!new_msg)
285 return -ENOMEM;
286
287 memcpy(new_msg, msg, count);
288 new_msg[count] = 0;
289
290 if (ctx->message)
291 devm_kfree(&ctx->pdev->dev, ctx->message);
292
293 ctx->message = new_msg;
294 ctx->message_len = count;
295 ctx->scroll_pos = 0;
296
297
298 img_ascii_lcd_scroll(&ctx->timer);
299
300 return 0;
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314 static ssize_t message_show(struct device *dev, struct device_attribute *attr,
315 char *buf)
316 {
317 struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev);
318
319 return sprintf(buf, "%s\n", ctx->message);
320 }
321
322
323
324
325
326
327
328
329
330
331
332
333 static ssize_t message_store(struct device *dev, struct device_attribute *attr,
334 const char *buf, size_t count)
335 {
336 struct img_ascii_lcd_ctx *ctx = dev_get_drvdata(dev);
337 int err;
338
339 err = img_ascii_lcd_display(ctx, buf, count);
340 return err ?: count;
341 }
342
343 static DEVICE_ATTR_RW(message);
344
345
346
347
348
349
350
351
352
353
354 static int img_ascii_lcd_probe(struct platform_device *pdev)
355 {
356 const struct of_device_id *match;
357 const struct img_ascii_lcd_config *cfg;
358 struct img_ascii_lcd_ctx *ctx;
359 struct resource *res;
360 int err;
361
362 match = of_match_device(img_ascii_lcd_matches, &pdev->dev);
363 if (!match)
364 return -ENODEV;
365
366 cfg = match->data;
367 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx) + cfg->num_chars,
368 GFP_KERNEL);
369 if (!ctx)
370 return -ENOMEM;
371
372 if (cfg->external_regmap) {
373 ctx->regmap = syscon_node_to_regmap(pdev->dev.parent->of_node);
374 if (IS_ERR(ctx->regmap))
375 return PTR_ERR(ctx->regmap);
376
377 if (of_property_read_u32(pdev->dev.of_node, "offset",
378 &ctx->offset))
379 return -EINVAL;
380 } else {
381 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
382 ctx->base = devm_ioremap_resource(&pdev->dev, res);
383 if (IS_ERR(ctx->base))
384 return PTR_ERR(ctx->base);
385 }
386
387 ctx->pdev = pdev;
388 ctx->cfg = cfg;
389 ctx->message = NULL;
390 ctx->scroll_pos = 0;
391 ctx->scroll_rate = HZ / 2;
392
393
394 timer_setup(&ctx->timer, img_ascii_lcd_scroll, 0);
395
396 platform_set_drvdata(pdev, ctx);
397
398
399 err = img_ascii_lcd_display(ctx, "Linux " UTS_RELEASE " ", -1);
400 if (err)
401 goto out_del_timer;
402
403 err = device_create_file(&pdev->dev, &dev_attr_message);
404 if (err)
405 goto out_del_timer;
406
407 return 0;
408 out_del_timer:
409 del_timer_sync(&ctx->timer);
410 return err;
411 }
412
413
414
415
416
417
418
419
420
421
422 static int img_ascii_lcd_remove(struct platform_device *pdev)
423 {
424 struct img_ascii_lcd_ctx *ctx = platform_get_drvdata(pdev);
425
426 device_remove_file(&pdev->dev, &dev_attr_message);
427 del_timer_sync(&ctx->timer);
428 return 0;
429 }
430
431 static struct platform_driver img_ascii_lcd_driver = {
432 .driver = {
433 .name = "img-ascii-lcd",
434 .of_match_table = img_ascii_lcd_matches,
435 },
436 .probe = img_ascii_lcd_probe,
437 .remove = img_ascii_lcd_remove,
438 };
439 module_platform_driver(img_ascii_lcd_driver);
440
441 MODULE_DESCRIPTION("Imagination Technologies ASCII LCD Display");
442 MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
443 MODULE_LICENSE("GPL");