lpc32xx: add support for board work_92105
[oweals/u-boot.git] / board / work-microwave / work_92105 / work_92105_display.c
diff --git a/board/work-microwave/work_92105/work_92105_display.c b/board/work-microwave/work_92105/work_92105_display.c
new file mode 100644 (file)
index 0000000..c8b1013
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * work_92105 display support
+ *
+ * (C) Copyright 2014  DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ *
+ * The work_92105 display is a HD44780-compatible module
+ * controlled through a MAX6957AAX SPI port expander, two
+ * MAX518 I2C DACs and native LPC32xx GPO 15.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/emc.h>
+#include <asm/gpio.h>
+#include <spi.h>
+#include <i2c.h>
+#include <version.h>
+#include <vsprintf.h>
+
+/*
+ * GPO 15 in port 3 is gpio 3*32+15 = 111
+ */
+
+#define GPO_15 111
+
+/**
+ * MAX6957AAX registers that we will be using
+ */
+
+#define MAX6957_CONF           0x04
+
+#define MAX6957_CONF_08_11     0x0A
+#define MAX6957_CONF_12_15     0x0B
+#define MAX6957_CONF_16_19     0x0C
+
+/**
+ * Individual gpio ports (one per gpio) to HD44780
+ */
+
+#define MAX6957AAX_HD44780_RS  0x29
+#define MAX6957AAX_HD44780_R_W 0x2A
+#define MAX6957AAX_HD44780_EN  0x2B
+#define MAX6957AAX_HD44780_DATA        0x4C
+
+/**
+ * Display controller instructions
+ */
+
+/* Function set: eight bits, two lines, 8-dot font */
+#define HD44780_FUNCTION_SET           0x38
+
+/* Display ON / OFF: turn display on */
+#define HD44780_DISPLAY_ON_OFF_CONTROL 0x0C
+
+/* Entry mode: increment */
+#define HD44780_ENTRY_MODE_SET         0x06
+
+/* Clear */
+#define HD44780_CLEAR_DISPLAY          0x01
+
+/* Set DDRAM addr (to be ORed with exact address) */
+#define HD44780_SET_DDRAM_ADDR         0x80
+
+/* Set CGRAM addr (to be ORed with exact address) */
+#define HD44780_SET_CGRAM_ADDR         0x40
+
+/**
+ * Default value for contrats
+ */
+
+#define CONTRAST_DEFAULT  25
+
+/**
+ * Define slave as a module-wide local to save passing it around,
+ * plus we will need it after init for the "hd44780" command.
+ */
+
+static struct spi_slave *slave;
+
+/*
+ * Write a value into a MAX6957AAX register.
+ */
+
+static void max6957aax_write(uint8_t reg, uint8_t value)
+{
+       uint8_t dout[2];
+
+       dout[0] = reg;
+       dout[1] = value;
+       gpio_set_value(GPO_15, 0);
+       /* do SPI read/write (passing din==dout is OK) */
+       spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
+       gpio_set_value(GPO_15, 1);
+}
+
+/*
+ * Read a value from a MAX6957AAX register.
+ *
+ * According to the MAX6957AAX datasheet, we should release the chip
+ * select halfway through the read sequence, when the actual register
+ * value is read; but the WORK_92105 hardware prevents the MAX6957AAX
+ * SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected.
+ * so let's release the CS an hold it again while reading the result.
+ */
+
+static uint8_t max6957aax_read(uint8_t reg)
+{
+       uint8_t dout[2], din[2];
+
+       /* send read command */
+       dout[0] = reg | 0x80; /* set bit 7 to indicate read */
+       dout[1] = 0;
+       gpio_set_value(GPO_15, 0);
+       /* do SPI read/write (passing din==dout is OK) */
+       spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
+       /* latch read command */
+       gpio_set_value(GPO_15, 1);
+       /* read register -- din = noop on xmit, din[1] = reg on recv */
+       din[0] = 0;
+       din[1] = 0;
+       gpio_set_value(GPO_15, 0);
+       /* do SPI read/write (passing din==dout is OK) */
+       spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END);
+       /* end of read. */
+       gpio_set_value(GPO_15, 1);
+       return din[1];
+}
+
+static void hd44780_instruction(unsigned long instruction)
+{
+       max6957aax_write(MAX6957AAX_HD44780_RS, 0);
+       max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 1);
+       max6957aax_write(MAX6957AAX_HD44780_DATA, instruction);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+       /* HD44780 takes 37 us for most instructions, 1520 for clear */
+       if (instruction == HD44780_CLEAR_DISPLAY)
+               udelay(2000);
+       else
+               udelay(100);
+}
+
+static void hd44780_write_char(char c)
+{
+       max6957aax_write(MAX6957AAX_HD44780_RS, 1);
+       max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 1);
+       max6957aax_write(MAX6957AAX_HD44780_DATA, c);
+       max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+       /* HD44780 takes 37 us to write to DDRAM or CGRAM */
+       udelay(100);
+}
+
+static void hd44780_write_str(char *s)
+{
+       max6957aax_write(MAX6957AAX_HD44780_RS, 1);
+       max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
+       while (*s) {
+               max6957aax_write(MAX6957AAX_HD44780_EN, 1);
+               max6957aax_write(MAX6957AAX_HD44780_DATA, *s);
+               max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+               s++;
+               /* HD44780 takes 37 us to write to DDRAM or CGRAM */
+               udelay(100);
+       }
+}
+
+/*
+ * Existing user code might expect these custom characters to be
+ * recognized and displayed on the LCD
+ */
+
+static u8 char_gen_chars[] = {
+       /* #8, empty rectangle */
+       0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F,
+       /* #9, filled right arrow */
+       0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00,
+       /* #10, filled left arrow */
+       0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00,
+       /* #11, up and down arrow */
+       0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04,
+       /* #12, plus/minus */
+       0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00,
+       /* #13, fat exclamation mark */
+       0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00,
+       /* #14, empty square */
+       0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00,
+       /* #15, struck out square */
+       0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00,
+};
+
+static void hd44780_init_char_gen(void)
+{
+       int i;
+
+       hd44780_instruction(HD44780_SET_CGRAM_ADDR);
+
+       for (i = 0; i < sizeof(char_gen_chars); i++)
+               hd44780_write_char(char_gen_chars[i]);
+
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR);
+}
+
+void work_92105_display_init(void)
+{
+       int claim_err;
+       char *display_contrast_str;
+       uint8_t display_contrast = CONTRAST_DEFAULT;
+       uint8_t enable_backlight = 0x96;
+
+       slave = spi_setup_slave(0, 0, 500000, 0);
+
+       if (!slave) {
+               printf("Failed to set up SPI slave\n");
+               return;
+       }
+
+       claim_err = spi_claim_bus(slave);
+
+       if (claim_err)
+               debug("Failed to claim SPI bus: %d\n", claim_err);
+
+       /* enable backlight */
+       i2c_write(0x2c, 0x01, 1, &enable_backlight, 1);
+
+       /* set display contrast */
+       display_contrast_str = getenv("fwopt_dispcontrast");
+       if (display_contrast_str)
+               display_contrast = simple_strtoul(display_contrast_str,
+                       NULL, 10);
+       i2c_write(0x2c, 0x00, 1, &display_contrast, 1);
+
+       /* request GPO_15 as an output initially set to 1 */
+       gpio_request(GPO_15, "MAX6957_nCS");
+       gpio_direction_output(GPO_15, 1);
+
+       /* enable MAX6957 portexpander */
+       max6957aax_write(MAX6957_CONF, 0x01);
+       /* configure pin 8 as input, pins 9..19 as outputs */
+       max6957aax_write(MAX6957_CONF_08_11, 0x56);
+       max6957aax_write(MAX6957_CONF_12_15, 0x55);
+       max6957aax_write(MAX6957_CONF_16_19, 0x55);
+
+       /* initialize HD44780 */
+       max6957aax_write(MAX6957AAX_HD44780_EN, 0);
+       hd44780_instruction(HD44780_FUNCTION_SET);
+       hd44780_instruction(HD44780_DISPLAY_ON_OFF_CONTROL);
+       hd44780_instruction(HD44780_ENTRY_MODE_SET);
+
+       /* write custom character glyphs */
+       hd44780_init_char_gen();
+
+       /* Show U-Boot version, date and time as a sign-of-life */
+       hd44780_instruction(HD44780_CLEAR_DISPLAY);
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR | 0);
+       hd44780_write_str(U_BOOT_VERSION);
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64);
+       hd44780_write_str(U_BOOT_DATE);
+       hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64 | 20);
+       hd44780_write_str(U_BOOT_TIME);
+}
+
+#ifdef CONFIG_CMD_MAX6957
+
+static int do_max6957aax(cmd_tbl_t *cmdtp, int flag, int argc,
+                        char *const argv[])
+{
+       int reg, val;
+
+       if (argc != 3)
+               return CMD_RET_USAGE;
+       switch (argv[1][0]) {
+       case 'r':
+       case 'R':
+               reg = simple_strtoul(argv[2], NULL, 0);
+               val = max6957aax_read(reg);
+               printf("MAX6957 reg 0x%02x read 0x%02x\n", reg, val);
+               return 0;
+       default:
+               reg = simple_strtoul(argv[1], NULL, 0);
+               val = simple_strtoul(argv[2], NULL, 0);
+               max6957aax_write(reg, val);
+               printf("MAX6957 reg 0x%02x wrote 0x%02x\n", reg, val);
+               return 0;
+       }
+       return 1;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char max6957aax_help_text[] =
+       "max6957aax - write or read display register:\n"
+               "\tmax6957aax R|r reg - read display register;\n"
+               "\tmax6957aax reg val - write display register.";
+#endif
+
+U_BOOT_CMD(
+       max6957aax, 6, 1, do_max6957aax,
+       "SPI MAX6957 display write/read",
+       max6957aax_help_text
+);
+#endif /* CONFIG_CMD_MAX6957 */
+
+#ifdef CONFIG_CMD_HD44760
+
+/*
+ * We need the HUSH parser because we need string arguments, and
+ * only HUSH can understand them.
+ */
+
+#if !defined(CONFIG_SYS_HUSH_PARSER)
+#error CONFIG_CMD_HD44760 requires CONFIG_SYS_HUSH_PARSER
+#endif
+
+static int do_hd44780(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
+{
+       char *cmd;
+
+       if (argc != 3)
+               return CMD_RET_USAGE;
+
+       cmd = argv[1];
+
+       if (strcasecmp(cmd, "cmd") == 0)
+               hd44780_instruction(simple_strtol(argv[2], NULL, 0));
+       else if (strcasecmp(cmd, "data") == 0)
+               hd44780_write_char(simple_strtol(argv[2], NULL, 0));
+       else if (strcasecmp(cmd, "str") == 0)
+               hd44780_write_str(argv[2]);
+       return 0;
+}
+
+#ifdef CONFIG_SYS_LONGHELP
+static char hd44780_help_text[] =
+       "hd44780 - control LCD driver:\n"
+               "\thd44780 cmd <val> - send command <val> to driver;\n"
+               "\thd44780 data <val> - send data <val> to driver;\n"
+               "\thd44780 str \"<text>\" - send \"<text>\" to driver.";
+#endif
+
+U_BOOT_CMD(
+       hd44780, 6, 1, do_hd44780,
+       "HD44780 LCD driver control",
+       hd44780_help_text
+);
+#endif /* CONFIG_CMD_HD44780 */