From 6c4e976785011dfbe461821d0bfc58cfd60eac56 Mon Sep 17 00:00:00 2001 From: Cyril Bur Date: Fri, 17 Feb 2017 14:28:49 +1100 Subject: drivers/misc: Add Aspeed LPC control driver In order to manage server systems, there is typically another processor known as a BMC (Baseboard Management Controller) which is responsible for powering the server and other various elements, sometimes fans, often the system flash. The Aspeed BMC family which is what is used on OpenPOWER machines and a number of x86 as well is typically connected to the host via an LPC (Low Pin Count) bus (among others). The LPC bus is an ISA bus on steroids. It's generally used by the BMC chip to provide the host with access to the system flash (via MEM/FW cycles) that contains the BIOS or other host firmware along with a number of SuperIO-style IOs (via IO space) such as UARTs, IPMI controllers. On the BMC chip side, this is all configured via a bunch of registers whose content is related to a given policy of what devices are exposed at a per system level, which is system/vendor specific, so we don't want to bolt that into the BMC kernel. This started with a need to provide something nicer than /dev/mem for user space to configure these things. One important aspect of the configuration is how the MEM/FW space is exposed to the host (ie, the x86 or POWER). Some registers in that bridge can define a window remapping all or portion of the LPC MEM/FW space to a portion of the BMC internal bus, with no specific limits imposed in HW. I think it makes sense to ensure that this window is configured by a kernel driver that can apply some serious sanity checks on what it is configured to map. In practice, user space wants to control this by flipping the mapping between essentially two types of portions of the BMC address space: - The flash space. This is a region of the BMC MMIO space that more/less directly maps the system flash (at least for reads, writes are somewhat more complicated). - One (or more) reserved area(s) of the BMC physical memory. The latter is needed for a number of things, such as avoiding letting the host manipulate the innards of the BMC flash controller via some evil backdoor, we want to do flash updates by routing the window to a portion of memory (under control of a mailbox protocol via some separate set of registers) which the host can use to write new data in bulk and then request the BMC to flash it. There are other uses, such as allowing the host to boot from an in-memory flash image rather than the one in flash (very handy for continuous integration and test, the BMC can just download new images). It is important to note that due to the way the Aspeed chip lets the kernel configure the mapping between host LPC addresses and BMC ram addresses the offset within the window must be a multiple of size. Not doing so will fragment the accessible space rather than simply moving 'zero' upwards. This is caused by the nature of HICR8 being a mask and the way host LPC addresses are translated. Signed-off-by: Cyril Bur Reviewed-by: Joel Stanley Reviewed-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 8 ++ drivers/misc/Makefile | 1 + drivers/misc/aspeed-lpc-ctrl.c | 267 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 drivers/misc/aspeed-lpc-ctrl.c (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c290990d73ed..77b001e7cf85 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -771,6 +771,14 @@ config PANEL_BOOT_MESSAGE endif # PANEL +config ASPEED_LPC_CTRL + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON + tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" + ---help--- + Control Aspeed ast2400/2500 HOST LPC to BMC mappings through + ioctl()s, the driver also provides a read/write interface to a BMC ram + region where the host LPC read/write region can be buffered. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7a3ea89339b4..4925ea8e1952 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PANEL) += panel.o +obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c new file mode 100644 index 000000000000..f6acbe1d9378 --- /dev/null +++ b/drivers/misc/aspeed-lpc-ctrl.c @@ -0,0 +1,267 @@ +/* + * Copyright 2017 IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEVICE_NAME "aspeed-lpc-ctrl" + +#define HICR7 0x8 +#define HICR8 0xc + +struct aspeed_lpc_ctrl { + struct miscdevice miscdev; + struct regmap *regmap; + phys_addr_t mem_base; + resource_size_t mem_size; + u32 pnor_size; + u32 pnor_base; +}; + +static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file) +{ + return container_of(file->private_data, struct aspeed_lpc_ctrl, + miscdev); +} + +static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file); + unsigned long vsize = vma->vm_end - vma->vm_start; + pgprot_t prot = vma->vm_page_prot; + + if (vma->vm_pgoff + vsize > lpc_ctrl->mem_base + lpc_ctrl->mem_size) + return -EINVAL; + + /* ast2400/2500 AHB accesses are not cache coherent */ + prot = pgprot_dmacoherent(prot); + + if (remap_pfn_range(vma, vma->vm_start, + (lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff, + vsize, prot)) + return -EAGAIN; + + return 0; +} + +static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd, + unsigned long param) +{ + struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file); + void __user *p = (void __user *)param; + struct aspeed_lpc_ctrl_mapping map; + u32 addr; + u32 size; + long rc; + + if (copy_from_user(&map, p, sizeof(map))) + return -EFAULT; + + if (map.flags != 0) + return -EINVAL; + + switch (cmd) { + case ASPEED_LPC_CTRL_IOCTL_GET_SIZE: + /* The flash windows don't report their size */ + if (map.window_type != ASPEED_LPC_CTRL_WINDOW_MEMORY) + return -EINVAL; + + /* Support more than one window id in the future */ + if (map.window_id != 0) + return -EINVAL; + + map.size = lpc_ctrl->mem_size; + + return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0; + case ASPEED_LPC_CTRL_IOCTL_MAP: + + /* + * The top half of HICR7 is the MSB of the BMC address of the + * mapping. + * The bottom half of HICR7 is the MSB of the HOST LPC + * firmware space address of the mapping. + * + * The 1 bits in the top of half of HICR8 represent the bits + * (in the requested address) that should be ignored and + * replaced with those from the top half of HICR7. + * The 1 bits in the bottom half of HICR8 represent the bits + * (in the requested address) that should be kept and pass + * into the BMC address space. + */ + + /* + * It doesn't make sense to talk about a size or offset with + * low 16 bits set. Both HICR7 and HICR8 talk about the top 16 + * bits of addresses and sizes. + */ + + if ((map.size & 0x0000ffff) || (map.offset & 0x0000ffff)) + return -EINVAL; + + /* + * Because of the way the masks work in HICR8 offset has to + * be a multiple of size. + */ + if (map.offset & (map.size - 1)) + return -EINVAL; + + if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) { + addr = lpc_ctrl->pnor_base; + size = lpc_ctrl->pnor_size; + } else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) { + addr = lpc_ctrl->mem_base; + size = lpc_ctrl->mem_size; + } else { + return -EINVAL; + } + + /* Check overflow first! */ + if (map.offset + map.size < map.offset || + map.offset + map.size > size) + return -EINVAL; + + if (map.size == 0 || map.size > size) + return -EINVAL; + + addr += map.offset; + + /* + * addr (host lpc address) is safe regardless of values. This + * simply changes the address the host has to request on its + * side of the LPC bus. This cannot impact the hosts own + * memory space by surprise as LPC specific accessors are + * required. The only strange thing that could be done is + * setting the lower 16 bits but the shift takes care of that. + */ + + rc = regmap_write(lpc_ctrl->regmap, HICR7, + (addr | (map.addr >> 16))); + if (rc) + return rc; + + return regmap_write(lpc_ctrl->regmap, HICR8, + (~(map.size - 1)) | ((map.size >> 16) - 1)); + } + + return -EINVAL; +} + +static const struct file_operations aspeed_lpc_ctrl_fops = { + .owner = THIS_MODULE, + .mmap = aspeed_lpc_ctrl_mmap, + .unlocked_ioctl = aspeed_lpc_ctrl_ioctl, +}; + +static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) +{ + struct aspeed_lpc_ctrl *lpc_ctrl; + struct device_node *node; + struct resource resm; + struct device *dev; + int rc; + + dev = &pdev->dev; + + lpc_ctrl = devm_kzalloc(dev, sizeof(*lpc_ctrl), GFP_KERNEL); + if (!lpc_ctrl) + return -ENOMEM; + + node = of_parse_phandle(dev->of_node, "flash", 0); + if (!node) { + dev_err(dev, "Didn't find host pnor flash node\n"); + return -ENODEV; + } + + rc = of_address_to_resource(node, 1, &resm); + of_node_put(node); + if (rc) { + dev_err(dev, "Couldn't address to resource for flash\n"); + return rc; + } + + lpc_ctrl->pnor_size = resource_size(&resm); + lpc_ctrl->pnor_base = resm.start; + + dev_set_drvdata(&pdev->dev, lpc_ctrl); + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) { + dev_err(dev, "Didn't find reserved memory\n"); + return -EINVAL; + } + + rc = of_address_to_resource(node, 0, &resm); + of_node_put(node); + if (rc) { + dev_err(dev, "Couldn't address to resource for reserved memory\n"); + return -ENOMEM; + } + + lpc_ctrl->mem_size = resource_size(&resm); + lpc_ctrl->mem_base = resm.start; + + lpc_ctrl->regmap = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(lpc_ctrl->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; + lpc_ctrl->miscdev.name = DEVICE_NAME; + lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; + lpc_ctrl->miscdev.parent = dev; + rc = misc_register(&lpc_ctrl->miscdev); + if (rc) + dev_err(dev, "Unable to register device\n"); + else + dev_info(dev, "Loaded at 0x%08x (0x%08x)\n", + lpc_ctrl->mem_base, lpc_ctrl->mem_size); + + return rc; +} + +static int aspeed_lpc_ctrl_remove(struct platform_device *pdev) +{ + struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev); + + misc_deregister(&lpc_ctrl->miscdev); + + return 0; +} + +static const struct of_device_id aspeed_lpc_ctrl_match[] = { + { .compatible = "aspeed,ast2400-lpc-ctrl" }, + { .compatible = "aspeed,ast2500-lpc-ctrl" }, + { }, +}; + +static struct platform_driver aspeed_lpc_ctrl_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_lpc_ctrl_match, + }, + .probe = aspeed_lpc_ctrl_probe, + .remove = aspeed_lpc_ctrl_remove, +}; + +module_platform_driver(aspeed_lpc_ctrl_driver); + +MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cyril Bur "); +MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings"); -- cgit v1.2.3 From 39f8ea46724efbed3ca021863a22337c31be264c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 10 Mar 2017 15:15:17 +0100 Subject: auxdisplay: charlcd: Extract character LCD core from misc/panel Extract the character LCD core from the Parallel port LCD/Keypad Panel driver in the misc subsystem, and convert it into a subdriver in the auxdisplay subsystem. This allows the character LCD core to be used by other drivers later. Compilation is controlled by its own Kconfig symbol CHARLCD, which is to be selected by its users, but can be enabled manually for compile-testing. All functions changed their prefix from "lcd_" to "charlcd_", and gained a "struct charlcd *" parameter to operate on a specific instance. While the driver API thus is ready to support multiple instances, the current limitation of a single display (/dev/lcd has a single misc minor assigned) is retained. No functional changes intended. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 1 + drivers/misc/panel.c | 827 ++++++--------------------------------------------- 2 files changed, 93 insertions(+), 735 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 77b001e7cf85..fb933b0b9297 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -495,6 +495,7 @@ config VEXPRESS_SYSCFG config PANEL tristate "Parallel port LCD/Keypad Panel support" depends on PARPORT + select CHARLCD ---help--- Say Y here if you have an HD44780 or KS-0074 LCD connected to your parallel port. This driver also features 4 and 6-key keypads. The LCD diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index ef2ece0f26af..e0c014c2356f 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -1,6 +1,7 @@ /* * Front panel driver for Linux * Copyright (C) 2000-2008, Willy Tarreau + * Copyright (C) 2016-2017 Glider bvba * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,15 +55,12 @@ #include #include #include -#include -#include -#include -#include #include #include -#define LCD_MINOR 156 +#include + #define KEYPAD_MINOR 185 #define LCD_MAXBYTES 256 /* max burst write */ @@ -76,9 +74,6 @@ /* a key repeats this times INPUT_POLL_TIME */ #define KEYPAD_REP_DELAY (2) -/* keep the light on this many seconds for each flash */ -#define FLASH_LIGHT_TEMPO (4) - /* converts an r_str() input to an active high, bits string : 000BAOSE */ #define PNL_PINPUT(a) ((((unsigned char)(a)) ^ 0x7F) >> 3) @@ -120,40 +115,6 @@ #define PIN_SELECP 17 #define PIN_NOT_SET 127 -#define LCD_FLAG_B 0x0004 /* blink on */ -#define LCD_FLAG_C 0x0008 /* cursor on */ -#define LCD_FLAG_D 0x0010 /* display on */ -#define LCD_FLAG_F 0x0020 /* large font mode */ -#define LCD_FLAG_N 0x0040 /* 2-rows mode */ -#define LCD_FLAG_L 0x0080 /* backlight enabled */ - -/* LCD commands */ -#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */ - -#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */ -#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */ - -#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */ -#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */ -#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */ -#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */ - -#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */ -#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */ -#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */ - -#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */ -#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */ -#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */ -#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */ - -#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */ - -#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */ - -#define LCD_ESCAPE_LEN 24 /* max chars for LCD escape command */ -#define LCD_ESCAPE_CHAR 27 /* use char 27 for escape command */ - #define NOT_SET -1 /* macros to simplify use of the parallel port */ @@ -245,19 +206,10 @@ static wait_queue_head_t keypad_read_wait; static struct { bool enabled; bool initialized; - bool must_clear; - int height; - int width; - int bwidth; - int hwidth; int charset; int proto; - struct delayed_work bl_work; - struct mutex bl_tempo_lock; /* Protects access to bl_tempo */ - bool bl_tempo; - /* TODO: use union here? */ struct { int e; @@ -268,20 +220,7 @@ static struct { int bl; } pins; - /* contains the LCD config state */ - unsigned long int flags; - - /* Contains the LCD X and Y offset */ - struct { - unsigned long int x; - unsigned long int y; - } addr; - - /* Current escape sequence and it's length or -1 if outside */ - struct { - char buf[LCD_ESCAPE_LEN + 1]; - int len; - } esc_seq; + struct charlcd *charlcd; } lcd; /* Needed only for init */ @@ -464,17 +403,12 @@ static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES]; /* global variables */ /* Device single-open policy control */ -static atomic_t lcd_available = ATOMIC_INIT(1); static atomic_t keypad_available = ATOMIC_INIT(1); static struct pardevice *pprt; static int keypad_initialized; -static void (*lcd_write_cmd)(int); -static void (*lcd_write_data)(int); -static void (*lcd_clear_fast)(void); - static DEFINE_SPINLOCK(pprt_lock); static struct timer_list scan_timer; @@ -574,8 +508,6 @@ static int keypad_enabled = NOT_SET; module_param(keypad_enabled, int, 0000); MODULE_PARM_DESC(keypad_enabled, "Deprecated option, use keypad_type instead"); -static const unsigned char *lcd_char_conv; - /* for some LCD drivers (ks0074) we need a charset conversion table. */ static const unsigned char lcd_char_conv_ks0074[256] = { /* 0|8 1|9 2|A 3|B 4|C 5|D 6|E 7|F */ @@ -752,15 +684,6 @@ static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val) } } -/* sleeps that many milliseconds with a reschedule */ -static void long_sleep(int ms) -{ - if (in_interrupt()) - mdelay(ms); - else - schedule_timeout_interruptible(msecs_to_jiffies(ms)); -} - /* * send a serial byte to the LCD panel. The caller is responsible for locking * if needed. @@ -792,8 +715,11 @@ static void lcd_send_serial(int byte) } /* turn the backlight on or off */ -static void __lcd_backlight(int on) +static void lcd_backlight(struct charlcd *charlcd, int on) { + if (lcd.pins.bl == PIN_NONE) + return; + /* The backlight is activated by setting the AUTOFEED line to +5V */ spin_lock_irq(&pprt_lock); if (on) @@ -804,46 +730,8 @@ static void __lcd_backlight(int on) spin_unlock_irq(&pprt_lock); } -static void lcd_backlight(int on) -{ - if (lcd.pins.bl == PIN_NONE) - return; - - mutex_lock(&lcd.bl_tempo_lock); - if (!lcd.bl_tempo) - __lcd_backlight(on); - mutex_unlock(&lcd.bl_tempo_lock); -} - -static void lcd_bl_off(struct work_struct *work) -{ - mutex_lock(&lcd.bl_tempo_lock); - if (lcd.bl_tempo) { - lcd.bl_tempo = false; - if (!(lcd.flags & LCD_FLAG_L)) - __lcd_backlight(0); - } - mutex_unlock(&lcd.bl_tempo_lock); -} - -/* turn the backlight on for a little while */ -static void lcd_poke(void) -{ - if (lcd.pins.bl == PIN_NONE) - return; - - cancel_delayed_work_sync(&lcd.bl_work); - - mutex_lock(&lcd.bl_tempo_lock); - if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L)) - __lcd_backlight(1); - lcd.bl_tempo = true; - schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ); - mutex_unlock(&lcd.bl_tempo_lock); -} - /* send a command to the LCD panel in serial mode */ -static void lcd_write_cmd_s(int cmd) +static void lcd_write_cmd_s(struct charlcd *charlcd, int cmd) { spin_lock_irq(&pprt_lock); lcd_send_serial(0x1F); /* R/W=W, RS=0 */ @@ -854,7 +742,7 @@ static void lcd_write_cmd_s(int cmd) } /* send data to the LCD panel in serial mode */ -static void lcd_write_data_s(int data) +static void lcd_write_data_s(struct charlcd *charlcd, int data) { spin_lock_irq(&pprt_lock); lcd_send_serial(0x5F); /* R/W=W, RS=1 */ @@ -865,7 +753,7 @@ static void lcd_write_data_s(int data) } /* send a command to the LCD panel in 8 bits parallel mode */ -static void lcd_write_cmd_p8(int cmd) +static void lcd_write_cmd_p8(struct charlcd *charlcd, int cmd) { spin_lock_irq(&pprt_lock); /* present the data to the data port */ @@ -887,7 +775,7 @@ static void lcd_write_cmd_p8(int cmd) } /* send data to the LCD panel in 8 bits parallel mode */ -static void lcd_write_data_p8(int data) +static void lcd_write_data_p8(struct charlcd *charlcd, int data) { spin_lock_irq(&pprt_lock); /* present the data to the data port */ @@ -909,7 +797,7 @@ static void lcd_write_data_p8(int data) } /* send a command to the TI LCD panel */ -static void lcd_write_cmd_tilcd(int cmd) +static void lcd_write_cmd_tilcd(struct charlcd *charlcd, int cmd) { spin_lock_irq(&pprt_lock); /* present the data to the control port */ @@ -919,7 +807,7 @@ static void lcd_write_cmd_tilcd(int cmd) } /* send data to the TI LCD panel */ -static void lcd_write_data_tilcd(int data) +static void lcd_write_data_tilcd(struct charlcd *charlcd, int data) { spin_lock_irq(&pprt_lock); /* present the data to the data port */ @@ -928,47 +816,13 @@ static void lcd_write_data_tilcd(int data) spin_unlock_irq(&pprt_lock); } -static void lcd_gotoxy(void) -{ - lcd_write_cmd(LCD_CMD_SET_DDRAM_ADDR - | (lcd.addr.y ? lcd.hwidth : 0) - /* - * we force the cursor to stay at the end of the - * line if it wants to go farther - */ - | ((lcd.addr.x < lcd.bwidth) ? lcd.addr.x & - (lcd.hwidth - 1) : lcd.bwidth - 1)); -} - -static void lcd_home(void) -{ - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); -} - -static void lcd_print(char c) -{ - if (lcd.addr.x < lcd.bwidth) { - if (lcd_char_conv) - c = lcd_char_conv[(unsigned char)c]; - lcd_write_data(c); - lcd.addr.x++; - } - /* prevents the cursor from wrapping onto the next line */ - if (lcd.addr.x == lcd.bwidth) - lcd_gotoxy(); -} - /* fills the display with spaces and resets X/Y */ -static void lcd_clear_fast_s(void) +static void lcd_clear_fast_s(struct charlcd *charlcd) { int pos; - lcd_home(); - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { + for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) { lcd_send_serial(0x5F); /* R/W=W, RS=1 */ lcd_send_serial(' ' & 0x0F); lcd_send_serial((' ' >> 4) & 0x0F); @@ -976,19 +830,15 @@ static void lcd_clear_fast_s(void) udelay(40); } spin_unlock_irq(&pprt_lock); - - lcd_home(); } /* fills the display with spaces and resets X/Y */ -static void lcd_clear_fast_p8(void) +static void lcd_clear_fast_p8(struct charlcd *charlcd) { int pos; - lcd_home(); - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { + for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) { /* present the data to the data port */ w_dtr(pprt, ' '); @@ -1010,488 +860,62 @@ static void lcd_clear_fast_p8(void) udelay(45); } spin_unlock_irq(&pprt_lock); - - lcd_home(); } /* fills the display with spaces and resets X/Y */ -static void lcd_clear_fast_tilcd(void) +static void lcd_clear_fast_tilcd(struct charlcd *charlcd) { int pos; - lcd_home(); - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { + for (pos = 0; pos < charlcd->height * charlcd->hwidth; pos++) { /* present the data to the data port */ w_dtr(pprt, ' '); udelay(60); } spin_unlock_irq(&pprt_lock); - - lcd_home(); -} - -/* clears the display and resets X/Y */ -static void lcd_clear_display(void) -{ - lcd_write_cmd(LCD_CMD_DISPLAY_CLEAR); - lcd.addr.x = 0; - lcd.addr.y = 0; - /* we must wait a few milliseconds (15) */ - long_sleep(15); } -static void lcd_init_display(void) -{ - lcd.flags = ((lcd.height > 1) ? LCD_FLAG_N : 0) - | LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B; - - long_sleep(20); /* wait 20 ms after power-up for the paranoid */ - - /* 8bits, 1 line, small fonts; let's do it 3 times */ - lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS); - long_sleep(10); - lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS); - long_sleep(10); - lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS); - long_sleep(10); - - /* set font height and lines number */ - lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS - | ((lcd.flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) - | ((lcd.flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0) - ); - long_sleep(10); - - /* display off, cursor off, blink off */ - lcd_write_cmd(LCD_CMD_DISPLAY_CTRL); - long_sleep(10); - - lcd_write_cmd(LCD_CMD_DISPLAY_CTRL /* set display mode */ - | ((lcd.flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) - | ((lcd.flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) - | ((lcd.flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0) - ); - - lcd_backlight((lcd.flags & LCD_FLAG_L) ? 1 : 0); - - long_sleep(10); - - /* entry mode set : increment, cursor shifting */ - lcd_write_cmd(LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC); +static struct charlcd_ops charlcd_serial_ops = { + .write_cmd = lcd_write_cmd_s, + .write_data = lcd_write_data_s, + .clear_fast = lcd_clear_fast_s, + .backlight = lcd_backlight, +}; - lcd_clear_display(); -} +static struct charlcd_ops charlcd_parallel_ops = { + .write_cmd = lcd_write_cmd_p8, + .write_data = lcd_write_data_p8, + .clear_fast = lcd_clear_fast_p8, + .backlight = lcd_backlight, +}; -/* - * These are the file operation function for user access to /dev/lcd - * This function can also be called from inside the kernel, by - * setting file and ppos to NULL. - * - */ +static struct charlcd_ops charlcd_tilcd_ops = { + .write_cmd = lcd_write_cmd_tilcd, + .write_data = lcd_write_data_tilcd, + .clear_fast = lcd_clear_fast_tilcd, + .backlight = lcd_backlight, +}; -static inline int handle_lcd_special_code(void) +/* initialize the LCD driver */ +static void lcd_init(void) { - /* LCD special codes */ - - int processed = 0; + struct charlcd *charlcd; - char *esc = lcd.esc_seq.buf + 2; - int oldflags = lcd.flags; - - /* check for display mode flags */ - switch (*esc) { - case 'D': /* Display ON */ - lcd.flags |= LCD_FLAG_D; - processed = 1; - break; - case 'd': /* Display OFF */ - lcd.flags &= ~LCD_FLAG_D; - processed = 1; - break; - case 'C': /* Cursor ON */ - lcd.flags |= LCD_FLAG_C; - processed = 1; - break; - case 'c': /* Cursor OFF */ - lcd.flags &= ~LCD_FLAG_C; - processed = 1; - break; - case 'B': /* Blink ON */ - lcd.flags |= LCD_FLAG_B; - processed = 1; - break; - case 'b': /* Blink OFF */ - lcd.flags &= ~LCD_FLAG_B; - processed = 1; - break; - case '+': /* Back light ON */ - lcd.flags |= LCD_FLAG_L; - processed = 1; - break; - case '-': /* Back light OFF */ - lcd.flags &= ~LCD_FLAG_L; - processed = 1; - break; - case '*': - /* flash back light */ - lcd_poke(); - processed = 1; - break; - case 'f': /* Small Font */ - lcd.flags &= ~LCD_FLAG_F; - processed = 1; - break; - case 'F': /* Large Font */ - lcd.flags |= LCD_FLAG_F; - processed = 1; - break; - case 'n': /* One Line */ - lcd.flags &= ~LCD_FLAG_N; - processed = 1; - break; - case 'N': /* Two Lines */ - lcd.flags |= LCD_FLAG_N; - break; - case 'l': /* Shift Cursor Left */ - if (lcd.addr.x > 0) { - /* back one char if not at end of line */ - if (lcd.addr.x < lcd.bwidth) - lcd_write_cmd(LCD_CMD_SHIFT); - lcd.addr.x--; - } - processed = 1; - break; - case 'r': /* shift cursor right */ - if (lcd.addr.x < lcd.width) { - /* allow the cursor to pass the end of the line */ - if (lcd.addr.x < (lcd.bwidth - 1)) - lcd_write_cmd(LCD_CMD_SHIFT | - LCD_CMD_SHIFT_RIGHT); - lcd.addr.x++; - } - processed = 1; - break; - case 'L': /* shift display left */ - lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT); - processed = 1; - break; - case 'R': /* shift display right */ - lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT | - LCD_CMD_SHIFT_RIGHT); - processed = 1; - break; - case 'k': { /* kill end of line */ - int x; - - for (x = lcd.addr.x; x < lcd.bwidth; x++) - lcd_write_data(' '); - - /* restore cursor position */ - lcd_gotoxy(); - processed = 1; - break; - } - case 'I': /* reinitialize display */ - lcd_init_display(); - processed = 1; - break; - case 'G': { - /* Generator : LGcxxxxx...xx; must have between '0' - * and '7', representing the numerical ASCII code of the - * redefined character, and a sequence of 16 - * hex digits representing 8 bytes for each character. - * Most LCDs will only use 5 lower bits of the 7 first - * bytes. - */ - - unsigned char cgbytes[8]; - unsigned char cgaddr; - int cgoffset; - int shift; - char value; - int addr; - - if (!strchr(esc, ';')) - break; - - esc++; - - cgaddr = *(esc++) - '0'; - if (cgaddr > 7) { - processed = 1; - break; - } - - cgoffset = 0; - shift = 0; - value = 0; - while (*esc && cgoffset < 8) { - shift ^= 4; - if (*esc >= '0' && *esc <= '9') { - value |= (*esc - '0') << shift; - } else if (*esc >= 'A' && *esc <= 'Z') { - value |= (*esc - 'A' + 10) << shift; - } else if (*esc >= 'a' && *esc <= 'z') { - value |= (*esc - 'a' + 10) << shift; - } else { - esc++; - continue; - } - - if (shift == 0) { - cgbytes[cgoffset++] = value; - value = 0; - } - - esc++; - } - - lcd_write_cmd(LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8)); - for (addr = 0; addr < cgoffset; addr++) - lcd_write_data(cgbytes[addr]); - - /* ensures that we stop writing to CGRAM */ - lcd_gotoxy(); - processed = 1; - break; - } - case 'x': /* gotoxy : LxXXX[yYYY]; */ - case 'y': /* gotoxy : LyYYY[xXXX]; */ - if (!strchr(esc, ';')) - break; - - while (*esc) { - if (*esc == 'x') { - esc++; - if (kstrtoul(esc, 10, &lcd.addr.x) < 0) - break; - } else if (*esc == 'y') { - esc++; - if (kstrtoul(esc, 10, &lcd.addr.y) < 0) - break; - } else { - break; - } - } - - lcd_gotoxy(); - processed = 1; - break; - } - - /* TODO: This indent party here got ugly, clean it! */ - /* Check whether one flag was changed */ - if (oldflags != lcd.flags) { - /* check whether one of B,C,D flags were changed */ - if ((oldflags ^ lcd.flags) & - (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D)) - /* set display mode */ - lcd_write_cmd(LCD_CMD_DISPLAY_CTRL - | ((lcd.flags & LCD_FLAG_D) - ? LCD_CMD_DISPLAY_ON : 0) - | ((lcd.flags & LCD_FLAG_C) - ? LCD_CMD_CURSOR_ON : 0) - | ((lcd.flags & LCD_FLAG_B) - ? LCD_CMD_BLINK_ON : 0)); - /* check whether one of F,N flags was changed */ - else if ((oldflags ^ lcd.flags) & (LCD_FLAG_F | LCD_FLAG_N)) - lcd_write_cmd(LCD_CMD_FUNCTION_SET - | LCD_CMD_DATA_LEN_8BITS - | ((lcd.flags & LCD_FLAG_F) - ? LCD_CMD_FONT_5X10_DOTS - : 0) - | ((lcd.flags & LCD_FLAG_N) - ? LCD_CMD_TWO_LINES - : 0)); - /* check whether L flag was changed */ - else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) - lcd_backlight(!!(lcd.flags & LCD_FLAG_L)); - } - - return processed; -} - -static void lcd_write_char(char c) -{ - /* first, we'll test if we're in escape mode */ - if ((c != '\n') && lcd.esc_seq.len >= 0) { - /* yes, let's add this char to the buffer */ - lcd.esc_seq.buf[lcd.esc_seq.len++] = c; - lcd.esc_seq.buf[lcd.esc_seq.len] = 0; - } else { - /* aborts any previous escape sequence */ - lcd.esc_seq.len = -1; - - switch (c) { - case LCD_ESCAPE_CHAR: - /* start of an escape sequence */ - lcd.esc_seq.len = 0; - lcd.esc_seq.buf[lcd.esc_seq.len] = 0; - break; - case '\b': - /* go back one char and clear it */ - if (lcd.addr.x > 0) { - /* - * check if we're not at the - * end of the line - */ - if (lcd.addr.x < lcd.bwidth) - /* back one char */ - lcd_write_cmd(LCD_CMD_SHIFT); - lcd.addr.x--; - } - /* replace with a space */ - lcd_write_data(' '); - /* back one char again */ - lcd_write_cmd(LCD_CMD_SHIFT); - break; - case '\014': - /* quickly clear the display */ - lcd_clear_fast(); - break; - case '\n': - /* - * flush the remainder of the current line and - * go to the beginning of the next line - */ - for (; lcd.addr.x < lcd.bwidth; lcd.addr.x++) - lcd_write_data(' '); - lcd.addr.x = 0; - lcd.addr.y = (lcd.addr.y + 1) % lcd.height; - lcd_gotoxy(); - break; - case '\r': - /* go to the beginning of the same line */ - lcd.addr.x = 0; - lcd_gotoxy(); - break; - case '\t': - /* print a space instead of the tab */ - lcd_print(' '); - break; - default: - /* simply print this char */ - lcd_print(c); - break; - } - } + charlcd = charlcd_alloc(0); + if (!charlcd) + return; /* - * now we'll see if we're in an escape mode and if the current - * escape sequence can be understood. + * Init lcd struct with load-time values to preserve exact + * current functionality (at least for now). */ - if (lcd.esc_seq.len >= 2) { - int processed = 0; - - if (!strcmp(lcd.esc_seq.buf, "[2J")) { - /* clear the display */ - lcd_clear_fast(); - processed = 1; - } else if (!strcmp(lcd.esc_seq.buf, "[H")) { - /* cursor to home */ - lcd_home(); - processed = 1; - } - /* codes starting with ^[[L */ - else if ((lcd.esc_seq.len >= 3) && - (lcd.esc_seq.buf[0] == '[') && - (lcd.esc_seq.buf[1] == 'L')) { - processed = handle_lcd_special_code(); - } - - /* LCD special escape codes */ - /* - * flush the escape sequence if it's been processed - * or if it is getting too long. - */ - if (processed || (lcd.esc_seq.len >= LCD_ESCAPE_LEN)) - lcd.esc_seq.len = -1; - } /* escape codes */ -} + charlcd->height = lcd_height; + charlcd->width = lcd_width; + charlcd->bwidth = lcd_bwidth; + charlcd->hwidth = lcd_hwidth; -static ssize_t lcd_write(struct file *file, - const char __user *buf, size_t count, loff_t *ppos) -{ - const char __user *tmp = buf; - char c; - - for (; count-- > 0; (*ppos)++, tmp++) { - if (!in_interrupt() && (((count + 1) & 0x1f) == 0)) - /* - * let's be a little nice with other processes - * that need some CPU - */ - schedule(); - - if (get_user(c, tmp)) - return -EFAULT; - - lcd_write_char(c); - } - - return tmp - buf; -} - -static int lcd_open(struct inode *inode, struct file *file) -{ - if (!atomic_dec_and_test(&lcd_available)) - return -EBUSY; /* open only once at a time */ - - if (file->f_mode & FMODE_READ) /* device is write-only */ - return -EPERM; - - if (lcd.must_clear) { - lcd_clear_display(); - lcd.must_clear = false; - } - return nonseekable_open(inode, file); -} - -static int lcd_release(struct inode *inode, struct file *file) -{ - atomic_inc(&lcd_available); - return 0; -} - -static const struct file_operations lcd_fops = { - .write = lcd_write, - .open = lcd_open, - .release = lcd_release, - .llseek = no_llseek, -}; - -static struct miscdevice lcd_dev = { - .minor = LCD_MINOR, - .name = "lcd", - .fops = &lcd_fops, -}; - -/* public function usable from the kernel for any purpose */ -static void panel_lcd_print(const char *s) -{ - const char *tmp = s; - int count = strlen(s); - - if (lcd.enabled && lcd.initialized) { - for (; count-- > 0; tmp++) { - if (!in_interrupt() && (((count + 1) & 0x1f) == 0)) - /* - * let's be a little nice with other processes - * that need some CPU - */ - schedule(); - - lcd_write_char(*tmp); - } - } -} - -/* initialize the LCD driver */ -static void lcd_init(void) -{ switch (selected_lcd_type) { case LCD_TYPE_OLD: /* parallel mode, 8 bits */ @@ -1500,10 +924,10 @@ static void lcd_init(void) lcd.pins.e = PIN_STROBE; lcd.pins.rs = PIN_AUTOLF; - lcd.width = 40; - lcd.bwidth = 40; - lcd.hwidth = 64; - lcd.height = 2; + charlcd->width = 40; + charlcd->bwidth = 40; + charlcd->hwidth = 64; + charlcd->height = 2; break; case LCD_TYPE_KS0074: /* serial mode, ks0074 */ @@ -1513,10 +937,10 @@ static void lcd_init(void) lcd.pins.cl = PIN_STROBE; lcd.pins.da = PIN_D0; - lcd.width = 16; - lcd.bwidth = 40; - lcd.hwidth = 16; - lcd.height = 2; + charlcd->width = 16; + charlcd->bwidth = 40; + charlcd->hwidth = 16; + charlcd->height = 2; break; case LCD_TYPE_NEXCOM: /* parallel mode, 8 bits, generic */ @@ -1526,10 +950,10 @@ static void lcd_init(void) lcd.pins.rs = PIN_SELECP; lcd.pins.rw = PIN_INITP; - lcd.width = 16; - lcd.bwidth = 40; - lcd.hwidth = 64; - lcd.height = 2; + charlcd->width = 16; + charlcd->bwidth = 40; + charlcd->hwidth = 64; + charlcd->height = 2; break; case LCD_TYPE_CUSTOM: /* customer-defined */ @@ -1545,22 +969,22 @@ static void lcd_init(void) lcd.pins.e = PIN_STROBE; lcd.pins.rs = PIN_SELECP; - lcd.width = 16; - lcd.bwidth = 40; - lcd.hwidth = 64; - lcd.height = 2; + charlcd->width = 16; + charlcd->bwidth = 40; + charlcd->hwidth = 64; + charlcd->height = 2; break; } /* Overwrite with module params set on loading */ if (lcd_height != NOT_SET) - lcd.height = lcd_height; + charlcd->height = lcd_height; if (lcd_width != NOT_SET) - lcd.width = lcd_width; + charlcd->width = lcd_width; if (lcd_bwidth != NOT_SET) - lcd.bwidth = lcd_bwidth; + charlcd->bwidth = lcd_bwidth; if (lcd_hwidth != NOT_SET) - lcd.hwidth = lcd_hwidth; + charlcd->hwidth = lcd_hwidth; if (lcd_charset != NOT_SET) lcd.charset = lcd_charset; if (lcd_proto != NOT_SET) @@ -1579,19 +1003,17 @@ static void lcd_init(void) lcd.pins.bl = lcd_bl_pin; /* this is used to catch wrong and default values */ - if (lcd.width <= 0) - lcd.width = DEFAULT_LCD_WIDTH; - if (lcd.bwidth <= 0) - lcd.bwidth = DEFAULT_LCD_BWIDTH; - if (lcd.hwidth <= 0) - lcd.hwidth = DEFAULT_LCD_HWIDTH; - if (lcd.height <= 0) - lcd.height = DEFAULT_LCD_HEIGHT; + if (charlcd->width <= 0) + charlcd->width = DEFAULT_LCD_WIDTH; + if (charlcd->bwidth <= 0) + charlcd->bwidth = DEFAULT_LCD_BWIDTH; + if (charlcd->hwidth <= 0) + charlcd->hwidth = DEFAULT_LCD_HWIDTH; + if (charlcd->height <= 0) + charlcd->height = DEFAULT_LCD_HEIGHT; if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */ - lcd_write_cmd = lcd_write_cmd_s; - lcd_write_data = lcd_write_data_s; - lcd_clear_fast = lcd_clear_fast_s; + charlcd->ops = &charlcd_serial_ops; if (lcd.pins.cl == PIN_NOT_SET) lcd.pins.cl = DEFAULT_LCD_PIN_SCL; @@ -1599,9 +1021,7 @@ static void lcd_init(void) lcd.pins.da = DEFAULT_LCD_PIN_SDA; } else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */ - lcd_write_cmd = lcd_write_cmd_p8; - lcd_write_data = lcd_write_data_p8; - lcd_clear_fast = lcd_clear_fast_p8; + charlcd->ops = &charlcd_parallel_ops; if (lcd.pins.e == PIN_NOT_SET) lcd.pins.e = DEFAULT_LCD_PIN_E; @@ -1610,9 +1030,7 @@ static void lcd_init(void) if (lcd.pins.rw == PIN_NOT_SET) lcd.pins.rw = DEFAULT_LCD_PIN_RW; } else { - lcd_write_cmd = lcd_write_cmd_tilcd; - lcd_write_data = lcd_write_data_tilcd; - lcd_clear_fast = lcd_clear_fast_tilcd; + charlcd->ops = &charlcd_tilcd_ops; } if (lcd.pins.bl == PIN_NOT_SET) @@ -1635,14 +1053,9 @@ static void lcd_init(void) lcd.charset = DEFAULT_LCD_CHARSET; if (lcd.charset == LCD_CHARSET_KS0074) - lcd_char_conv = lcd_char_conv_ks0074; + charlcd->char_conv = lcd_char_conv_ks0074; else - lcd_char_conv = NULL; - - if (lcd.pins.bl != PIN_NONE) { - mutex_init(&lcd.bl_tempo_lock); - INIT_DELAYED_WORK(&lcd.bl_work, lcd_bl_off); - } + charlcd->char_conv = NULL; pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E], lcd_bits[LCD_PORT_C][LCD_BIT_E]); @@ -1657,25 +1070,8 @@ static void lcd_init(void) pin_to_bits(lcd.pins.da, lcd_bits[LCD_PORT_D][LCD_BIT_DA], lcd_bits[LCD_PORT_C][LCD_BIT_DA]); - /* - * before this line, we must NOT send anything to the display. - * Since lcd_init_display() needs to write data, we have to - * enable mark the LCD initialized just before. - */ + lcd.charlcd = charlcd; lcd.initialized = true; - lcd_init_display(); - - /* display a short message */ -#ifdef CONFIG_PANEL_CHANGE_MESSAGE -#ifdef CONFIG_PANEL_BOOT_MESSAGE - panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE); -#endif -#else - panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE); -#endif - /* clear the display on the next device opening */ - lcd.must_clear = true; - lcd_home(); } /* @@ -2011,7 +1407,7 @@ static void panel_scan_timer(void) } if (keypressed && lcd.enabled && lcd.initialized) - lcd_poke(); + charlcd_poke(lcd.charlcd); mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME); } @@ -2175,35 +1571,6 @@ static void keypad_init(void) /* device initialization */ /**************************************************/ -static int panel_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) -{ - if (lcd.enabled && lcd.initialized) { - switch (code) { - case SYS_DOWN: - panel_lcd_print - ("\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+"); - break; - case SYS_HALT: - panel_lcd_print - ("\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+"); - break; - case SYS_POWER_OFF: - panel_lcd_print("\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+"); - break; - default: - break; - } - } - return NOTIFY_DONE; -} - -static struct notifier_block panel_notifier = { - panel_notify_sys, - NULL, - 0 -}; - static void panel_attach(struct parport *port) { struct pardev_cb panel_cb; @@ -2239,7 +1606,7 @@ static void panel_attach(struct parport *port) */ if (lcd.enabled) { lcd_init(); - if (misc_register(&lcd_dev)) + if (!lcd.charlcd || charlcd_register(lcd.charlcd)) goto err_unreg_device; } @@ -2248,13 +1615,14 @@ static void panel_attach(struct parport *port) if (misc_register(&keypad_dev)) goto err_lcd_unreg; } - register_reboot_notifier(&panel_notifier); return; err_lcd_unreg: if (lcd.enabled) - misc_deregister(&lcd_dev); + charlcd_unregister(lcd.charlcd); err_unreg_device: + kfree(lcd.charlcd); + lcd.charlcd = NULL; parport_unregister_device(pprt); pprt = NULL; } @@ -2278,20 +1646,16 @@ static void panel_detach(struct parport *port) } if (lcd.enabled) { - panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); - misc_deregister(&lcd_dev); - if (lcd.pins.bl != PIN_NONE) { - cancel_delayed_work_sync(&lcd.bl_work); - __lcd_backlight(0); - } + charlcd_unregister(lcd.charlcd); lcd.initialized = false; + kfree(lcd.charlcd); + lcd.charlcd = NULL; } /* TODO: free all input signals */ parport_release(pprt); parport_unregister_device(pprt); pprt = NULL; - unregister_reboot_notifier(&panel_notifier); } static struct parport_driver panel_driver = { @@ -2369,10 +1733,6 @@ static int __init panel_init_module(void) * Init lcd struct with load-time values to preserve exact * current functionality (at least for now). */ - lcd.height = lcd_height; - lcd.width = lcd_width; - lcd.bwidth = lcd_bwidth; - lcd.hwidth = lcd_hwidth; lcd.charset = lcd_charset; lcd.proto = lcd_proto; lcd.pins.e = lcd_e_pin; @@ -2381,9 +1741,6 @@ static int __init panel_init_module(void) lcd.pins.cl = lcd_cl_pin; lcd.pins.da = lcd_da_pin; lcd.pins.bl = lcd_bl_pin; - - /* Leave it for now, just in case */ - lcd.esc_seq.len = -1; } switch (selected_keypad_type) { -- cgit v1.2.3 From 132c93d4215c5ea12f8bb291249ede159d507b6e Mon Sep 17 00:00:00 2001 From: Cyril Bur Date: Wed, 22 Mar 2017 14:13:28 +1100 Subject: drivers/misc: Aspeed LPC control fix compile error and warning pgprot_dmachoerent() is not defined on every architecture. Having COMPILE_TEST set for the driver causes it to be compiled on architectures which do not have pgprot_dmachoerent(): drivers/misc/aspeed-lpc-ctrl.c: In function 'aspeed_lpc_ctrl_mmap': drivers/misc/aspeed-lpc-ctrl.c:51:9: error: implicit declaration of function 'pgprot_dmacoherent' [-Werror=implicit-function-declaration] prot = pgprot_dmacoherent(prot); There are two possible solutions: 1. Remove COMPILE_TEST to ensure the driver is only compiled on ARM 2. Use pgprot_noncached() instead of pgprot_dmachoerent() The first option results in less compile testing of the LPC control driver which is undesirable. The second option uses a function that is declared on all architectures and therefore should always build. Currently there is no practical difference between pgprot_noncached() and pgprot_dmachoerent() for the aspeed chips that this driver is compatible with. The reason for pgprot_dmachoerent() was that there may be chips made at some point in the future that could include hardware that pgprot_dmachoerent() could optimise for. As none of this hardware has even been announced there isn't really a need for pgprot_dmachoerent(). Using pgprot_noncached() is completely correct and optimal for all existing hardware on which the LPC control driver will run. This commit also addresses that phys_addr_t should be printed using %pap rather than %x: In file included from include/linux/miscdevice.h:6:0, from drivers/misc/aspeed-lpc-ctrl.c:11: drivers/misc/aspeed-lpc-ctrl.c: In function 'aspeed_lpc_ctrl_probe': drivers/misc/aspeed-lpc-ctrl.c:232:17: warning: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'phys_addr_t {aka long long unsigned int}' [-Wformat=] dev_info(dev, "Loaded at 0x%08x (0x%08x)\n", Signed-off-by: Cyril Bur Reported-by: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/misc/aspeed-lpc-ctrl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c index f6acbe1d9378..c654651a7b6d 100644 --- a/drivers/misc/aspeed-lpc-ctrl.c +++ b/drivers/misc/aspeed-lpc-ctrl.c @@ -48,7 +48,7 @@ static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; /* ast2400/2500 AHB accesses are not cache coherent */ - prot = pgprot_dmacoherent(prot); + prot = pgprot_noncached(prot); if (remap_pfn_range(vma, vma->vm_start, (lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff, @@ -229,8 +229,8 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) if (rc) dev_err(dev, "Unable to register device\n"); else - dev_info(dev, "Loaded at 0x%08x (0x%08x)\n", - lpc_ctrl->mem_base, lpc_ctrl->mem_size); + dev_info(dev, "Loaded at %pap (0x%08x)\n", + &lpc_ctrl->mem_base, lpc_ctrl->mem_size); return rc; } -- cgit v1.2.3 From 8ff0c5664a634bab05c646960c1730b7fd33ed90 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 23 Mar 2017 17:08:08 +0100 Subject: drivers/misc: aspeed-lpc-ctrl: fix printk format warning again The format string is still broken after the first attempt to fix it: drivers/misc/aspeed-lpc-ctrl.c: In function 'aspeed_lpc_ctrl_probe': drivers/misc/aspeed-lpc-ctrl.c:232:17: error: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'resource_size_t {aka long long unsigned int}' [-Werror=format=] We can actually just print the resource structure directly here. Fixes: 132c93d4215c ("drivers/misc: Aspeed LPC control fix compile error and warning") Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/aspeed-lpc-ctrl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c index c654651a7b6d..b5439643f54b 100644 --- a/drivers/misc/aspeed-lpc-ctrl.c +++ b/drivers/misc/aspeed-lpc-ctrl.c @@ -229,8 +229,7 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) if (rc) dev_err(dev, "Unable to register device\n"); else - dev_info(dev, "Loaded at %pap (0x%08x)\n", - &lpc_ctrl->mem_base, lpc_ctrl->mem_size); + dev_info(dev, "Loaded at %pr\n", &resm); return rc; } -- cgit v1.2.3 From 5c4c0106e944930315ad3ea8d77043bed13222eb Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 20 Mar 2017 15:04:02 +0200 Subject: mei: add pci driver ops shutdown handler. The shutdown handler quiesces the device, it performs link reset in order to close all connections and notify the device that is not longer managed by the driver. This is essentially a stripped down version of the PCI remove() function where only the necessary amount of work is done to stop any further activity. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/pci-me.c | 31 +++++++++++++++++++++++++++++-- drivers/misc/mei/pci-txe.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 3 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 0a668fdfbbe9..8621a198a2ce 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -241,12 +241,39 @@ end: return err; } +/** + * mei_me_shutdown - Device Removal Routine + * + * @pdev: PCI device structure + * + * mei_me_shutdown is called from the reboot notifier + * it's a simplified version of remove so we go down + * faster. + */ +static void mei_me_shutdown(struct pci_dev *pdev) +{ + struct mei_device *dev; + + dev = pci_get_drvdata(pdev); + if (!dev) + return; + + dev_dbg(&pdev->dev, "shutdown\n"); + mei_stop(dev); + + if (!pci_dev_run_wake(pdev)) + mei_me_unset_pm_domain(dev); + + mei_disable_interrupts(dev); + free_irq(pdev->irq, dev); +} + /** * mei_me_remove - Device Removal Routine * * @pdev: PCI device structure * - * mei_remove is called by the PCI subsystem to alert the driver + * mei_me_remove is called by the PCI subsystem to alert the driver * that it should release a PCI device. */ static void mei_me_remove(struct pci_dev *pdev) @@ -456,7 +483,7 @@ static struct pci_driver mei_me_driver = { .id_table = mei_me_pci_tbl, .probe = mei_me_probe, .remove = mei_me_remove, - .shutdown = mei_me_remove, + .shutdown = mei_me_shutdown, .driver.pm = MEI_ME_PM_OPS, }; diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index fe088b40daf9..f811cd524468 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -160,6 +160,33 @@ end: return err; } +/** + * mei_txe_remove - Device Shutdown Routine + * + * @pdev: PCI device structure + * + * mei_txe_shutdown is called from the reboot notifier + * it's a simplified version of remove so we go down + * faster. + */ +static void mei_txe_shutdown(struct pci_dev *pdev) +{ + struct mei_device *dev; + + dev = pci_get_drvdata(pdev); + if (!dev) + return; + + dev_dbg(&pdev->dev, "shutdown\n"); + mei_stop(dev); + + if (!pci_dev_run_wake(pdev)) + mei_txe_unset_pm_domain(dev); + + mei_disable_interrupts(dev); + free_irq(pdev->irq, dev); +} + /** * mei_txe_remove - Device Removal Routine * @@ -386,7 +413,7 @@ static struct pci_driver mei_txe_driver = { .id_table = mei_txe_pci_tbl, .probe = mei_txe_probe, .remove = mei_txe_remove, - .shutdown = mei_txe_remove, + .shutdown = mei_txe_shutdown, .driver.pm = MEI_TXE_PM_OPS, }; -- cgit v1.2.3 From 394a77d0bb63756871750400068d8b0c3582fba7 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 20 Mar 2017 15:04:03 +0200 Subject: mei: drop amthif internal client AMTHIF has special support in the mei drive, it handles multiplexing multiple user space connection above single me client connection. Since there is no additional addressing information there is a strict requirement on the traffic order on each connection and on the "read after write" order within the connection. This creates a lot of complexity mostly because the other client types do not necessarily fall under the same restriction. After carefully studying the use of the AMTHIF client, we came to conclusion that the multiplexing is not really utilized by any application and we may safely remove that support and significantly simplify the driver. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Makefile | 1 - drivers/misc/mei/amthif.c | 340 ------------------------------------------- drivers/misc/mei/bus.c | 6 - drivers/misc/mei/client.c | 14 +- drivers/misc/mei/client.h | 5 - drivers/misc/mei/init.c | 14 +- drivers/misc/mei/interrupt.c | 38 +---- drivers/misc/mei/main.c | 53 +------ drivers/misc/mei/mei_dev.h | 51 ------- 9 files changed, 15 insertions(+), 507 deletions(-) delete mode 100644 drivers/misc/mei/amthif.c (limited to 'drivers/misc') diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 59e6b0aede34..12cceb011a23 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -8,7 +8,6 @@ mei-objs += hbm.o mei-objs += interrupt.o mei-objs += client.o mei-objs += main.o -mei-objs += amthif.o mei-objs += bus.o mei-objs += bus-fixup.o mei-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c deleted file mode 100644 index 0e7406ccb6dd..000000000000 --- a/drivers/misc/mei/amthif.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * - * Intel Management Engine Interface (Intel MEI) Linux driver - * Copyright (c) 2003-2012, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "mei_dev.h" -#include "hbm.h" -#include "client.h" - -const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, - 0xac, 0xa8, 0x46, 0xe0, - 0xff, 0x65, 0x81, 0x4c); - -/** - * mei_amthif_reset_params - initializes mei device iamthif - * - * @dev: the device structure - */ -void mei_amthif_reset_params(struct mei_device *dev) -{ - /* reset iamthif parameters. */ - dev->iamthif_canceled = false; - dev->iamthif_state = MEI_IAMTHIF_IDLE; - dev->iamthif_stall_timer = 0; - dev->iamthif_open_count = 0; -} - -/** - * mei_amthif_host_init - mei initialization amthif client. - * - * @dev: the device structure - * @me_cl: me client - * - * Return: 0 on success, <0 on failure. - */ -int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) -{ - struct mei_cl *cl = &dev->iamthif_cl; - int ret; - - mutex_lock(&dev->device_lock); - - if (mei_cl_is_connected(cl)) { - ret = 0; - goto out; - } - - dev->iamthif_state = MEI_IAMTHIF_IDLE; - - mei_cl_init(cl, dev); - - ret = mei_cl_link(cl); - if (ret < 0) { - dev_err(dev->dev, "amthif: failed cl_link %d\n", ret); - goto out; - } - - ret = mei_cl_connect(cl, me_cl, NULL); - -out: - mutex_unlock(&dev->device_lock); - return ret; -} - -/** - * mei_amthif_read_start - queue message for sending read credential - * - * @cl: host client - * @fp: file pointer of message recipient - * - * Return: 0 on success, <0 on failure. - */ -static int mei_amthif_read_start(struct mei_cl *cl, const struct file *fp) -{ - struct mei_device *dev = cl->dev; - struct mei_cl_cb *cb; - - cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp); - if (!cb) - return -ENOMEM; - - cl->rx_flow_ctrl_creds++; - - dev->iamthif_state = MEI_IAMTHIF_READING; - cl->fp = cb->fp; - - return 0; -} - -/** - * mei_amthif_run_next_cmd - send next amt command from queue - * - * @dev: the device structure - * - * Return: 0 on success, <0 on failure. - */ -int mei_amthif_run_next_cmd(struct mei_device *dev) -{ - struct mei_cl *cl = &dev->iamthif_cl; - struct mei_cl_cb *cb; - int ret; - - dev->iamthif_canceled = false; - - dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); - - cb = list_first_entry_or_null(&dev->amthif_cmd_list, typeof(*cb), list); - if (!cb) { - dev->iamthif_state = MEI_IAMTHIF_IDLE; - cl->fp = NULL; - return 0; - } - - list_del_init(&cb->list); - dev->iamthif_state = MEI_IAMTHIF_WRITING; - cl->fp = cb->fp; - - ret = mei_cl_write(cl, cb); - if (ret < 0) - return ret; - - if (cb->completed) - cb->status = mei_amthif_read_start(cl, cb->fp); - - return 0; -} - -/** - * mei_amthif_write - write amthif data to amthif client - * - * @cl: host client - * @cb: mei call back struct - * - * Return: 0 on success, <0 on failure. - */ -int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) -{ - - struct mei_device *dev = cl->dev; - - list_add_tail(&cb->list, &dev->amthif_cmd_list); - - /* - * The previous request is still in processing, queue this one. - */ - if (dev->iamthif_state != MEI_IAMTHIF_IDLE) - return 0; - - return mei_amthif_run_next_cmd(dev); -} - -/** - * mei_amthif_poll - the amthif poll function - * - * @file: pointer to file structure - * @wait: pointer to poll_table structure - * - * Return: poll mask - * - * Locking: called under "dev->device_lock" lock - */ -unsigned int mei_amthif_poll(struct file *file, poll_table *wait) -{ - struct mei_cl *cl = file->private_data; - struct mei_cl_cb *cb = mei_cl_read_cb(cl, file); - unsigned int mask = 0; - - poll_wait(file, &cl->rx_wait, wait); - if (cb) - mask |= POLLIN | POLLRDNORM; - - return mask; -} - -/** - * mei_amthif_irq_write - write iamthif command in irq thread context. - * - * @cl: private data of the file object. - * @cb: callback block. - * @cmpl_list: complete list. - * - * Return: 0, OK; otherwise, error. - */ -int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct list_head *cmpl_list) -{ - int ret; - - ret = mei_cl_irq_write(cl, cb, cmpl_list); - if (ret) - return ret; - - if (cb->completed) - cb->status = mei_amthif_read_start(cl, cb->fp); - - return 0; -} - -/** - * mei_amthif_irq_read_msg - read routine after ISR to - * handle the read amthif message - * - * @cl: mei client - * @mei_hdr: header of amthif message - * @cmpl_list: completed callbacks list - * - * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status - */ -int mei_amthif_irq_read_msg(struct mei_cl *cl, - struct mei_msg_hdr *mei_hdr, - struct list_head *cmpl_list) -{ - struct mei_device *dev; - int ret; - - dev = cl->dev; - - if (dev->iamthif_state != MEI_IAMTHIF_READING) { - mei_irq_discard_msg(dev, mei_hdr); - return 0; - } - - ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); - if (ret) - return ret; - - if (!mei_hdr->msg_complete) - return 0; - - dev_dbg(dev->dev, "completed amthif read.\n "); - dev->iamthif_stall_timer = 0; - - return 0; -} - -/** - * mei_amthif_complete - complete amthif callback. - * - * @cl: host client - * @cb: callback block. - */ -void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) -{ - struct mei_device *dev = cl->dev; - - dev_dbg(dev->dev, "completing amthif call back.\n"); - switch (cb->fop_type) { - case MEI_FOP_WRITE: - if (!cb->status) { - dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; - mei_schedule_stall_timer(dev); - mei_io_cb_free(cb); - return; - } - dev->iamthif_state = MEI_IAMTHIF_IDLE; - cl->fp = NULL; - if (!dev->iamthif_canceled) { - /* - * in case of error enqueue the write cb to complete - * read list so it can be propagated to the reader - */ - list_add_tail(&cb->list, &cl->rd_completed); - wake_up_interruptible(&cl->rx_wait); - } else { - mei_io_cb_free(cb); - } - break; - case MEI_FOP_READ: - if (!dev->iamthif_canceled) { - list_add_tail(&cb->list, &cl->rd_completed); - dev_dbg(dev->dev, "amthif read completed\n"); - wake_up_interruptible(&cl->rx_wait); - } else { - mei_io_cb_free(cb); - } - - dev->iamthif_stall_timer = 0; - mei_amthif_run_next_cmd(dev); - break; - default: - WARN_ON(1); - } -} - -/** -* mei_amthif_release - the release function -* -* @dev: device structure -* @fp: pointer to file structure -* -* Return: 0 on success, <0 on error -*/ -int mei_amthif_release(struct mei_device *dev, struct file *fp) -{ - struct mei_cl *cl = fp->private_data; - - if (dev->iamthif_open_count > 0) - dev->iamthif_open_count--; - - if (cl->fp == fp && dev->iamthif_state != MEI_IAMTHIF_IDLE) { - - dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", - dev->iamthif_state); - dev->iamthif_canceled = true; - } - - /* Don't clean ctrl_rd_list here, the reads has to be completed */ - mei_io_list_free_fp(&dev->amthif_cmd_list, fp); - mei_io_list_free_fp(&cl->rd_completed, fp); - - return 0; -} diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index df5f78ae3d25..d1928fdd0f43 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -1076,12 +1076,6 @@ void mei_cl_bus_rescan_work(struct work_struct *work) { struct mei_device *bus = container_of(work, struct mei_device, bus_rescan_work); - struct mei_me_client *me_cl; - - me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid); - if (me_cl) - mei_amthif_host_init(bus, me_cl); - mei_me_cl_put(me_cl); mei_cl_bus_rescan(bus); } diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index d3e3372424d6..be64969d986a 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -428,7 +428,7 @@ static inline void mei_io_list_free_cl(struct list_head *head, * @head: io list * @fp: file pointer (matching cb file object), may be NULL */ -void mei_io_list_free_fp(struct list_head *head, const struct file *fp) +static void mei_io_list_free_fp(struct list_head *head, const struct file *fp) { struct mei_cl_cb *cb, *next; @@ -554,7 +554,7 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) * @cl: host client to be initialized * @dev: mei device */ -void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) +static void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) { memset(cl, 0, sizeof(struct mei_cl)); init_waitqueue_head(&cl->wait); @@ -600,7 +600,6 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) int mei_cl_link(struct mei_cl *cl) { struct mei_device *dev; - long open_handle_count; int id; if (WARN_ON(!cl || !cl->dev)) @@ -614,8 +613,7 @@ int mei_cl_link(struct mei_cl *cl) return -EMFILE; } - open_handle_count = dev->open_handle_count + dev->iamthif_open_count; - if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { + if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { dev_err(dev->dev, "open_handle_count exceeded %d", MEI_MAX_OPEN_HANDLE_COUNT); return -EMFILE; @@ -649,8 +647,7 @@ int mei_cl_unlink(struct mei_cl *cl) if (!cl) return 0; - /* amthif might not be initialized */ - if (!cl->dev) + if (WARN_ON(!cl->dev)) return 0; dev = cl->dev; @@ -763,7 +760,6 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) mei_io_list_free_cl(&dev->write_waiting_list, cl); mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); - mei_io_list_free_cl(&dev->amthif_cmd_list, cl); mei_cl_wake_all(cl); cl->rx_flow_ctrl_creds = 0; cl->tx_flow_ctrl_creds = 0; @@ -1478,7 +1474,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) return -ENOTTY; } - if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl) + if (mei_cl_is_fixed_address(cl)) return 0; /* HW currently supports only one pending read */ diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 545ae319ba90..5371df4d8af3 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -83,15 +83,12 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) * MEI IO Functions */ void mei_io_cb_free(struct mei_cl_cb *priv_cb); -void mei_io_list_free_fp(struct list_head *head, const