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