xtensa: xtfpga: fix hardware lockup caused by LCD driver
authorMax Filippov <jcmvbkbc@gmail.com>
Fri, 27 Feb 2015 03:28:00 +0000 (06:28 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 6 May 2015 19:56:26 +0000 (21:56 +0200)
commit 4949009eb8d40a441dcddcd96e101e77d31cf1b2 upstream.

LCD driver is always built for the XTFPGA platform, but its base address
is not configurable, and is wrong for ML605/KC705. Its initialization
locks up KC705 board hardware.

Make the whole driver optional, and its base address and bus width
configurable. Implement 4-bit bus access method.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/xtensa/Kconfig
arch/xtensa/platforms/xtfpga/Makefile
arch/xtensa/platforms/xtfpga/include/platform/hardware.h
arch/xtensa/platforms/xtfpga/include/platform/lcd.h
arch/xtensa/platforms/xtfpga/lcd.c

index 0a1b95f81a32b165ae27421fbddf2423a4da4ca9..2b086a6ae6c74af2216df54ac8c3daed418077cf 100644 (file)
@@ -287,6 +287,36 @@ menu "Executable file formats"
 
 source "fs/Kconfig.binfmt"
 
+config XTFPGA_LCD
+       bool "Enable XTFPGA LCD driver"
+       depends on XTENSA_PLATFORM_XTFPGA
+       default n
+       help
+         There's a 2x16 LCD on most of XTFPGA boards, kernel may output
+         progress messages there during bootup/shutdown. It may be useful
+         during board bringup.
+
+         If unsure, say N.
+
+config XTFPGA_LCD_BASE_ADDR
+       hex "XTFPGA LCD base address"
+       depends on XTFPGA_LCD
+       default "0x0d0c0000"
+       help
+         Base address of the LCD controller inside KIO region.
+         Different boards from XTFPGA family have LCD controller at different
+         addresses. Please consult prototyping user guide for your board for
+         the correct address. Wrong address here may lead to hardware lockup.
+
+config XTFPGA_LCD_8BIT_ACCESS
+       bool "Use 8-bit access to XTFPGA LCD"
+       depends on XTFPGA_LCD
+       default n
+       help
+         LCD may be connected with 4- or 8-bit interface, 8-bit access may
+         only be used with 8-bit interface. Please consult prototyping user
+         guide for your board for the correct interface width.
+
 endmenu
 
 source "net/Kconfig"
index b9ae206340cd51012775e22af3391d3839be985e..7839d38b2337885862df5470e06452ea4efce211 100644 (file)
@@ -6,4 +6,5 @@
 #
 # Note 2! The CFLAGS definitions are in the main makefile...
 
-obj-y                  = setup.o lcd.o
+obj-y                  += setup.o
+obj-$(CONFIG_XTFPGA_LCD) += lcd.o
index 4416773cbde5c1755c17abf3d5028babaf80ef6c..b39fbcf5c611614009ac303177070bd551afc0e3 100644 (file)
@@ -44,9 +44,6 @@
 
 /* UART */
 #define DUART16552_PADDR       (XCHAL_KIO_PADDR + 0x0D050020)
-/* LCD instruction and data addresses. */
-#define LCD_INSTR_ADDR         ((char *)IOADDR(0x0D040000))
-#define LCD_DATA_ADDR          ((char *)IOADDR(0x0D040004))
 
 /* Misc. */
 #define XTFPGA_FPGAREGS_VADDR  IOADDR(0x0D020000)
index 0e435645af5a1ed2043944b6d6fa46647a7a3d86..4c8541ed11396e52bd570cb2cbd0d7bda1782f00 100644 (file)
 #ifndef __XTENSA_XTAVNET_LCD_H
 #define __XTENSA_XTAVNET_LCD_H
 
+#ifdef CONFIG_XTFPGA_LCD
 /* Display string STR at position POS on the LCD. */
 void lcd_disp_at_pos(char *str, unsigned char pos);
 
 /* Shift the contents of the LCD display left or right. */
 void lcd_shiftleft(void);
 void lcd_shiftright(void);
+#else
+static inline void lcd_disp_at_pos(char *str, unsigned char pos)
+{
+}
+
+static inline void lcd_shiftleft(void)
+{
+}
+
+static inline void lcd_shiftright(void)
+{
+}
+#endif
+
 #endif
index 2872301598df266ffe2822f9eb782b0e23b7156c..4dc0c1b43f4bfd917a65fc04b225a790d0fdfeaa 100644 (file)
@@ -1,50 +1,63 @@
 /*
- * Driver for the LCD display on the Tensilica LX60 Board.
+ * Driver for the LCD display on the Tensilica XTFPGA board family.
+ * http://www.mytechcorp.com/cfdata/productFile/File1/MOC-16216B-B-A0A04.pdf
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
  * Copyright (C) 2001, 2006 Tensilica Inc.
+ * Copyright (C) 2015 Cadence Design Systems Inc.
  */
 
-/*
- *
- * FIXME: this code is from the examples from the LX60 user guide.
- *
- * The lcd_pause function does busy waiting, which is probably not
- * great. Maybe the code could be changed to use kernel timers, or
- * change the hardware to not need to wait.
- */
-
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/io.h>
 
 #include <platform/hardware.h>
 #include <platform/lcd.h>
-#include <linux/delay.h>
 
-#define LCD_PAUSE_ITERATIONS   4000
+/* LCD instruction and data addresses. */
+#define LCD_INSTR_ADDR         ((char *)IOADDR(CONFIG_XTFPGA_LCD_BASE_ADDR))
+#define LCD_DATA_ADDR          (LCD_INSTR_ADDR + 4)
+
 #define LCD_CLEAR              0x1
 #define LCD_DISPLAY_ON         0xc
 
 /* 8bit and 2 lines display */
 #define LCD_DISPLAY_MODE8BIT   0x38
+#define LCD_DISPLAY_MODE4BIT   0x28
 #define LCD_DISPLAY_POS                0x80
 #define LCD_SHIFT_LEFT         0x18
 #define LCD_SHIFT_RIGHT                0x1c
 
+static void lcd_put_byte(u8 *addr, u8 data)
+{
+#ifdef CONFIG_XTFPGA_LCD_8BIT_ACCESS
+       ACCESS_ONCE(*addr) = data;
+#else
+       ACCESS_ONCE(*addr) = data & 0xf0;
+       ACCESS_ONCE(*addr) = (data << 4) & 0xf0;
+#endif
+}
+
 static int __init lcd_init(void)
 {
-       *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT;
+       ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE8BIT;
        mdelay(5);
-       *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT;
+       ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE8BIT;
        udelay(200);
-       *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT;
+       ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE8BIT;
+       udelay(50);
+#ifndef CONFIG_XTFPGA_LCD_8BIT_ACCESS
+       ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE4BIT;
+       udelay(50);
+       lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_MODE4BIT);
        udelay(50);
-       *LCD_INSTR_ADDR = LCD_DISPLAY_ON;
+#endif
+       lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_ON);
        udelay(50);
-       *LCD_INSTR_ADDR = LCD_CLEAR;
+       lcd_put_byte(LCD_INSTR_ADDR, LCD_CLEAR);
        mdelay(10);
        lcd_disp_at_pos("XTENSA LINUX", 0);
        return 0;
@@ -52,10 +65,10 @@ static int __init lcd_init(void)
 
 void lcd_disp_at_pos(char *str, unsigned char pos)
 {
-       *LCD_INSTR_ADDR = LCD_DISPLAY_POS | pos;
+       lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_POS | pos);
        udelay(100);
        while (*str != 0) {
-               *LCD_DATA_ADDR = *str;
+               lcd_put_byte(LCD_DATA_ADDR, *str);
                udelay(200);
                str++;
        }
@@ -63,13 +76,13 @@ void lcd_disp_at_pos(char *str, unsigned char pos)
 
 void lcd_shiftleft(void)
 {
-       *LCD_INSTR_ADDR = LCD_SHIFT_LEFT;
+       lcd_put_byte(LCD_INSTR_ADDR, LCD_SHIFT_LEFT);
        udelay(50);
 }
 
 void lcd_shiftright(void)
 {
-       *LCD_INSTR_ADDR = LCD_SHIFT_RIGHT;
+       lcd_put_byte(LCD_INSTR_ADDR, LCD_SHIFT_RIGHT);
        udelay(50);
 }