Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/auxdisplay/arm-charlcd.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Driver for the on-board character LCD found on some ARM reference boards
4
* This is basically an Hitachi HD44780 LCD with a custom IP block to drive it
5
* https://en.wikipedia.org/wiki/HD44780_Character_LCD
6
* Currently it will just display the text "ARM Linux" and the linux version
7
*
8
* Author: Linus Walleij <[email protected]>
9
*/
10
#include <linux/init.h>
11
#include <linux/interrupt.h>
12
#include <linux/platform_device.h>
13
#include <linux/of.h>
14
#include <linux/completion.h>
15
#include <linux/delay.h>
16
#include <linux/io.h>
17
#include <linux/slab.h>
18
#include <linux/workqueue.h>
19
#include <generated/utsrelease.h>
20
21
#define DRIVERNAME "arm-charlcd"
22
#define CHARLCD_TIMEOUT (msecs_to_jiffies(1000))
23
24
/* Offsets to registers */
25
#define CHAR_COM 0x00U
26
#define CHAR_DAT 0x04U
27
#define CHAR_RD 0x08U
28
#define CHAR_RAW 0x0CU
29
#define CHAR_MASK 0x10U
30
#define CHAR_STAT 0x14U
31
32
#define CHAR_RAW_CLEAR 0x00000000U
33
#define CHAR_RAW_VALID 0x00000100U
34
35
/* Hitachi HD44780 display commands */
36
#define HD_CLEAR 0x01U
37
#define HD_HOME 0x02U
38
#define HD_ENTRYMODE 0x04U
39
#define HD_ENTRYMODE_INCREMENT 0x02U
40
#define HD_ENTRYMODE_SHIFT 0x01U
41
#define HD_DISPCTRL 0x08U
42
#define HD_DISPCTRL_ON 0x04U
43
#define HD_DISPCTRL_CURSOR_ON 0x02U
44
#define HD_DISPCTRL_CURSOR_BLINK 0x01U
45
#define HD_CRSR_SHIFT 0x10U
46
#define HD_CRSR_SHIFT_DISPLAY 0x08U
47
#define HD_CRSR_SHIFT_DISPLAY_RIGHT 0x04U
48
#define HD_FUNCSET 0x20U
49
#define HD_FUNCSET_8BIT 0x10U
50
#define HD_FUNCSET_2_LINES 0x08U
51
#define HD_FUNCSET_FONT_5X10 0x04U
52
#define HD_SET_CGRAM 0x40U
53
#define HD_SET_DDRAM 0x80U
54
#define HD_BUSY_FLAG 0x80U
55
56
/**
57
* struct charlcd - Private data structure
58
* @dev: a pointer back to containing device
59
* @phybase: the offset to the controller in physical memory
60
* @physize: the size of the physical page
61
* @virtbase: the offset to the controller in virtual memory
62
* @irq: reserved interrupt number
63
* @complete: completion structure for the last LCD command
64
* @init_work: delayed work structure to initialize the display on boot
65
*/
66
struct charlcd {
67
struct device *dev;
68
u32 phybase;
69
u32 physize;
70
void __iomem *virtbase;
71
int irq;
72
struct completion complete;
73
struct delayed_work init_work;
74
};
75
76
static irqreturn_t charlcd_interrupt(int irq, void *data)
77
{
78
struct charlcd *lcd = data;
79
u8 status;
80
81
status = readl(lcd->virtbase + CHAR_STAT) & 0x01;
82
/* Clear IRQ */
83
writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
84
if (status)
85
complete(&lcd->complete);
86
else
87
dev_info(lcd->dev, "Spurious IRQ (%02x)\n", status);
88
return IRQ_HANDLED;
89
}
90
91
92
static void charlcd_wait_complete_irq(struct charlcd *lcd)
93
{
94
int ret;
95
96
ret = wait_for_completion_interruptible_timeout(&lcd->complete,
97
CHARLCD_TIMEOUT);
98
/* Disable IRQ after completion */
99
writel(0x00, lcd->virtbase + CHAR_MASK);
100
101
if (ret < 0) {
102
dev_err(lcd->dev,
103
"wait_for_completion_interruptible_timeout() "
104
"returned %d waiting for ready\n", ret);
105
return;
106
}
107
108
if (ret == 0) {
109
dev_err(lcd->dev, "charlcd controller timed out "
110
"waiting for ready\n");
111
return;
112
}
113
}
114
115
static u8 charlcd_4bit_read_char(struct charlcd *lcd)
116
{
117
u8 data;
118
u32 val;
119
int i;
120
121
/* If we can, use an IRQ to wait for the data, else poll */
122
if (lcd->irq >= 0)
123
charlcd_wait_complete_irq(lcd);
124
else {
125
i = 0;
126
val = 0;
127
while (!(val & CHAR_RAW_VALID) && i < 10) {
128
udelay(100);
129
val = readl(lcd->virtbase + CHAR_RAW);
130
i++;
131
}
132
133
writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
134
}
135
msleep(1);
136
137
/* Read the 4 high bits of the data */
138
data = readl(lcd->virtbase + CHAR_RD) & 0xf0;
139
140
/*
141
* The second read for the low bits does not trigger an IRQ
142
* so in this case we have to poll for the 4 lower bits
143
*/
144
i = 0;
145
val = 0;
146
while (!(val & CHAR_RAW_VALID) && i < 10) {
147
udelay(100);
148
val = readl(lcd->virtbase + CHAR_RAW);
149
i++;
150
}
151
writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
152
msleep(1);
153
154
/* Read the 4 low bits of the data */
155
data |= (readl(lcd->virtbase + CHAR_RD) >> 4) & 0x0f;
156
157
return data;
158
}
159
160
static bool charlcd_4bit_read_bf(struct charlcd *lcd)
161
{
162
if (lcd->irq >= 0) {
163
/*
164
* If we'll use IRQs to wait for the busyflag, clear any
165
* pending flag and enable IRQ
166
*/
167
writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
168
init_completion(&lcd->complete);
169
writel(0x01, lcd->virtbase + CHAR_MASK);
170
}
171
readl(lcd->virtbase + CHAR_COM);
172
return charlcd_4bit_read_char(lcd) & HD_BUSY_FLAG ? true : false;
173
}
174
175
static void charlcd_4bit_wait_busy(struct charlcd *lcd)
176
{
177
int retries = 50;
178
179
udelay(100);
180
while (charlcd_4bit_read_bf(lcd) && retries)
181
retries--;
182
if (!retries)
183
dev_err(lcd->dev, "timeout waiting for busyflag\n");
184
}
185
186
static void charlcd_4bit_command(struct charlcd *lcd, u8 cmd)
187
{
188
u32 cmdlo = (cmd << 4) & 0xf0;
189
u32 cmdhi = (cmd & 0xf0);
190
191
writel(cmdhi, lcd->virtbase + CHAR_COM);
192
udelay(10);
193
writel(cmdlo, lcd->virtbase + CHAR_COM);
194
charlcd_4bit_wait_busy(lcd);
195
}
196
197
static void charlcd_4bit_char(struct charlcd *lcd, u8 ch)
198
{
199
u32 chlo = (ch << 4) & 0xf0;
200
u32 chhi = (ch & 0xf0);
201
202
writel(chhi, lcd->virtbase + CHAR_DAT);
203
udelay(10);
204
writel(chlo, lcd->virtbase + CHAR_DAT);
205
charlcd_4bit_wait_busy(lcd);
206
}
207
208
static void charlcd_4bit_print(struct charlcd *lcd, int line, const char *str)
209
{
210
u8 offset;
211
int i;
212
213
/*
214
* We support line 0, 1
215
* Line 1 runs from 0x00..0x27
216
* Line 2 runs from 0x28..0x4f
217
*/
218
if (line == 0)
219
offset = 0;
220
else if (line == 1)
221
offset = 0x28;
222
else
223
return;
224
225
/* Set offset */
226
charlcd_4bit_command(lcd, HD_SET_DDRAM | offset);
227
228
/* Send string */
229
for (i = 0; i < strlen(str) && i < 0x28; i++)
230
charlcd_4bit_char(lcd, str[i]);
231
}
232
233
static void charlcd_4bit_init(struct charlcd *lcd)
234
{
235
/* These commands cannot be checked with the busy flag */
236
writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
237
msleep(5);
238
writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
239
udelay(100);
240
writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
241
udelay(100);
242
/* Go to 4bit mode */
243
writel(HD_FUNCSET, lcd->virtbase + CHAR_COM);
244
udelay(100);
245
/*
246
* 4bit mode, 2 lines, 5x8 font, after this the number of lines
247
* and the font cannot be changed until the next initialization sequence
248
*/
249
charlcd_4bit_command(lcd, HD_FUNCSET | HD_FUNCSET_2_LINES);
250
charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
251
charlcd_4bit_command(lcd, HD_ENTRYMODE | HD_ENTRYMODE_INCREMENT);
252
charlcd_4bit_command(lcd, HD_CLEAR);
253
charlcd_4bit_command(lcd, HD_HOME);
254
/* Put something useful in the display */
255
charlcd_4bit_print(lcd, 0, "ARM Linux");
256
charlcd_4bit_print(lcd, 1, UTS_RELEASE);
257
}
258
259
static void charlcd_init_work(struct work_struct *work)
260
{
261
struct charlcd *lcd =
262
container_of(work, struct charlcd, init_work.work);
263
264
charlcd_4bit_init(lcd);
265
}
266
267
static int __init charlcd_probe(struct platform_device *pdev)
268
{
269
int ret;
270
struct charlcd *lcd;
271
struct resource *res;
272
273
lcd = kzalloc(sizeof(*lcd), GFP_KERNEL);
274
if (!lcd)
275
return -ENOMEM;
276
277
lcd->dev = &pdev->dev;
278
279
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
280
if (!res) {
281
ret = -ENOENT;
282
goto out_no_resource;
283
}
284
lcd->phybase = res->start;
285
lcd->physize = resource_size(res);
286
287
if (request_mem_region(lcd->phybase, lcd->physize,
288
DRIVERNAME) == NULL) {
289
ret = -EBUSY;
290
goto out_no_memregion;
291
}
292
293
lcd->virtbase = ioremap(lcd->phybase, lcd->physize);
294
if (!lcd->virtbase) {
295
ret = -ENOMEM;
296
goto out_no_memregion;
297
}
298
299
lcd->irq = platform_get_irq(pdev, 0);
300
/* If no IRQ is supplied, we'll survive without it */
301
if (lcd->irq >= 0) {
302
if (request_irq(lcd->irq, charlcd_interrupt, 0,
303
DRIVERNAME, lcd)) {
304
ret = -EIO;
305
goto out_no_irq;
306
}
307
}
308
309
platform_set_drvdata(pdev, lcd);
310
311
/*
312
* Initialize the display in a delayed work, because
313
* it is VERY slow and would slow down the boot of the system.
314
*/
315
INIT_DELAYED_WORK(&lcd->init_work, charlcd_init_work);
316
schedule_delayed_work(&lcd->init_work, 0);
317
318
dev_info(&pdev->dev, "initialized ARM character LCD at %08x\n",
319
lcd->phybase);
320
321
return 0;
322
323
out_no_irq:
324
iounmap(lcd->virtbase);
325
out_no_memregion:
326
release_mem_region(lcd->phybase, SZ_4K);
327
out_no_resource:
328
kfree(lcd);
329
return ret;
330
}
331
332
static int charlcd_suspend(struct device *dev)
333
{
334
struct charlcd *lcd = dev_get_drvdata(dev);
335
336
/* Power the display off */
337
charlcd_4bit_command(lcd, HD_DISPCTRL);
338
return 0;
339
}
340
341
static int charlcd_resume(struct device *dev)
342
{
343
struct charlcd *lcd = dev_get_drvdata(dev);
344
345
/* Turn the display back on */
346
charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
347
return 0;
348
}
349
350
static const struct dev_pm_ops charlcd_pm_ops = {
351
.suspend = charlcd_suspend,
352
.resume = charlcd_resume,
353
};
354
355
static const struct of_device_id charlcd_match[] = {
356
{ .compatible = "arm,versatile-lcd", },
357
{}
358
};
359
360
static struct platform_driver charlcd_driver = {
361
.driver = {
362
.name = DRIVERNAME,
363
.pm = &charlcd_pm_ops,
364
.suppress_bind_attrs = true,
365
.of_match_table = of_match_ptr(charlcd_match),
366
},
367
};
368
builtin_platform_driver_probe(charlcd_driver, charlcd_probe);
369
370