diff options
Diffstat (limited to 'drivers/misc')
41 files changed, 2179 insertions, 3937 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c290990d73ed..2cba76e6fa3c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -419,16 +419,6 @@ config VMWARE_BALLOON To compile this driver as a module, choose M here: the module will be called vmw_balloon. -config ARM_CHARLCD - bool "ARM Ltd. Character LCD Driver" - depends on PLAT_VERSATILE - help - This is a driver for the character LCD found on the ARM Ltd. - Versatile and RealView Platform Baseboards. It doesn't do - very much more than display the text "ARM Linux" on the first - line and the Linux version on the second line, but that's - still useful. - config PCH_PHUB tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" select GENERIC_NET_UTILS @@ -492,284 +482,20 @@ config VEXPRESS_SYSCFG bus. System Configuration interface is one of the possible means of generating transactions on this bus. -config PANEL - tristate "Parallel port LCD/Keypad Panel support" - depends on PARPORT - ---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 - is accessible through the /dev/lcd char device (10, 156), and the - keypad through /dev/keypad (10, 185). This code can either be - compiled as a module, or linked into the kernel and started at boot. - If you don't understand what all this is about, say N. - -if PANEL - -config PANEL_PARPORT - int "Default parallel port number (0=LPT1)" - range 0 255 - default "0" - ---help--- - This is the index of the parallel port the panel is connected to. One - driver instance only supports one parallel port, so if your keypad - and LCD are connected to two separate ports, you have to start two - modules with different arguments. Numbering starts with '0' for LPT1, - and so on. - -config PANEL_PROFILE - int "Default panel profile (0-5, 0=custom)" - range 0 5 - default "5" - ---help--- - To ease configuration, the driver supports different configuration - profiles for past and recent wirings. These profiles can also be - used to define an approximative configuration, completed by a few - other options. Here are the profiles : - - 0 = custom (see further) - 1 = 2x16 parallel LCD, old keypad - 2 = 2x16 serial LCD (KS-0074), new keypad - 3 = 2x16 parallel LCD (Hantronix), no keypad - 4 = 2x16 parallel LCD (Nexcom NSA1045) with Nexcom's keypad - 5 = 2x40 parallel LCD (old one), with old keypad - - Custom configurations allow you to define how your display is - wired to the parallel port, and how it works. This is only intended - for experts. - -config PANEL_KEYPAD - depends on PANEL_PROFILE="0" - int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)" - range 0 3 - default 0 - ---help--- - This enables and configures a keypad connected to the parallel port. - The keys will be read from character device 10,185. Valid values are : - - 0 : do not enable this driver - 1 : old 6 keys keypad - 2 : new 6 keys keypad, as used on the server at www.ant-computing.com - 3 : Nexcom NSA1045's 4 keys keypad - - New profiles can be described in the driver source. The driver also - supports simultaneous keys pressed when the keypad supports them. - -config PANEL_LCD - depends on PANEL_PROFILE="0" - int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)" - range 0 5 - default 0 - ---help--- - This enables and configures an LCD connected to the parallel port. - The driver includes an interpreter for escape codes starting with - '\e[L' which are specific to the LCD, and a few ANSI codes. The - driver will be registered as character device 10,156, usually - under the name '/dev/lcd'. There are a total of 6 supported types : - - 0 : do not enable the driver - 1 : custom configuration and wiring (see further) - 2 : 2x16 & 2x40 parallel LCD (old wiring) - 3 : 2x16 serial LCD (KS-0074 based) - 4 : 2x16 parallel LCD (Hantronix wiring) - 5 : 2x16 parallel LCD (Nexcom wiring) - - When type '1' is specified, other options will appear to configure - more precise aspects (wiring, dimensions, protocol, ...). Please note - that those values changed from the 2.4 driver for better consistency. - -config PANEL_LCD_HEIGHT - depends on PANEL_PROFILE="0" && PANEL_LCD="1" - int "Number of lines on the LCD (1-2)" - range 1 2 - default 2 - ---help--- - This is the number of visible character lines on the LCD in custom profile. - It can either be 1 or 2. - -config PANEL_LCD_WIDTH - depends on PANEL_PROFILE="0" && PANEL_LCD="1" - int "Number of characters per line on the LCD (1-40)" - range 1 40 - default 40 - ---help--- - This is the number of characters per line on the LCD in custom profile. - Common values are 16,20,24,40. - -config PANEL_LCD_BWIDTH - depends on PANEL_PROFILE="0" && PANEL_LCD="1" - int "Internal LCD line width (1-40, 40 by default)" - range 1 40 - default 40 - ---help--- - Most LCDs use a standard controller which supports hardware lines of 40 - characters, although sometimes only 16, 20 or 24 of them are really wired - to the terminal. This results in some non-visible but addressable characters, - and is the case for most parallel LCDs. Other LCDs, and some serial ones, - however, use the same line width internally as what is visible. The KS0074 - for example, uses 16 characters per line for 16 visible characters per line. - - This option lets you configure the value used by your LCD in 'custom' profile. - If you don't know, put '40' here. - -config PANEL_LCD_HWIDTH - depends on PANEL_PROFILE="0" && PANEL_LCD="1" - int "Hardware LCD line width (1-64, 64 by default)" - range 1 64 - default 64 - ---help--- - Most LCDs use a single address bit to differentiate line 0 and line 1. Since - some of them need to be able to address 40 chars with the lower bits, they - often use the immediately superior power of 2, which is 64, to address the - next line. - - If you don't know what your LCD uses, in doubt let 16 here for a 2x16, and - 64 here for a 2x40. - -config PANEL_LCD_CHARSET - depends on PANEL_PROFILE="0" && PANEL_LCD="1" - int "LCD character set (0=normal, 1=KS0074)" - range 0 1 - default 0 - ---help--- - Some controllers such as the KS0074 use a somewhat strange character set - where many symbols are at unusual places. The driver knows how to map - 'standard' ASCII characters to the character sets used by these controllers. - Valid values are : - - 0 : normal (untranslated) character set - 1 : KS0074 character set - - If you don't know, use the normal one (0). - -config PANEL_LCD_PROTO - depends on PANEL_PROFILE="0" && PANEL_LCD="1" - int "LCD communication mode (0=parallel 8 bits, 1=serial)" - range 0 1 - default 0 - ---help--- - This driver now supports any serial or parallel LCD wired to a parallel - port. But before assigning signals, the driver needs to know if it will - be driving a serial LCD or a parallel one. Serial LCDs only use 2 wires - (SDA/SCL), while parallel ones use 2 or 3 wires for the control signals - (E, RS, sometimes RW), and 4 or 8 for the data. Use 0 here for a 8 bits - parallel LCD, and 1 for a serial LCD. - -config PANEL_LCD_PIN_E - depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" - int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) " - range -17 17 - default 14 - ---help--- - This describes the number of the parallel port pin to which the LCD 'E' - signal has been connected. It can be : - - 0 : no connection (eg: connected to ground) - 1..17 : directly connected to any of these pins on the DB25 plug - -1..-17 : connected to the same pin through an inverter (eg: transistor). - - Default for the 'E' pin in custom profile is '14' (AUTOFEED). - -config PANEL_LCD_PIN_RS - depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" - int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) " - range -17 17 - default 17 - ---help--- - This describes the number of the parallel port pin to which the LCD 'RS' - signal has been connected. It can be : - - 0 : no connection (eg: connected to ground) - 1..17 : directly connected to any of these pins on the DB25 plug - -1..-17 : connected to the same pin through an inverter (eg: transistor). - - Default for the 'RS' pin in custom profile is '17' (SELECT IN). - -config PANEL_LCD_PIN_RW - depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" - int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) " - range -17 17 - default 16 +config ASPEED_LPC_CTRL + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON + tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" ---help--- - This describes the number of the parallel port pin to which the LCD 'RW' - signal has been connected. It can be : - - 0 : no connection (eg: connected to ground) - 1..17 : directly connected to any of these pins on the DB25 plug - -1..-17 : connected to the same pin through an inverter (eg: transistor). - - Default for the 'RW' pin in custom profile is '16' (INIT). - -config PANEL_LCD_PIN_SCL - depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" - int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) " - range -17 17 - default 1 - ---help--- - This describes the number of the parallel port pin to which the serial - LCD 'SCL' signal has been connected. It can be : - - 0 : no connection (eg: connected to ground) - 1..17 : directly connected to any of these pins on the DB25 plug - -1..-17 : connected to the same pin through an inverter (eg: transistor). - - Default for the 'SCL' pin in custom profile is '1' (STROBE). + 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. -config PANEL_LCD_PIN_SDA - depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" - int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) " - range -17 17 - default 2 - ---help--- - This describes the number of the parallel port pin to which the serial - LCD 'SDA' signal has been connected. It can be : - - 0 : no connection (eg: connected to ground) - 1..17 : directly connected to any of these pins on the DB25 plug - -1..-17 : connected to the same pin through an inverter (eg: transistor). - - Default for the 'SDA' pin in custom profile is '2' (D0). - -config PANEL_LCD_PIN_BL - depends on PANEL_PROFILE="0" && PANEL_LCD="1" - int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) " - range -17 17 - default 0 - ---help--- - This describes the number of the parallel port pin to which the LCD 'BL' signal - has been connected. It can be : - - 0 : no connection (eg: connected to ground) - 1..17 : directly connected to any of these pins on the DB25 plug - -1..-17 : connected to the same pin through an inverter (eg: transistor). - - Default for the 'BL' pin in custom profile is '0' (uncontrolled). - -config PANEL_CHANGE_MESSAGE - bool "Change LCD initialization message ?" - default "n" - ---help--- - This allows you to replace the boot message indicating the kernel version - and the driver version with a custom message. This is useful on appliances - where a simple 'Starting system' message can be enough to stop a customer - from worrying. - - If you say 'Y' here, you'll be able to choose a message yourself. Otherwise, - say 'N' and keep the default message with the version. - -config PANEL_BOOT_MESSAGE - depends on PANEL_CHANGE_MESSAGE="y" - string "New initialization message" - default "" +config PCI_ENDPOINT_TEST + depends on PCI + tristate "PCI Endpoint Test driver" ---help--- - This allows you to replace the boot message indicating the kernel version - and the driver version with a custom message. This is useful on appliances - where a simple 'Starting system' message can be enough to stop a customer - from worrying. - - An empty message will only clear the display at driver init time. Any other - printf()-formatted message is valid with newline and escape codes. - -endif # PANEL + Enable this configuration option to enable the host side test driver + for PCI Endpoint. source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7a3ea89339b4..81ef3e67acc9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -37,7 +37,6 @@ obj-y += eeprom/ obj-y += cb710/ obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o -obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o obj-$(CONFIG_PCH_PHUB) += pch_phub.o obj-y += ti-st/ obj-y += lis3lv02d/ @@ -53,7 +52,8 @@ obj-$(CONFIG_GENWQE) += genwqe/ 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 +obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o @@ -62,6 +62,8 @@ lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o +KCOV_INSTRUMENT_lkdtm_rodata.o := n + OBJCOPYFLAGS := OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \ --set-section-flags .text=alloc,readonly \ diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c deleted file mode 100644 index b3176ee92b90..000000000000 --- a/drivers/misc/arm-charlcd.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Driver for the on-board character LCD found on some ARM reference boards - * This is basically an Hitachi HD44780 LCD with a custom IP block to drive it - * http://en.wikipedia.org/wiki/HD44780_Character_LCD - * Currently it will just display the text "ARM Linux" and the linux version - * - * License terms: GNU General Public License (GPL) version 2 - * Author: Linus Walleij <triad@df.lth.se> - */ -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/of.h> -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/workqueue.h> -#include <generated/utsrelease.h> - -#define DRIVERNAME "arm-charlcd" -#define CHARLCD_TIMEOUT (msecs_to_jiffies(1000)) - -/* Offsets to registers */ -#define CHAR_COM 0x00U -#define CHAR_DAT 0x04U -#define CHAR_RD 0x08U -#define CHAR_RAW 0x0CU -#define CHAR_MASK 0x10U -#define CHAR_STAT 0x14U - -#define CHAR_RAW_CLEAR 0x00000000U -#define CHAR_RAW_VALID 0x00000100U - -/* Hitachi HD44780 display commands */ -#define HD_CLEAR 0x01U -#define HD_HOME 0x02U -#define HD_ENTRYMODE 0x04U -#define HD_ENTRYMODE_INCREMENT 0x02U -#define HD_ENTRYMODE_SHIFT 0x01U -#define HD_DISPCTRL 0x08U -#define HD_DISPCTRL_ON 0x04U -#define HD_DISPCTRL_CURSOR_ON 0x02U -#define HD_DISPCTRL_CURSOR_BLINK 0x01U -#define HD_CRSR_SHIFT 0x10U -#define HD_CRSR_SHIFT_DISPLAY 0x08U -#define HD_CRSR_SHIFT_DISPLAY_RIGHT 0x04U -#define HD_FUNCSET 0x20U -#define HD_FUNCSET_8BIT 0x10U -#define HD_FUNCSET_2_LINES 0x08U -#define HD_FUNCSET_FONT_5X10 0x04U -#define HD_SET_CGRAM 0x40U -#define HD_SET_DDRAM 0x80U -#define HD_BUSY_FLAG 0x80U - -/** - * @dev: a pointer back to containing device - * @phybase: the offset to the controller in physical memory - * @physize: the size of the physical page - * @virtbase: the offset to the controller in virtual memory - * @irq: reserved interrupt number - * @complete: completion structure for the last LCD command - */ -struct charlcd { - struct device *dev; - u32 phybase; - u32 physize; - void __iomem *virtbase; - int irq; - struct completion complete; - struct delayed_work init_work; -}; - -static irqreturn_t charlcd_interrupt(int irq, void *data) -{ - struct charlcd *lcd = data; - u8 status; - - status = readl(lcd->virtbase + CHAR_STAT) & 0x01; - /* Clear IRQ */ - writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW); - if (status) - complete(&lcd->complete); - else - dev_info(lcd->dev, "Spurious IRQ (%02x)\n", status); - return IRQ_HANDLED; -} - - -static void charlcd_wait_complete_irq(struct charlcd *lcd) -{ - int ret; - - ret = wait_for_completion_interruptible_timeout(&lcd->complete, - CHARLCD_TIMEOUT); - /* Disable IRQ after completion */ - writel(0x00, lcd->virtbase + CHAR_MASK); - - if (ret < 0) { - dev_err(lcd->dev, - "wait_for_completion_interruptible_timeout() " - "returned %d waiting for ready\n", ret); - return; - } - - if (ret == 0) { - dev_err(lcd->dev, "charlcd controller timed out " - "waiting for ready\n"); - return; - } -} - -static u8 charlcd_4bit_read_char(struct charlcd *lcd) -{ - u8 data; - u32 val; - int i; - - /* If we can, use an IRQ to wait for the data, else poll */ - if (lcd->irq >= 0) - charlcd_wait_complete_irq(lcd); - else { - i = 0; - val = 0; - while (!(val & CHAR_RAW_VALID) && i < 10) { - udelay(100); - val = readl(lcd->virtbase + CHAR_RAW); - i++; - } - - writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW); - } - msleep(1); - - /* Read the 4 high bits of the data */ - data = readl(lcd->virtbase + CHAR_RD) & 0xf0; - - /* - * The second read for the low bits does not trigger an IRQ - * so in this case we have to poll for the 4 lower bits - */ - i = 0; - val = 0; - while (!(val & CHAR_RAW_VALID) && i < 10) { - udelay(100); - val = readl(lcd->virtbase + CHAR_RAW); - i++; - } - writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW); - msleep(1); - - /* Read the 4 low bits of the data */ - data |= (readl(lcd->virtbase + CHAR_RD) >> 4) & 0x0f; - - return data; -} - -static bool charlcd_4bit_read_bf(struct charlcd *lcd) -{ - if (lcd->irq >= 0) { - /* - * If we'll use IRQs to wait for the busyflag, clear any - * pending flag and enable IRQ - */ - writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW); - init_completion(&lcd->complete); - writel(0x01, lcd->virtbase + CHAR_MASK); - } - readl(lcd->virtbase + CHAR_COM); - return charlcd_4bit_read_char(lcd) & HD_BUSY_FLAG ? true : false; -} - -static void charlcd_4bit_wait_busy(struct charlcd *lcd) -{ - int retries = 50; - - udelay(100); - while (charlcd_4bit_read_bf(lcd) && retries) - retries--; - if (!retries) - dev_err(lcd->dev, "timeout waiting for busyflag\n"); -} - -static void charlcd_4bit_command(struct charlcd *lcd, u8 cmd) -{ - u32 cmdlo = (cmd << 4) & 0xf0; - u32 cmdhi = (cmd & 0xf0); - - writel(cmdhi, lcd->virtbase + CHAR_COM); - udelay(10); - writel(cmdlo, lcd->virtbase + CHAR_COM); - charlcd_4bit_wait_busy(lcd); -} - -static void charlcd_4bit_char(struct charlcd *lcd, u8 ch) -{ - u32 chlo = (ch << 4) & 0xf0; - u32 chhi = (ch & 0xf0); - - writel(chhi, lcd->virtbase + CHAR_DAT); - udelay(10); - writel(chlo, lcd->virtbase + CHAR_DAT); - charlcd_4bit_wait_busy(lcd); -} - -static void charlcd_4bit_print(struct charlcd *lcd, int line, const char *str) -{ - u8 offset; - int i; - - /* - * We support line 0, 1 - * Line 1 runs from 0x00..0x27 - * Line 2 runs from 0x28..0x4f - */ - if (line == 0) - offset = 0; - else if (line == 1) - offset = 0x28; - else - return; - - /* Set offset */ - charlcd_4bit_command(lcd, HD_SET_DDRAM | offset); - - /* Send string */ - for (i = 0; i < strlen(str) && i < 0x28; i++) - charlcd_4bit_char(lcd, str[i]); -} - -static void charlcd_4bit_init(struct charlcd *lcd) -{ - /* These commands cannot be checked with the busy flag */ - writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM); - msleep(5); - writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM); - udelay(100); - writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM); - udelay(100); - /* Go to 4bit mode */ - writel(HD_FUNCSET, lcd->virtbase + CHAR_COM); - udelay(100); - /* - * 4bit mode, 2 lines, 5x8 font, after this the number of lines - * and the font cannot be changed until the next initialization sequence - */ - charlcd_4bit_command(lcd, HD_FUNCSET | HD_FUNCSET_2_LINES); - charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON); - charlcd_4bit_command(lcd, HD_ENTRYMODE | HD_ENTRYMODE_INCREMENT); - charlcd_4bit_command(lcd, HD_CLEAR); - charlcd_4bit_command(lcd, HD_HOME); - /* Put something useful in the display */ - charlcd_4bit_print(lcd, 0, "ARM Linux"); - charlcd_4bit_print(lcd, 1, UTS_RELEASE); -} - -static void charlcd_init_work(struct work_struct *work) -{ - struct charlcd *lcd = - container_of(work, struct charlcd, init_work.work); - - charlcd_4bit_init(lcd); -} - -static int __init charlcd_probe(struct platform_device *pdev) -{ - int ret; - struct charlcd *lcd; - struct resource *res; - - lcd = kzalloc(sizeof(struct charlcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; - - lcd->dev = &pdev->dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto out_no_resource; - } - lcd->phybase = res->start; - lcd->physize = resource_size(res); - - if (request_mem_region(lcd->phybase, lcd->physize, - DRIVERNAME) == NULL) { - ret = -EBUSY; - goto out_no_memregion; - } - - lcd->virtbase = ioremap(lcd->phybase, lcd->physize); - if (!lcd->virtbase) { - ret = -ENOMEM; - goto out_no_memregion; - } - - lcd->irq = platform_get_irq(pdev, 0); - /* If no IRQ is supplied, we'll survive without it */ - if (lcd->irq >= 0) { - if (request_irq(lcd->irq, charlcd_interrupt, 0, - DRIVERNAME, lcd)) { - ret = -EIO; - goto out_no_irq; - } - } - - platform_set_drvdata(pdev, lcd); - - /* - * Initialize the display in a delayed work, because - * it is VERY slow and would slow down the boot of the system. - */ - INIT_DELAYED_WORK(&lcd->init_work, charlcd_init_work); - schedule_delayed_work(&lcd->init_work, 0); - - dev_info(&pdev->dev, "initialized ARM character LCD at %08x\n", - lcd->phybase); - - return 0; - -out_no_irq: - iounmap(lcd->virtbase); -out_no_memregion: - release_mem_region(lcd->phybase, SZ_4K); -out_no_resource: - kfree(lcd); - return ret; -} - -static int charlcd_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct charlcd *lcd = platform_get_drvdata(pdev); - - /* Power the display off */ - charlcd_4bit_command(lcd, HD_DISPCTRL); - return 0; -} - -static int charlcd_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct charlcd *lcd = platform_get_drvdata(pdev); - - /* Turn the display back on */ - charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON); - return 0; -} - -static const struct dev_pm_ops charlcd_pm_ops = { - .suspend = charlcd_suspend, - .resume = charlcd_resume, -}; - -static const struct of_device_id charlcd_match[] = { - { .compatible = "arm,versatile-lcd", }, - {} -}; - -static struct platform_driver charlcd_driver = { - .driver = { - .name = DRIVERNAME, - .pm = &charlcd_pm_ops, - .suppress_bind_attrs = true, - .of_match_table = of_match_ptr(charlcd_match), - }, -}; -builtin_platform_driver_probe(charlcd_driver, charlcd_probe); diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c new file mode 100644 index 000000000000..b5439643f54b --- /dev/null +++ b/drivers/misc/aspeed-lpc-ctrl.c @@ -0,0 +1,266 @@ +/* + * 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 <linux/mfd/syscon.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/regmap.h> + +#include <linux/aspeed-lpc-ctrl.h> + +#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_noncached(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 %pr\n", &resm); + + 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 <cyrilbur@gmail.com>"); +MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings"); diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c index 5484301d57d9..3dc61ea7dc64 100644 --- a/drivers/misc/c2port/c2port-duramar2150.c +++ b/drivers/misc/c2port/c2port-duramar2150.c @@ -129,8 +129,8 @@ static int __init duramar2150_c2port_init(void) duramar2150_c2port_dev = c2port_device_register("uc", &duramar2150_c2port_ops, NULL); - if (!duramar2150_c2port_dev) { - ret = -ENODEV; + if (IS_ERR(duramar2150_c2port_dev)) { + ret = PTR_ERR(duramar2150_c2port_dev); goto free_region; } diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index bcc030eacab7..1a138c83f877 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c @@ -14,6 +14,7 @@ #include <linux/msi.h> #include <linux/module.h> #include <linux/mount.h> +#include <linux/sched/mm.h> #include "cxl.h" @@ -321,19 +322,29 @@ int cxl_start_context(struct cxl_context *ctx, u64 wed, if (task) { ctx->pid = get_task_pid(task, PIDTYPE_PID); - ctx->glpid = get_task_pid(task->group_leader, PIDTYPE_PID); kernel = false; ctx->real_mode = false; + + /* acquire a reference to the task's mm */ + ctx->mm = get_task_mm(current); + + /* ensure this mm_struct can't be freed */ + cxl_context_mm_count_get(ctx); + + /* decrement the use count */ + if (ctx->mm) + mmput(ctx->mm); } cxl_ctx_get(); if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) { - put_pid(ctx->glpid); put_pid(ctx->pid); - ctx->glpid = ctx->pid = NULL; + ctx->pid = NULL; cxl_adapter_context_put(ctx->afu->adapter); cxl_ctx_put(); + if (task) + cxl_context_mm_count_put(ctx); goto out; } diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 062bf6ca2625..4472ce11f98d 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -17,6 +17,7 @@ #include <linux/debugfs.h> #include <linux/slab.h> #include <linux/idr.h> +#include <linux/sched/mm.h> #include <asm/cputable.h> #include <asm/current.h> #include <asm/copro.h> @@ -38,23 +39,26 @@ int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master) { int i; - spin_lock_init(&ctx->sste_lock); ctx->afu = afu; ctx->master = master; - ctx->pid = ctx->glpid = NULL; /* Set in start work ioctl */ + ctx->pid = NULL; /* Set in start work ioctl */ mutex_init(&ctx->mapping_lock); ctx->mapping = NULL; - /* - * Allocate the segment table before we put it in the IDR so that we - * can always access it when dereferenced from IDR. For the same - * reason, the segment table is only destroyed after the context is - * removed from the IDR. Access to this in the IOCTL is protected by - * Linux filesytem symantics (can't IOCTL until open is complete). - */ - i = cxl_alloc_sst(ctx); - if (i) - return i; + if (cxl_is_psl8(afu)) { + spin_lock_init(&ctx->sste_lock); + + /* + * Allocate the segment table before we put it in the IDR so that we + * can always access it when dereferenced from IDR. For the same + * reason, the segment table is only destroyed after the context is + * removed from the IDR. Access to this in the IOCTL is protected by + * Linux filesytem symantics (can't IOCTL until open is complete). + */ + i = cxl_alloc_sst(ctx); + if (i) + return i; + } INIT_WORK(&ctx->fault_work, cxl_handle_fault); @@ -184,13 +188,26 @@ int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma) if (ctx->afu->current_mode == CXL_MODE_DEDICATED) { if (start + len > ctx->afu->adapter->ps_size) return -EINVAL; + + if (cxl_is_psl9(ctx->afu)) { + /* + * Make sure there is a valid problem state + * area space for this AFU. + */ + if (ctx->master && !ctx->afu->psa) { + pr_devel("AFU doesn't support mmio space\n"); + return -EINVAL; + } + + /* Can't mmap until the AFU is enabled */ + if (!ctx->afu->enabled) + return -EBUSY; + } } else { if (start + len > ctx->psn_size) return -EINVAL; - } - if (ctx->afu->current_mode != CXL_MODE_DEDICATED) { - /* make sure there is a valid per process space for this AFU */ + /* Make sure there is a valid per process space for this AFU */ if ((ctx->master && !ctx->afu->psa) || (!ctx->afu->pp_psa)) { pr_devel("AFU doesn't support mmio space\n"); return -EINVAL; @@ -242,12 +259,16 @@ int __detach_context(struct cxl_context *ctx) /* release the reference to the group leader and mm handling pid */ put_pid(ctx->pid); - put_pid(ctx->glpid); cxl_ctx_put(); /* Decrease the attached context count on the adapter */ cxl_adapter_context_put(ctx->afu->adapter); + + /* Decrease the mm count on the context */ + cxl_context_mm_count_put(ctx); + ctx->mm = NULL; + return 0; } @@ -303,7 +324,8 @@ static void reclaim_ctx(struct rcu_head *rcu) { struct cxl_context *ctx = container_of(rcu, struct cxl_context, rcu); - free_page((u64)ctx->sstp); + if (cxl_is_psl8(ctx->afu)) + free_page((u64)ctx->sstp); if (ctx->ff_page) __free_page(ctx->ff_page); ctx->sstp = NULL; @@ -325,3 +347,15 @@ void cxl_context_free(struct cxl_context *ctx) mutex_unlock(&ctx->afu->contexts_lock); call_rcu(&ctx->rcu, reclaim_ctx); } + +void cxl_context_mm_count_get(struct cxl_context *ctx) +{ + if (ctx->mm) + atomic_inc(&ctx->mm->mm_count); +} + +void cxl_context_mm_count_put(struct cxl_context *ctx) +{ + if (ctx->mm) + mmdrop(ctx->mm); +} diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 79e60ec70bd3..c8568ea7c518 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -63,7 +63,7 @@ typedef struct { /* Memory maps. Ref CXL Appendix A */ /* PSL Privilege 1 Memory Map */ -/* Configuration and Control area */ +/* Configuration and Control area - CAIA 1&2 */ static const cxl_p1_reg_t CXL_PSL_CtxTime = {0x0000}; static const cxl_p1_reg_t CXL_PSL_ErrIVTE = {0x0008}; static const cxl_p1_reg_t CXL_PSL_KEY1 = {0x0010}; @@ -73,7 +73,7 @@ static const cxl_p1_reg_t CXL_PSL_Control = {0x0020}; static const cxl_p1_reg_t CXL_PSL_DLCNTL = {0x0060}; static const cxl_p1_reg_t CXL_PSL_DLADDR = {0x0068}; -/* PSL Lookaside Buffer Management Area */ +/* PSL Lookaside Buffer Management Area - CAIA 1 */ static const cxl_p1_reg_t CXL_PSL_LBISEL = {0x0080}; static const cxl_p1_reg_t CXL_PSL_SLBIE = {0x0088}; static const cxl_p1_reg_t CXL_PSL_SLBIA = {0x0090}; @@ -82,7 +82,7 @@ static const cxl_p1_reg_t CXL_PSL_TLBIA = {0x00A8}; static const cxl_p1_reg_t CXL_PSL_AFUSEL = {0x00B0}; /* 0x00C0:7EFF Implementation dependent area */ -/* PSL registers */ +/* PSL registers - CAIA 1 */ static const cxl_p1_reg_t CXL_PSL_FIR1 = {0x0100}; static const cxl_p1_reg_t CXL_PSL_FIR2 = {0x0108}; static const cxl_p1_reg_t CXL_PSL_Timebase = {0x0110}; @@ -98,61 +98,83 @@ static const cxl_p1_reg_t CXL_XSL_Timebase = {0x0100}; static const cxl_p1_reg_t CXL_XSL_TB_CTLSTAT = {0x0108}; static const cxl_p1_reg_t CXL_XSL_FEC = {0x0158}; static const cxl_p1_reg_t CXL_XSL_DSNCTL = {0x0168}; +/* PSL registers - CAIA 2 */ +static const cxl_p1_reg_t CXL_PSL9_CONTROL = {0x0020}; +static const cxl_p1_reg_t CXL_XSL9_DSNCTL = {0x0168}; +static const cxl_p1_reg_t CXL_PSL9_FIR1 = {0x0300}; +static const cxl_p1_reg_t CXL_PSL9_FIR2 = {0x0308}; +static const cxl_p1_reg_t CXL_PSL9_Timebase = {0x0310}; +static const cxl_p1_reg_t CXL_PSL9_DEBUG = {0x0320}; +static const cxl_p1_reg_t CXL_PSL9_FIR_CNTL = {0x0348}; +static const cxl_p1_reg_t CXL_PSL9_DSNDCTL = {0x0350}; +static const cxl_p1_reg_t CXL_PSL9_TB_CTLSTAT = {0x0340}; +static const cxl_p1_reg_t CXL_PSL9_TRACECFG = {0x0368}; +static const cxl_p1_reg_t CXL_PSL9_APCDEDALLOC = {0x0378}; +static const cxl_p1_reg_t CXL_PSL9_APCDEDTYPE = {0x0380}; +static const cxl_p1_reg_t CXL_PSL9_TNR_ADDR = {0x0388}; +static const cxl_p1_reg_t CXL_PSL9_GP_CT = {0x0398}; +static const cxl_p1_reg_t CXL_XSL9_IERAT = {0x0588}; +static const cxl_p1_reg_t CXL_XSL9_ILPP = {0x0590}; + /* 0x7F00:7FFF Reserved PCIe MSI-X Pending Bit Array area */ /* 0x8000:FFFF Reserved PCIe MSI-X Table Area */ /* PSL Slice Privilege 1 Memory Map */ -/* Configuration Area */ +/* Configuration Area - CAIA 1&2 */ static const cxl_p1n_reg_t CXL_PSL_SR_An = {0x00}; static const cxl_p1n_reg_t CXL_PSL_LPID_An = {0x08}; static const cxl_p1n_reg_t CXL_PSL_AMBAR_An = {0x10}; static const cxl_p1n_reg_t CXL_PSL_SPOffset_An = {0x18}; static const cxl_p1n_reg_t CXL_PSL_ID_An = {0x20}; static const cxl_p1n_reg_t CXL_PSL_SERR_An = {0x28}; -/* Memory Management and Lookaside Buffer Management */ +/* Memory Management and Lookaside Buffer Management - CAIA 1*/ static const cxl_p1n_reg_t CXL_PSL_SDR_An = {0x30}; +/* Memory Management and Lookaside Buffer Management - CAIA 1&2 */ static const cxl_p1n_reg_t CXL_PSL_AMOR_An = {0x38}; -/* Pointer Area */ +/* Pointer Area - CAIA 1&2 */ static const cxl_p1n_reg_t CXL_HAURP_An = {0x80}; static const cxl_p1n_reg_t CXL_PSL_SPAP_An = {0x88}; static const cxl_p1n_reg_t CXL_PSL_LLCMD_An = {0x90}; -/* Control Area */ +/* Control Area - CAIA 1&2 */ static const cxl_p1n_reg_t CXL_PSL_SCNTL_An = {0xA0}; static const cxl_p1n_reg_t CXL_PSL_CtxTime_An = {0xA8}; static const cxl_p1n_reg_t CXL_PSL_IVTE_Offset_An = {0xB0}; static const cxl_p1n_reg_t CXL_PSL_IVTE_Limit_An = {0xB8}; -/* 0xC0:FF Implementation Dependent Area */ +/* 0xC0:FF Implementation Dependent Area - CAIA 1&2 */ static const cxl_p1n_reg_t CXL_PSL_FIR_SLICE_An = {0xC0}; static const cxl_p1n_reg_t CXL_AFU_DEBUG_An = {0xC8}; +/* 0xC0:FF Implementation Dependent Area - CAIA 1 */ static const cxl_p1n_reg_t CXL_PSL_APCALLOC_A = {0xD0}; static const cxl_p1n_reg_t CXL_PSL_COALLOC_A = {0xD8}; static const cxl_p1n_reg_t CXL_PSL_RXCTL_A = {0xE0}; static const cxl_p1n_reg_t CXL_PSL_SLICE_TRACE = {0xE8}; /* PSL Slice Privilege 2 Memory Map */ -/* Configuration and Control Area */ +/* Configuration and Control Area - CAIA 1&2 */ static const cxl_p2n_reg_t CXL_PSL_PID_TID_An = {0x000}; static const cxl_p2n_reg_t CXL_CSRP_An = {0x008}; +/* Configuration and Control Area - CAIA 1 */ static const cxl_p2n_reg_t CXL_AURP0_An = {0x010}; static const cxl_p2n_reg_t CXL_AURP1_An = {0x018}; static const cxl_p2n_reg_t CXL_SSTP0_An = {0x020}; static const cxl_p2n_reg_t CXL_SSTP1_An = {0x028}; +/* Configuration and Control Area - CAIA 1 */ static const cxl_p2n_reg_t CXL_PSL_AMR_An = {0x030}; -/* Segment Lookaside Buffer Management */ +/* Segment Lookaside Buffer Management - CAIA 1 */ static const cxl_p2n_reg_t CXL_SLBIE_An = {0x040}; static const cxl_p2n_reg_t CXL_SLBIA_An = {0x048}; static const cxl_p2n_reg_t CXL_SLBI_Select_An = {0x050}; -/* Interrupt Registers */ +/* Interrupt Registers - CAIA 1&2 */ static const cxl_p2n_reg_t CXL_PSL_DSISR_An = {0x060}; static const cxl_p2n_reg_t CXL_PSL_DAR_An = {0x068}; static const cxl_p2n_reg_t CXL_PSL_DSR_An = {0x070}; static const cxl_p2n_reg_t CXL_PSL_TFC_An = {0x078}; static const cxl_p2n_reg_t CXL_PSL_PEHandle_An = {0x080}; static const cxl_p2n_reg_t CXL_PSL_ErrStat_An = {0x088}; -/* AFU Registers */ +/* AFU Registers - CAIA 1&2 */ static const cxl_p2n_reg_t CXL_AFU_Cntl_An = {0x090}; static const cxl_p2n_reg_t CXL_AFU_ERR_An = {0x098}; -/* Work Element Descriptor */ +/* Work Element Descriptor - CAIA 1&2 */ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; /* 0x0C0:FFF Implementation Dependent Area */ @@ -179,6 +201,10 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PSL_SR_An_SF MSR_SF /* 64bit */ #define CXL_PSL_SR_An_TA (1ull << (63-1)) /* Tags active, GA1: 0 */ #define CXL_PSL_SR_An_HV MSR_HV /* Hypervisor, GA1: 0 */ +#define CXL_PSL_SR_An_XLAT_hpt (0ull << (63-6))/* Hashed page table (HPT) mode */ +#define CXL_PSL_SR_An_XLAT_roh (2ull << (63-6))/* Radix on HPT mode */ +#define CXL_PSL_SR_An_XLAT_ror (3ull << (63-6))/* Radix on Radix mode */ +#define CXL_PSL_SR_An_BOT (1ull << (63-10)) /* Use the in-memory segment table */ #define CXL_PSL_SR_An_PR MSR_PR /* Problem state, GA1: 1 */ #define CXL_PSL_SR_An_ISL (1ull << (63-53)) /* Ignore Segment Large Page */ #define CXL_PSL_SR_An_TC (1ull << (63-54)) /* Page Table secondary hash */ @@ -202,6 +228,24 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PSL_SERR_An_llcmdto (1ull << (63-6)) #define CXL_PSL_SERR_An_afupar (1ull << (63-7)) #define CXL_PSL_SERR_An_afudup (1ull << (63-8)) +#define CXL_PSL_SERR_An_IRQS ( \ + CXL_PSL_SERR_An_afuto | CXL_PSL_SERR_An_afudis | CXL_PSL_SERR_An_afuov | \ + CXL_PSL_SERR_An_badsrc | CXL_PSL_SERR_An_badctx | CXL_PSL_SERR_An_llcmdis | \ + CXL_PSL_SERR_An_llcmdto | CXL_PSL_SERR_An_afupar | CXL_PSL_SERR_An_afudup) +#define CXL_PSL_SERR_An_afuto_mask (1ull << (63-32)) +#define CXL_PSL_SERR_An_afudis_mask (1ull << (63-33)) +#define CXL_PSL_SERR_An_afuov_mask (1ull << (63-34)) +#define CXL_PSL_SERR_An_badsrc_mask (1ull << (63-35)) +#define CXL_PSL_SERR_An_badctx_mask (1ull << (63-36)) +#define CXL_PSL_SERR_An_llcmdis_mask (1ull << (63-37)) +#define CXL_PSL_SERR_An_llcmdto_mask (1ull << (63-38)) +#define CXL_PSL_SERR_An_afupar_mask (1ull << (63-39)) +#define CXL_PSL_SERR_An_afudup_mask (1ull << (63-40)) +#define CXL_PSL_SERR_An_IRQ_MASKS ( \ + CXL_PSL_SERR_An_afuto_mask | CXL_PSL_SERR_An_afudis_mask | CXL_PSL_SERR_An_afuov_mask | \ + CXL_PSL_SERR_An_badsrc_mask | CXL_PSL_SERR_An_badctx_mask | CXL_PSL_SERR_An_llcmdis_mask | \ + CXL_PSL_SERR_An_llcmdto_mask | CXL_PSL_SERR_An_afupar_mask | CXL_PSL_SERR_An_afudup_mask) + #define CXL_PSL_SERR_An_AE (1ull << (63-30)) /****** CXL_PSL_SCNTL_An ****************************************************/ @@ -257,7 +301,7 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_SSTP1_An_STVA_L_MASK (~((1ull << (63-55))-1)) #define CXL_SSTP1_An_V (1ull << (63-63)) -/****** CXL_PSL_SLBIE_[An] **************************************************/ +/****** CXL_PSL_SLBIE_[An] - CAIA 1 **************************************************/ /* write: */ #define CXL_SLBIE_C PPC_BIT(36) /* Class */ #define CXL_SLBIE_SS PPC_BITMASK(37, 38) /* Segment Size */ @@ -267,10 +311,10 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_SLBIE_MAX PPC_BITMASK(24, 31) #define CXL_SLBIE_PENDING PPC_BITMASK(56, 63) -/****** Common to all CXL_TLBIA/SLBIA_[An] **********************************/ +/****** Common to all CXL_TLBIA/SLBIA_[An] - CAIA 1 **********************************/ #define CXL_TLB_SLB_P (1ull) /* Pending (read) */ -/****** Common to all CXL_TLB/SLB_IA/IE_[An] registers **********************/ +/****** Common to all CXL_TLB/SLB_IA/IE_[An] registers - CAIA 1 **********************/ #define CXL_TLB_SLB_IQ_ALL (0ull) /* Inv qualifier */ #define CXL_TLB_SLB_IQ_LPID (1ull) /* Inv qualifier */ #define CXL_TLB_SLB_IQ_LPIDPID (3ull) /* Inv qualifier */ @@ -278,7 +322,7 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; /****** CXL_PSL_AFUSEL ******************************************************/ #define CXL_PSL_AFUSEL_A (1ull << (63-55)) /* Adapter wide invalidates affect all AFUs */ -/****** CXL_PSL_DSISR_An ****************************************************/ +/****** CXL_PSL_DSISR_An - CAIA 1 ****************************************************/ #define CXL_PSL_DSISR_An_DS (1ull << (63-0)) /* Segment not found */ #define CXL_PSL_DSISR_An_DM (1ull << (63-1)) /* PTE not found (See also: M) or protection fault */ #define CXL_PSL_DSISR_An_ST (1ull << (63-2)) /* Segment Table PTE not found */ @@ -295,12 +339,39 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PSL_DSISR_An_S DSISR_ISSTORE /* Access was afu_wr or afu_zero */ #define CXL_PSL_DSISR_An_K DSISR_KEYFAULT /* Access not permitted by virtual page class key protection */ +/****** CXL_PSL_DSISR_An - CAIA 2 ****************************************************/ +#define CXL_PSL9_DSISR_An_TF (1ull << (63-3)) /* Translation fault */ +#define CXL_PSL9_DSISR_An_PE (1ull << (63-4)) /* PSL Error (implementation specific) */ +#define CXL_PSL9_DSISR_An_AE (1ull << (63-5)) /* AFU Error */ +#define CXL_PSL9_DSISR_An_OC (1ull << (63-6)) /* OS Context Warning */ +#define CXL_PSL9_DSISR_An_S (1ull << (63-38)) /* TF for a write operation */ +#define CXL_PSL9_DSISR_PENDING (CXL_PSL9_DSISR_An_TF | CXL_PSL9_DSISR_An_PE | CXL_PSL9_DSISR_An_AE | CXL_PSL9_DSISR_An_OC) +/* + * NOTE: Bits 56:63 (Checkout Response Status) are valid when DSISR_An[TF] = 1 + * Status (0:7) Encoding + */ +#define CXL_PSL9_DSISR_An_CO_MASK 0x00000000000000ffULL +#define CXL_PSL9_DSISR_An_SF 0x0000000000000080ULL /* Segment Fault 0b10000000 */ +#define CXL_PSL9_DSISR_An_PF_SLR 0x0000000000000088ULL /* PTE not found (Single Level Radix) 0b10001000 */ +#define CXL_PSL9_DSISR_An_PF_RGC 0x000000000000008CULL /* PTE not found (Radix Guest (child)) 0b10001100 */ +#define CXL_PSL9_DSISR_An_PF_RGP 0x0000000000000090ULL /* PTE not found (Radix Guest (parent)) 0b10010000 */ +#define CXL_PSL9_DSISR_An_PF_HRH 0x0000000000000094ULL /* PTE not found (HPT/Radix Host) 0b10010100 */ +#define CXL_PSL9_DSISR_An_PF_STEG 0x000000000000009CULL /* PTE not found (STEG VA) 0b10011100 */ + /****** CXL_PSL_TFC_An ******************************************************/ #define CXL_PSL_TFC_An_A (1ull << (63-28)) /* Acknowledge non-translation fault */ #define CXL_PSL_TFC_An_C (1ull << (63-29)) /* Continue (abort transaction) */ #define CXL_PSL_TFC_An_AE (1ull << (63-30)) /* Restart PSL with address error */ #define CXL_PSL_TFC_An_R (1ull << (63-31)) /* Restart PSL transaction */ +/****** CXL_XSL9_IERAT_ERAT - CAIA 2 **********************************/ +#define CXL_XSL9_IERAT_MLPID (1ull << (63-0)) /* Match LPID */ +#define CXL_XSL9_IERAT_MPID (1ull << (63-1)) /* Match PID */ +#define CXL_XSL9_IERAT_PRS (1ull << (63-4)) /* PRS bit for Radix invalidations */ +#define CXL_XSL9_IERAT_INVR (1ull << (63-3)) /* Invalidate Radix */ +#define CXL_XSL9_IERAT_IALL (1ull << (63-8)) /* Invalidate All */ +#define CXL_XSL9_IERAT_IINPROG (1ull << (63-63)) /* Invalidate in progress */ + /* cxl_process_element->software_status */ #define CXL_PE_SOFTWARE_STATE_V (1ul << (31 - 0)) /* Valid */ #define CXL_PE_SOFTWARE_STATE_C (1ul << (31 - 29)) /* Complete */ @@ -482,8 +553,6 @@ struct cxl_context { unsigned int sst_size, sst_lru; wait_queue_head_t wq; - /* pid of the group leader associated with the pid */ - struct pid *glpid; /* use mm context associated with this pid for ds faults */ struct pid *pid; spinlock_t lock; /* Protects pending_irq_mask, pending_fault and fault_addr */ @@ -551,15 +620,27 @@ struct cxl_context { * CX4 only: */ struct list_head extra_irq_contexts; + + struct mm_struct *mm; }; +struct cxl_irq_info; + struct cxl_service_layer_ops { int (*adapter_regs_init)(struct cxl *adapter, struct pci_dev *dev); + int (*invalidate_all)(struct cxl *adapter); int (*afu_regs_init)(struct cxl_afu *afu); + int (*sanitise_afu_regs)(struct cxl_afu *afu); int (*register_serr_irq)(struct cxl_afu *afu); void (*release_serr_irq)(struct cxl_afu *afu); - void (*debugfs_add_adapter_sl_regs)(struct cxl *adapter, struct dentry *dir); - void (*debugfs_add_afu_sl_regs)(struct cxl_afu *afu, struct dentry *dir); + irqreturn_t (*handle_interrupt)(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info); + irqreturn_t (*fail_irq)(struct cxl_afu *afu, struct cxl_irq_info *irq_info); + int (*activate_dedicated_process)(struct cxl_afu *afu); + int (*attach_afu_directed)(struct cxl_context *ctx, u64 wed, u64 amr); + int (*attach_dedicated_process)(struct cxl_context *ctx, u64 wed, u64 amr); + void (*update_dedicated_ivtes)(struct cxl_context *ctx); + void (*debugfs_add_adapter_regs)(struct cxl *adapter, struct dentry *dir); + void (*debugfs_add_afu_regs)(struct cxl_afu *afu, struct dentry *dir); void (*psl_irq_dump_registers)(struct cxl_context *ctx); void (*err_irq_dump_registers)(struct cxl *adapter); void (*debugfs_stop_trace)(struct cxl *adapter); @@ -641,25 +722,38 @@ int cxl_pci_reset(struct cxl *adapter); void cxl_pci_release_afu(struct device *dev); ssize_t cxl_pci_read_adapter_vpd(struct cxl *adapter, void *buf, size_t len); -/* common == phyp + powernv */ +/* common == phyp + powernv - CAIA 1&2 */ struct cxl_process_element_common { __be32 tid; __be32 pid; __be64 csrp; - __be64 aurp0; - __be64 aurp1; - __be64 sstp0; - __be64 sstp1; + union { + struct { + __be64 aurp0; + __be64 aurp1; + __be64 sstp0; + __be64 sstp1; + } psl8; /* CAIA 1 */ + struct { + u8 reserved2[8]; + u8 reserved3[8]; + u8 reserved4[8]; + u8 reserved5[8]; + } psl9; /* CAIA 2 */ + } u; __be64 amr; - u8 reserved3[4]; + u8 reserved6[4]; __be64 wed; } __packed; -/* just powernv */ +/* just powernv - CAIA 1&2 */ struct cxl_process_element { __be64 sr; __be64 SPOffset; - __be64 sdr; + union { + __be64 sdr; /* CAIA 1 */ + u8 reserved1[8]; /* CAIA 2 */ + } u; __be64 haurp; __be32 ctxtime; __be16 ivte_offsets[4]; @@ -739,6 +833,39 @@ static inline u64 cxl_p2n_read(struct cxl_afu *afu, cxl_p2n_reg_t reg) return ~0ULL; } +static inline bool cxl_is_power8(void) +{ + if ((pvr_version_is(PVR_POWER8E)) || + (pvr_version_is(PVR_POWER8NVL)) || + (pvr_version_is(PVR_POWER8))) + return true; + return false; +} + +static inline bool cxl_is_power9(void) +{ + /* intermediate solution */ + if (!cxl_is_power8() && + (cpu_has_feature(CPU_FTRS_POWER9) || + cpu_has_feature(CPU_FTR_POWER9_DD1))) + return true; + return false; +} + +static inline bool cxl_is_psl8(struct cxl_afu *afu) +{ + if (afu->adapter->caia_major == 1) + return true; + return false; +} + +static inline bool cxl_is_psl9(struct cxl_afu *afu) +{ + if (afu->adapter->caia_major == 2) + return true; + return false; +} + ssize_t cxl_pci_afu_read_err_buffer(struct cxl_afu *afu, char *buf, loff_t off, size_t count); @@ -765,7 +892,6 @@ int cxl_update_properties(struct device_node *dn, struct property *new_prop); void cxl_remove_adapter_nr(struct cxl *adapter); -int cxl_alloc_spa(struct cxl_afu *afu); void cxl_release_spa(struct cxl_afu *afu); dev_t cxl_get_dev(void); @@ -803,6 +929,15 @@ int afu_register_irqs(struct cxl_context *ctx, u32 count); void afu_release_irqs(struct cxl_context *ctx, void *cookie); void afu_irq_name_free(struct cxl_context *ctx); +int cxl_attach_afu_directed_psl9(struct cxl_context *ctx, u64 wed, u64 amr); +int cxl_attach_afu_directed_psl8(struct cxl_context *ctx, u64 wed, u64 amr); +int cxl_activate_dedicated_process_psl9(struct cxl_afu *afu); +int cxl_activate_dedicated_process_psl8(struct cxl_afu *afu); +int cxl_attach_dedicated_process_psl9(struct cxl_context *ctx, u64 wed, u64 amr); +int cxl_attach_dedicated_process_psl8(struct cxl_context *ctx, u64 wed, u64 amr); +void cxl_update_dedicated_ivtes_psl9(struct cxl_context *ctx); +void cxl_update_dedicated_ivtes_psl8(struct cxl_context *ctx); + #ifdef CONFIG_DEBUG_FS int cxl_debugfs_init(void); @@ -811,10 +946,13 @@ int cxl_debugfs_adapter_add(struct cxl *adapter); void cxl_debugfs_adapter_remove(struct cxl *adapter); int cxl_debugfs_afu_add(struct cxl_afu *afu); void cxl_debugfs_afu_remove(struct cxl_afu *afu); -void cxl_stop_trace(struct cxl *cxl); -void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, struct dentry *dir); -void cxl_debugfs_add_adapter_xsl_regs(struct cxl *adapter, struct dentry *dir); -void cxl_debugfs_add_afu_psl_regs(struct cxl_afu *afu, struct dentry *dir); +void cxl_stop_trace_psl9(struct cxl *cxl); +void cxl_stop_trace_psl8(struct cxl *cxl); +void cxl_debugfs_add_adapter_regs_psl9(struct cxl *adapter, struct dentry *dir); +void cxl_debugfs_add_adapter_regs_psl8(struct cxl *adapter, struct dentry *dir); +void cxl_debugfs_add_adapter_regs_xsl(struct cxl *adapter, struct dentry *dir); +void cxl_debugfs_add_afu_regs_psl9(struct cxl_afu *afu, struct dentry *dir); +void cxl_debugfs_add_afu_regs_psl8(struct cxl_afu *afu, struct dentry *dir); #else /* CONFIG_DEBUG_FS */ @@ -845,21 +983,34 @@ static inline void cxl_debugfs_afu_remove(struct cxl_afu *afu) { } -static inline void cxl_stop_trace(struct cxl *cxl) +static inline void cxl_stop_trace_psl9(struct cxl *cxl) { } -static inline void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, +static inline void cxl_stop_trace_psl8(struct cxl *cxl) +{ +} + +static inline void cxl_debugfs_add_adapter_regs_psl9(struct cxl *adapter, struct dentry *dir) { } -static inline void cxl_debugfs_add_adapter_xsl_regs(struct cxl *adapter, +static inline void cxl_debugfs_add_adapter_regs_psl8(struct cxl *adapter, struct dentry *dir) { } -static inline void cxl_debugfs_add_afu_psl_regs(struct cxl_afu *afu, struct dentry *dir) +static inline void cxl_debugfs_add_adapter_regs_xsl(struct cxl *adapter, + struct dentry *dir) +{ +} + +static inline void cxl_debugfs_add_afu_regs_psl9(struct cxl_afu *afu, struct dentry *dir) +{ +} + +static inline void cxl_debugfs_add_afu_regs_psl8(struct cxl_afu *afu, struct dentry *dir) { } @@ -888,27 +1039,15 @@ int __detach_context(struct cxl_context *ctx); /* * This must match the layout of the H_COLLECT_CA_INT_INFO retbuf defined * in PAPR. - * A word about endianness: a pointer to this structure is passed when - * calling the hcall. However, it is not a block of memory filled up by - * the hypervisor. The return values are found in registers, and copied - * one by one when returning from the hcall. See the end of the call to - * plpar_hcall9() in hvCall.S - * As a consequence: - * - we don't need to do any endianness conversion - * - the pid and tid are an exception. They are 32-bit values returned in - * the same 64-bit register. So we do need to worry about byte ordering. + * Field pid_tid is now 'reserved' because it's no more used on bare-metal. + * On a guest environment, PSL_PID_An is located on the upper 32 bits and + * PSL_TID_An register in the lower 32 bits. */ struct cxl_irq_info { u64 dsisr; u64 dar; u64 dsr; -#ifndef CONFIG_CPU_LITTLE_ENDIAN - u32 pid; - u32 tid; -#else - u32 tid; - u32 pid; -#endif + u64 reserved; u64 afu_err; u64 errstat; u64 proc_handle; @@ -916,19 +1055,23 @@ struct cxl_irq_info { }; void cxl_assign_psn_space(struct cxl_context *ctx); -irqreturn_t cxl_irq(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info); +int cxl_invalidate_all_psl9(struct cxl *adapter); +int cxl_invalidate_all_psl8(struct cxl *adapter); +irqreturn_t cxl_irq_psl9(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info); +irqreturn_t cxl_irq_psl8(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info); +irqreturn_t cxl_fail_irq_psl(struct cxl_afu *afu, struct cxl_irq_info *irq_info); int cxl_register_one_irq(struct cxl *adapter, irq_handler_t handler, void *cookie, irq_hw_number_t *dest_hwirq, unsigned int *dest_virq, const char *name); int cxl_check_error(struct cxl_afu *afu); int cxl_afu_slbia(struct cxl_afu *afu); -int cxl_tlb_slb_invalidate(struct cxl *adapter); int cxl_data_cache_flush(struct cxl *adapter); int cxl_afu_disable(struct cxl_afu *afu); int cxl_psl_purge(struct cxl_afu *afu); -void cxl_native_psl_irq_dump_regs(struct cxl_context *ctx); +void cxl_native_irq_dump_regs_psl9(struct cxl_context *ctx); +void cxl_native_irq_dump_regs_psl8(struct cxl_context *ctx); void cxl_native_err_irq_dump_regs(struct cxl *adapter); int cxl_pci_vphb_add(struct cxl_afu *afu); void cxl_pci_vphb_remove(struct cxl_afu *afu); @@ -1024,4 +1167,10 @@ int cxl_adapter_context_lock(struct cxl *adapter); /* Unlock the contexts-lock if taken. Warn and force unlock otherwise */ void cxl_adapter_context_unlock(struct cxl *adapter); +/* Increases the reference count to "struct mm_struct" */ +void cxl_context_mm_count_get(struct cxl_context *ctx); + +/* Decrements the reference count to "struct mm_struct" */ +void cxl_context_mm_count_put(struct cxl_context *ctx); + #endif diff --git a/drivers/misc/cxl/debugfs.c b/drivers/misc/cxl/debugfs.c index 9c06ac8fa5ac..eae9d749f967 100644 --- a/drivers/misc/cxl/debugfs.c +++ b/drivers/misc/cxl/debugfs.c @@ -15,7 +15,13 @@ static struct dentry *cxl_debugfs; -void cxl_stop_trace(struct cxl *adapter) +void cxl_stop_trace_psl9(struct cxl *adapter) +{ + /* Stop the trace */ + cxl_p1_write(adapter, CXL_PSL9_TRACECFG, 0x4480000000000000ULL); +} + +void cxl_stop_trace_psl8(struct cxl *adapter) { int slice; @@ -53,7 +59,15 @@ static struct dentry *debugfs_create_io_x64(const char *name, umode_t mode, (void __force *)value, &fops_io_x64); } -void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, struct dentry *dir) +void cxl_debugfs_add_adapter_regs_psl9(struct cxl *adapter, struct dentry *dir) +{ + debugfs_create_io_x64("fir1", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL9_FIR1)); + debugfs_create_io_x64("fir2", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL9_FIR2)); + debugfs_create_io_x64("fir_cntl", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL9_FIR_CNTL)); + debugfs_create_io_x64("trace", S_IRUSR | S_IWUSR, dir, _cxl_p1_addr(adapter, CXL_PSL9_TRACECFG)); +} + +void cxl_debugfs_add_adapter_regs_psl8(struct cxl *adapter, struct dentry *dir) { debugfs_create_io_x64("fir1", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR1)); debugfs_create_io_x64("fir2", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR2)); @@ -61,7 +75,7 @@ void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, struct dentry *dir) debugfs_create_io_x64("trace", S_IRUSR | S_IWUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_TRACE)); } -void cxl_debugfs_add_adapter_xsl_regs(struct cxl *adapter, struct dentry *dir) +void cxl_debugfs_add_adapter_regs_xsl(struct cxl *adapter, struct dentry *dir) { debugfs_create_io_x64("fec", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_XSL_FEC)); } @@ -82,8 +96,8 @@ int cxl_debugfs_adapter_add(struct cxl *adapter) debugfs_create_io_x64("err_ivte", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_ErrIVTE)); - if (adapter->native->sl_ops->debugfs_add_adapter_sl_regs) - adapter->native->sl_ops->debugfs_add_adapter_sl_regs(adapter, dir); + if (adapter->native->sl_ops->debugfs_add_adapter_regs) + adapter->native->sl_ops->debugfs_add_adapter_regs(adapter, dir); return 0; } @@ -92,8 +106,16 @@ void cxl_debugfs_adapter_remove(struct cxl *adapter) debugfs_remove_recursive(adapter->debugfs); } -void cxl_debugfs_add_afu_psl_regs(struct cxl_afu *afu, struct dentry *dir) +void cxl_debugfs_add_afu_regs_psl9(struct cxl_afu *afu, struct dentry *dir) { + debugfs_create_io_x64("serr", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SERR_An)); +} + +void cxl_debugfs_add_afu_regs_psl8(struct cxl_afu *afu, struct dentry *dir) +{ + debugfs_create_io_x64("sstp0", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP0_An)); + debugfs_create_io_x64("sstp1", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP1_An)); + debugfs_create_io_x64("fir", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_FIR_SLICE_An)); debugfs_create_io_x64("serr", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SERR_An)); debugfs_create_io_x64("afu_debug", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_AFU_DEBUG_An)); @@ -117,12 +139,11 @@ int cxl_debugfs_afu_add(struct cxl_afu *afu) debugfs_create_io_x64("sr", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SR_An)); debugfs_create_io_x64("dsisr", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_DSISR_An)); debugfs_create_io_x64("dar", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_DAR_An)); - debugfs_create_io_x64("sstp0", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP0_An)); - debugfs_create_io_x64("sstp1", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP1_An)); + debugfs_create_io_x64("err_status", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_ErrStat_An)); - if (afu->adapter->native->sl_ops->debugfs_add_afu_sl_regs) - afu->adapter->native->sl_ops->debugfs_add_afu_sl_regs(afu, dir); + if (afu->adapter->native->sl_ops->debugfs_add_afu_regs) + afu->adapter->native->sl_ops->debugfs_add_afu_regs(afu, dir); return 0; } diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c index 2fa015c05561..5344448f514e 100644 --- a/drivers/misc/cxl/fault.c +++ b/drivers/misc/cxl/fault.c @@ -146,108 +146,67 @@ static void cxl_handle_page_fault(struct cxl_context *ctx, return cxl_ack_ae(ctx); } - /* - * update_mmu_cache() will not have loaded the hash since current->trap - * is not a 0x400 or 0x300, so just call hash_page_mm() here. - */ - access = _PAGE_PRESENT | _PAGE_READ; - if (dsisr & CXL_PSL_DSISR_An_S) - access |= _PAGE_WRITE; - - access |= _PAGE_PRIVILEGED; - if ((!ctx->kernel) || (REGION_ID(dar) == USER_REGION_ID)) - access &= ~_PAGE_PRIVILEGED; - - if (dsisr & DSISR_NOHPTE) - inv_flags |= HPTE_NOHPTE_UPDATE; - - local_irq_save(flags); - hash_page_mm(mm, dar, access, 0x300, inv_flags); - local_irq_restore(flags); - + if (!radix_enabled()) { + /* + * update_mmu_cache() will not have loaded the hash since current->trap + * is not a 0x400 or 0x300, so just call hash_page_mm() here. + */ + access = _PAGE_PRESENT | _PAGE_READ; + if (dsisr & CXL_PSL_DSISR_An_S) + access |= _PAGE_WRITE; + + access |= _PAGE_PRIVILEGED; + if ((!ctx->kernel) || (REGION_ID(dar) == USER_REGION_ID)) + access &= ~_PAGE_PRIVILEGED; + + if (dsisr & DSISR_NOHPTE) + inv_flags |= HPTE_NOHPTE_UPDATE; + + local_irq_save(flags); + hash_page_mm(mm, dar, access, 0x300, inv_flags); + local_irq_restore(flags); + } pr_devel("Page fault successfully handled for pe: %i!\n", ctx->pe); cxl_ops->ack_irq(ctx, CXL_PSL_TFC_An_R, 0); } /* - * Returns the mm_struct corresponding to the context ctx via ctx->pid - * In case the task has exited we use the task group leader accessible - * via ctx->glpid to find the next task in the thread group that has a - * valid mm_struct associated with it. If a task with valid mm_struct - * is found the ctx->pid is updated to use the task struct for subsequent - * translations. In case no valid mm_struct is found in the task group to - * service the fault a NULL is returned. + * Returns the mm_struct corresponding to the context ctx. + * mm_users == 0, the context may be in the process of being closed. */ static struct mm_struct *get_mem_context(struct cxl_context *ctx) { - struct task_struct *task = NULL; - struct mm_struct *mm = NULL; - struct pid *old_pid = ctx->pid; - - if (old_pid == NULL) { - pr_warn("%s: Invalid context for pe=%d\n", - __func__, ctx->pe); + if (ctx->mm == NULL) return NULL; - } - task = get_pid_task(old_pid, PIDTYPE_PID); + if (!atomic_inc_not_zero(&ctx->mm->mm_users)) + return NULL; - /* - * pid_alive may look racy but this saves us from costly - * get_task_mm when the task is a zombie. In worst case - * we may think a task is alive, which is about to die - * but get_task_mm will return NULL. - */ - if (task != NULL && pid_alive(task)) - mm = get_task_mm(task); + return ctx->mm; +} - /* release the task struct that was taken earlier */ - if (task) - put_task_struct(task); - else - pr_devel("%s: Context owning pid=%i for pe=%i dead\n", - __func__, pid_nr(old_pid), ctx->pe); - - /* - * If we couldn't find the mm context then use the group - * leader to iterate over the task group and find a task - * that gives us mm_struct. - */ - if (unlikely(mm == NULL && ctx->glpid != NULL)) { - - rcu_read_lock(); - task = pid_task(ctx->glpid, PIDTYPE_PID); - if (task) - do { - mm = get_task_mm(task); - if (mm) { - ctx->pid = get_task_pid(task, - PIDTYPE_PID); - break; - } - task = next_thread(task); - } while (task && !thread_group_leader(task)); - rcu_read_unlock(); - - /* check if we switched pid */ - if (ctx->pid != old_pid) { - if (mm) - pr_devel("%s:pe=%i switch pid %i->%i\n", - __func__, ctx->pe, pid_nr(old_pid), - pid_nr(ctx->pid)); - else - pr_devel("%s:Cannot find mm for pid=%i\n", - __func__, pid_nr(old_pid)); - - /* drop the reference to older pid */ - put_pid(old_pid); - } - } +static bool cxl_is_segment_miss(struct cxl_context *ctx, u64 dsisr) +{ + if ((cxl_is_psl8(ctx->afu)) && (dsisr & CXL_PSL_DSISR_An_DS)) + return true; - return mm; + return false; } +static bool cxl_is_page_fault(struct cxl_context *ctx, u64 dsisr) +{ + if ((cxl_is_psl8(ctx->afu)) && (dsisr & CXL_PSL_DSISR_An_DM)) + return true; + + if ((cxl_is_psl9(ctx->afu)) && + ((dsisr & CXL_PSL9_DSISR_An_CO_MASK) & + (CXL_PSL9_DSISR_An_PF_SLR | CXL_PSL9_DSISR_An_PF_RGC | + CXL_PSL9_DSISR_An_PF_RGP | CXL_PSL9_DSISR_An_PF_HRH | + CXL_PSL9_DSISR_An_PF_STEG))) + return true; + return false; +} void cxl_handle_fault(struct work_struct *fault_work) { @@ -282,7 +241,6 @@ void cxl_handle_fault(struct work_struct *fault_work) if (!ctx->kernel) { mm = get_mem_context(ctx); - /* indicates all the thread in task group have exited */ if (mm == NULL) { pr_devel("%s: unable to get mm for pe=%d pid=%i\n", __func__, ctx->pe, pid_nr(ctx->pid)); @@ -294,9 +252,9 @@ void cxl_handle_fault(struct work_struct *fault_work) } } - if (dsisr & CXL_PSL_DSISR_An_DS) + if (cxl_is_segment_miss(ctx, dsisr)) cxl_handle_segment_miss(ctx, mm, dar); - else if (dsisr & CXL_PSL_DSISR_An_DM) + else if (cxl_is_page_fault(ctx, dsisr)) cxl_handle_page_fault(ctx, mm, dsisr, dar); else WARN(1, "cxl_handle_fault has nothing to handle\n"); diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index e7139c76f961..17b433f1ce23 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -18,6 +18,7 @@ #include <linux/fs.h> #include <linux/mm.h> #include <linux/slab.h> +#include <linux/sched/mm.h> #include <asm/cputable.h> #include <asm/current.h> #include <asm/copro.h> @@ -216,8 +217,16 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, * process is still accessible. */ ctx->pid = get_task_pid(current, PIDTYPE_PID); - ctx->glpid = get_task_pid(current->group_leader, PIDTYPE_PID); + /* acquire a reference to the task's mm */ + ctx->mm = get_task_mm(current); + + /* ensure this mm_struct can't be freed */ + cxl_context_mm_count_get(ctx); + + /* decrement the use count */ + if (ctx->mm) + mmput(ctx->mm); trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr); @@ -225,9 +234,9 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, amr))) { afu_release_irqs(ctx, ctx); cxl_adapter_context_put(ctx->afu->adapter); - put_pid(ctx->glpid); put_pid(ctx->pid); - ctx->glpid = ctx->pid = NULL; + ctx->pid = NULL; + cxl_context_mm_count_put(ctx); goto out; } diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c index e04bc4ddfd74..f58b4b6c79f2 100644 --- a/drivers/misc/cxl/guest.c +++ b/drivers/misc/cxl/guest.c @@ -169,7 +169,7 @@ static irqreturn_t guest_psl_irq(int irq, void *data) return IRQ_HANDLED; } - rc = cxl_irq(irq, ctx, &irq_info); + rc = cxl_irq_psl8(irq, ctx, &irq_info); return rc; } @@ -551,13 +551,13 @@ static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) elem->common.tid = cpu_to_be32(0); /* Unused */ elem->common.pid = cpu_to_be32(pid); elem->common.csrp = cpu_to_be64(0); /* disable */ - elem->common.aurp0 = cpu_to_be64(0); /* disable */ - elem->common.aurp1 = cpu_to_be64(0); /* disable */ + elem->common.u.psl8.aurp0 = cpu_to_be64(0); /* disable */ + elem->common.u.psl8.aurp1 = cpu_to_be64(0); /* disable */ cxl_prefault(ctx, wed); - elem->common.sstp0 = cpu_to_be64(ctx->sstp0); - elem->common.sstp1 = cpu_to_be64(ctx->sstp1); + elem->common.u.psl8.sstp0 = cpu_to_be64(ctx->sstp0); + elem->common.u.psl8.sstp1 = cpu_to_be64(ctx->sstp1); /* * Ensure we have at least one interrupt allocated to take faults for diff --git a/drivers/misc/cxl/hcalls.c b/drivers/misc/cxl/hcalls.c index d6d11f4056d7..9b8bb0f80c3b 100644 --- a/drivers/misc/cxl/hcalls.c +++ b/drivers/misc/cxl/hcalls.c @@ -413,9 +413,9 @@ long cxl_h_collect_int_info(u64 unit_address, u64 process_token, switch (rc) { case H_SUCCESS: /* The interrupt info is returned in return registers. */ - pr_devel("dsisr:%#llx, dar:%#llx, dsr:%#llx, pid:%u, tid:%u, afu_err:%#llx, errstat:%#llx\n", - info->dsisr, info->dar, info->dsr, info->pid, - info->tid, info->afu_err, info->errstat); + pr_devel("dsisr:%#llx, dar:%#llx, dsr:%#llx, pid_tid:%#llx, afu_err:%#llx, errstat:%#llx\n", + info->dsisr, info->dar, info->dsr, info->reserved, + info->afu_err, info->errstat); return 0; case H_PARAMETER: /* An incorrect parameter was supplied. */ return -EINVAL; diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index 1a402bbed687..ce08a9f22308 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -34,7 +34,58 @@ static irqreturn_t schedule_cxl_fault(struct cxl_context *ctx, u64 dsisr, u64 da return IRQ_HANDLED; } -irqreturn_t cxl_irq(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info) +irqreturn_t cxl_irq_psl9(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info) +{ + u64 dsisr, dar; + + dsisr = irq_info->dsisr; + dar = irq_info->dar; + + trace_cxl_psl9_irq(ctx, irq, dsisr, dar); + + pr_devel("CXL interrupt %i for afu pe: %i DSISR: %#llx DAR: %#llx\n", irq, ctx->pe, dsisr, dar); + + if (dsisr & CXL_PSL9_DSISR_An_TF) { + pr_devel("CXL interrupt: Scheduling translation fault handling for later (pe: %i)\n", ctx->pe); + return schedule_cxl_fault(ctx, dsisr, dar); + } + + if (dsisr & CXL_PSL9_DSISR_An_PE) + return cxl_ops->handle_psl_slice_error(ctx, dsisr, + irq_info->errstat); + if (dsisr & CXL_PSL9_DSISR_An_AE) { + pr_devel("CXL interrupt: AFU Error 0x%016llx\n", irq_info->afu_err); + + if (ctx->pending_afu_err) { + /* + * This shouldn't happen - the PSL treats these errors + * as fatal and will have reset the AFU, so there's not + * much point buffering multiple AFU errors. + * OTOH if we DO ever see a storm of these come in it's + * probably best that we log them somewhere: + */ + dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error undelivered to pe %i: 0x%016llx\n", + ctx->pe, irq_info->afu_err); + } else { + spin_lock(&ctx->lock); + ctx->afu_err = irq_info->afu_err; + ctx->pending_afu_err = 1; + spin_unlock(&ctx->lock); + + wake_up_all(&ctx->wq); + } + + cxl_ops->ack_irq(ctx, CXL_PSL_TFC_An_A, 0); + return IRQ_HANDLED; + } + if (dsisr & CXL_PSL9_DSISR_An_OC) + pr_devel("CXL interrupt: OS Context Warning\n"); + + WARN(1, "Unhandled CXL PSL IRQ\n"); + return IRQ_HANDLED; +} + +irqreturn_t cxl_irq_psl8(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info) { u64 dsisr, dar; diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c index b0b6ed31918e..1703655072b1 100644 --- a/drivers/misc/cxl/main.c +++ b/drivers/misc/cxl/main.c @@ -59,16 +59,10 @@ int cxl_afu_slbia(struct cxl_afu *afu) static inline void _cxl_slbia(struct cxl_context *ctx, struct mm_struct *mm) { - struct task_struct *task; unsigned long flags; - if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) { - pr_devel("%s unable to get task %i\n", - __func__, pid_nr(ctx->pid)); - return; - } - if (task->mm != mm) - goto out_put; + if (ctx->mm != mm) + return; pr_devel("%s matched mm - card: %i afu: %i pe: %i\n", __func__, ctx->afu->adapter->adapter_num, ctx->afu->slice, ctx->pe); @@ -79,8 +73,6 @@ static inline void _cxl_slbia(struct cxl_context *ctx, struct mm_struct *mm) spin_unlock_irqrestore(&ctx->sste_lock, flags); mb(); cxl_afu_slbia(ctx->afu); -out_put: - put_task_struct(task); } static inline void cxl_slbia_core(struct mm_struct *mm) diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index 7ae710585267..871a2f09c718 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -95,12 +95,23 @@ int cxl_afu_disable(struct cxl_afu *afu) /* This will disable as well as reset */ static int native_afu_reset(struct cxl_afu *afu) { + int rc; + u64 serr; + pr_devel("AFU reset request\n"); - return afu_control(afu, CXL_AFU_Cntl_An_RA, 0, + rc = afu_control(afu, CXL_AFU_Cntl_An_RA, 0, CXL_AFU_Cntl_An_RS_Complete | CXL_AFU_Cntl_An_ES_Disabled, CXL_AFU_Cntl_An_RS_MASK | CXL_AFU_Cntl_An_ES_MASK, false); + + /* Re-enable any masked interrupts */ + serr = cxl_p1n_read(afu, CXL_PSL_SERR_An); + serr &= ~CXL_PSL_SERR_An_IRQ_MASKS; + cxl_p1n_write(afu, CXL_PSL_SERR_An, serr); + + + return rc; } static int native_afu_check_and_enable(struct cxl_afu *afu) @@ -120,6 +131,7 @@ int cxl_psl_purge(struct cxl_afu *afu) u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); u64 dsisr, dar; u64 start, end; + u64 trans_fault = 0x0ULL; unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); int rc = 0; @@ -127,6 +139,11 @@ int cxl_psl_purge(struct cxl_afu *afu) pr_devel("PSL purge request\n"); + if (cxl_is_psl8(afu)) + trans_fault = CXL_PSL_DSISR_TRANS; + if (cxl_is_psl9(afu)) + trans_fault = CXL_PSL9_DSISR_An_TF; + if (!cxl_ops->link_ok(afu->adapter, afu)) { dev_warn(&afu->dev, "PSL Purge called with link down, ignoring\n"); rc = -EIO; @@ -155,13 +172,17 @@ int cxl_psl_purge(struct cxl_afu *afu) } dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); - pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%016llx PSL_DSISR: 0x%016llx\n", PSL_CNTL, dsisr); - if (dsisr & CXL_PSL_DSISR_TRANS) { + pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%016llx PSL_DSISR: 0x%016llx\n", + PSL_CNTL, dsisr); + + if (dsisr & trans_fault) { dar = cxl_p2n_read(afu, CXL_PSL_DAR_An); - dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%016llx, DAR: 0x%016llx\n", dsisr, dar); + dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%016llx, DAR: 0x%016llx\n", + dsisr, dar); cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE); } else if (dsisr) { - dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%016llx\n", dsisr); + dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%016llx\n", + dsisr); cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A); } else { cpu_relax(); @@ -196,7 +217,7 @@ static int spa_max_procs(int spa_size) return ((spa_size / 8) - 96) / 17; } -int cxl_alloc_spa(struct cxl_afu *afu) +static int cxl_alloc_spa(struct cxl_afu *afu, int mode) { unsigned spa_size; @@ -209,7 +230,8 @@ int cxl_alloc_spa(struct cxl_afu *afu) if (spa_size > 0x100000) { dev_warn(&afu->dev, "num_of_processes too large for the SPA, limiting to %i (0x%x)\n", afu->native->spa_max_procs, afu->native->spa_size); - afu->num_procs = afu->native->spa_max_procs; + if (mode != CXL_MODE_DEDICATED) + afu->num_procs = afu->native->spa_max_procs; break; } @@ -258,7 +280,37 @@ void cxl_release_spa(struct cxl_afu *afu) } } -int cxl_tlb_slb_invalidate(struct cxl *adapter) +/* + * Invalidation of all ERAT entries is no longer required by CAIA2. Use + * only for debug. + */ +int cxl_invalidate_all_psl9(struct cxl *adapter) +{ + unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + u64 ierat; + + pr_devel("CXL adapter - invalidation of all ERAT entries\n"); + + /* Invalidates all ERAT entries for Radix or HPT */ + ierat = CXL_XSL9_IERAT_IALL; + if (radix_enabled()) + ierat |= CXL_XSL9_IERAT_INVR; + cxl_p1_write(adapter, CXL_XSL9_IERAT, ierat); + + while (cxl_p1_read(adapter, CXL_XSL9_IERAT) & CXL_XSL9_IERAT_IINPROG) { + if (time_after_eq(jiffies, timeout)) { + dev_warn(&adapter->dev, + "WARNING: CXL adapter invalidation of all ERAT entries timed out!\n"); + return -EBUSY; + } + if (!cxl_ops->link_ok(adapter, NULL)) + return -EIO; + cpu_relax(); + } + return 0; +} + +int cxl_invalidate_all_psl8(struct cxl *adapter) { unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); @@ -466,7 +518,8 @@ static int remove_process_element(struct cxl_context *ctx) if (!rc) ctx->pe_inserted = false; - slb_invalid(ctx); + if (cxl_is_power8()) + slb_invalid(ctx); pr_devel("%s Remove pe: %i finished\n", __func__, ctx->pe); mutex_unlock(&ctx->afu->native->spa_mutex); @@ -493,13 +546,14 @@ static int activate_afu_directed(struct cxl_afu *afu) afu->num_procs = afu->max_procs_virtualised; if (afu->native->spa == NULL) { - if (cxl_alloc_spa(afu)) + if (cxl_alloc_spa(afu, CXL_MODE_DIRECTED)) return -ENOMEM; } attach_spa(afu); cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_AFU); - cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL); + if (cxl_is_power8()) + cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL); cxl_p1n_write(afu, CXL_PSL_ID_An, CXL_PSL_ID_An_F | CXL_PSL_ID_An_L); afu->current_mode = CXL_MODE_DIRECTED; @@ -542,10 +596,19 @@ static u64 calculate_sr(struct cxl_context *ctx) sr |= (mfmsr() & MSR_SF) | CXL_PSL_SR_An_HV; } else { sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R; - sr &= ~(CXL_PSL_SR_An_HV); + if (radix_enabled()) + sr |= CXL_PSL_SR_An_HV; + else + sr &= ~(CXL_PSL_SR_An_HV); if (!test_tsk_thread_flag(current, TIF_32BIT)) sr |= CXL_PSL_SR_An_SF; } + if (cxl_is_psl9(ctx->afu)) { + if (radix_enabled()) + sr |= CXL_PSL_SR_An_XLAT_ror; + else + sr |= CXL_PSL_SR_An_XLAT_hpt; + } return sr; } @@ -578,7 +641,71 @@ static void update_ivtes_directed(struct cxl_context *ctx) WARN_ON(add_process_element(ctx)); } -static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) +static int process_element_entry_psl9(struct cxl_context *ctx, u64 wed, u64 amr) +{ + u32 pid; + + cxl_assign_psn_space(ctx); + + ctx->elem->ctxtime = 0; /* disable */ + ctx->elem->lpid = cpu_to_be32(mfspr(SPRN_LPID)); + ctx->elem->haurp = 0; /* disable */ + + if (ctx->kernel) + pid = 0; + else { + if (ctx->mm == NULL) { + pr_devel("%s: unable to get mm for pe=%d pid=%i\n", + __func__, ctx->pe, pid_nr(ctx->pid)); + return -EINVAL; + } + pid = ctx->mm->context.id; + } + + ctx->elem->common.tid = 0; + ctx->elem->common.pid = cpu_to_be32(pid); + + ctx->elem->sr = cpu_to_be64(calculate_sr(ctx)); + + ctx->elem->common.csrp = 0; /* disable */ + + cxl_prefault(ctx, wed); + + /* + * Ensure we have the multiplexed PSL interrupt set up to take faults + * for kernel contexts that may not have allocated any AFU IRQs at all: + */ + if (ctx->irqs.range[0] == 0) { + ctx->irqs.offset[0] = ctx->afu->native->psl_hwirq; + ctx->irqs.range[0] = 1; + } + + ctx->elem->common.amr = cpu_to_be64(amr); + ctx->elem->common.wed = cpu_to_be64(wed); + + return 0; +} + +int cxl_attach_afu_directed_psl9(struct cxl_context *ctx, u64 wed, u64 amr) +{ + int result; + + /* fill the process element entry */ + result = process_element_entry_psl9(ctx, wed, amr); + if (result) + return result; + + update_ivtes_directed(ctx); + + /* first guy needs to enable */ + result = cxl_ops->afu_check_and_enable(ctx->afu); + if (result) + return result; + + return add_process_element(ctx); +} + +int cxl_attach_afu_directed_psl8(struct cxl_context *ctx, u64 wed, u64 amr) { u32 pid; int result; @@ -588,7 +715,7 @@ static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) ctx->elem->ctxtime = 0; /* disable */ ctx->elem->lpid = cpu_to_be32(mfspr(SPRN_LPID)); ctx->elem->haurp = 0; /* disable */ - ctx->elem->sdr = cpu_to_be64(mfspr(SPRN_SDR1)); + ctx->elem->u.sdr = cpu_to_be64(mfspr(SPRN_SDR1)); pid = current->pid; if (ctx->kernel) @@ -599,13 +726,13 @@ static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) ctx->elem->sr = cpu_to_be64(calculate_sr(ctx)); ctx->elem->common.csrp = 0; /* disable */ - ctx->elem->common.aurp0 = 0; /* disable */ - ctx->elem->common.aurp1 = 0; /* disable */ + ctx->elem->common.u.psl8.aurp0 = 0; /* disable */ + ctx->elem->common.u.psl8.aurp1 = 0; /* disable */ cxl_prefault(ctx, wed); - ctx->elem->common.sstp0 = cpu_to_be64(ctx->sstp0); - ctx->elem->common.sstp1 = cpu_to_be64(ctx->sstp1); + ctx->elem->common.u.psl8.sstp0 = cpu_to_be64(ctx->sstp0); + ctx->elem->common.u.psl8.sstp1 = cpu_to_be64(ctx->sstp1); /* * Ensure we have the multiplexed PSL interrupt set up to take faults @@ -671,7 +798,33 @@ static int deactivate_afu_directed(struct cxl_afu *afu) return 0; } -static int activate_dedicated_process(struct cxl_afu *afu) +int cxl_activate_dedicated_process_psl9(struct cxl_afu *afu) +{ + dev_info(&afu->dev, "Activating dedicated process mode\n"); + + /* + * If XSL is set to dedicated mode (Set in PSL_SCNTL reg), the + * XSL and AFU are programmed to work with a single context. + * The context information should be configured in the SPA area + * index 0 (so PSL_SPAP must be configured before enabling the + * AFU). + */ + afu->num_procs = 1; + if (afu->native->spa == NULL) { + if (cxl_alloc_spa(afu, CXL_MODE_DEDICATED)) + return -ENOMEM; + } + attach_spa(afu); + + cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_Process); + cxl_p1n_write(afu, CXL_PSL_ID_An, CXL_PSL_ID_An_F | CXL_PSL_ID_An_L); + + afu->current_mode = CXL_MODE_DEDICATED; + + return cxl_chardev_d_afu_add(afu); +} + +int cxl_activate_dedicated_process_psl8(struct cxl_afu *afu) { dev_info(&afu->dev, "Activating dedicated process mode\n"); @@ -694,7 +847,17 @@ static int activate_dedicated_process(struct cxl_afu *afu) return cxl_chardev_d_afu_add(afu); } -static void update_ivtes_dedicated(struct cxl_context *ctx) +void cxl_update_dedicated_ivtes_psl9(struct cxl_context *ctx) +{ + int r; + + for (r = 0; r < CXL_IRQ_RANGES; r++) { + ctx->elem->ivte_offsets[r] = cpu_to_be16(ctx->irqs.offset[r]); + ctx->elem->ivte_ranges[r] = cpu_to_be16(ctx->irqs.range[r]); + } +} + +void cxl_update_dedicated_ivtes_psl8(struct cxl_context *ctx) { struct cxl_afu *afu = ctx->afu; @@ -710,7 +873,27 @@ static void update_ivtes_dedicated(struct cxl_context *ctx) ((u64)ctx->irqs.range[3] & 0xffff)); } -static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr) +int cxl_attach_dedicated_process_psl9(struct cxl_context *ctx, u64 wed, u64 amr) +{ + struct cxl_afu *afu = ctx->afu; + int result; + + /* fill the process element entry */ + result = process_element_entry_psl9(ctx, wed, amr); + if (result) + return result; + + if (ctx->afu->adapter->native->sl_ops->update_dedicated_ivtes) + afu->adapter->native->sl_ops->update_dedicated_ivtes(ctx); + + result = cxl_ops->afu_reset(afu); + if (result) + return result; + + return afu_enable(afu); +} + +int cxl_attach_dedicated_process_psl8(struct cxl_context *ctx, u64 wed, u64 amr) { struct cxl_afu *afu = ctx->afu; u64 pid; @@ -728,7 +911,8 @@ static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr) cxl_prefault(ctx, wed); - update_ivtes_dedicated(ctx); + if (ctx->afu->adapter->native->sl_ops->update_dedicated_ivtes) + afu->adapter->native->sl_ops->update_dedicated_ivtes(ctx); cxl_p2n_write(afu, CXL_PSL_AMR_An, amr); @@ -778,8 +962,9 @@ static int native_afu_activate_mode(struct cxl_afu *afu, int mode) if (mode == CXL_MODE_DIRECTED) return activate_afu_directed(afu); - if (mode == CXL_MODE_DEDICATED) - return activate_dedicated_process(afu); + if ((mode == CXL_MODE_DEDICATED) && + (afu->adapter->native->sl_ops->activate_dedicated_process)) + return afu->adapter->native->sl_ops->activate_dedicated_process(afu); return -EINVAL; } @@ -793,11 +978,13 @@ static int native_attach_process(struct cxl_context *ctx, bool kernel, } ctx->kernel = kernel; - if (ctx->afu->current_mode == CXL_MODE_DIRECTED) - return attach_afu_directed(ctx, wed, amr); + if ((ctx->afu->current_mode == CXL_MODE_DIRECTED) && + (ctx->afu->adapter->native->sl_ops->attach_afu_directed)) + return ctx->afu->adapter->native->sl_ops->attach_afu_directed(ctx, wed, amr); - if (ctx->afu->current_mode == CXL_MODE_DEDICATED) - return attach_dedicated(ctx, wed, amr); + if ((ctx->afu->current_mode == CXL_MODE_DEDICATED) && + (ctx->afu->adapter->native->sl_ops->attach_dedicated_process)) + return ctx->afu->adapter->native->sl_ops->attach_dedicated_process(ctx, wed, amr); return -EINVAL; } @@ -830,8 +1017,9 @@ static void native_update_ivtes(struct cxl_context *ctx) { if (ctx->afu->current_mode == CXL_MODE_DIRECTED) return update_ivtes_directed(ctx); - if (ctx->afu->current_mode == CXL_MODE_DEDICATED) - return update_ivtes_dedicated(ctx); + if ((ctx->afu->current_mode == CXL_MODE_DEDICATED) && + (ctx->afu->adapter->native->sl_ops->update_dedicated_ivtes)) + return ctx->afu->adapter->native->sl_ops->update_dedicated_ivtes(ctx); WARN(1, "native_update_ivtes: Bad mode\n"); } @@ -859,8 +1047,6 @@ static int native_detach_process(struct cxl_context *ctx) static int native_get_irq_info(struct cxl_afu *afu, struct cxl_irq_info *info) { - u64 pidtid; - /* If the adapter has gone away, we can't get any meaningful * information. */ @@ -869,10 +1055,8 @@ static int native_get_irq_info(struct cxl_afu *afu, struct cxl_irq_info *info) info->dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); info->dar = cxl_p2n_read(afu, CXL_PSL_DAR_An); - info->dsr = cxl_p2n_read(afu, CXL_PSL_DSR_An); - pidtid = cxl_p2n_read(afu, CXL_PSL_PID_TID_An); - info->pid = pidtid >> 32; - info->tid = pidtid & 0xffffffff; + if (cxl_is_power8()) + info->dsr = cxl_p2n_read(afu, CXL_PSL_DSR_An); info->afu_err = cxl_p2n_read(afu, CXL_AFU_ERR_An); info->errstat = cxl_p2n_read(afu, CXL_PSL_ErrStat_An); info->proc_handle = 0; @@ -880,7 +1064,22 @@ static int native_get_irq_info(struct cxl_afu *afu, struct cxl_irq_info *info) return 0; } -void cxl_native_psl_irq_dump_regs(struct cxl_context *ctx) +void cxl_native_irq_dump_regs_psl9(struct cxl_context *ctx) +{ + u64 fir1, fir2, serr; + + fir1 = cxl_p1_read(ctx->afu->adapter, CXL_PSL9_FIR1); + fir2 = cxl_p1_read(ctx->afu->adapter, CXL_PSL9_FIR2); + + dev_crit(&ctx->afu->dev, "PSL_FIR1: 0x%016llx\n", fir1); + dev_crit(&ctx->afu->dev, "PSL_FIR2: 0x%016llx\n", fir2); + if (ctx->afu->adapter->native->sl_ops->register_serr_irq) { + serr = cxl_p1n_read(ctx->afu, CXL_PSL_SERR_An); + cxl_afu_decode_psl_serr(ctx->afu, serr); + } +} + +void cxl_native_irq_dump_regs_psl8(struct cxl_context *ctx) { u64 fir1, fir2, fir_slice, serr, afu_debug; @@ -916,9 +1115,20 @@ static irqreturn_t native_handle_psl_slice_error(struct cxl_context *ctx, return cxl_ops->ack_irq(ctx, 0, errstat); } -static irqreturn_t fail_psl_irq(struct cxl_afu *afu, struct cxl_irq_info *irq_info) +static bool cxl_is_translation_fault(struct cxl_afu *afu, u64 dsisr) +{ + if ((cxl_is_psl8(afu)) && (dsisr & CXL_PSL_DSISR_TRANS)) + return true; + + if ((cxl_is_psl9(afu)) && (dsisr & CXL_PSL9_DSISR_An_TF)) + return true; + + return false; +} + +irqreturn_t cxl_fail_irq_psl(struct cxl_afu *afu, struct cxl_irq_info *irq_info) { - if (irq_info->dsisr & CXL_PSL_DSISR_TRANS) + if (cxl_is_translation_fault(afu, irq_info->dsisr)) cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE); else cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A); @@ -932,7 +1142,7 @@ static irqreturn_t native_irq_multiplexed(int irq, void *data) struct cxl_context *ctx; struct cxl_irq_info irq_info; u64 phreg = cxl_p2n_read(afu, CXL_PSL_PEHandle_An); - int ph, ret; + int ph, ret = IRQ_HANDLED, res; /* check if eeh kicked in while the interrupt was in flight */ if (unlikely(phreg == ~0ULL)) { @@ -943,15 +1153,18 @@ static irqreturn_t native_irq_multiplexed(int irq, void *data) } /* Mask the pe-handle from register value */ ph = phreg & 0xffff; - if ((ret = native_get_irq_info(afu, &irq_info))) { - WARN(1, "Unable to get CXL IRQ Info: %i\n", ret); - return fail_psl_irq(afu, &irq_info); + if ((res = native_get_irq_info(afu, &irq_info))) { + WARN(1, "Unable to get CXL IRQ Info: %i\n", res); + if (afu->adapter->native->sl_ops->fail_irq) + return afu->adapter->native->sl_ops->fail_irq(afu, &irq_info); + return ret; } rcu_read_lock(); ctx = idr_find(&afu->contexts_idr, ph); if (ctx) { - ret = cxl_irq(irq, ctx, &irq_info); + if (afu->adapter->native->sl_ops->handle_interrupt) + ret = afu->adapter->native->sl_ops->handle_interrupt(irq, ctx, &irq_info); rcu_read_unlock(); return ret; } @@ -961,7 +1174,9 @@ static irqreturn_t native_irq_multiplexed(int irq, void *data) " %016llx\n(Possible AFU HW issue - was a term/remove acked" " with outstanding transactions?)\n", ph, irq_info.dsisr, irq_info.dar); - return fail_psl_irq(afu, &irq_info); + if (afu->adapter->native->sl_ops->fail_irq) + ret = afu->adapter->native->sl_ops->fail_irq(afu, &irq_info); + return ret; } static void native_irq_wait(struct cxl_context *ctx) @@ -979,7 +1194,11 @@ static void native_irq_wait(struct cxl_context *ctx) if (ph != ctx->pe) return; dsisr = cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An); - if ((dsisr & CXL_PSL_DSISR_PENDING) == 0) + if (cxl_is_psl8(ctx->afu) && + ((dsisr & CXL_PSL_DSISR_PENDING) == 0)) + return; + if (cxl_is_psl9(ctx->afu) && + ((dsisr & CXL_PSL9_DSISR_PENDING) == 0)) return; /* * We are waiting for the workqueue to process our @@ -996,25 +1215,33 @@ static void native_irq_wait(struct cxl_context *ctx) static irqreturn_t native_slice_irq_err(int irq, void *data) { struct cxl_afu *afu = data; - u64 fir_slice, errstat, serr, afu_debug, afu_error, dsisr; + u64 errstat, serr, afu_error, dsisr; + u64 fir_slice, afu_debug, irq_mask; /* * slice err interrupt is only used with full PSL (no XSL) */ serr = cxl_p1n_read(afu, CXL_PSL_SERR_An); - fir_slice = cxl_p1n_read(afu, CXL_PSL_FIR_SLICE_An); errstat = cxl_p2n_read(afu, CXL_PSL_ErrStat_An); - afu_debug = cxl_p1n_read(afu, CXL_AFU_DEBUG_An); afu_error = cxl_p2n_read(afu, CXL_AFU_ERR_An); dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); cxl_afu_decode_psl_serr(afu, serr); - dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%016llx\n", fir_slice); + + if (cxl_is_power8()) { + fir_slice = cxl_p1n_read(afu, CXL_PSL_FIR_SLICE_An); + afu_debug = cxl_p1n_read(afu, CXL_AFU_DEBUG_An); + dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%016llx\n", fir_slice); + dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%016llx\n", afu_debug); + } dev_crit(&afu->dev, "CXL_PSL_ErrStat_An: 0x%016llx\n", errstat); - dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%016llx\n", afu_debug); dev_crit(&afu->dev, "AFU_ERR_An: 0x%.16llx\n", afu_error); dev_crit(&afu->dev, "PSL_DSISR_An: 0x%.16llx\n", dsisr); + /* mask off the IRQ so it won't retrigger until the AFU is reset */ + irq_mask = (serr & CXL_PSL_SERR_An_IRQS) >> 32; + serr |= irq_mask; cxl_p1n_write(afu, CXL_PSL_SERR_An, serr); + dev_info(&afu->dev, "Further such interrupts will be masked until the AFU is reset\n"); return IRQ_HANDLED; } @@ -1103,7 +1330,15 @@ int cxl_native_register_serr_irq(struct cxl_afu *afu) } serr = cxl_p1n_read(afu, CXL_PSL_SERR_An); - serr = (serr & 0x00ffffffffff0000ULL) | (afu->serr_hwirq & 0xffff); + if (cxl_is_power8()) + serr = (serr & 0x00ffffffffff0000ULL) | (afu->serr_hwirq & 0xffff); + if (cxl_is_power9()) { + /* + * By default, all errors are masked. So don't set all masks. + * Slice errors will be transfered. + */ + serr = (serr & ~0xff0000007fffffffULL) | (afu->serr_hwirq & 0xffff); + } cxl_p1n_write(afu, CXL_PSL_SERR_An, serr); return 0; diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index b27ea98b781f..6dc1ee5b92c9 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -60,7 +60,7 @@ #define CXL_VSEC_PROTOCOL_MASK 0xe0 #define CXL_VSEC_PROTOCOL_1024TB 0x80 #define CXL_VSEC_PROTOCOL_512TB 0x40 -#define CXL_VSEC_PROTOCOL_256TB 0x20 /* Power 8 uses this */ +#define CXL_VSEC_PROTOCOL_256TB 0x20 /* Power 8/9 uses this */ #define CXL_VSEC_PROTOCOL_ENABLE 0x01 #define CXL_READ_VSEC_PSL_REVISION(dev, vsec, dest) \ @@ -123,6 +123,8 @@ static const struct pci_device_id cxl_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), }, { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x04cf), }, { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0601), }, + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0623), }, + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0628), }, { PCI_DEVICE_CLASS(0x120000, ~0), }, { } @@ -324,38 +326,59 @@ static void dump_afu_descriptor(struct cxl_afu *afu) #undef show_reg } -#define CAPP_UNIT0_ID 0xBA -#define CAPP_UNIT1_ID 0XBE +#define P8_CAPP_UNIT0_ID 0xBA +#define P8_CAPP_UNIT1_ID 0XBE +#define P9_CAPP_UNIT0_ID 0xC0 +#define P9_CAPP_UNIT1_ID 0xE0 -static u64 get_capp_unit_id(struct device_node *np) +static int get_phb_index(struct device_node *np, u32 *phb_index) { - u32 phb_index; + if (of_property_read_u32(np, "ibm,phb-index", phb_index)) + return -ENODEV; + return 0; +} +static u64 get_capp_unit_id(struct device_node *np, u32 phb_index) +{ /* - * For chips other than POWER8NVL, we only have CAPP 0, - * irrespective of which PHB is used. + * POWER 8: + * - For chips other than POWER8NVL, we only have CAPP 0, + * irrespective of which PHB is used. + * - For POWER8NVL, assume CAPP 0 is attached to PHB0 and + * CAPP 1 is attached to PHB1. */ - if (!pvr_version_is(PVR_POWER8NVL)) - return CAPP_UNIT0_ID; + if (cxl_is_power8()) { + if (!pvr_version_is(PVR_POWER8NVL)) + return P8_CAPP_UNIT0_ID; + + if (phb_index == 0) + return P8_CAPP_UNIT0_ID; + + if (phb_index == 1) + return P8_CAPP_UNIT1_ID; + } /* - * For POWER8NVL, assume CAPP 0 is attached to PHB0 and - * CAPP 1 is attached to PHB1. + * POWER 9: + * PEC0 (PHB0). Capp ID = CAPP0 (0b1100_0000) + * PEC1 (PHB1 - PHB2). No capi mode + * PEC2 (PHB3 - PHB4 - PHB5): Capi mode on PHB3 only. Capp ID = CAPP1 (0b1110_0000) */ - if (of_property_read_u32(np, "ibm,phb-index", &phb_index)) - return 0; + if (cxl_is_power9()) { + if (phb_index == 0) + return P9_CAPP_UNIT0_ID; - if (phb_index == 0) - return CAPP_UNIT0_ID; - - if (phb_index == 1) - return CAPP_UNIT1_ID; + if (phb_index == 3) + return P9_CAPP_UNIT1_ID; + } return 0; } -static int calc_capp_routing(struct pci_dev *dev, u64 *chipid, u64 *capp_unit_id) +static int calc_capp_routing(struct pci_dev *dev, u64 *chipid, + u32 *phb_index, u64 *capp_unit_id) { + int rc; struct device_node *np; const __be32 *prop; @@ -366,8 +389,16 @@ static int calc_capp_routing(struct pci_dev *dev, u64 *chipid, u64 *capp_unit_id np = of_get_next_parent(np); if (!np) return -ENODEV; + *chipid = be32_to_cpup(prop); - *capp_unit_id = get_capp_unit_id(np); + + rc = get_phb_index(np, phb_index); + if (rc) { + pr_err("cxl: invalid phb index\n"); + return rc; + } + + *capp_unit_id = get_capp_unit_id(np, *phb_index); of_node_put(np); if (!*capp_unit_id) { pr_err("cxl: invalid capp unit id\n"); @@ -377,14 +408,104 @@ static int calc_capp_routing(struct pci_dev *dev, u64 *chipid, u64 *capp_unit_id return 0; } -static int init_implementation_adapter_psl_regs(struct cxl *adapter, struct pci_dev *dev) +static int init_implementation_adapter_regs_psl9(struct cxl *adapter, struct pci_dev *dev) +{ + u64 xsl_dsnctl, psl_fircntl; + u64 chipid; + u32 phb_index; + u64 capp_unit_id; + int rc; + + rc = calc_capp_routing(dev, &chipid, &phb_index, &capp_unit_id); + if (rc) + return rc; + + /* + * CAPI Identifier bits [0:7] + * bit 61:60 MSI bits --> 0 + * bit 59 TVT selector --> 0 + */ + + /* + * Tell XSL where to route data to. + * The field chipid should match the PHB CAPI_CMPM register + */ + xsl_dsnctl = ((u64)0x2 << (63-7)); /* Bit 57 */ + xsl_dsnctl |= (capp_unit_id << (63-15)); + + /* nMMU_ID Defaults to: b’000001001’*/ + xsl_dsnctl |= ((u64)0x09 << (63-28)); + + if (cxl_is_power9() && !cpu_has_feature(CPU_FTR_POWER9_DD1)) { + /* + * Used to identify CAPI packets which should be sorted into + * the Non-Blocking queues by the PHB. This field should match + * the PHB PBL_NBW_CMPM register + * nbwind=0x03, bits [57:58], must include capi indicator. + * Not supported on P9 DD1. + */ + xsl_dsnctl |= ((u64)0x03 << (63-47)); + + /* + * Upper 16b address bits of ASB_Notify messages sent to the + * system. Need to match the PHB’s ASN Compare/Mask Register. + * Not supported on P9 DD1. + */ + xsl_dsnctl |= ((u64)0x04 << (63-55)); + } + + cxl_p1_write(adapter, CXL_XSL9_DSNCTL, xsl_dsnctl); + + /* Set fir_cntl to recommended value for production env */ + psl_fircntl = (0x2ULL << (63-3)); /* ce_report */ + psl_fircntl |= (0x1ULL << (63-6)); /* FIR_report */ + psl_fircntl |= 0x1ULL; /* ce_thresh */ + cxl_p1_write(adapter, CXL_PSL9_FIR_CNTL, psl_fircntl); + + /* vccredits=0x1 pcklat=0x4 */ + cxl_p1_write(adapter, CXL_PSL9_DSNDCTL, 0x0000000000001810ULL); + + /* + * For debugging with trace arrays. + * Configure RX trace 0 segmented mode. + * Configure CT trace 0 segmented mode. + * Configure LA0 trace 0 segmented mode. + * Configure LA1 trace 0 segmented mode. + */ + cxl_p1_write(adapter, CXL_PSL9_TRACECFG, 0x8040800080000000ULL); + cxl_p1_write(adapter, CXL_PSL9_TRACECFG, 0x8040800080000003ULL); + cxl_p1_write(adapter, CXL_PSL9_TRACECFG, 0x8040800080000005ULL); + cxl_p1_write(adapter, CXL_PSL9_TRACECFG, 0x8040800080000006ULL); + + /* + * A response to an ASB_Notify request is returned by the + * system as an MMIO write to the address defined in + * the PSL_TNR_ADDR register + */ + /* PSL_TNR_ADDR */ + + /* NORST */ + cxl_p1_write(adapter, CXL_PSL9_DEBUG, 0x8000000000000000ULL); + + /* allocate the apc machines */ + cxl_p1_write(adapter, CXL_PSL9_APCDEDTYPE, 0x40000003FFFF0000ULL); + + /* Disable vc dd1 fix */ + if ((cxl_is_power9() && cpu_has_feature(CPU_FTR_POWER9_DD1))) + cxl_p1_write(adapter, CXL_PSL9_GP_CT, 0x0400000000000001ULL); + + return 0; +} + +static int init_implementation_adapter_regs_psl8(struct cxl *adapter, struct pci_dev *dev) { u64 psl_dsnctl, psl_fircntl; u64 chipid; + u32 phb_index; u64 capp_unit_id; int rc; - rc = calc_capp_routing(dev, &chipid, &capp_unit_id); + rc = calc_capp_routing(dev, &chipid, &phb_index, &capp_unit_id); if (rc) return rc; @@ -409,14 +530,15 @@ static int init_implementation_adapter_psl_regs(struct cxl *adapter, struct pci_ return 0; } -static int init_implementation_adapter_xsl_regs(struct cxl *adapter, struct pci_dev *dev) +static int init_implementation_adapter_regs_xsl(struct cxl *adapter, struct pci_dev *dev) { u64 xsl_dsnctl; u64 chipid; + u32 phb_index; u64 capp_unit_id; int rc; - rc = calc_capp_routing(dev, &chipid, &capp_unit_id); + rc = calc_capp_routing(dev, &chipid, &phb_index, &capp_unit_id); if (rc) return rc; @@ -434,7 +556,13 @@ static int init_implementation_adapter_xsl_regs(struct cxl *adapter, struct pci_ /* For the PSL this is a multiple for 0 < n <= 7: */ #define PSL_2048_250MHZ_CYCLES 1 -static void write_timebase_ctrl_psl(struct cxl *adapter) +static void write_timebase_ctrl_psl9(struct cxl *adapter) +{ + cxl_p1_write(adapter, CXL_PSL9_TB_CTLSTAT, + TBSYNC_CNT(2 * PSL_2048_250MHZ_CYCLES)); +} + +static void write_timebase_ctrl_psl8(struct cxl *adapter) { cxl_p1_write(adapter, CXL_PSL_TB_CTLSTAT, TBSYNC_CNT(2 * PSL_2048_250MHZ_CYCLES)); @@ -455,7 +583,12 @@ static void write_timebase_ctrl_xsl(struct cxl *adapter) TBSYNC_CNT(XSL_4000_CLOCKS)); } -static u64 timebase_read_psl(struct cxl *adapter) +static u64 timebase_read_psl9(struct cxl *adapter) +{ + return cxl_p1_read(adapter, CXL_PSL9_Timebase); +} + +static u64 timebase_read_psl8(struct cxl *adapter) { return cxl_p1_read(adapter, CXL_PSL_Timebase); } @@ -513,7 +646,12 @@ static void cxl_setup_psl_timebase(struct cxl *adapter, struct pci_dev *dev) return; } -static int init_implementation_afu_psl_regs(struct cxl_afu *afu) +static int init_implementation_afu_regs_psl9(struct cxl_afu *afu) +{ + return 0; +} + +static int init_implementation_afu_regs_psl8(struct cxl_afu *afu) { /* read/write masks for this slice */ cxl_p1n_write(afu, CXL_PSL_APCALLOC_A, 0xFFFFFFFEFEFEFEFEULL); @@ -611,7 +749,7 @@ static int setup_cxl_bars(struct pci_dev *dev) /* * BAR 4/5 has a special meaning for CXL and must be programmed with a * special value corresponding to the CXL protocol address range. - * For POWER 8 that means bits 48:49 must be set to 10 + * For POWER 8/9 that means bits 48:49 must be set to 10 */ pci_write_config_dword(dev, PCI_BASE_ADDRESS_4, 0x00000000); pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, 0x00020000); @@ -968,7 +1106,7 @@ static int cxl_afu_descriptor_looks_ok(struct cxl_afu *afu) } if (afu->pp_psa && (afu->pp_size < PAGE_SIZE)) - dev_warn(&afu->dev, "AFU uses < PAGE_SIZE per-process PSA!"); + dev_warn(&afu->dev, "AFU uses pp_size(%#016llx) < PAGE_SIZE per-process PSA!\n", afu->pp_size); for (i = 0; i < afu->crs_num; i++) { rc = cxl_ops->afu_cr_read32(afu, i, 0, &val); @@ -996,7 +1134,53 @@ static int cxl_afu_descriptor_looks_ok(struct cxl_afu *afu) return 0; } -static int sanitise_afu_regs(struct cxl_afu *afu) +static int sanitise_afu_regs_psl9(struct cxl_afu *afu) +{ + u64 reg; + + /* + * Clear out any regs that contain either an IVTE or address or may be + * waiting on an acknowledgment to try to be a bit safer as we bring + * it online + */ + reg = cxl_p2n_read(afu, CXL_AFU_Cntl_An); + if ((reg & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) { + dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#016llx\n", reg); + if (cxl_ops->afu_reset(afu)) + return -EIO; + if (cxl_afu_disable(afu)) + return -EIO; + if (cxl_psl_purge(afu)) + return -EIO; + } + cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0x0000000000000000); + cxl_p1n_write(afu, CXL_PSL_AMBAR_An, 0x0000000000000000); + reg = cxl_p2n_read(afu, CXL_PSL_DSISR_An); + if (reg) { + dev_warn(&afu->dev, "AFU had pending DSISR: %#016llx\n", reg); + if (reg & CXL_PSL9_DSISR_An_TF) + cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE); + else + cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A); + } + if (afu->adapter->native->sl_ops->register_serr_irq) { + reg = cxl_p1n_read(afu, CXL_PSL_SERR_An); + if (reg) { + if (reg & ~0x000000007fffffff) + dev_warn(&afu->dev, "AFU had pending SERR: %#016llx\n", reg); + cxl_p1n_write(afu, CXL_PSL_SERR_An, reg & ~0xffff); + } + } + reg = cxl_p2n_read(afu, CXL_PSL_ErrStat_An); + if (reg) { + dev_warn(&afu->dev, "AFU had pending error status: %#016llx\n", reg); + cxl_p2n_write(afu, CXL_PSL_ErrStat_An, reg); + } + + return 0; +} + +static int sanitise_afu_regs_psl8(struct cxl_afu *afu) { u64 reg; @@ -1102,8 +1286,11 @@ static int pci_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pc if ((rc = pci_map_slice_regs(afu, adapter, dev))) return rc; - if ((rc = sanitise_afu_regs(afu))) - goto err1; + if (adapter->native->sl_ops->sanitise_afu_regs) { + rc = adapter->native->sl_ops->sanitise_afu_regs(afu); + if (rc) + goto err1; + } /* We need to reset the AFU before we can read the AFU descriptor */ if ((rc = cxl_ops->afu_reset(afu))) @@ -1248,8 +1435,13 @@ int cxl_pci_reset(struct cxl *adapter) dev_info(&dev->dev, "CXL reset\n"); - /* the adapter is about to be reset, so ignore errors */ - cxl_data_cache_flush(adapter); + /* + * The adapter is about to be reset, so ignore errors. + * Not supported on P9 DD1 + */ + if ((cxl_is_power8()) || + ((cxl_is_power9() && !cpu_has_feature(CPU_FTR_POWER9_DD1)))) + cxl_data_cache_flush(adapter); /* pcie_warm_reset requests a fundamental pci reset which includes a * PERST assert/deassert. PERST triggers a loading of the image @@ -1332,6 +1524,7 @@ static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev) CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state); adapter->user_image_loaded = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); adapter->perst_select_user = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); + adapter->perst_loads_image = !!(image_state & CXL_VSEC_PERST_LOADS_IMAGE); CXL_READ_VSEC_NAFUS(dev, vsec, &adapter->slices); CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, &afu_desc_off); @@ -1378,6 +1571,17 @@ static void cxl_fixup_malformed_tlp(struct cxl *adapter, struct pci_dev *dev) pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, data); } +static bool cxl_compatible_caia_version(struct cxl *adapter) +{ + if (cxl_is_power8() && (adapter->caia_major == 1)) + return true; + + if (cxl_is_power9() && (adapter->caia_major == 2)) + return true; + + return false; +} + static int cxl_vsec_looks_ok(struct cxl *adapter, struct pci_dev *dev) { if (adapter->vsec_status & CXL_STATUS_SECOND_PORT) @@ -1388,6 +1592,12 @@ static int cxl_vsec_looks_ok(struct cxl *adapter, struct pci_dev *dev) return -EINVAL; } + if (!cxl_compatible_caia_version(adapter)) { + dev_info(&dev->dev, "Ignoring card. PSL type is not supported (caia version: %d)\n", + adapter->caia_major); + return -ENODEV; + } + if (!adapter->slices) { /* Once we support dynamic reprogramming we can use the card if * it supports loadable AFUs */ @@ -1431,9 +1641,19 @@ static void cxl_release_adapter(struct device *dev) static int sanitise_adapter_regs(struct cxl *adapter) { + int rc = 0; + /* Clear PSL tberror bit by writing 1 to it */ cxl_p1_write(adapter, CXL_PSL_ErrIVTE, CXL_PSL_ErrIVTE_tberror); - return cxl_tlb_slb_invalidate(adapter); + + if (adapter->native->sl_ops->invalidate_all) { + /* do not invalidate ERAT entries when not reloading on PERST */ + if (cxl_is_power9() && (adapter->perst_loads_image)) + return 0; + rc = adapter->native->sl_ops->invalidate_all(adapter); + } + + return rc; } /* This should contain *only* operations that can safely be done in @@ -1496,8 +1716,6 @@ static int cxl_configure_adapter(struct cxl *adapter, struct pci_dev *dev) if ((rc = cxl_native_register_psl_err_irq(adapter))) goto err; - /* Release the context lock as adapter is configured */ - cxl_adapter_context_unlock(adapter); return 0; err: @@ -1516,25 +1734,65 @@ static void cxl_deconfigure_adapter(struct cxl *adapter) pci_disable_device(pdev); } -static const struct cxl_service_layer_ops psl_ops = { - .adapter_regs_init = init_implementation_adapter_psl_regs, - .afu_regs_init = init_implementation_afu_psl_regs, +static const struct cxl_service_layer_ops psl9_ops = { + .adapter_regs_init = init_implementation_adapter_regs_psl9, + .invalidate_all = cxl_invalidate_all_psl9, + .afu_regs_init = init_implementation_afu_regs_psl9, + .sanitise_afu_regs = sanitise_afu_regs_psl9, .register_serr_irq = cxl_native_register_serr_irq, .release_serr_irq = cxl_native_release_serr_irq, - .debugfs_add_adapter_sl_regs = cxl_debugfs_add_adapter_psl_regs, - .debugfs_add_afu_sl_regs = cxl_debugfs_add_afu_psl_regs, - .psl_irq_dump_registers = cxl_native_psl_irq_dump_regs, + .handle_interrupt = cxl_irq_psl9, + .fail_irq = cxl_fail_irq_psl, + .activate_dedicated_process = cxl_activate_dedicated_process_psl9, + .attach_afu_directed = cxl_attach_afu_directed_psl9, + .attach_dedicated_process = cxl_attach_dedicated_process_psl9, + .update_dedicated_ivtes = cxl_update_dedicated_ivtes_psl9, + .debugfs_add_adapter_regs = cxl_debugfs_add_adapter_regs_psl9, + .debugfs_add_afu_regs = cxl_debugfs_add_afu_regs_psl9, + .psl_irq_dump_registers = cxl_native_irq_dump_regs_psl9, .err_irq_dump_registers = cxl_native_err_irq_dump_regs, - .debugfs_stop_trace = cxl_stop_trace, - .write_timebase_ctrl = write_timebase_ctrl_psl, - .timebase_read = timebase_read_psl, + .debugfs_stop_trace = cxl_stop_trace_psl9, + .write_timebase_ctrl = write_timebase_ctrl_psl9, + .timebase_read = timebase_read_psl9, + .capi_mode = OPAL_PHB_CAPI_MODE_CAPI, + .needs_reset_before_disable = true, +}; + +static const struct cxl_service_layer_ops psl8_ops = { + .adapter_regs_init = init_implementation_adapter_regs_psl8, + .invalidate_all = cxl_invalidate_all_psl8, + .afu_regs_init = init_implementation_afu_regs_psl8, + .sanitise_afu_regs = sanitise_afu_regs_psl8, + .register_serr_irq = cxl_native_register_serr_irq, + .release_serr_irq = cxl_native_release_serr_irq, + .handle_interrupt = cxl_irq_psl8, + .fail_irq = cxl_fail_irq_psl, + .activate_dedicated_process = cxl_activate_dedicated_process_psl8, + .attach_afu_directed = cxl_attach_afu_directed_psl8, + .attach_dedicated_process = cxl_attach_dedicated_process_psl8, + .update_dedicated_ivtes = cxl_update_dedicated_ivtes_psl8, + .debugfs_add_adapter_regs = cxl_debugfs_add_adapter_regs_psl8, + .debugfs_add_afu_regs = cxl_debugfs_add_afu_regs_psl8, + .psl_irq_dump_registers = cxl_native_irq_dump_regs_psl8, + .err_irq_dump_registers = cxl_native_err_irq_dump_regs, + .debugfs_stop_trace = cxl_stop_trace_psl8, + .write_timebase_ctrl = write_timebase_ctrl_psl8, + .timebase_read = timebase_read_psl8, .capi_mode = OPAL_PHB_CAPI_MODE_CAPI, .needs_reset_before_disable = true, }; static const struct cxl_service_layer_ops xsl_ops = { - .adapter_regs_init = init_implementation_adapter_xsl_regs, - .debugfs_add_adapter_sl_regs = cxl_debugfs_add_adapter_xsl_regs, + .adapter_regs_init = init_implementation_adapter_regs_xsl, + .invalidate_all = cxl_invalidate_all_psl8, + .sanitise_afu_regs = sanitise_afu_regs_psl8, + .handle_interrupt = cxl_irq_psl8, + .fail_irq = cxl_fail_irq_psl, + .activate_dedicated_process = cxl_activate_dedicated_process_psl8, + .attach_afu_directed = cxl_attach_afu_directed_psl8, + .attach_dedicated_process = cxl_attach_dedicated_process_psl8, + .update_dedicated_ivtes = cxl_update_dedicated_ivtes_psl8, + .debugfs_add_adapter_regs = cxl_debugfs_add_adapter_regs_xsl, .write_timebase_ctrl = write_timebase_ctrl_xsl, .timebase_read = timebase_read_xsl, .capi_mode = OPAL_PHB_CAPI_MODE_DMA, @@ -1548,8 +1806,13 @@ static void set_sl_ops(struct cxl *adapter, struct pci_dev *dev) adapter->native->sl_ops = &xsl_ops; adapter->min_pe = 1; /* Workaround for CX-4 hardware bug */ } else { - dev_info(&dev->dev, "Device uses a PSL\n"); - adapter->native->sl_ops = &psl_ops; + if (cxl_is_power8()) { + dev_info(&dev->dev, "Device uses a PSL8\n"); + adapter->native->sl_ops = &psl8_ops; + } else { + dev_info(&dev->dev, "Device uses a PSL9\n"); + adapter->native->sl_ops = &psl9_ops; + } } } @@ -1596,6 +1859,9 @@ static struct cxl *cxl_pci_init_adapter(struct pci_dev *dev) if ((rc = cxl_sysfs_adapter_add(adapter))) goto err_put1; + /* Release the context lock as adapter is configured */ + cxl_adapter_context_unlock(adapter); + return adapter; err_put1: @@ -1619,8 +1885,13 @@ static void cxl_pci_remove_adapter(struct cxl *adapter) cxl_sysfs_adapter_remove(adapter); cxl_debugfs_adapter_remove(adapter); - /* Flush adapter datacache as its about to be removed */ - cxl_data_cache_flush(adapter); + /* + * Flush adapter datacache as its about to be removed. + * Not supported on P9 DD1. + */ + if ((cxl_is_power8()) || + ((cxl_is_power9() && !cpu_has_feature(CPU_FTR_POWER9_DD1)))) + cxl_data_cache_flush(adapter); cxl_deconfigure_adapter(adapter); @@ -1704,6 +1975,11 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENODEV; } + if (cxl_is_power9() && !radix_enabled()) { + dev_info(&dev->dev, "Only Radix mode supported\n"); + return -ENODEV; + } + if (cxl_verbose) dump_cxl_config_space(dev); @@ -1781,7 +2057,7 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev, { struct cxl *adapter = pci_get_drvdata(pdev); struct cxl_afu *afu; - pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET; + pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET, afu_result; int i; /* At this point, we could still have an interrupt pending. @@ -1885,16 +2161,26 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev, for (i = 0; i < adapter->slices; i++) { afu = adapter->afu[i]; - result = cxl_vphb_error_detected(afu, state); - - /* Only continue if everyone agrees on NEED_RESET */ - if (result != PCI_ERS_RESULT_NEED_RESET) - return result; + afu_result = cxl_vphb_error_detected(afu, state); cxl_context_detach_all(afu); cxl_ops->afu_deactivate_mode(afu, afu->current_mode); pci_deconfigure_afu(afu); + + /* Disconnect trumps all, NONE trumps NEED_RESET */ + if (afu_result == PCI_ERS_RESULT_DISCONNECT) + result = PCI_ERS_RESULT_DISCONNECT; + else if ((afu_result == PCI_ERS_RESULT_NONE) && + (result == PCI_ERS_RESULT_NEED_RESET)) + result = PCI_ERS_RESULT_NONE; } + + /* should take the context lock here */ + if (cxl_adapter_context_lock(adapter) != 0) + dev_warn(&adapter->dev, + "Couldn't take context lock with %d active-contexts\n", + atomic_read(&adapter->contexts_num)); + cxl_deconfigure_adapter(adapter); return result; @@ -1913,6 +2199,13 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev) if (cxl_configure_adapter(adapter, pdev)) goto err; + /* + * Unlock context activation for the adapter. Ideally this should be + * done in cxl_pci_resume but cxlflash module tries to activate the + * master context as part of slot_reset callback. + */ + cxl_adapter_context_unlock(adapter); + for (i = 0; i < adapter->slices; i++) { afu = adapter->afu[i]; diff --git a/drivers/misc/cxl/trace.h b/drivers/misc/cxl/trace.h index 751d6119683e..b8e300af0e55 100644 --- a/drivers/misc/cxl/trace.h +++ b/drivers/misc/cxl/trace.h @@ -17,6 +17,15 @@ #include "cxl.h" +#define dsisr_psl9_flags(flags) \ + __print_flags(flags, "|", \ + { CXL_PSL9_DSISR_An_CO_MASK, "FR" }, \ + { CXL_PSL9_DSISR_An_TF, "TF" }, \ + { CXL_PSL9_DSISR_An_PE, "PE" }, \ + { CXL_PSL9_DSISR_An_AE, "AE" }, \ + { CXL_PSL9_DSISR_An_OC, "OC" }, \ + { CXL_PSL9_DSISR_An_S, "S" }) + #define DSISR_FLAGS \ { CXL_PSL_DSISR_An_DS, "DS" }, \ { CXL_PSL_DSISR_An_DM, "DM" }, \ @@ -154,6 +163,40 @@ TRACE_EVENT(cxl_afu_irq, ) ); +TRACE_EVENT(cxl_psl9_irq, + TP_PROTO(struct cxl_context *ctx, int irq, u64 dsisr, u64 dar), + + TP_ARGS(ctx, irq, dsisr, dar), + + TP_STRUCT__entry( + __field(u8, card) + __field(u8, afu) + __field(u16, pe) + __field(int, irq) + __field(u64, dsisr) + __field(u64, dar) + ), + + TP_fast_assign( + __entry->card = ctx->afu->adapter->adapter_num; + __entry->afu = ctx->afu->slice; + __entry->pe = ctx->pe; + __entry->irq = irq; + __entry->dsisr = dsisr; + __entry->dar = dar; + ), + + TP_printk("afu%i.%i pe=%i irq=%i dsisr=0x%016llx dsisr=%s dar=0x%016llx", + __entry->card, + __entry->afu, + __entry->pe, + __entry->irq, + __entry->dsisr, + dsisr_psl9_flags(__entry->dsisr), + __entry->dar + ) +); + TRACE_EVENT(cxl_psl_irq, TP_PROTO(struct cxl_context *ctx, int irq, u64 dsisr, u64 dar), diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index c7112276a039..28bb495f0cf1 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -227,9 +227,16 @@ static const struct i2c_device_id ds1682_id[] = { }; MODULE_DEVICE_TABLE(i2c, ds1682_id); +static const struct of_device_id ds1682_of_match[] = { + { .compatible = "dallas,ds1682", }, + {} +}; +MODULE_DEVICE_TABLE(of, ds1682_of_match); + static struct i2c_driver ds1682_driver = { .driver = { .name = "ds1682", + .of_match_table = ds1682_of_match, }, .probe = ds1682_probe, .remove = ds1682_remove, diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 4a22a1d99395..ab0df6a17690 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1541,12 +1541,69 @@ static const struct i2c_device_id idt_ids[] = { }; MODULE_DEVICE_TABLE(i2c, idt_ids); +static const struct of_device_id idt_of_match[] = { + { .compatible = "idt,89hpes8nt2", }, + { .compatible = "idt,89hpes12nt3", }, + + { .compatible = "idt,89hpes24nt6ag2", }, + { .compatible = "idt,89hpes32nt8ag2", }, + { .compatible = "idt,89hpes32nt8bg2", }, + { .compatible = "idt,89hpes12nt12g2", }, + { .compatible = "idt,89hpes16nt16g2", }, + { .compatible = "idt,89hpes24nt24g2", }, + { .compatible = "idt,89hpes32nt24ag2", }, + { .compatible = "idt,89hpes32nt24bg2", }, + + { .compatible = "idt,89hpes12n3", }, + { .compatible = "idt,89hpes12n3a", }, + { .compatible = "idt,89hpes24n3", }, + { .compatible = "idt,89hpes24n3a", }, + + { .compatible = "idt,89hpes32h8", }, + { .compatible = "idt,89hpes32h8g2", }, + { .compatible = "idt,89hpes48h12", }, + { .compatible = "idt,89hpes48h12g2", }, + { .compatible = "idt,89hpes48h12ag2", }, + { .compatible = "idt,89hpes16h16", }, + { .compatible = "idt,89hpes22h16", }, + { .compatible = "idt,89hpes22h16g2", }, + { .compatible = "idt,89hpes34h16", }, + { .compatible = "idt,89hpes34h16g2", }, + { .compatible = "idt,89hpes64h16", }, + { .compatible = "idt,89hpes64h16g2", }, + { .compatible = "idt,89hpes64h16ag2", }, + + { .compatible = "idt,89hpes12t3g2", }, + { .compatible = "idt,89hpes24t3g2", }, + + { .compatible = "idt,89hpes16t4", }, + { .compatible = "idt,89hpes4t4g2", }, + { .compatible = "idt,89hpes10t4g2", }, + { .compatible = "idt,89hpes16t4g2", }, + { .compatible = "idt,89hpes16t4ag2", }, + { .compatible = "idt,89hpes5t5", }, + { .compatible = "idt,89hpes6t5", }, + { .compatible = "idt,89hpes8t5", }, + { .compatible = "idt,89hpes8t5a", }, + { .compatible = "idt,89hpes24t6", }, + { .compatible = "idt,89hpes6t6g2", }, + { .compatible = "idt,89hpes24t6g2", }, + { .compatible = "idt,89hpes16t7", }, + { .compatible = "idt,89hpes32t8", }, + { .compatible = "idt,89hpes32t8g2", }, + { .compatible = "idt,89hpes48t12", }, + { .compatible = "idt,89hpes48t12g2", }, + { }, +}; +MODULE_DEVICE_TABLE(of, idt_of_match); + /* * idt_driver - IDT 89HPESx driver structure */ static struct i2c_driver idt_driver = { .driver = { .name = IDT_NAME, + .of_match_table = idt_of_match, }, .probe = idt_probe, .remove = idt_remove, diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index 67d27be60405..3b4976396ec4 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h @@ -27,6 +27,7 @@ void lkdtm_REFCOUNT_ZERO_SUB(void); void lkdtm_REFCOUNT_ZERO_ADD(void); void lkdtm_CORRUPT_LIST_ADD(void); void lkdtm_CORRUPT_LIST_DEL(void); +void lkdtm_CORRUPT_USER_DS(void); /* lkdtm_heap.c */ void lkdtm_OVERWRITE_ALLOCATION(void); diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index e3f4cd8876b5..d9028ef50fbe 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -8,6 +8,8 @@ #include <linux/list.h> #include <linux/refcount.h> #include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/uaccess.h> struct lkdtm_list { struct list_head node; @@ -67,7 +69,7 @@ void lkdtm_WARNING(void) void lkdtm_EXCEPTION(void) { - *((int *) 0) = 0; + *((volatile int *) 0) = 0; } void lkdtm_LOOP(void) @@ -279,3 +281,12 @@ void lkdtm_CORRUPT_LIST_DEL(void) else pr_err("list_del() corruption not detected!\n"); } + +void lkdtm_CORRUPT_USER_DS(void) +{ + pr_info("setting bad task size limit\n"); + set_fs(KERNEL_DS); + + /* Make sure we do not keep running with a KERNEL_DS! */ + force_sig(SIGKILL, current); +} diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index b9a4cd4a9b68..42d2b8e31e6b 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -199,6 +199,7 @@ struct crashtype crashtypes[] = { CRASHTYPE(OVERFLOW), CRASHTYPE(CORRUPT_LIST_ADD), CRASHTYPE(CORRUPT_LIST_DEL), + CRASHTYPE(CORRUPT_USER_DS), CRASHTYPE(CORRUPT_STACK), CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(OVERWRITE_ALLOCATION), 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 <linux/kernel.h> -#include <linux/fs.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/ioctl.h> -#include <linux/cdev.h> -#include <linux/list.h> -#include <linux/delay.h> -#include <linux/sched.h> -#include <linux/uuid.h> -#include <linux/jiffies.h> -#include <linux/uaccess.h> -#include <linux/slab.h> - -#include <linux/mei.h> - -#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-fixup.c b/drivers/misc/mei/bus-fixup.c index 29f2daed37e0..0208c4b027c5 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -110,12 +110,13 @@ struct mkhi_msg { u8 data[0]; } __packed; +#define MKHI_OSVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \ + sizeof(struct mkhi_fwcaps) + \ + sizeof(struct mei_os_ver)) static int mei_osver(struct mei_cl_device *cldev) { - const size_t size = sizeof(struct mkhi_msg_hdr) + - sizeof(struct mkhi_fwcaps) + - sizeof(struct mei_os_ver); - char buf[size]; + const size_t size = MKHI_OSVER_BUF_LEN; + char buf[MKHI_OSVER_BUF_LEN]; struct mkhi_msg *req; struct mkhi_fwcaps *fwcaps; struct mei_os_ver *os_ver; 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 struct file *fp); /* * MEI Host Client Functions */ struct mei_cl *mei_cl_allocate(struct mei_device *dev); -void mei_cl_init(struct mei_cl *cl, struct mei_device *dev); - int mei_cl_link(struct mei_cl *cl); int mei_cl_unlink(struct mei_cl *cl); @@ -205,8 +202,6 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list); int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp); -int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, - struct list_head *cmpl_list); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index ba3a774c8d71..fe6595fe94f1 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -166,9 +166,8 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len) * * Return: 0 on success, <0 on failure. */ -static inline -int mei_hbm_cl_write(struct mei_device *dev, struct mei_cl *cl, - u8 hbm_cmd, u8 *buf, size_t len) +static inline int mei_hbm_cl_write(struct mei_device *dev, struct mei_cl *cl, + u8 hbm_cmd, void *buf, size_t len) { struct mei_msg_hdr mei_hdr; @@ -632,11 +631,11 @@ static int mei_hbm_stop_req(struct mei_device *dev) */ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) { - const size_t len = sizeof(struct hbm_flow_control); - u8 buf[len]; + struct hbm_flow_control req; cl_dbg(dev, cl, "sending flow control\n"); - return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, buf, len); + return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, + &req, sizeof(req)); } /** @@ -710,10 +709,10 @@ static void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev, */ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) { - const size_t len = sizeof(struct hbm_client_connect_request); - u8 buf[len]; + struct hbm_client_connect_request req; - return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, buf, len); + return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, + &req, sizeof(req)); } /** @@ -726,10 +725,10 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) */ int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl) { - const size_t len = sizeof(struct hbm_client_connect_response); - u8 buf[len]; + struct hbm_client_connect_response resp; - return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, buf, len); + return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, + &resp, sizeof(resp)); } /** @@ -763,10 +762,10 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct mei_cl *cl, */ int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl) { - const size_t len = sizeof(struct hbm_client_connect_request); - u8 buf[len]; + struct hbm_client_connect_request req; - return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, buf, len); + return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, + &req, sizeof(req)); } /** diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 13c55b8f9261..c8ad9ee7cb80 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -146,18 +146,9 @@ int mei_reset(struct mei_device *dev) /* fall through and remove the sw state even if hw reset has failed */ /* no need to clean up software state in case of power up */ - if (state != MEI_DEV_INITIALIZING && - state != MEI_DEV_POWER_UP) { - - /* remove all waiting requests */ + if (state != MEI_DEV_INITIALIZING && state != MEI_DEV_POWER_UP) mei_cl_all_disconnect(dev); - /* remove entry if already in list */ - dev_dbg(dev->dev, "remove iamthif from the file list.\n"); - mei_cl_unlink(&dev->iamthif_cl); - mei_amthif_reset_params(dev); - } - mei_hbm_reset(dev); dev->rd_msg_hdr = 0; @@ -401,9 +392,6 @@ void mei_device_init(struct mei_device *dev, INIT_WORK(&dev->reset_work, mei_reset_work); INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); - INIT_LIST_HEAD(&dev->iamthif_cl.link); - INIT_LIST_HEAD(&dev->amthif_cmd_list); - bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); dev->open_handle_count = 0; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 406e9e2b2fff..c14e35201721 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -47,10 +47,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list) list_del_init(&cb->list); dev_dbg(dev->dev, "completing call back.\n"); - if (cl == &dev->iamthif_cl) - mei_amthif_complete(cl, cb); - else - mei_cl_complete(cl, cb); + mei_cl_complete(cl, cb); } } EXPORT_SYMBOL_GPL(mei_irq_compl_handler); @@ -76,7 +73,7 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl, * @dev: mei device * @hdr: message header */ -void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) +static void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) { /* * no need to check for size as it is guarantied @@ -96,9 +93,9 @@ void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) * * Return: always 0 */ -int mei_cl_irq_read_msg(struct mei_cl *cl, - struct mei_msg_hdr *mei_hdr, - struct list_head *cmpl_list) +static int mei_cl_irq_read_msg(struct mei_cl *cl, + struct mei_msg_hdr *mei_hdr, + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; @@ -313,11 +310,7 @@ int mei_irq_read_handler(struct mei_device *dev, goto end; } - if (cl == &dev->iamthif_cl) { - ret = mei_amthif_irq_read_msg(cl, mei_hdr, cmpl_list); - } else { - ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); - } + ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); reset_slots: @@ -423,10 +416,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list) dev_dbg(dev->dev, "complete write list cb.\n"); list_for_each_entry_safe(cb, next, &dev->write_list, list) { cl = cb->cl; - if (cl == &dev->iamthif_cl) - ret = mei_amthif_irq_write(cl, cb, cmpl_list); - else - ret = mei_cl_irq_write(cl, cb, cmpl_list); + ret = mei_cl_irq_write(cl, cb, cmpl_list); if (ret) return ret; } @@ -512,20 +502,6 @@ void mei_timer(struct work_struct *work) } } - if (!mei_cl_is_connected(&dev->iamthif_cl)) - goto out; - - if (dev->iamthif_stall_timer) { - if (--dev->iamthif_stall_timer == 0) { - dev_err(dev->dev, "timer: amthif hanged.\n"); - mei_reset(dev); - - mei_amthif_run_next_cmd(dev); - goto out; - } - reschedule_timer = true; - } - out: if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer) mei_schedule_stall_timer(dev); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index bf816449cd40..e825f013e54e 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -103,10 +103,7 @@ static int mei_release(struct inode *inode, struct file *file) dev = cl->dev; mutex_lock(&dev->device_lock); - if (cl == &dev->iamthif_cl) { - rets = mei_amthif_release(dev, file); - goto out; - } + rets = mei_cl_disconnect(cl); mei_cl_flush_queues(cl, file); @@ -117,7 +114,7 @@ static int mei_release(struct inode *inode, struct file *file) file->private_data = NULL; kfree(cl); -out: + mutex_unlock(&dev->device_lock); return rets; } @@ -182,8 +179,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - -again: mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, !list_empty(&cl->rd_completed) || @@ -201,14 +196,6 @@ again: cb = mei_cl_read_cb(cl, file); if (!cb) { - /* - * For amthif all the waiters are woken up, - * but only fp with matching cb->fp get the cb, - * the others have to return to wait on read. - */ - if (cl == &dev->iamthif_cl) - goto again; - rets = 0; goto out; } @@ -319,13 +306,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - if (cl == &dev->iamthif_cl) { - rets = mei_amthif_write(cl, cb); - if (!rets) - rets = length; - goto out; - } - rets = mei_cl_write(cl, cb); out: mutex_unlock(&dev->device_lock); @@ -388,30 +368,6 @@ static int mei_ioctl_connect_client(struct file *file, dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n", me_cl->props.max_msg_length); - /* if we're connecting to amthif client then we will use the - * existing connection - */ - if (uuid_le_cmp(data->in_client_uuid, mei_amthif_guid) == 0) { - dev_dbg(dev->dev, "FW Client is amthi\n"); - if (!mei_cl_is_connected(&dev->iamthif_cl)) { - rets = -ENODEV; - goto end; - } - mei_cl_unlink(cl); - - kfree(cl); - cl = NULL; - dev->iamthif_open_count++; - file->private_data = &dev->iamthif_cl; - - client = &data->out_client_properties; - client->max_msg_length = me_cl->props.max_msg_length; - client->protocol_version = me_cl->props.protocol_version; - rets = dev->iamthif_cl.status; - - goto end; - } - /* prepare the output buffer */ client = &data->out_client_properties; client->max_msg_length = me_cl->props.max_msg_length; @@ -615,11 +571,6 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) mask |= POLLPRI; } - if (cl == &dev->iamthif_cl) { - mask |= mei_amthif_poll(file, wait); - goto out; - } - if (req_events & (POLLIN | POLLRDNORM)) { poll_wait(file, &cl->rx_wait, wait); @@ -635,6 +586,77 @@ out: } /** + * mei_cl_is_write_queued - check if the client has pending writes. + * + * @cl: writing host client + * + * Return: true if client is writing, false otherwise. + */ +static bool mei_cl_is_write_queued(struct mei_cl *cl) +{ + struct mei_device *dev = cl->dev; + struct mei_cl_cb *cb; + + list_for_each_entry(cb, &dev->write_list, list) + if (cb->cl == cl) + return true; + list_for_each_entry(cb, &dev->write_waiting_list, list) + if (cb->cl == cl) + return true; + return false; +} + +/** + * mei_fsync - the fsync handler + * + * @fp: pointer to file structure + * @start: unused + * @end: unused + * @datasync: unused + * + * Return: 0 on success, -ENODEV if client is not connected + */ +static int mei_fsync(struct file *fp, loff_t start, loff_t end, int datasync) +{ + struct mei_cl *cl = fp->private_data; + struct mei_device *dev; + int rets; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + mutex_lock(&dev->device_lock); + + if (dev->dev_state != MEI_DEV_ENABLED || !mei_cl_is_connected(cl)) { + rets = -ENODEV; + goto out; + } + + while (mei_cl_is_write_queued(cl)) { + mutex_unlock(&dev->device_lock); + rets = wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE || + !mei_cl_is_connected(cl)); + mutex_lock(&dev->device_lock); + if (rets) { + if (signal_pending(current)) + rets = -EINTR; + goto out; + } + if (!mei_cl_is_connected(cl)) { + rets = -ENODEV; + goto out; + } + } + rets = 0; +out: + mutex_unlock(&dev->device_lock); + return rets; +} + +/** * mei_fasync - asynchronous io support * * @fd: file descriptor @@ -749,6 +771,7 @@ static const struct file_operations mei_fops = { .release = mei_release, .write = mei_write, .poll = mei_poll, + .fsync = mei_fsync, .fasync = mei_fasync, .llseek = no_llseek }; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index d41aac53a2ac..63a67c99fc78 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -26,12 +26,6 @@ #include "hw.h" #include "hbm.h" - -/* - * AMTHI Client UUID - */ -extern const uuid_le mei_amthif_guid; - #define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) /* @@ -78,12 +72,6 @@ enum mei_dev_state { const char *mei_dev_state_str(int state); -enum iamthif_states { - MEI_IAMTHIF_IDLE, - MEI_IAMTHIF_WRITING, - MEI_IAMTHIF_READING, -}; - enum mei_file_transaction_states { MEI_IDLE, MEI_WRITING, @@ -418,13 +406,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @allow_fixed_address: allow user space to connect a fixed client * @override_fixed_address: force allow fixed address behavior * - * @amthif_cmd_list : amthif list for cmd waiting - * @iamthif_cl : amthif host client - * @iamthif_open_count : number of opened amthif connections - * @iamthif_stall_timer : timer to detect amthif hang - * @iamthif_state : amthif processor state - * @iamthif_canceled : current amthif command is canceled - * * @reset_work : work item for the device reset * @bus_rescan_work : work item for the bus rescan * @@ -500,14 +481,6 @@ struct mei_device { bool allow_fixed_address; bool override_fixed_address; - /* amthif list for cmd waiting */ - struct list_head amthif_cmd_list; - struct mei_cl iamthif_cl; - long iamthif_open_count; - u32 iamthif_stall_timer; - enum iamthif_states iamthif_state; - bool iamthif_canceled; - struct work_struct reset_work; struct work_struct bus_rescan_work; @@ -579,28 +552,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list); void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list); /* - * AMTHIF - AMT Host Interface Functions - */ -void mei_amthif_reset_params(struct mei_device *dev); - -int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl); - -unsigned int mei_amthif_poll(struct file *file, poll_table *wait); - -int mei_amthif_release(struct mei_device *dev, struct file *file); - -int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); -int mei_amthif_run_next_cmd(struct mei_device *dev); -int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct list_head *cmpl_list); - -void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb); -int mei_amthif_irq_read_msg(struct mei_cl *cl, - struct mei_msg_hdr *mei_hdr, - struct list_head *cmpl_list); -int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); - -/* * Register Access Function */ @@ -711,8 +662,6 @@ bool mei_hbuf_acquire(struct mei_device *dev); bool mei_write_is_idle(struct mei_device *dev); -void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr); - #if IS_ENABLED(CONFIG_DEBUG_FS) int mei_dbgfs_register(struct mei_device *dev, const char *name); void mei_dbgfs_deregister(struct mei_device *dev); 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 @@ -242,11 +242,38 @@ end: } /** + * 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 @@ -161,6 +161,33 @@ end: } /** + * 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 * * @pdev: PCI device structure @@ -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, }; diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c deleted file mode 100644 index ef2ece0f26af..000000000000 --- a/drivers/misc/panel.c +++ /dev/null @@ -1,2439 +0,0 @@ -/* - * Front panel driver for Linux - * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu> - * - * 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. - * - * This code drives an LCD module (/dev/lcd), and a keypad (/dev/keypad) - * connected to a parallel printer port. - * - * The LCD module may either be an HD44780-like 8-bit parallel LCD, or a 1-bit - * serial module compatible with Samsung's KS0074. The pins may be connected in - * any combination, everything is programmable. - * - * The keypad consists in a matrix of push buttons connecting input pins to - * data output pins or to the ground. The combinations have to be hard-coded - * in the driver, though several profiles exist and adding new ones is easy. - * - * Several profiles are provided for commonly found LCD+keypad modules on the - * market, such as those found in Nexcom's appliances. - * - * FIXME: - * - the initialization/deinitialization process is very dirty and should - * be rewritten. It may even be buggy. - * - * TODO: - * - document 24 keys keyboard (3 rows of 8 cols, 32 diodes + 2 inputs) - * - make the LCD a part of a virtual screen of Vx*Vy - * - make the inputs list smp-safe - * - change the keyboard to a double mapping : signals -> key_id -> values - * so that applications can change values without knowing signals - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> -#include <linux/miscdevice.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/ctype.h> -#include <linux/parport.h> -#include <linux/list.h> -#include <linux/notifier.h> -#include <linux/reboot.h> -#include <linux/workqueue.h> -#include <generated/utsrelease.h> - -#include <linux/io.h> -#include <linux/uaccess.h> - -#define LCD_MINOR 156 -#define KEYPAD_MINOR 185 - -#define LCD_MAXBYTES 256 /* max burst write */ - -#define KEYPAD_BUFFER 64 - -/* poll the keyboard this every second */ -#define INPUT_POLL_TIME (HZ / 50) -/* a key starts to repeat after this times INPUT_POLL_TIME */ -#define KEYPAD_REP_START (10) -/* 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) - -#define PNL_PBUSY 0x80 /* inverted input, active low */ -#define PNL_PACK 0x40 /* direct input, active low */ -#define PNL_POUTPA 0x20 /* direct input, active high */ -#define PNL_PSELECD 0x10 /* direct input, active high */ -#define PNL_PERRORP 0x08 /* direct input, active low */ - -#define PNL_PBIDIR 0x20 /* bi-directional ports */ -/* high to read data in or-ed with data out */ -#define PNL_PINTEN 0x10 -#define PNL_PSELECP 0x08 /* inverted output, active low */ -#define PNL_PINITP 0x04 /* direct output, active low */ -#define PNL_PAUTOLF 0x02 /* inverted output, active low */ -#define PNL_PSTROBE 0x01 /* inverted output */ - -#define PNL_PD0 0x01 -#define PNL_PD1 0x02 -#define PNL_PD2 0x04 -#define PNL_PD3 0x08 -#define PNL_PD4 0x10 -#define PNL_PD5 0x20 -#define PNL_PD6 0x40 -#define PNL_PD7 0x80 - -#define PIN_NONE 0 -#define PIN_STROBE 1 -#define PIN_D0 2 -#define PIN_D1 3 -#define PIN_D2 4 -#define PIN_D3 5 -#define PIN_D4 6 -#define PIN_D5 7 -#define PIN_D6 8 -#define PIN_D7 9 -#define PIN_AUTOLF 14 -#define PIN_INITP 16 -#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 */ -#define r_ctr(x) (parport_read_control((x)->port)) -#define r_dtr(x) (parport_read_data((x)->port)) -#define r_str(x) (parport_read_status((x)->port)) -#define w_ctr(x, y) (parport_write_control((x)->port, (y))) -#define w_dtr(x, y) (parport_write_data((x)->port, (y))) - -/* this defines which bits are to be used and which ones to be ignored */ -/* logical or of the output bits involved in the scan matrix */ -static __u8 scan_mask_o; -/* logical or of the input bits involved in the scan matrix */ -static __u8 scan_mask_i; - -enum input_type { - INPUT_TYPE_STD, - INPUT_TYPE_KBD, -}; - -enum input_state { - INPUT_ST_LOW, - INPUT_ST_RISING, - INPUT_ST_HIGH, - INPUT_ST_FALLING, -}; - -struct logical_input { - struct list_head list; - __u64 mask; - __u64 value; - enum input_type type; - enum input_state state; - __u8 rise_time, fall_time; - __u8 rise_timer, fall_timer, high_timer; - - union { - struct { /* valid when type == INPUT_TYPE_STD */ - void (*press_fct)(int); - void (*release_fct)(int); - int press_data; - int release_data; - } std; - struct { /* valid when type == INPUT_TYPE_KBD */ - /* strings can be non null-terminated */ - char press_str[sizeof(void *) + sizeof(int)]; - char repeat_str[sizeof(void *) + sizeof(int)]; - char release_str[sizeof(void *) + sizeof(int)]; - } kbd; - } u; -}; - -static LIST_HEAD(logical_inputs); /* list of all defined logical inputs */ - -/* physical contacts history - * Physical contacts are a 45 bits string of 9 groups of 5 bits each. - * The 8 lower groups correspond to output bits 0 to 7, and the 9th group - * corresponds to the ground. - * Within each group, bits are stored in the same order as read on the port : - * BAPSE (busy=4, ack=3, paper empty=2, select=1, error=0). - * So, each __u64 is represented like this : - * 0000000000000000000BAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSE - * <-----unused------><gnd><d07><d06><d05><d04><d03><d02><d01><d00> - */ - -/* what has just been read from the I/O ports */ -static __u64 phys_read; -/* previous phys_read */ -static __u64 phys_read_prev; -/* stabilized phys_read (phys_read|phys_read_prev) */ -static __u64 phys_curr; -/* previous phys_curr */ -static __u64 phys_prev; -/* 0 means that at least one logical signal needs be computed */ -static char inputs_stable; - -/* these variables are specific to the keypad */ -static struct { - bool enabled; -} keypad; - -static char keypad_buffer[KEYPAD_BUFFER]; -static int keypad_buflen; -static int keypad_start; -static char keypressed; -static wait_queue_head_t keypad_read_wait; - -/* lcd-specific variables */ -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; - int rs; - int rw; - int cl; - int da; - 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; -} lcd; - -/* Needed only for init */ -static int selected_lcd_type = NOT_SET; - -/* - * Bit masks to convert LCD signals to parallel port outputs. - * _d_ are values for data port, _c_ are for control port. - * [0] = signal OFF, [1] = signal ON, [2] = mask - */ -#define BIT_CLR 0 -#define BIT_SET 1 -#define BIT_MSK 2 -#define BIT_STATES 3 -/* - * one entry for each bit on the LCD - */ -#define LCD_BIT_E 0 -#define LCD_BIT_RS 1 -#define LCD_BIT_RW 2 -#define LCD_BIT_BL 3 -#define LCD_BIT_CL 4 -#define LCD_BIT_DA 5 -#define LCD_BITS 6 - -/* - * each bit can be either connected to a DATA or CTRL port - */ -#define LCD_PORT_C 0 -#define LCD_PORT_D 1 -#define LCD_PORTS 2 - -static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES]; - -/* - * LCD protocols - */ -#define LCD_PROTO_PARALLEL 0 -#define LCD_PROTO_SERIAL 1 -#define LCD_PROTO_TI_DA8XX_LCD 2 - -/* - * LCD character sets - */ -#define LCD_CHARSET_NORMAL 0 -#define LCD_CHARSET_KS0074 1 - -/* - * LCD types - */ -#define LCD_TYPE_NONE 0 -#define LCD_TYPE_CUSTOM 1 -#define LCD_TYPE_OLD 2 -#define LCD_TYPE_KS0074 3 -#define LCD_TYPE_HANTRONIX 4 -#define LCD_TYPE_NEXCOM 5 - -/* - * keypad types - */ -#define KEYPAD_TYPE_NONE 0 -#define KEYPAD_TYPE_OLD 1 -#define KEYPAD_TYPE_NEW 2 -#define KEYPAD_TYPE_NEXCOM 3 - -/* - * panel profiles - */ -#define PANEL_PROFILE_CUSTOM 0 -#define PANEL_PROFILE_OLD 1 -#define PANEL_PROFILE_NEW 2 -#define PANEL_PROFILE_HANTRONIX 3 -#define PANEL_PROFILE_NEXCOM 4 -#define PANEL_PROFILE_LARGE 5 - -/* - * Construct custom config from the kernel's configuration - */ -#define DEFAULT_PARPORT 0 -#define DEFAULT_PROFILE PANEL_PROFILE_LARGE -#define DEFAULT_KEYPAD_TYPE KEYPAD_TYPE_OLD -#define DEFAULT_LCD_TYPE LCD_TYPE_OLD -#define DEFAULT_LCD_HEIGHT 2 -#define DEFAULT_LCD_WIDTH 40 -#define DEFAULT_LCD_BWIDTH 40 -#define DEFAULT_LCD_HWIDTH 64 -#define DEFAULT_LCD_CHARSET LCD_CHARSET_NORMAL -#define DEFAULT_LCD_PROTO LCD_PROTO_PARALLEL - -#define DEFAULT_LCD_PIN_E PIN_AUTOLF -#define DEFAULT_LCD_PIN_RS PIN_SELECP -#define DEFAULT_LCD_PIN_RW PIN_INITP -#define DEFAULT_LCD_PIN_SCL PIN_STROBE -#define DEFAULT_LCD_PIN_SDA PIN_D0 -#define DEFAULT_LCD_PIN_BL PIN_NOT_SET - -#ifdef CONFIG_PANEL_PARPORT -#undef DEFAULT_PARPORT -#define DEFAULT_PARPORT CONFIG_PANEL_PARPORT -#endif - -#ifdef CONFIG_PANEL_PROFILE -#undef DEFAULT_PROFILE -#define DEFAULT_PROFILE CONFIG_PANEL_PROFILE -#endif - -#if DEFAULT_PROFILE == 0 /* custom */ -#ifdef CONFIG_PANEL_KEYPAD -#undef DEFAULT_KEYPAD_TYPE -#define DEFAULT_KEYPAD_TYPE CONFIG_PANEL_KEYPAD -#endif - -#ifdef CONFIG_PANEL_LCD -#undef DEFAULT_LCD_TYPE -#define DEFAULT_LCD_TYPE CONFIG_PANEL_LCD -#endif - -#ifdef CONFIG_PANEL_LCD_HEIGHT -#undef DEFAULT_LCD_HEIGHT -#define DEFAULT_LCD_HEIGHT CONFIG_PANEL_LCD_HEIGHT -#endif - -#ifdef CONFIG_PANEL_LCD_WIDTH -#undef DEFAULT_LCD_WIDTH -#define DEFAULT_LCD_WIDTH CONFIG_PANEL_LCD_WIDTH -#endif - -#ifdef CONFIG_PANEL_LCD_BWIDTH -#undef DEFAULT_LCD_BWIDTH -#define DEFAULT_LCD_BWIDTH CONFIG_PANEL_LCD_BWIDTH -#endif - -#ifdef CONFIG_PANEL_LCD_HWIDTH -#undef DEFAULT_LCD_HWIDTH -#define DEFAULT_LCD_HWIDTH CONFIG_PANEL_LCD_HWIDTH -#endif - -#ifdef CONFIG_PANEL_LCD_CHARSET -#undef DEFAULT_LCD_CHARSET -#define DEFAULT_LCD_CHARSET CONFIG_PANEL_LCD_CHARSET -#endif - -#ifdef CONFIG_PANEL_LCD_PROTO -#undef DEFAULT_LCD_PROTO -#define DEFAULT_LCD_PROTO CONFIG_PANEL_LCD_PROTO -#endif - -#ifdef CONFIG_PANEL_LCD_PIN_E -#undef DEFAULT_LCD_PIN_E -#define DEFAULT_LCD_PIN_E CONFIG_PANEL_LCD_PIN_E -#endif - -#ifdef CONFIG_PANEL_LCD_PIN_RS -#undef DEFAULT_LCD_PIN_RS -#define DEFAULT_LCD_PIN_RS CONFIG_PANEL_LCD_PIN_RS -#endif - -#ifdef CONFIG_PANEL_LCD_PIN_RW -#undef DEFAULT_LCD_PIN_RW -#define DEFAULT_LCD_PIN_RW CONFIG_PANEL_LCD_PIN_RW -#endif - -#ifdef CONFIG_PANEL_LCD_PIN_SCL -#undef DEFAULT_LCD_PIN_SCL -#define DEFAULT_LCD_PIN_SCL CONFIG_PANEL_LCD_PIN_SCL -#endif - -#ifdef CONFIG_PANEL_LCD_PIN_SDA -#undef DEFAULT_LCD_PIN_SDA -#define DEFAULT_LCD_PIN_SDA CONFIG_PANEL_LCD_PIN_SDA -#endif - -#ifdef CONFIG_PANEL_LCD_PIN_BL -#undef DEFAULT_LCD_PIN_BL -#define DEFAULT_LCD_PIN_BL CONFIG_PANEL_LCD_PIN_BL -#endif - -#endif /* DEFAULT_PROFILE == 0 */ - -/* 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; - -MODULE_DESCRIPTION("Generic parallel port LCD/Keypad driver"); - -static int parport = DEFAULT_PARPORT; -module_param(parport, int, 0000); -MODULE_PARM_DESC(parport, "Parallel port index (0=lpt1, 1=lpt2, ...)"); - -static int profile = DEFAULT_PROFILE; -module_param(profile, int, 0000); -MODULE_PARM_DESC(profile, - "1=16x2 old kp; 2=serial 16x2, new kp; 3=16x2 hantronix; " - "4=16x2 nexcom; default=40x2, old kp"); - -static int keypad_type = NOT_SET; -module_param(keypad_type, int, 0000); -MODULE_PARM_DESC(keypad_type, - "Keypad type: 0=none, 1=old 6 keys, 2=new 6+1 keys, 3=nexcom 4 keys"); - -static int lcd_type = NOT_SET; -module_param(lcd_type, int, 0000); -MODULE_PARM_DESC(lcd_type, - "LCD type: 0=none, 1=compiled-in, 2=old, 3=serial ks0074, 4=hantronix, 5=nexcom"); - -static int lcd_height = NOT_SET; -module_param(lcd_height, int, 0000); -MODULE_PARM_DESC(lcd_height, "Number of lines on the LCD"); - -static int lcd_width = NOT_SET; -module_param(lcd_width, int, 0000); -MODULE_PARM_DESC(lcd_width, "Number of columns on the LCD"); - -static int lcd_bwidth = NOT_SET; /* internal buffer width (usually 40) */ -module_param(lcd_bwidth, int, 0000); -MODULE_PARM_DESC(lcd_bwidth, "Internal LCD line width (40)"); - -static int lcd_hwidth = NOT_SET; /* hardware buffer width (usually 64) */ -module_param(lcd_hwidth, int, 0000); -MODULE_PARM_DESC(lcd_hwidth, "LCD line hardware address (64)"); - -static int lcd_charset = NOT_SET; -module_param(lcd_charset, int, 0000); -MODULE_PARM_DESC(lcd_charset, "LCD character set: 0=standard, 1=KS0074"); - -static int lcd_proto = NOT_SET; -module_param(lcd_proto, int, 0000); -MODULE_PARM_DESC(lcd_proto, - "LCD communication: 0=parallel (//), 1=serial, 2=TI LCD Interface"); - -/* - * These are the parallel port pins the LCD control signals are connected to. - * Set this to 0 if the signal is not used. Set it to its opposite value - * (negative) if the signal is negated. -MAXINT is used to indicate that the - * pin has not been explicitly specified. - * - * WARNING! no check will be performed about collisions with keypad ! - */ - -static int lcd_e_pin = PIN_NOT_SET; -module_param(lcd_e_pin, int, 0000); -MODULE_PARM_DESC(lcd_e_pin, - "# of the // port pin connected to LCD 'E' signal, with polarity (-17..17)"); - -static int lcd_rs_pin = PIN_NOT_SET; -module_param(lcd_rs_pin, int, 0000); -MODULE_PARM_DESC(lcd_rs_pin, - "# of the // port pin connected to LCD 'RS' signal, with polarity (-17..17)"); - -static int lcd_rw_pin = PIN_NOT_SET; -module_param(lcd_rw_pin, int, 0000); -MODULE_PARM_DESC(lcd_rw_pin, - "# of the // port pin connected to LCD 'RW' signal, with polarity (-17..17)"); - -static int lcd_cl_pin = PIN_NOT_SET; -module_param(lcd_cl_pin, int, 0000); -MODULE_PARM_DESC(lcd_cl_pin, - "# of the // port pin connected to serial LCD 'SCL' signal, with polarity (-17..17)"); - -static int lcd_da_pin = PIN_NOT_SET; -module_param(lcd_da_pin, int, 0000); -MODULE_PARM_DESC(lcd_da_pin, - "# of the // port pin connected to serial LCD 'SDA' signal, with polarity (-17..17)"); - -static int lcd_bl_pin = PIN_NOT_SET; -module_param(lcd_bl_pin, int, 0000); -MODULE_PARM_DESC(lcd_bl_pin, - "# of the // port pin connected to LCD backlight, with polarity (-17..17)"); - -/* Deprecated module parameters - consider not using them anymore */ - -static int lcd_enabled = NOT_SET; -module_param(lcd_enabled, int, 0000); -MODULE_PARM_DESC(lcd_enabled, "Deprecated option, use lcd_type instead"); - -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 */ - /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - /* 0x08 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - /* 0x10 */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - /* 0x18 */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - /* 0x20 */ 0x20, 0x21, 0x22, 0x23, 0xa2, 0x25, 0x26, 0x27, - /* 0x28 */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - /* 0x30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - /* 0x38 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - /* 0x40 */ 0xa0, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - /* 0x48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - /* 0x50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - /* 0x58 */ 0x58, 0x59, 0x5a, 0xfa, 0xfb, 0xfc, 0x1d, 0xc4, - /* 0x60 */ 0x96, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - /* 0x68 */ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - /* 0x70 */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - /* 0x78 */ 0x78, 0x79, 0x7a, 0xfd, 0xfe, 0xff, 0xce, 0x20, - /* 0x80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - /* 0x88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - /* 0x90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - /* 0x98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, - /* 0xA0 */ 0x20, 0x40, 0xb1, 0xa1, 0x24, 0xa3, 0xfe, 0x5f, - /* 0xA8 */ 0x22, 0xc8, 0x61, 0x14, 0x97, 0x2d, 0xad, 0x96, - /* 0xB0 */ 0x80, 0x8c, 0x82, 0x83, 0x27, 0x8f, 0x86, 0xdd, - /* 0xB8 */ 0x2c, 0x81, 0x6f, 0x15, 0x8b, 0x8a, 0x84, 0x60, - /* 0xC0 */ 0xe2, 0xe2, 0xe2, 0x5b, 0x5b, 0xae, 0xbc, 0xa9, - /* 0xC8 */ 0xc5, 0xbf, 0xc6, 0xf1, 0xe3, 0xe3, 0xe3, 0xe3, - /* 0xD0 */ 0x44, 0x5d, 0xa8, 0xe4, 0xec, 0xec, 0x5c, 0x78, - /* 0xD8 */ 0xab, 0xa6, 0xe5, 0x5e, 0x5e, 0xe6, 0xaa, 0xbe, - /* 0xE0 */ 0x7f, 0xe7, 0xaf, 0x7b, 0x7b, 0xaf, 0xbd, 0xc8, - /* 0xE8 */ 0xa4, 0xa5, 0xc7, 0xf6, 0xa7, 0xe8, 0x69, 0x69, - /* 0xF0 */ 0xed, 0x7d, 0xa8, 0xe4, 0xec, 0x5c, 0x5c, 0x25, - /* 0xF8 */ 0xac, 0xa6, 0xea, 0xef, 0x7e, 0xeb, 0xb2, 0x79, -}; - -static const char old_keypad_profile[][4][9] = { - {"S0", "Left\n", "Left\n", ""}, - {"S1", "Down\n", "Down\n", ""}, - {"S2", "Up\n", "Up\n", ""}, - {"S3", "Right\n", "Right\n", ""}, - {"S4", "Esc\n", "Esc\n", ""}, - {"S5", "Ret\n", "Ret\n", ""}, - {"", "", "", ""} -}; - -/* signals, press, repeat, release */ -static const char new_keypad_profile[][4][9] = { - {"S0", "Left\n", "Left\n", ""}, - {"S1", "Down\n", "Down\n", ""}, - {"S2", "Up\n", "Up\n", ""}, - {"S3", "Right\n", "Right\n", ""}, - {"S4s5", "", "Esc\n", "Esc\n"}, - {"s4S5", "", "Ret\n", "Ret\n"}, - {"S4S5", "Help\n", "", ""}, - /* add new signals above this line */ - {"", "", "", ""} -}; - -/* signals, press, repeat, release */ -static const char nexcom_keypad_profile[][4][9] = { - {"a-p-e-", "Down\n", "Down\n", ""}, - {"a-p-E-", "Ret\n", "Ret\n", ""}, - {"a-P-E-", "Esc\n", "Esc\n", ""}, - {"a-P-e-", "Up\n", "Up\n", ""}, - /* add new signals above this line */ - {"", "", "", ""} -}; - -static const char (*keypad_profile)[4][9] = old_keypad_profile; - -static DECLARE_BITMAP(bits, LCD_BITS); - -static void lcd_get_bits(unsigned int port, int *val) -{ - unsigned int bit, state; - - for (bit = 0; bit < LCD_BITS; bit++) { - state = test_bit(bit, bits) ? BIT_SET : BIT_CLR; - *val &= lcd_bits[port][bit][BIT_MSK]; - *val |= lcd_bits[port][bit][state]; - } -} - -/* sets data port bits according to current signals values */ -static int set_data_bits(void) -{ - int val; - - val = r_dtr(pprt); - lcd_get_bits(LCD_PORT_D, &val); - w_dtr(pprt, val); - return val; -} - -/* sets ctrl port bits according to current signals values */ -static int set_ctrl_bits(void) -{ - int val; - - val = r_ctr(pprt); - lcd_get_bits(LCD_PORT_C, &val); - w_ctr(pprt, val); - return val; -} - -/* sets ctrl & data port bits according to current signals values */ -static void panel_set_bits(void) -{ - set_data_bits(); - set_ctrl_bits(); -} - -/* - * Converts a parallel port pin (from -25 to 25) to data and control ports - * masks, and data and control port bits. The signal will be considered - * unconnected if it's on pin 0 or an invalid pin (<-25 or >25). - * - * Result will be used this way : - * out(dport, in(dport) & d_val[2] | d_val[signal_state]) - * out(cport, in(cport) & c_val[2] | c_val[signal_state]) - */ -static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val) -{ - int d_bit, c_bit, inv; - - d_val[0] = 0; - c_val[0] = 0; - d_val[1] = 0; - c_val[1] = 0; - d_val[2] = 0xFF; - c_val[2] = 0xFF; - - if (pin == 0) - return; - - inv = (pin < 0); - if (inv) - pin = -pin; - - d_bit = 0; - c_bit = 0; - - switch (pin) { - case PIN_STROBE: /* strobe, inverted */ - c_bit = PNL_PSTROBE; - inv = !inv; - break; - case PIN_D0...PIN_D7: /* D0 - D7 = 2 - 9 */ - d_bit = 1 << (pin - 2); - break; - case PIN_AUTOLF: /* autofeed, inverted */ - c_bit = PNL_PAUTOLF; - inv = !inv; - break; - case PIN_INITP: /* init, direct */ - c_bit = PNL_PINITP; - break; - case PIN_SELECP: /* select_in, inverted */ - c_bit = PNL_PSELECP; - inv = !inv; - break; - default: /* unknown pin, ignore */ - break; - } - - if (c_bit) { - c_val[2] &= ~c_bit; - c_val[!inv] = c_bit; - } else if (d_bit) { - d_val[2] &= ~d_bit; - d_val[!inv] = d_bit; - } -} - -/* 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. - */ -static void lcd_send_serial(int byte) -{ - int bit; - - /* - * the data bit is set on D0, and the clock on STROBE. - * LCD reads D0 on STROBE's rising edge. - */ - for (bit = 0; bit < 8; bit++) { - clear_bit(LCD_BIT_CL, bits); /* CLK low */ - panel_set_bits(); - if (byte & 1) { - set_bit(LCD_BIT_DA, bits); - } else { - clear_bit(LCD_BIT_DA, bits); - } - - panel_set_bits(); - udelay(2); /* maintain the data during 2 us before CLK up */ - set_bit(LCD_BIT_CL, bits); /* CLK high */ - panel_set_bits(); - udelay(1); /* maintain the strobe during 1 us */ - byte >>= 1; - } -} - -/* turn the backlight on or off */ -static void __lcd_backlight(int on) -{ - /* The backlight is activated by setting the AUTOFEED line to +5V */ - spin_lock_irq(&pprt_lock); - if (on) - set_bit(LCD_BIT_BL, bits); - else - clear_bit(LCD_BIT_BL, bits); - panel_set_bits(); - 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) -{ - spin_lock_irq(&pprt_lock); - lcd_send_serial(0x1F); /* R/W=W, RS=0 */ - lcd_send_serial(cmd & 0x0F); - lcd_send_serial((cmd >> 4) & 0x0F); - udelay(40); /* the shortest command takes at least 40 us */ - spin_unlock_irq(&pprt_lock); -} - -/* send data to the LCD panel in serial mode */ -static void lcd_write_data_s(int data) -{ - spin_lock_irq(&pprt_lock); - lcd_send_serial(0x5F); /* R/W=W, RS=1 */ - lcd_send_serial(data & 0x0F); - lcd_send_serial((data >> 4) & 0x0F); - udelay(40); /* the shortest data takes at least 40 us */ - spin_unlock_irq(&pprt_lock); -} - -/* send a command to the LCD panel in 8 bits parallel mode */ -static void lcd_write_cmd_p8(int cmd) -{ - spin_lock_irq(&pprt_lock); - /* present the data to the data port */ - w_dtr(pprt, cmd); - udelay(20); /* maintain the data during 20 us before the strobe */ - - set_bit(LCD_BIT_E, bits); - clear_bit(LCD_BIT_RS, bits); - clear_bit(LCD_BIT_RW, bits); - set_ctrl_bits(); - - udelay(40); /* maintain the strobe during 40 us */ - - clear_bit(LCD_BIT_E, bits); - set_ctrl_bits(); - - udelay(120); /* the shortest command takes at least 120 us */ - spin_unlock_irq(&pprt_lock); -} - -/* send data to the LCD panel in 8 bits parallel mode */ -static void lcd_write_data_p8(int data) -{ - spin_lock_irq(&pprt_lock); - /* present the data to the data port */ - w_dtr(pprt, data); - udelay(20); /* maintain the data during 20 us before the strobe */ - - set_bit(LCD_BIT_E, bits); - set_bit(LCD_BIT_RS, bits); - clear_bit(LCD_BIT_RW, bits); - set_ctrl_bits(); - - udelay(40); /* maintain the strobe during 40 us */ - - clear_bit(LCD_BIT_E, bits); - set_ctrl_bits(); - - udelay(45); /* the shortest data takes at least 45 us */ - spin_unlock_irq(&pprt_lock); -} - -/* send a command to the TI LCD panel */ -static void lcd_write_cmd_tilcd(int cmd) -{ - spin_lock_irq(&pprt_lock); - /* present the data to the control port */ - w_ctr(pprt, cmd); - udelay(60); - spin_unlock_irq(&pprt_lock); -} - -/* send data to the TI LCD panel */ -static void lcd_write_data_tilcd(int data) -{ - spin_lock_irq(&pprt_lock); - /* present the data to the data port */ - w_dtr(pprt, data); - udelay(60); - 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) -{ - int pos; - - lcd_home(); - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { - lcd_send_serial(0x5F); /* R/W=W, RS=1 */ - lcd_send_serial(' ' & 0x0F); - lcd_send_serial((' ' >> 4) & 0x0F); - /* the shortest data takes at least 40 us */ - 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) -{ - int pos; - - lcd_home(); - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { - /* present the data to the data port */ - w_dtr(pprt, ' '); - - /* maintain the data during 20 us before the strobe */ - udelay(20); - - set_bit(LCD_BIT_E, bits); - set_bit(LCD_BIT_RS, bits); - clear_bit(LCD_BIT_RW, bits); - set_ctrl_bits(); - - /* maintain the strobe during 40 us */ - udelay(40); - - clear_bit(LCD_BIT_E, bits); - set_ctrl_bits(); - - /* the shortest data takes at least 45 us */ - 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) -{ - int pos; - - lcd_home(); - - spin_lock_irq(&pprt_lock); - for (pos = 0; pos < lcd.height * lcd.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); - - lcd_clear_display(); -} - -/* - * 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 inline int handle_lcd_special_code(void) -{ - /* LCD special codes */ - - int processed = 0; - - 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 <c> between '0' - * and '7', representing the numerical ASCII code of the - * redefined character, and <xx...xx> 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; - } - } - - /* - * now we'll see if we're in an escape mode and if the current - * escape sequence can be understood. - */ - 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 */ -} - -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 */ - lcd.proto = LCD_PROTO_PARALLEL; - lcd.charset = LCD_CHARSET_NORMAL; - lcd.pins.e = PIN_STROBE; - lcd.pins.rs = PIN_AUTOLF; - - lcd.width = 40; - lcd.bwidth = 40; - lcd.hwidth = 64; - lcd.height = 2; - break; - case LCD_TYPE_KS0074: - /* serial mode, ks0074 */ - lcd.proto = LCD_PROTO_SERIAL; - lcd.charset = LCD_CHARSET_KS0074; - lcd.pins.bl = PIN_AUTOLF; - lcd.pins.cl = PIN_STROBE; - lcd.pins.da = PIN_D0; - - lcd.width = 16; - lcd.bwidth = 40; - lcd.hwidth = 16; - lcd.height = 2; - break; - case LCD_TYPE_NEXCOM: - /* parallel mode, 8 bits, generic */ - lcd.proto = LCD_PROTO_PARALLEL; - lcd.charset = LCD_CHARSET_NORMAL; - lcd.pins.e = PIN_AUTOLF; - lcd.pins.rs = PIN_SELECP; - lcd.pins.rw = PIN_INITP; - - lcd.width = 16; - lcd.bwidth = 40; - lcd.hwidth = 64; - lcd.height = 2; - break; - case LCD_TYPE_CUSTOM: - /* customer-defined */ - lcd.proto = DEFAULT_LCD_PROTO; - lcd.charset = DEFAULT_LCD_CHARSET; - /* default geometry will be set later */ - break; - case LCD_TYPE_HANTRONIX: - /* parallel mode, 8 bits, hantronix-like */ - default: - lcd.proto = LCD_PROTO_PARALLEL; - lcd.charset = LCD_CHARSET_NORMAL; - lcd.pins.e = PIN_STROBE; - lcd.pins.rs = PIN_SELECP; - - lcd.width = 16; - lcd.bwidth = 40; - lcd.hwidth = 64; - lcd.height = 2; - break; - } - - /* Overwrite with module params set on loading */ - if (lcd_height != NOT_SET) - lcd.height = lcd_height; - if (lcd_width != NOT_SET) - lcd.width = lcd_width; - if (lcd_bwidth != NOT_SET) - lcd.bwidth = lcd_bwidth; - if (lcd_hwidth != NOT_SET) - lcd.hwidth = lcd_hwidth; - if (lcd_charset != NOT_SET) - lcd.charset = lcd_charset; - if (lcd_proto != NOT_SET) - lcd.proto = lcd_proto; - if (lcd_e_pin != PIN_NOT_SET) - lcd.pins.e = lcd_e_pin; - if (lcd_rs_pin != PIN_NOT_SET) - lcd.pins.rs = lcd_rs_pin; - if (lcd_rw_pin != PIN_NOT_SET) - lcd.pins.rw = lcd_rw_pin; - if (lcd_cl_pin != PIN_NOT_SET) - lcd.pins.cl = lcd_cl_pin; - if (lcd_da_pin != PIN_NOT_SET) - lcd.pins.da = lcd_da_pin; - if (lcd_bl_pin != PIN_NOT_SET) - 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 (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; - - if (lcd.pins.cl == PIN_NOT_SET) - lcd.pins.cl = DEFAULT_LCD_PIN_SCL; - if (lcd.pins.da == PIN_NOT_SET) - 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; - - if (lcd.pins.e == PIN_NOT_SET) - lcd.pins.e = DEFAULT_LCD_PIN_E; - if (lcd.pins.rs == PIN_NOT_SET) - lcd.pins.rs = DEFAULT_LCD_PIN_RS; - 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; - } - - if (lcd.pins.bl == PIN_NOT_SET) - lcd.pins.bl = DEFAULT_LCD_PIN_BL; - - if (lcd.pins.e == PIN_NOT_SET) - lcd.pins.e = PIN_NONE; - if (lcd.pins.rs == PIN_NOT_SET) - lcd.pins.rs = PIN_NONE; - if (lcd.pins.rw == PIN_NOT_SET) - lcd.pins.rw = PIN_NONE; - if (lcd.pins.bl == PIN_NOT_SET) - lcd.pins.bl = PIN_NONE; - if (lcd.pins.cl == PIN_NOT_SET) - lcd.pins.cl = PIN_NONE; - if (lcd.pins.da == PIN_NOT_SET) - lcd.pins.da = PIN_NONE; - - if (lcd.charset == NOT_SET) - lcd.charset = DEFAULT_LCD_CHARSET; - - if (lcd.charset == LCD_CHARSET_KS0074) - lcd_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); - } - - pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E], - lcd_bits[LCD_PORT_C][LCD_BIT_E]); - pin_to_bits(lcd.pins.rs, lcd_bits[LCD_PORT_D][LCD_BIT_RS], - lcd_bits[LCD_PORT_C][LCD_BIT_RS]); - pin_to_bits(lcd.pins.rw, lcd_bits[LCD_PORT_D][LCD_BIT_RW], - lcd_bits[LCD_PORT_C][LCD_BIT_RW]); - pin_to_bits(lcd.pins.bl, lcd_bits[LCD_PORT_D][LCD_BIT_BL], - lcd_bits[LCD_PORT_C][LCD_BIT_BL]); - pin_to_bits(lcd.pins.cl, lcd_bits[LCD_PORT_D][LCD_BIT_CL], - lcd_bits[LCD_PORT_C][LCD_BIT_CL]); - 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.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(); -} - -/* - * These are the file operation function for user access to /dev/keypad - */ - -static ssize_t keypad_read(struct file *file, - char __user *buf, size_t count, loff_t *ppos) -{ - unsigned i = *ppos; - char __user *tmp = buf; - - if (keypad_buflen == 0) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - if (wait_event_interruptible(keypad_read_wait, - keypad_buflen != 0)) - return -EINTR; - } - - for (; count-- > 0 && (keypad_buflen > 0); - ++i, ++tmp, --keypad_buflen) { - put_user(keypad_buffer[keypad_start], tmp); - keypad_start = (keypad_start + 1) % KEYPAD_BUFFER; - } - *ppos = i; - - return tmp - buf; -} - -static int keypad_open(struct inode *inode, struct file *file) -{ - if (!atomic_dec_and_test(&keypad_available)) - return -EBUSY; /* open only once at a time */ - - if (file->f_mode & FMODE_WRITE) /* device is read-only */ - return -EPERM; - - keypad_buflen = 0; /* flush the buffer on opening */ - return 0; -} - -static int keypad_release(struct inode *inode, struct file *file) -{ - atomic_inc(&keypad_available); - return 0; -} - -static const struct file_operations keypad_fops = { - .read = keypad_read, /* read */ - .open = keypad_open, /* open */ - .release = keypad_release, /* close */ - .llseek = default_llseek, -}; - -static struct miscdevice keypad_dev = { - .minor = KEYPAD_MINOR, - .name = "keypad", - .fops = &keypad_fops, -}; - -static void keypad_send_key(const char *string, int max_len) -{ - /* send the key to the device only if a process is attached to it. */ - if (!atomic_read(&keypad_available)) { - while (max_len-- && keypad_buflen < KEYPAD_BUFFER && *string) { - keypad_buffer[(keypad_start + keypad_buflen++) % - KEYPAD_BUFFER] = *string++; - } - wake_up_interruptible(&keypad_read_wait); - } -} - -/* this function scans all the bits involving at least one logical signal, - * and puts the results in the bitfield "phys_read" (one bit per established - * contact), and sets "phys_read_prev" to "phys_read". - * - * Note: to debounce input signals, we will only consider as switched a signal - * which is stable across 2 measures. Signals which are different between two - * reads will be kept as they previously were in their logical form (phys_prev). - * A signal which has just switched will have a 1 in - * (phys_read ^ phys_read_prev). - */ -static void phys_scan_contacts(void) -{ - int bit, bitval; - char oldval; - char bitmask; - char gndmask; - - phys_prev = phys_curr; - phys_read_prev = phys_read; - phys_read = 0; /* flush all signals */ - - /* keep track of old value, with all outputs disabled */ - oldval = r_dtr(pprt) | scan_mask_o; - /* activate all keyboard outputs (active low) */ - w_dtr(pprt, oldval & ~scan_mask_o); - - /* will have a 1 for each bit set to gnd */ - bitmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i; - /* disable all matrix signals */ - w_dtr(pprt, oldval); - - /* now that all outputs are cleared, the only active input bits are - * directly connected to the ground - */ - - /* 1 for each grounded input */ - gndmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i; - - /* grounded inputs are signals 40-44 */ - phys_read |= (__u64)gndmask << 40; - - if (bitmask != gndmask) { - /* - * since clearing the outputs changed some inputs, we know - * that some input signals are currently tied to some outputs. - * So we'll scan them. - */ - for (bit = 0; bit < 8; bit++) { - bitval = BIT(bit); - - if (!(scan_mask_o & bitval)) /* skip unused bits */ - continue; - - w_dtr(pprt, oldval & ~bitval); /* enable this output */ - bitmask = PNL_PINPUT(r_str(pprt)) & ~gndmask; - phys_read |= (__u64)bitmask << (5 * bit); - } - w_dtr(pprt, oldval); /* disable all outputs */ - } - /* - * this is easy: use old bits when they are flapping, - * use new ones when stable - */ - phys_curr = (phys_prev & (phys_read ^ phys_read_prev)) | - (phys_read & ~(phys_read ^ phys_read_prev)); -} - -static inline int input_state_high(struct logical_input *input) -{ -#if 0 - /* FIXME: - * this is an invalid test. It tries to catch - * transitions from single-key to multiple-key, but - * doesn't take into account the contacts polarity. - * The only solution to the problem is to parse keys - * from the most complex to the simplest combinations, - * and mark them as 'caught' once a combination - * matches, then unmatch it for all other ones. - */ - - /* try to catch dangerous transitions cases : - * someone adds a bit, so this signal was a false - * positive resulting from a transition. We should - * invalidate the signal immediately and not call the - * release function. - * eg: 0 -(press A)-> A -(press B)-> AB : don't match A's release. - */ - if (((phys_prev & input->mask) == input->value) && - ((phys_curr & input->mask) > input->value)) { - input->state = INPUT_ST_LOW; /* invalidate */ - return 1; - } -#endif - - if ((phys_curr & input->mask) == input->value) { - if ((input->type == INPUT_TYPE_STD) && - (input->high_timer == 0)) { - input->high_timer++; - if (input->u.std.press_fct) - input->u.std.press_fct(input->u.std.press_data); - } else if (input->type == INPUT_TYPE_KBD) { - /* will turn on the light */ - keypressed = 1; - - if (input->high_timer == 0) { - char *press_str = input->u.kbd.press_str; - - if (press_str[0]) { - int s = sizeof(input->u.kbd.press_str); - - keypad_send_key(press_str, s); - } - } - - if (input->u.kbd.repeat_str[0]) { - char *repeat_str = input->u.kbd.repeat_str; - - if (input->high_timer >= KEYPAD_REP_START) { - int s = sizeof(input->u.kbd.repeat_str); - - input->high_timer -= KEYPAD_REP_DELAY; - keypad_send_key(repeat_str, s); - } - /* we will need to come back here soon */ - inputs_stable = 0; - } - - if (input->high_timer < 255) - input->high_timer++; - } - return 1; - } - - /* else signal falling down. Let's fall through. */ - input->state = INPUT_ST_FALLING; - input->fall_timer = 0; - - return 0; -} - -static inline void input_state_falling(struct logical_input *input) -{ -#if 0 - /* FIXME !!! same comment as in input_state_high */ - if (((phys_prev & input->mask) == input->value) && - ((phys_curr & input->mask) > input->value)) { - input->state = INPUT_ST_LOW; /* invalidate */ - return; - } -#endif - - if ((phys_curr & input->mask) == input->value) { - if (input->type == INPUT_TYPE_KBD) { - /* will turn on the light */ - keypressed = 1; - - if (input->u.kbd.repeat_str[0]) { - char *repeat_str = input->u.kbd.repeat_str; - - if (input->high_timer >= KEYPAD_REP_START) { - int s = sizeof(input->u.kbd.repeat_str); - - input->high_timer -= KEYPAD_REP_DELAY; - keypad_send_key(repeat_str, s); - } - /* we will need to come back here soon */ - inputs_stable = 0; - } - - if (input->high_timer < 255) - input->high_timer++; - } - input->state = INPUT_ST_HIGH; - } else if (input->fall_timer >= input->fall_time) { - /* call release event */ - if (input->type == INPUT_TYPE_STD) { - void (*release_fct)(int) = input->u.std.release_fct; - - if (release_fct) - release_fct(input->u.std.release_data); - } else if (input->type == INPUT_TYPE_KBD) { - char *release_str = input->u.kbd.release_str; - - if (release_str[0]) { - int s = sizeof(input->u.kbd.release_str); - - keypad_send_key(release_str, s); - } - } - - input->state = INPUT_ST_LOW; - } else { - input->fall_timer++; - inputs_stable = 0; - } -} - -static void panel_process_inputs(void) -{ - struct list_head *item; - struct logical_input *input; - - keypressed = 0; - inputs_stable = 1; - list_for_each(item, &logical_inputs) { - input = list_entry(item, struct logical_input, list); - - switch (input->state) { - case INPUT_ST_LOW: - if ((phys_curr & input->mask) != input->value) - break; - /* if all needed ones were already set previously, - * this means that this logical signal has been - * activated by the releasing of another combined - * signal, so we don't want to match. - * eg: AB -(release B)-> A -(release A)-> 0 : - * don't match A. - */ - if ((phys_prev & input->mask) == input->value) - break; - input->rise_timer = 0; - input->state = INPUT_ST_RISING; - /* no break here, fall through */ - case INPUT_ST_RISING: - if ((phys_curr & input->mask) != input->value) { - input->state = INPUT_ST_LOW; - break; - } - if (input->rise_timer < input->rise_time) { - inputs_stable = 0; - input->rise_timer++; - break; - } - input->high_timer = 0; - input->state = INPUT_ST_HIGH; - /* no break here, fall through */ - case INPUT_ST_HIGH: - if (input_state_high(input)) - break; - /* no break here, fall through */ - case INPUT_ST_FALLING: - input_state_falling(input); - } - } -} - -static void panel_scan_timer(void) -{ - if (keypad.enabled && keypad_initialized) { - if (spin_trylock_irq(&pprt_lock)) { - phys_scan_contacts(); - - /* no need for the parport anymore */ - spin_unlock_irq(&pprt_lock); - } - - if (!inputs_stable || phys_curr != phys_prev) - panel_process_inputs(); - } - - if (keypressed && lcd.enabled && lcd.initialized) - lcd_poke(); - - mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME); -} - -static void init_scan_timer(void) -{ - if (scan_timer.function) - return; /* already started */ - - setup_timer(&scan_timer, (void *)&panel_scan_timer, 0); - scan_timer.expires = jiffies + INPUT_POLL_TIME; - add_timer(&scan_timer); -} - -/* converts a name of the form "({BbAaPpSsEe}{01234567-})*" to a series of bits. - * if <omask> or <imask> are non-null, they will be or'ed with the bits - * corresponding to out and in bits respectively. - * returns 1 if ok, 0 if error (in which case, nothing is written). - */ -static u8 input_name2mask(const char *name, __u64 *mask, __u64 *value, - u8 *imask, u8 *omask) -{ - const char sigtab[] = "EeSsPpAaBb"; - u8 im, om; - __u64 m, v; - - om = 0; - im = 0; - m = 0ULL; - v = 0ULL; - while (*name) { - int in, out, bit, neg; - const char *idx; - - idx = strchr(sigtab, *name); - if (!idx) - return 0; /* input name not found */ - - in = idx - sigtab; - neg = (in & 1); /* odd (lower) names are negated */ - in >>= 1; - im |= BIT(in); - - name++; - if (*name >= '0' && *name <= '7') { - out = *name - '0'; - om |= BIT(out); - } else if (*name == '-') { - out = 8; - } else { - return 0; /* unknown bit name */ - } - - bit = (out * 5) + in; - - m |= 1ULL << bit; - if (!neg) - v |= 1ULL << bit; - name++; - } - *mask = m; - *value = v; - if (imask) - *imask |= im; - if (omask) - *omask |= om; - return 1; -} - -/* tries to bind a key to the signal name <name>. The key will send the - * strings <press>, <repeat>, <release> for these respective events. - * Returns the pointer to the new key if ok, NULL if the key could not be bound. - */ -static struct logical_input *panel_bind_key(const char *name, const char *press, - const char *repeat, - const char *release) -{ - struct logical_input *key; - - key = kzalloc(sizeof(*key), GFP_KERNEL); - if (!key) - return NULL; - - if (!input_name2mask(name, &key->mask, &key->value, &scan_mask_i, - &scan_mask_o)) { - kfree(key); - return NULL; - } - - key->type = INPUT_TYPE_KBD; - key->state = INPUT_ST_LOW; - key->rise_time = 1; - key->fall_time = 1; - - strncpy(key->u.kbd.press_str, press, sizeof(key->u.kbd.press_str)); - strncpy(key->u.kbd.repeat_str, repeat, sizeof(key->u.kbd.repeat_str)); - strncpy(key->u.kbd.release_str, release, - sizeof(key->u.kbd.release_str)); - list_add(&key->list, &logical_inputs); - return key; -} - -#if 0 -/* tries to bind a callback function to the signal name <name>. The function - * <press_fct> will be called with the <press_data> arg when the signal is - * activated, and so on for <release_fct>/<release_data> - * Returns the pointer to the new signal if ok, NULL if the signal could not - * be bound. - */ -static struct logical_input *panel_bind_callback(char *name, - void (*press_fct)(int), - int press_data, - void (*release_fct)(int), - int release_data) -{ - struct logical_input *callback; - - callback = kmalloc(sizeof(*callback), GFP_KERNEL); - if (!callback) - return NULL; - - memset(callback, 0, sizeof(struct logical_input)); - if (!input_name2mask(name, &callback->mask, &callback->value, - &scan_mask_i, &scan_mask_o)) - return NULL; - - callback->type = INPUT_TYPE_STD; - callback->state = INPUT_ST_LOW; - callback->rise_time = 1; - callback->fall_time = 1; - callback->u.std.press_fct = press_fct; - callback->u.std.press_data = press_data; - callback->u.std.release_fct = release_fct; - callback->u.std.release_data = release_data; - list_add(&callback->list, &logical_inputs); - return callback; -} -#endif - -static void keypad_init(void) -{ - int keynum; - - init_waitqueue_head(&keypad_read_wait); - keypad_buflen = 0; /* flushes any eventual noisy keystroke */ - - /* Let's create all known keys */ - - for (keynum = 0; keypad_profile[keynum][0][0]; keynum++) { - panel_bind_key(keypad_profile[keynum][0], - keypad_profile[keynum][1], - keypad_profile[keynum][2], - keypad_profile[keynum][3]); - } - - init_scan_timer(); - keypad_initialized = 1; -} - -/**************************************************/ -/* 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; - - if (port->number != parport) - return; - - if (pprt) { - pr_err("%s: port->number=%d parport=%d, already registered!\n", - __func__, port->number, parport); - return; - } - - memset(&panel_cb, 0, sizeof(panel_cb)); - panel_cb.private = &pprt; - /* panel_cb.flags = 0 should be PARPORT_DEV_EXCL? */ - - pprt = parport_register_dev_model(port, "panel", &panel_cb, 0); - if (!pprt) { - pr_err("%s: port->number=%d parport=%d, parport_register_device() failed\n", - __func__, port->number, parport); - return; - } - - if (parport_claim(pprt)) { - pr_err("could not claim access to parport%d. Aborting.\n", - parport); - goto err_unreg_device; - } - - /* must init LCD first, just in case an IRQ from the keypad is - * generated at keypad init - */ - if (lcd.enabled) { - lcd_init(); - if (misc_register(&lcd_dev)) - goto err_unreg_device; - } - - if (keypad.enabled) { - keypad_init(); - 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); -err_unreg_device: - parport_unregister_device(pprt); - pprt = NULL; -} - -static void panel_detach(struct parport *port) -{ - if (port->number != parport) - return; - - if (!pprt) { - pr_err("%s: port->number=%d parport=%d, nothing to unregister.\n", - __func__, port->number, parport); - return; - } - if (scan_timer.function) - del_timer_sync(&scan_timer); - - if (keypad.enabled) { - misc_deregister(&keypad_dev); - keypad_initialized = 0; - } - - 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); - } - lcd.initialized = false; - } - - /* 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 = { - .name = "panel", - .match_port = panel_attach, - .detach = panel_detach, - .devmodel = true, -}; - -/* init function */ -static int __init panel_init_module(void) -{ - int selected_keypad_type = NOT_SET, err; - - /* take care of an eventual profile */ - switch (profile) { - case PANEL_PROFILE_CUSTOM: - /* custom profile */ - selected_keypad_type = DEFAULT_KEYPAD_TYPE; - selected_lcd_type = DEFAULT_LCD_TYPE; - break; - case PANEL_PROFILE_OLD: - /* 8 bits, 2*16, old keypad */ - selected_keypad_type = KEYPAD_TYPE_OLD; - selected_lcd_type = LCD_TYPE_OLD; - - /* TODO: This two are a little hacky, sort it out later */ - if (lcd_width == NOT_SET) - lcd_width = 16; - if (lcd_hwidth == NOT_SET) - lcd_hwidth = 16; - break; - case PANEL_PROFILE_NEW: - /* serial, 2*16, new keypad */ - selected_keypad_type = KEYPAD_TYPE_NEW; - selected_lcd_type = LCD_TYPE_KS0074; - break; - case PANEL_PROFILE_HANTRONIX: - /* 8 bits, 2*16 hantronix-like, no keypad */ - selected_keypad_type = KEYPAD_TYPE_NONE; - selected_lcd_type = LCD_TYPE_HANTRONIX; - break; - case PANEL_PROFILE_NEXCOM: - /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */ - selected_keypad_type = KEYPAD_TYPE_NEXCOM; - selected_lcd_type = LCD_TYPE_NEXCOM; - break; - case PANEL_PROFILE_LARGE: - /* 8 bits, 2*40, old keypad */ - selected_keypad_type = KEYPAD_TYPE_OLD; - selected_lcd_type = LCD_TYPE_OLD; - break; - } - - /* - * Overwrite selection with module param values (both keypad and lcd), - * where the deprecated params have lower prio. - */ - if (keypad_enabled != NOT_SET) - selected_keypad_type = keypad_enabled; - if (keypad_type != NOT_SET) - selected_keypad_type = keypad_type; - - keypad.enabled = (selected_keypad_type > 0); - - if (lcd_enabled != NOT_SET) - selected_lcd_type = lcd_enabled; - if (lcd_type != NOT_SET) - selected_lcd_type = lcd_type; - - lcd.enabled = (selected_lcd_type > 0); - - if (lcd.enabled) { - /* - * 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; - lcd.pins.rs = lcd_rs_pin; - lcd.pins.rw = lcd_rw_pin; - 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) { - case KEYPAD_TYPE_OLD: - keypad_profile = old_keypad_profile; - break; - case KEYPAD_TYPE_NEW: - keypad_profile = new_keypad_profile; - break; - case KEYPAD_TYPE_NEXCOM: - keypad_profile = nexcom_keypad_profile; - break; - default: - keypad_profile = NULL; - break; - } - - if (!lcd.enabled && !keypad.enabled) { - /* no device enabled, let's exit */ - pr_err("panel driver disabled.\n"); - return -ENODEV; - } - - err = parport_register_driver(&panel_driver); - if (err) { - pr_err("could not register with parport. Aborting.\n"); - return err; - } - - if (pprt) - pr_info("panel driver registered on parport%d (io=0x%lx).\n", - parport, pprt->port->base); - else - pr_info("panel driver not yet registered\n"); - return 0; -} - -static void __exit panel_cleanup_module(void) -{ - parport_unregister_driver(&panel_driver); -} - -module_init(panel_init_module); -module_exit(panel_cleanup_module); -MODULE_AUTHOR("Willy Tarreau"); -MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-indent-level: 4 - * tab-width: 8 - * End: - */ diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c new file mode 100644 index 000000000000..09c10f426b64 --- /dev/null +++ b/drivers/misc/pci_endpoint_test.c @@ -0,0 +1,534 @@ +/** + * Host side test driver to test endpoint functionality + * + * Copyright (C) 2017 Texas Instruments + * Author: Kishon Vijay Abraham I <kishon@ti.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/crc32.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#include <linux/pci_regs.h> + +#include <uapi/linux/pcitest.h> + +#define DRV_MODULE_NAME "pci-endpoint-test" + +#define PCI_ENDPOINT_TEST_MAGIC 0x0 + +#define PCI_ENDPOINT_TEST_COMMAND 0x4 +#define COMMAND_RAISE_LEGACY_IRQ BIT(0) +#define COMMAND_RAISE_MSI_IRQ BIT(1) +#define MSI_NUMBER_SHIFT 2 +/* 6 bits for MSI number */ +#define COMMAND_READ BIT(8) +#define COMMAND_WRITE BIT(9) +#define COMMAND_COPY BIT(10) + +#define PCI_ENDPOINT_TEST_STATUS 0x8 +#define STATUS_READ_SUCCESS BIT(0) +#define STATUS_READ_FAIL BIT(1) +#define STATUS_WRITE_SUCCESS BIT(2) +#define STATUS_WRITE_FAIL BIT(3) +#define STATUS_COPY_SUCCESS BIT(4) +#define STATUS_COPY_FAIL BIT(5) +#define STATUS_IRQ_RAISED BIT(6) +#define STATUS_SRC_ADDR_INVALID BIT(7) +#define STATUS_DST_ADDR_INVALID BIT(8) + +#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0xc +#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10 + +#define PCI_ENDPOINT_TEST_LOWER_DST_ADDR 0x14 +#define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18 + +#define PCI_ENDPOINT_TEST_SIZE 0x1c +#define PCI_ENDPOINT_TEST_CHECKSUM 0x20 + +static DEFINE_IDA(pci_endpoint_test_ida); + +#define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \ + miscdev) +enum pci_barno { + BAR_0, + BAR_1, + BAR_2, + BAR_3, + BAR_4, + BAR_5, +}; + +struct pci_endpoint_test { + struct pci_dev *pdev; + void __iomem *base; + void __iomem *bar[6]; + struct completion irq_raised; + int last_irq; + /* mutex to protect the ioctls */ + struct mutex mutex; + struct miscdevice miscdev; +}; + +static int bar_size[] = { 4, 512, 1024, 16384, 131072, 1048576 }; + +static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, + u32 offset) +{ + return readl(test->base + offset); +} + +static inline void pci_endpoint_test_writel(struct pci_endpoint_test *test, + u32 offset, u32 value) +{ + writel(value, test->base + offset); +} + +static inline u32 pci_endpoint_test_bar_readl(struct pci_endpoint_test *test, + int bar, int offset) +{ + return readl(test->bar[bar] + offset); +} + +static inline void pci_endpoint_test_bar_writel(struct pci_endpoint_test *test, + int bar, u32 offset, u32 value) +{ + writel(value, test->bar[bar] + offset); +} + +static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id) +{ + struct pci_endpoint_test *test = dev_id; + u32 reg; + + reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); + if (reg & STATUS_IRQ_RAISED) { + test->last_irq = irq; + complete(&test->irq_raised); + reg &= ~STATUS_IRQ_RAISED; + } + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, + reg); + + return IRQ_HANDLED; +} + +static bool pci_endpoint_test_bar(struct pci_endpoint_test *test, + enum pci_barno barno) +{ + int j; + u32 val; + int size; + + if (!test->bar[barno]) + return false; + + size = bar_size[barno]; + + for (j = 0; j < size; j += 4) + pci_endpoint_test_bar_writel(test, barno, j, 0xA0A0A0A0); + + for (j = 0; j < size; j += 4) { + val = pci_endpoint_test_bar_readl(test, barno, j); + if (val != 0xA0A0A0A0) + return false; + } + + return true; +} + +static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test) +{ + u32 val; + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + COMMAND_RAISE_LEGACY_IRQ); + val = wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000)); + if (!val) + return false; + + return true; +} + +static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, + u8 msi_num) +{ + u32 val; + struct pci_dev *pdev = test->pdev; + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + msi_num << MSI_NUMBER_SHIFT | + COMMAND_RAISE_MSI_IRQ); + val = wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000)); + if (!val) + return false; + + if (test->last_irq - pdev->irq == msi_num - 1) + return true; + + return false; +} + +static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) +{ + bool ret = false; + void *src_addr; + void *dst_addr; + dma_addr_t src_phys_addr; + dma_addr_t dst_phys_addr; + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + u32 src_crc32; + u32 dst_crc32; + + src_addr = dma_alloc_coherent(dev, size, &src_phys_addr, GFP_KERNEL); + if (!src_addr) { + dev_err(dev, "failed to allocate source buffer\n"); + ret = false; + goto err; + } + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR, + lower_32_bits(src_phys_addr)); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR, + upper_32_bits(src_phys_addr)); + + get_random_bytes(src_addr, size); + src_crc32 = crc32_le(~0, src_addr, size); + + dst_addr = dma_alloc_coherent(dev, size, &dst_phys_addr, GFP_KERNEL); + if (!dst_addr) { + dev_err(dev, "failed to allocate destination address\n"); + ret = false; + goto err_src_addr; + } + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR, + lower_32_bits(dst_phys_addr)); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR, + upper_32_bits(dst_phys_addr)); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, + size); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + 1 << MSI_NUMBER_SHIFT | COMMAND_COPY); + + wait_for_completion(&test->irq_raised); + + dst_crc32 = crc32_le(~0, dst_addr, size); + if (dst_crc32 == src_crc32) + ret = true; + + dma_free_coherent(dev, size, dst_addr, dst_phys_addr); + +err_src_addr: + dma_free_coherent(dev, size, src_addr, src_phys_addr); + +err: + return ret; +} + +static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) +{ + bool ret = false; + u32 reg; + void *addr; + dma_addr_t phys_addr; + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + u32 crc32; + + addr = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL); + if (!addr) { + dev_err(dev, "failed to allocate address\n"); + ret = false; + goto err; + } + + get_random_bytes(addr, size); + + crc32 = crc32_le(~0, addr, size); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_CHECKSUM, + crc32); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR, + lower_32_bits(phys_addr)); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR, + upper_32_bits(phys_addr)); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + 1 << MSI_NUMBER_SHIFT | COMMAND_READ); + + wait_for_completion(&test->irq_raised); + + reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); + if (reg & STATUS_READ_SUCCESS) + ret = true; + + dma_free_coherent(dev, size, addr, phys_addr); + +err: + return ret; +} + +static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) +{ + bool ret = false; + void *addr; + dma_addr_t phys_addr; + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + u32 crc32; + + addr = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL); + if (!addr) { + dev_err(dev, "failed to allocate destination address\n"); + ret = false; + goto err; + } + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR, + lower_32_bits(phys_addr)); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR, + upper_32_bits(phys_addr)); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, + 1 << MSI_NUMBER_SHIFT | COMMAND_WRITE); + + wait_for_completion(&test->irq_raised); + + crc32 = crc32_le(~0, addr, size); + if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM)) + ret = true; + + dma_free_coherent(dev, size, addr, phys_addr); +err: + return ret; +} + +static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -EINVAL; + enum pci_barno bar; + struct pci_endpoint_test *test = to_endpoint_test(file->private_data); + + mutex_lock(&test->mutex); + switch (cmd) { + case PCITEST_BAR: + bar = arg; + if (bar < 0 || bar > 5) + goto ret; + ret = pci_endpoint_test_bar(test, bar); + break; + case PCITEST_LEGACY_IRQ: + ret = pci_endpoint_test_legacy_irq(test); + break; + case PCITEST_MSI: + ret = pci_endpoint_test_msi_irq(test, arg); + break; + case PCITEST_WRITE: + ret = pci_endpoint_test_write(test, arg); + break; + case PCITEST_READ: + ret = pci_endpoint_test_read(test, arg); + break; + case PCITEST_COPY: + ret = pci_endpoint_test_copy(test, arg); + break; + } + +ret: + mutex_unlock(&test->mutex); + return ret; +} + +static const struct file_operations pci_endpoint_test_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = pci_endpoint_test_ioctl, +}; + +static int pci_endpoint_test_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int i; + int err; + int irq; + int id; + char name[20]; + enum pci_barno bar; + void __iomem *base; + struct device *dev = &pdev->dev; + struct pci_endpoint_test *test; + struct miscdevice *misc_device; + + if (pci_is_bridge(pdev)) + return -ENODEV; + + test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL); + if (!test) + return -ENOMEM; + + test->pdev = pdev; + init_completion(&test->irq_raised); + mutex_init(&test->mutex); + + err = pci_enable_device(pdev); + if (err) { + dev_err(dev, "Cannot enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRV_MODULE_NAME); + if (err) { + dev_err(dev, "Cannot obtain PCI resources\n"); + goto err_disable_pdev; + } + + pci_set_master(pdev); + + irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI); + if (irq < 0) + dev_err(dev, "failed to get MSI interrupts\n"); + + err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler, + IRQF_SHARED, DRV_MODULE_NAME, test); + if (err) { + dev_err(dev, "failed to request IRQ %d\n", pdev->irq); + goto err_disable_msi; + } + + for (i = 1; i < irq; i++) { + err = devm_request_irq(dev, pdev->irq + i, + pci_endpoint_test_irqhandler, + IRQF_SHARED, DRV_MODULE_NAME, test); + if (err) + dev_err(dev, "failed to request IRQ %d for MSI %d\n", + pdev->irq + i, i + 1); + } + + for (bar = BAR_0; bar <= BAR_5; bar++) { + base = pci_ioremap_bar(pdev, bar); + if (!base) { + dev_err(dev, "failed to read BAR%d\n", bar); + WARN_ON(bar == BAR_0); + } + test->bar[bar] = base; + } + + test->base = test->bar[0]; + if (!test->base) { + dev_err(dev, "Cannot perform PCI test without BAR0\n"); + goto err_iounmap; + } + + pci_set_drvdata(pdev, test); + + id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + dev_err(dev, "unable to get id\n"); + goto err_iounmap; + } + + snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id); + misc_device = &test->miscdev; + misc_device->minor = MISC_DYNAMIC_MINOR; + misc_device->name = name; + misc_device->fops = &pci_endpoint_test_fops, + + err = misc_register(misc_device); + if (err) { + dev_err(dev, "failed to register device\n"); + goto err_ida_remove; + } + + return 0; + +err_ida_remove: + ida_simple_remove(&pci_endpoint_test_ida, id); + +err_iounmap: + for (bar = BAR_0; bar <= BAR_5; bar++) { + if (test->bar[bar]) + pci_iounmap(pdev, test->bar[bar]); + } + +err_disable_msi: + pci_disable_msi(pdev); + pci_release_regions(pdev); + +err_disable_pdev: + pci_disable_device(pdev); + + return err; +} + +static void pci_endpoint_test_remove(struct pci_dev *pdev) +{ + int id; + enum pci_barno bar; + struct pci_endpoint_test *test = pci_get_drvdata(pdev); + struct miscdevice *misc_device = &test->miscdev; + + if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1) + return; + + misc_deregister(&test->miscdev); + ida_simple_remove(&pci_endpoint_test_ida, id); + for (bar = BAR_0; bar <= BAR_5; bar++) { + if (test->bar[bar]) + pci_iounmap(pdev, test->bar[bar]); + } + pci_disable_msi(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static const struct pci_device_id pci_endpoint_test_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) }, + { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) }, + { } +}; +MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl); + +static struct pci_driver pci_endpoint_test_driver = { + .name = DRV_MODULE_NAME, + .id_table = pci_endpoint_test_tbl, + .probe = pci_endpoint_test_probe, + .remove = pci_endpoint_test_remove, +}; +module_pci_driver(pci_endpoint_test_driver); + +MODULE_DESCRIPTION("PCI ENDPOINT TEST HOST DRIVER"); +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/sram-exec.c b/drivers/misc/sram-exec.c index ac522417c462..3d528a13b8fc 100644 --- a/drivers/misc/sram-exec.c +++ b/drivers/misc/sram-exec.c @@ -16,9 +16,10 @@ #include <linux/device.h> #include <linux/genalloc.h> +#include <linux/mm.h> #include <linux/sram.h> -#include <asm/cacheflush.h> +#include <asm/set_memory.h> #include "sram.h" diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 87a13374fdc0..adf46072cb37 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -443,9 +443,16 @@ static const struct i2c_device_id tsl2550_id[] = { }; MODULE_DEVICE_TABLE(i2c, tsl2550_id); +static const struct of_device_id tsl2550_of_match[] = { + { .compatible = "taos,tsl2550" }, + { } +}; +MODULE_DEVICE_TABLE(of, tsl2550_of_match); + static struct i2c_driver tsl2550_driver = { .driver = { .name = TSL2550_DRV_NAME, + .of_match_table = tsl2550_of_match, .pm = TSL2550_PM_OPS, }, .probe = tsl2550_probe, diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 498c0854305f..06c4974ee8dd 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -298,8 +298,11 @@ static void *qp_alloc_queue(u64 size, u32 flags) size_t pas_size; size_t vas_size; size_t queue_size = sizeof(*queue) + sizeof(*queue->kernel_if); - const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; + u64 num_pages; + if (size > SIZE_MAX - PAGE_SIZE) + return NULL; + num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; if (num_pages > (SIZE_MAX - queue_size) / (sizeof(*queue->kernel_if->u.g.pas) + @@ -624,9 +627,12 @@ static struct vmci_queue *qp_host_alloc_queue(u64 size) { struct vmci_queue *queue; size_t queue_page_size; - const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; + u64 num_pages; const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if)); + if (size > SIZE_MAX - PAGE_SIZE) + return NULL; + num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1; if (num_pages > (SIZE_MAX - queue_size) / sizeof(*queue->kernel_if->u.h.page)) return NULL; |