Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xtensa: xtfpga: fix hardware lockup caused by LCD driver

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.

Cc: stable@vger.kernel.org
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>

+81 -25
+30
arch/xtensa/Kconfig
··· 428 428 429 429 If unsure, leave the default value here. 430 430 431 + config XTFPGA_LCD 432 + bool "Enable XTFPGA LCD driver" 433 + depends on XTENSA_PLATFORM_XTFPGA 434 + default n 435 + help 436 + There's a 2x16 LCD on most of XTFPGA boards, kernel may output 437 + progress messages there during bootup/shutdown. It may be useful 438 + during board bringup. 439 + 440 + If unsure, say N. 441 + 442 + config XTFPGA_LCD_BASE_ADDR 443 + hex "XTFPGA LCD base address" 444 + depends on XTFPGA_LCD 445 + default "0x0d0c0000" 446 + help 447 + Base address of the LCD controller inside KIO region. 448 + Different boards from XTFPGA family have LCD controller at different 449 + addresses. Please consult prototyping user guide for your board for 450 + the correct address. Wrong address here may lead to hardware lockup. 451 + 452 + config XTFPGA_LCD_8BIT_ACCESS 453 + bool "Use 8-bit access to XTFPGA LCD" 454 + depends on XTFPGA_LCD 455 + default n 456 + help 457 + LCD may be connected with 4- or 8-bit interface, 8-bit access may 458 + only be used with 8-bit interface. Please consult prototyping user 459 + guide for your board for the correct interface width. 460 + 431 461 endmenu 432 462 433 463 menu "Executable file formats"
+2 -1
arch/xtensa/platforms/xtfpga/Makefile
··· 6 6 # 7 7 # Note 2! The CFLAGS definitions are in the main makefile... 8 8 9 - obj-y = setup.o lcd.o 9 + obj-y += setup.o 10 + obj-$(CONFIG_XTFPGA_LCD) += lcd.o
-3
arch/xtensa/platforms/xtfpga/include/platform/hardware.h
··· 40 40 41 41 /* UART */ 42 42 #define DUART16552_PADDR (XCHAL_KIO_PADDR + 0x0D050020) 43 - /* LCD instruction and data addresses. */ 44 - #define LCD_INSTR_ADDR ((char *)IOADDR(0x0D040000)) 45 - #define LCD_DATA_ADDR ((char *)IOADDR(0x0D040004)) 46 43 47 44 /* Misc. */ 48 45 #define XTFPGA_FPGAREGS_VADDR IOADDR(0x0D020000)
+15
arch/xtensa/platforms/xtfpga/include/platform/lcd.h
··· 11 11 #ifndef __XTENSA_XTAVNET_LCD_H 12 12 #define __XTENSA_XTAVNET_LCD_H 13 13 14 + #ifdef CONFIG_XTFPGA_LCD 14 15 /* Display string STR at position POS on the LCD. */ 15 16 void lcd_disp_at_pos(char *str, unsigned char pos); 16 17 17 18 /* Shift the contents of the LCD display left or right. */ 18 19 void lcd_shiftleft(void); 19 20 void lcd_shiftright(void); 21 + #else 22 + static inline void lcd_disp_at_pos(char *str, unsigned char pos) 23 + { 24 + } 25 + 26 + static inline void lcd_shiftleft(void) 27 + { 28 + } 29 + 30 + static inline void lcd_shiftright(void) 31 + { 32 + } 33 + #endif 34 + 20 35 #endif
+34 -21
arch/xtensa/platforms/xtfpga/lcd.c
··· 1 1 /* 2 - * Driver for the LCD display on the Tensilica LX60 Board. 2 + * Driver for the LCD display on the Tensilica XTFPGA board family. 3 + * http://www.mytechcorp.com/cfdata/productFile/File1/MOC-16216B-B-A0A04.pdf 3 4 * 4 5 * This file is subject to the terms and conditions of the GNU General Public 5 6 * License. See the file "COPYING" in the main directory of this archive 6 7 * for more details. 7 8 * 8 9 * Copyright (C) 2001, 2006 Tensilica Inc. 10 + * Copyright (C) 2015 Cadence Design Systems Inc. 9 11 */ 10 12 11 - /* 12 - * 13 - * FIXME: this code is from the examples from the LX60 user guide. 14 - * 15 - * The lcd_pause function does busy waiting, which is probably not 16 - * great. Maybe the code could be changed to use kernel timers, or 17 - * change the hardware to not need to wait. 18 - */ 19 - 13 + #include <linux/delay.h> 20 14 #include <linux/init.h> 21 15 #include <linux/io.h> 22 16 23 17 #include <platform/hardware.h> 24 18 #include <platform/lcd.h> 25 - #include <linux/delay.h> 26 19 27 - #define LCD_PAUSE_ITERATIONS 4000 20 + /* LCD instruction and data addresses. */ 21 + #define LCD_INSTR_ADDR ((char *)IOADDR(CONFIG_XTFPGA_LCD_BASE_ADDR)) 22 + #define LCD_DATA_ADDR (LCD_INSTR_ADDR + 4) 23 + 28 24 #define LCD_CLEAR 0x1 29 25 #define LCD_DISPLAY_ON 0xc 30 26 31 27 /* 8bit and 2 lines display */ 32 28 #define LCD_DISPLAY_MODE8BIT 0x38 29 + #define LCD_DISPLAY_MODE4BIT 0x28 33 30 #define LCD_DISPLAY_POS 0x80 34 31 #define LCD_SHIFT_LEFT 0x18 35 32 #define LCD_SHIFT_RIGHT 0x1c 36 33 34 + static void lcd_put_byte(u8 *addr, u8 data) 35 + { 36 + #ifdef CONFIG_XTFPGA_LCD_8BIT_ACCESS 37 + ACCESS_ONCE(*addr) = data; 38 + #else 39 + ACCESS_ONCE(*addr) = data & 0xf0; 40 + ACCESS_ONCE(*addr) = (data << 4) & 0xf0; 41 + #endif 42 + } 43 + 37 44 static int __init lcd_init(void) 38 45 { 39 - *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT; 46 + ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE8BIT; 40 47 mdelay(5); 41 - *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT; 48 + ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE8BIT; 42 49 udelay(200); 43 - *LCD_INSTR_ADDR = LCD_DISPLAY_MODE8BIT; 50 + ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE8BIT; 44 51 udelay(50); 45 - *LCD_INSTR_ADDR = LCD_DISPLAY_ON; 52 + #ifndef CONFIG_XTFPGA_LCD_8BIT_ACCESS 53 + ACCESS_ONCE(*LCD_INSTR_ADDR) = LCD_DISPLAY_MODE4BIT; 46 54 udelay(50); 47 - *LCD_INSTR_ADDR = LCD_CLEAR; 55 + lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_MODE4BIT); 56 + udelay(50); 57 + #endif 58 + lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_ON); 59 + udelay(50); 60 + lcd_put_byte(LCD_INSTR_ADDR, LCD_CLEAR); 48 61 mdelay(10); 49 62 lcd_disp_at_pos("XTENSA LINUX", 0); 50 63 return 0; ··· 65 52 66 53 void lcd_disp_at_pos(char *str, unsigned char pos) 67 54 { 68 - *LCD_INSTR_ADDR = LCD_DISPLAY_POS | pos; 55 + lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_POS | pos); 69 56 udelay(100); 70 57 while (*str != 0) { 71 - *LCD_DATA_ADDR = *str; 58 + lcd_put_byte(LCD_DATA_ADDR, *str); 72 59 udelay(200); 73 60 str++; 74 61 } ··· 76 63 77 64 void lcd_shiftleft(void) 78 65 { 79 - *LCD_INSTR_ADDR = LCD_SHIFT_LEFT; 66 + lcd_put_byte(LCD_INSTR_ADDR, LCD_SHIFT_LEFT); 80 67 udelay(50); 81 68 } 82 69 83 70 void lcd_shiftright(void) 84 71 { 85 - *LCD_INSTR_ADDR = LCD_SHIFT_RIGHT; 72 + lcd_put_byte(LCD_INSTR_ADDR, LCD_SHIFT_RIGHT); 86 73 udelay(50); 87 74 } 88 75