char/misc patches for 4.12-rc1

Here is the big set of new char/misc driver drivers and features for
 4.12-rc1.
 
 There's lots of new drivers added this time around, new firmware drivers
 from Google, more auxdisplay drivers, extcon drivers, fpga drivers, and
 a bunch of other driver updates.  Nothing major, except if you happen to
 have the hardware for these drivers, and then you will be happy :)
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <[email protected]>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWQvAgg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yknsACgzkAeyz16Z97J3UTaeejbR7nKUCAAoKY4WEHY
 8O9f9pr9gj8GMBwxeZQa
 =OIfB
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here is the big set of new char/misc driver drivers and features for
  4.12-rc1.

  There's lots of new drivers added this time around, new firmware
  drivers from Google, more auxdisplay drivers, extcon drivers, fpga
  drivers, and a bunch of other driver updates. Nothing major, except if
  you happen to have the hardware for these drivers, and then you will
  be happy :)

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'char-misc-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (136 commits)
  firmware: google memconsole: Fix return value check in platform_memconsole_init()
  firmware: Google VPD: Fix return value check in vpd_platform_init()
  goldfish_pipe: fix build warning about using too much stack.
  goldfish_pipe: An implementation of more parallel pipe
  fpga fr br: update supported version numbers
  fpga: region: release FPGA region reference in error path
  fpga altera-hps2fpga: disable/unprepare clock on error in alt_fpga_bridge_probe()
  mei: drop the TODO from samples
  firmware: Google VPD sysfs driver
  firmware: Google VPD: import lib_vpd source files
  misc: lkdtm: Add volatile to intentional NULL pointer reference
  eeprom: idt_89hpesx: Add OF device ID table
  misc: ds1682: Add OF device ID table
  misc: tsl2550: Add OF device ID table
  w1: Remove unneeded use of assert() and remove w1_log.h
  w1: Use kernel common min() implementation
  uio_mf624: Align memory regions to page size and set correct offsets
  uio_mf624: Refactor memory info initialization
  uio: Allow handling of non page-aligned memory regions
  hangcheck-timer: Fix typo in comment
  ...
This commit is contained in:
Linus Torvalds 2017-05-04 19:07:10 -07:00
commit af82455f7d
166 changed files with 8064 additions and 3259 deletions

View file

@ -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,13 @@ 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
config ASPEED_LPC_CTRL
depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control"
---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
---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).
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 ""
---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
Control Aspeed ast2400/2500 HOST LPC to BMC mappings through
ioctl()s, the driver also provides a read/write interface to a BMC ram
region where the host LPC read/write region can be buffered.
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"

View file

@ -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,7 @@ 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
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
@ -62,6 +61,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 \

View file

@ -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);

View file

@ -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 <[email protected]>");
MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings");

View file

@ -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,

View file

@ -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,

View file

@ -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);

View file

@ -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);
}

View file

@ -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),

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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 */

View file

@ -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);

View file

@ -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));
}
/**

View file

@ -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;

View file

@ -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);

View file

@ -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);
@ -634,6 +585,77 @@ out:
return mask;
}
/**
* 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
*
@ -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
};

View file

@ -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;
@ -578,28 +551,6 @@ int mei_irq_read_handler(struct mei_device *dev,
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);

View file

@ -241,12 +241,39 @@ end:
return err;
}
/**
* mei_me_shutdown - Device Removal Routine
*
* @pdev: PCI device structure
*
* mei_me_shutdown is called from the reboot notifier
* it's a simplified version of remove so we go down
* faster.
*/
static void mei_me_shutdown(struct pci_dev *pdev)
{
struct mei_device *dev;
dev = pci_get_drvdata(pdev);
if (!dev)
return;
dev_dbg(&pdev->dev, "shutdown\n");
mei_stop(dev);
if (!pci_dev_run_wake(pdev))
mei_me_unset_pm_domain(dev);
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
}
/**
* mei_me_remove - Device Removal Routine
*
* @pdev: PCI device structure
*
* mei_remove is called by the PCI subsystem to alert the driver
* mei_me_remove is called by the PCI subsystem to alert the driver
* that it should release a PCI device.
*/
static void mei_me_remove(struct pci_dev *pdev)
@ -456,7 +483,7 @@ static struct pci_driver mei_me_driver = {
.id_table = mei_me_pci_tbl,
.probe = mei_me_probe,
.remove = mei_me_remove,
.shutdown = mei_me_remove,
.shutdown = mei_me_shutdown,
.driver.pm = MEI_ME_PM_OPS,
};

View file

@ -160,6 +160,33 @@ end:
return err;
}
/**
* mei_txe_remove - Device Shutdown Routine
*
* @pdev: PCI device structure
*
* mei_txe_shutdown is called from the reboot notifier
* it's a simplified version of remove so we go down
* faster.
*/
static void mei_txe_shutdown(struct pci_dev *pdev)
{
struct mei_device *dev;
dev = pci_get_drvdata(pdev);
if (!dev)
return;
dev_dbg(&pdev->dev, "shutdown\n");
mei_stop(dev);
if (!pci_dev_run_wake(pdev))
mei_txe_unset_pm_domain(dev);
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
}
/**
* mei_txe_remove - Device Removal Routine
*
@ -386,7 +413,7 @@ static struct pci_driver mei_txe_driver = {
.id_table = mei_txe_pci_tbl,
.probe = mei_txe_probe,
.remove = mei_txe_remove,
.shutdown = mei_txe_remove,
.shutdown = mei_txe_shutdown,
.driver.pm = MEI_TXE_PM_OPS,
};

File diff suppressed because it is too large Load diff

View file

@ -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,