0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/init.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/of.h>
0014 #include <linux/completion.h>
0015 #include <linux/delay.h>
0016 #include <linux/io.h>
0017 #include <linux/slab.h>
0018 #include <linux/workqueue.h>
0019 #include <generated/utsrelease.h>
0020
0021 #define DRIVERNAME "arm-charlcd"
0022 #define CHARLCD_TIMEOUT (msecs_to_jiffies(1000))
0023
0024
0025 #define CHAR_COM 0x00U
0026 #define CHAR_DAT 0x04U
0027 #define CHAR_RD 0x08U
0028 #define CHAR_RAW 0x0CU
0029 #define CHAR_MASK 0x10U
0030 #define CHAR_STAT 0x14U
0031
0032 #define CHAR_RAW_CLEAR 0x00000000U
0033 #define CHAR_RAW_VALID 0x00000100U
0034
0035
0036 #define HD_CLEAR 0x01U
0037 #define HD_HOME 0x02U
0038 #define HD_ENTRYMODE 0x04U
0039 #define HD_ENTRYMODE_INCREMENT 0x02U
0040 #define HD_ENTRYMODE_SHIFT 0x01U
0041 #define HD_DISPCTRL 0x08U
0042 #define HD_DISPCTRL_ON 0x04U
0043 #define HD_DISPCTRL_CURSOR_ON 0x02U
0044 #define HD_DISPCTRL_CURSOR_BLINK 0x01U
0045 #define HD_CRSR_SHIFT 0x10U
0046 #define HD_CRSR_SHIFT_DISPLAY 0x08U
0047 #define HD_CRSR_SHIFT_DISPLAY_RIGHT 0x04U
0048 #define HD_FUNCSET 0x20U
0049 #define HD_FUNCSET_8BIT 0x10U
0050 #define HD_FUNCSET_2_LINES 0x08U
0051 #define HD_FUNCSET_FONT_5X10 0x04U
0052 #define HD_SET_CGRAM 0x40U
0053 #define HD_SET_DDRAM 0x80U
0054 #define HD_BUSY_FLAG 0x80U
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066 struct charlcd {
0067 struct device *dev;
0068 u32 phybase;
0069 u32 physize;
0070 void __iomem *virtbase;
0071 int irq;
0072 struct completion complete;
0073 struct delayed_work init_work;
0074 };
0075
0076 static irqreturn_t charlcd_interrupt(int irq, void *data)
0077 {
0078 struct charlcd *lcd = data;
0079 u8 status;
0080
0081 status = readl(lcd->virtbase + CHAR_STAT) & 0x01;
0082
0083 writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
0084 if (status)
0085 complete(&lcd->complete);
0086 else
0087 dev_info(lcd->dev, "Spurious IRQ (%02x)\n", status);
0088 return IRQ_HANDLED;
0089 }
0090
0091
0092 static void charlcd_wait_complete_irq(struct charlcd *lcd)
0093 {
0094 int ret;
0095
0096 ret = wait_for_completion_interruptible_timeout(&lcd->complete,
0097 CHARLCD_TIMEOUT);
0098
0099 writel(0x00, lcd->virtbase + CHAR_MASK);
0100
0101 if (ret < 0) {
0102 dev_err(lcd->dev,
0103 "wait_for_completion_interruptible_timeout() "
0104 "returned %d waiting for ready\n", ret);
0105 return;
0106 }
0107
0108 if (ret == 0) {
0109 dev_err(lcd->dev, "charlcd controller timed out "
0110 "waiting for ready\n");
0111 return;
0112 }
0113 }
0114
0115 static u8 charlcd_4bit_read_char(struct charlcd *lcd)
0116 {
0117 u8 data;
0118 u32 val;
0119 int i;
0120
0121
0122 if (lcd->irq >= 0)
0123 charlcd_wait_complete_irq(lcd);
0124 else {
0125 i = 0;
0126 val = 0;
0127 while (!(val & CHAR_RAW_VALID) && i < 10) {
0128 udelay(100);
0129 val = readl(lcd->virtbase + CHAR_RAW);
0130 i++;
0131 }
0132
0133 writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
0134 }
0135 msleep(1);
0136
0137
0138 data = readl(lcd->virtbase + CHAR_RD) & 0xf0;
0139
0140
0141
0142
0143
0144 i = 0;
0145 val = 0;
0146 while (!(val & CHAR_RAW_VALID) && i < 10) {
0147 udelay(100);
0148 val = readl(lcd->virtbase + CHAR_RAW);
0149 i++;
0150 }
0151 writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
0152 msleep(1);
0153
0154
0155 data |= (readl(lcd->virtbase + CHAR_RD) >> 4) & 0x0f;
0156
0157 return data;
0158 }
0159
0160 static bool charlcd_4bit_read_bf(struct charlcd *lcd)
0161 {
0162 if (lcd->irq >= 0) {
0163
0164
0165
0166
0167 writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
0168 init_completion(&lcd->complete);
0169 writel(0x01, lcd->virtbase + CHAR_MASK);
0170 }
0171 readl(lcd->virtbase + CHAR_COM);
0172 return charlcd_4bit_read_char(lcd) & HD_BUSY_FLAG ? true : false;
0173 }
0174
0175 static void charlcd_4bit_wait_busy(struct charlcd *lcd)
0176 {
0177 int retries = 50;
0178
0179 udelay(100);
0180 while (charlcd_4bit_read_bf(lcd) && retries)
0181 retries--;
0182 if (!retries)
0183 dev_err(lcd->dev, "timeout waiting for busyflag\n");
0184 }
0185
0186 static void charlcd_4bit_command(struct charlcd *lcd, u8 cmd)
0187 {
0188 u32 cmdlo = (cmd << 4) & 0xf0;
0189 u32 cmdhi = (cmd & 0xf0);
0190
0191 writel(cmdhi, lcd->virtbase + CHAR_COM);
0192 udelay(10);
0193 writel(cmdlo, lcd->virtbase + CHAR_COM);
0194 charlcd_4bit_wait_busy(lcd);
0195 }
0196
0197 static void charlcd_4bit_char(struct charlcd *lcd, u8 ch)
0198 {
0199 u32 chlo = (ch << 4) & 0xf0;
0200 u32 chhi = (ch & 0xf0);
0201
0202 writel(chhi, lcd->virtbase + CHAR_DAT);
0203 udelay(10);
0204 writel(chlo, lcd->virtbase + CHAR_DAT);
0205 charlcd_4bit_wait_busy(lcd);
0206 }
0207
0208 static void charlcd_4bit_print(struct charlcd *lcd, int line, const char *str)
0209 {
0210 u8 offset;
0211 int i;
0212
0213
0214
0215
0216
0217
0218 if (line == 0)
0219 offset = 0;
0220 else if (line == 1)
0221 offset = 0x28;
0222 else
0223 return;
0224
0225
0226 charlcd_4bit_command(lcd, HD_SET_DDRAM | offset);
0227
0228
0229 for (i = 0; i < strlen(str) && i < 0x28; i++)
0230 charlcd_4bit_char(lcd, str[i]);
0231 }
0232
0233 static void charlcd_4bit_init(struct charlcd *lcd)
0234 {
0235
0236 writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
0237 msleep(5);
0238 writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
0239 udelay(100);
0240 writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
0241 udelay(100);
0242
0243 writel(HD_FUNCSET, lcd->virtbase + CHAR_COM);
0244 udelay(100);
0245
0246
0247
0248
0249 charlcd_4bit_command(lcd, HD_FUNCSET | HD_FUNCSET_2_LINES);
0250 charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
0251 charlcd_4bit_command(lcd, HD_ENTRYMODE | HD_ENTRYMODE_INCREMENT);
0252 charlcd_4bit_command(lcd, HD_CLEAR);
0253 charlcd_4bit_command(lcd, HD_HOME);
0254
0255 charlcd_4bit_print(lcd, 0, "ARM Linux");
0256 charlcd_4bit_print(lcd, 1, UTS_RELEASE);
0257 }
0258
0259 static void charlcd_init_work(struct work_struct *work)
0260 {
0261 struct charlcd *lcd =
0262 container_of(work, struct charlcd, init_work.work);
0263
0264 charlcd_4bit_init(lcd);
0265 }
0266
0267 static int __init charlcd_probe(struct platform_device *pdev)
0268 {
0269 int ret;
0270 struct charlcd *lcd;
0271 struct resource *res;
0272
0273 lcd = kzalloc(sizeof(struct charlcd), GFP_KERNEL);
0274 if (!lcd)
0275 return -ENOMEM;
0276
0277 lcd->dev = &pdev->dev;
0278
0279 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0280 if (!res) {
0281 ret = -ENOENT;
0282 goto out_no_resource;
0283 }
0284 lcd->phybase = res->start;
0285 lcd->physize = resource_size(res);
0286
0287 if (request_mem_region(lcd->phybase, lcd->physize,
0288 DRIVERNAME) == NULL) {
0289 ret = -EBUSY;
0290 goto out_no_memregion;
0291 }
0292
0293 lcd->virtbase = ioremap(lcd->phybase, lcd->physize);
0294 if (!lcd->virtbase) {
0295 ret = -ENOMEM;
0296 goto out_no_memregion;
0297 }
0298
0299 lcd->irq = platform_get_irq(pdev, 0);
0300
0301 if (lcd->irq >= 0) {
0302 if (request_irq(lcd->irq, charlcd_interrupt, 0,
0303 DRIVERNAME, lcd)) {
0304 ret = -EIO;
0305 goto out_no_irq;
0306 }
0307 }
0308
0309 platform_set_drvdata(pdev, lcd);
0310
0311
0312
0313
0314
0315 INIT_DELAYED_WORK(&lcd->init_work, charlcd_init_work);
0316 schedule_delayed_work(&lcd->init_work, 0);
0317
0318 dev_info(&pdev->dev, "initialized ARM character LCD at %08x\n",
0319 lcd->phybase);
0320
0321 return 0;
0322
0323 out_no_irq:
0324 iounmap(lcd->virtbase);
0325 out_no_memregion:
0326 release_mem_region(lcd->phybase, SZ_4K);
0327 out_no_resource:
0328 kfree(lcd);
0329 return ret;
0330 }
0331
0332 static int charlcd_suspend(struct device *dev)
0333 {
0334 struct charlcd *lcd = dev_get_drvdata(dev);
0335
0336
0337 charlcd_4bit_command(lcd, HD_DISPCTRL);
0338 return 0;
0339 }
0340
0341 static int charlcd_resume(struct device *dev)
0342 {
0343 struct charlcd *lcd = dev_get_drvdata(dev);
0344
0345
0346 charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
0347 return 0;
0348 }
0349
0350 static const struct dev_pm_ops charlcd_pm_ops = {
0351 .suspend = charlcd_suspend,
0352 .resume = charlcd_resume,
0353 };
0354
0355 static const struct of_device_id charlcd_match[] = {
0356 { .compatible = "arm,versatile-lcd", },
0357 {}
0358 };
0359
0360 static struct platform_driver charlcd_driver = {
0361 .driver = {
0362 .name = DRIVERNAME,
0363 .pm = &charlcd_pm_ops,
0364 .suppress_bind_attrs = true,
0365 .of_match_table = of_match_ptr(charlcd_match),
0366 },
0367 };
0368 builtin_platform_driver_probe(charlcd_driver, charlcd_probe);