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