From ddc5c9a37be4517270453ce909d3dfcc8de58230 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sat, 3 Dec 2016 17:29:28 +0800 Subject: misc: set error code when devm_kstrdup fails In function sram_reserve_regions(), the value of return variable ret should be negative on failures. However, the value of ret may be 0 even if the call to devm_kstrdup() returns a NULL pointer. This patch explicitly assigns "-ENOMEM" to ret on the path that devm_kstrdup() fails. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=188651 Signed-off-by: Pan Bian Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index b33ab8ce47ab..07ec2a8a9343 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -249,8 +249,10 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) block->label = devm_kstrdup(sram->dev, label, GFP_KERNEL); - if (!block->label) + if (!block->label) { + ret = -ENOMEM; goto err_chunks; + } dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x\n", block->export ? "exported " : "", block->label, -- cgit v1.2.1 From 816c9311f1144a03da1fdc4feb2f6b0d3299fca0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 9 Jan 2017 11:20:16 +0300 Subject: misc: mic: double free on ioctl error path This function only has one caller. Freeing "vdev" here leads to a use after free bug. There are several other error paths in this function but this is the only one which frees "vdev". It looks like the kfree() can be safely removed. Fixes: 61e9c905df78 ("misc: mic: Enable VOP host side functionality") Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_vringh.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index 88e45234d527..fed992e2c258 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -292,7 +292,6 @@ static int vop_virtio_add_device(struct vop_vdev *vdev, if (ret) { dev_err(vop_dev(vdev), "%s %d err %d\n", __func__, __LINE__, ret); - kfree(vdev); return ret; } -- cgit v1.2.1 From e1873a479a4ac536661ad951a4b10fe1b26cec7b Mon Sep 17 00:00:00 2001 From: Cao jin Date: Mon, 19 Dec 2016 15:23:56 +0800 Subject: genwqe: drop .link_reset() In AER recovery, pci_error_handlers.link_reset() is never called, drop it now. Signed-off-by: Cao jin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 6c1f49a85023..4fd21e86ad56 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -1336,7 +1336,6 @@ static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) static struct pci_error_handlers genwqe_err_handler = { .error_detected = genwqe_err_error_detected, .mmio_enabled = genwqe_err_result_none, - .link_reset = genwqe_err_result_none, .slot_reset = genwqe_err_slot_reset, .resume = genwqe_err_resume, }; -- cgit v1.2.1 From cfad6425382ec81acd073fdf24361e1c30b4cb36 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 13 Jan 2017 15:16:52 +0300 Subject: eeprom: Add IDT 89HPESx EEPROM/CSR driver This driver provides an access to EEPROM of IDT PCIe-switches. IDT PCIe- switches expose a simple SMBus interface to perform IO-operations from/to EEPROM, which is located at private (so called Master) SMBus. The driver creates a simple binary sysfs-file to have an access to the EEPROM using the SMBus-slave interface in the i2c-device susfs-directory: /sys/bus/i2c/devices/-/eeprom In case if read-only flag is specified at dts-node of the device, User-space applications won't be able to write to the EEPROM sysfs-node. Additionally IDT 89HPESx SMBus interface has an ability to read/write values of device CSRs. This driver exposes debugfs-file to perform simple IO-operations using that ability for just basic debug purpose. Particularly the next file is created in the specific debugfs-directory: /sys/kernel/debug/idt_csr/ Format of the debugfs-file value is: $ cat /sys/kernel/debug/idt_csr/-/; : So reading the content of the file gives current CSR address and it value. If User-space application wishes to change current CSR address, it can just write a proper value to the sysfs-file: $ echo "" > /sys/kernel/debug/idt_csr/-/ If it wants to change the CSR value as well, the format of the write operation is: $ echo ":" > \ /sys/kernel/debug/idt_csr/-/; CSR address and value can be any of hexadecimal, decimal or octal format. Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/Kconfig | 10 + drivers/misc/eeprom/Makefile | 1 + drivers/misc/eeprom/idt_89hpesx.c | 1587 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1598 insertions(+) create mode 100644 drivers/misc/eeprom/idt_89hpesx.c (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index c4e41c26649e..de58762097c4 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -100,4 +100,14 @@ config EEPROM_DIGSY_MTC_CFG If unsure, say N. +config EEPROM_IDT_89HPESX + tristate "IDT 89HPESx PCIe-swtiches EEPROM / CSR support" + depends on I2C && SYSFS + help + Enable this driver to get read/write access to EEPROM / CSRs + over IDT PCIe-swtich i2c-slave interface. + + This driver can also be built as a module. If so, the module + will be called idt_89hpesx. + endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index fc1e81d29267..90a52624ddeb 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o +obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c new file mode 100644 index 000000000000..25d47d09e1cb --- /dev/null +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -0,0 +1,1587 @@ +/* + * This file is provided under a GPLv2 license. When using or + * redistributing this file, you may do so under that license. + * + * GPL LICENSE SUMMARY + * + * Copyright (C) 2016 T-Platforms. All Rights Reserved. + * + * 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 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, it can be found . + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * IDT PCIe-switch NTB Linux driver + * + * Contact Information: + * Serge Semin , + */ +/* + * NOTE of the IDT 89HPESx SMBus-slave interface driver + * This driver primarily is developed to have an access to EEPROM device of + * IDT PCIe-switches. IDT provides a simple SMBus interface to perform IO- + * operations from/to EEPROM, which is located at private (so called Master) + * SMBus of switches. Using that interface this the driver creates a simple + * binary sysfs-file in the device directory: + * /sys/bus/i2c/devices/-/eeprom + * In case if read-only flag is specified in the dts-node of device desription, + * User-space applications won't be able to write to the EEPROM sysfs-node. + * Additionally IDT 89HPESx SMBus interface has an ability to write/read + * data of device CSRs. This driver exposes debugf-file to perform simple IO + * operations using that ability for just basic debug purpose. Particularly + * next file is created in the specific debugfs-directory: + * /sys/kernel/debug/idt_csr/ + * Format of the debugfs-node is: + * $ cat /sys/kernel/debug/idt_csr/-/; + * : + * So reading the content of the file gives current CSR address and it value. + * If User-space application wishes to change current CSR address, + * it can just write a proper value to the sysfs-file: + * $ echo "" > /sys/kernel/debug/idt_csr/-/ + * If it wants to change the CSR value as well, the format of the write + * operation is: + * $ echo ":" > \ + * /sys/kernel/debug/idt_csr/-/; + * CSR address and value can be any of hexadecimal, decimal or octal format. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IDT_NAME "89hpesx" +#define IDT_89HPESX_DESC "IDT 89HPESx SMBus-slave interface driver" +#define IDT_89HPESX_VER "1.0" + +MODULE_DESCRIPTION(IDT_89HPESX_DESC); +MODULE_VERSION(IDT_89HPESX_VER); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("T-platforms"); + +/* + * csr_dbgdir - CSR read/write operations Debugfs directory + */ +static struct dentry *csr_dbgdir; + +/* + * struct idt_89hpesx_dev - IDT 89HPESx device data structure + * @eesize: Size of EEPROM in bytes (calculated from "idt,eecompatible") + * @eero: EEPROM Read-only flag + * @eeaddr: EEPROM custom address + * + * @inieecmd: Initial cmd value for EEPROM read/write operations + * @inicsrcmd: Initial cmd value for CSR read/write operations + * @iniccode: Initialial command code value for IO-operations + * + * @csr: CSR address to perform read operation + * + * @smb_write: SMBus write method + * @smb_read: SMBus read method + * @smb_mtx: SMBus mutex + * + * @client: i2c client used to perform IO operations + * + * @ee_file: EEPROM read/write sysfs-file + * @csr_file: CSR read/write debugfs-node + */ +struct idt_smb_seq; +struct idt_89hpesx_dev { + u32 eesize; + bool eero; + u8 eeaddr; + + u8 inieecmd; + u8 inicsrcmd; + u8 iniccode; + + u16 csr; + + int (*smb_write)(struct idt_89hpesx_dev *, const struct idt_smb_seq *); + int (*smb_read)(struct idt_89hpesx_dev *, struct idt_smb_seq *); + struct mutex smb_mtx; + + struct i2c_client *client; + + struct bin_attribute *ee_file; + struct dentry *csr_dir; + struct dentry *csr_file; +}; + +/* + * struct idt_smb_seq - sequence of data to be read/written from/to IDT 89HPESx + * @ccode: SMBus command code + * @bytecnt: Byte count of operation + * @data: Data to by written + */ +struct idt_smb_seq { + u8 ccode; + u8 bytecnt; + u8 *data; +}; + +/* + * struct idt_eeprom_seq - sequence of data to be read/written from/to EEPROM + * @cmd: Transaction CMD + * @eeaddr: EEPROM custom address + * @memaddr: Internal memory address of EEPROM + * @data: Data to be written at the memory address + */ +struct idt_eeprom_seq { + u8 cmd; + u8 eeaddr; + u16 memaddr; + u8 data; +} __packed; + +/* + * struct idt_csr_seq - sequence of data to be read/written from/to CSR + * @cmd: Transaction CMD + * @csraddr: Internal IDT device CSR address + * @data: Data to be read/written from/to the CSR address + */ +struct idt_csr_seq { + u8 cmd; + u16 csraddr; + u32 data; +} __packed; + +/* + * SMBus command code macros + * @CCODE_END: Indicates the end of transaction + * @CCODE_START: Indicates the start of transaction + * @CCODE_CSR: CSR read/write transaction + * @CCODE_EEPROM: EEPROM read/write transaction + * @CCODE_BYTE: Supplied data has BYTE length + * @CCODE_WORD: Supplied data has WORD length + * @CCODE_BLOCK: Supplied data has variable length passed in bytecnt + * byte right following CCODE byte + */ +#define CCODE_END ((u8)0x01) +#define CCODE_START ((u8)0x02) +#define CCODE_CSR ((u8)0x00) +#define CCODE_EEPROM ((u8)0x04) +#define CCODE_BYTE ((u8)0x00) +#define CCODE_WORD ((u8)0x20) +#define CCODE_BLOCK ((u8)0x40) +#define CCODE_PEC ((u8)0x80) + +/* + * EEPROM command macros + * @EEPROM_OP_WRITE: EEPROM write operation + * @EEPROM_OP_READ: EEPROM read operation + * @EEPROM_USA: Use specified address of EEPROM + * @EEPROM_NAERR: EEPROM device is not ready to respond + * @EEPROM_LAERR: EEPROM arbitration loss error + * @EEPROM_MSS: EEPROM misplace start & stop bits error + * @EEPROM_WR_CNT: Bytes count to perform write operation + * @EEPROM_WRRD_CNT: Bytes count to write before reading + * @EEPROM_RD_CNT: Bytes count to perform read operation + * @EEPROM_DEF_SIZE: Fall back size of EEPROM + * @EEPROM_DEF_ADDR: Defatul EEPROM address + * @EEPROM_TOUT: Timeout before retry read operation if eeprom is busy + */ +#define EEPROM_OP_WRITE ((u8)0x00) +#define EEPROM_OP_READ ((u8)0x01) +#define EEPROM_USA ((u8)0x02) +#define EEPROM_NAERR ((u8)0x08) +#define EEPROM_LAERR ((u8)0x10) +#define EEPROM_MSS ((u8)0x20) +#define EEPROM_WR_CNT ((u8)5) +#define EEPROM_WRRD_CNT ((u8)4) +#define EEPROM_RD_CNT ((u8)5) +#define EEPROM_DEF_SIZE ((u16)4096) +#define EEPROM_DEF_ADDR ((u8)0x50) +#define EEPROM_TOUT (100) + +/* + * CSR command macros + * @CSR_DWE: Enable all four bytes of the operation + * @CSR_OP_WRITE: CSR write operation + * @CSR_OP_READ: CSR read operation + * @CSR_RERR: Read operation error + * @CSR_WERR: Write operation error + * @CSR_WR_CNT: Bytes count to perform write operation + * @CSR_WRRD_CNT: Bytes count to write before reading + * @CSR_RD_CNT: Bytes count to perform read operation + * @CSR_MAX: Maximum CSR address + * @CSR_DEF: Default CSR address + * @CSR_REAL_ADDR: CSR real unshifted address + */ +#define CSR_DWE ((u8)0x0F) +#define CSR_OP_WRITE ((u8)0x00) +#define CSR_OP_READ ((u8)0x10) +#define CSR_RERR ((u8)0x40) +#define CSR_WERR ((u8)0x80) +#define CSR_WR_CNT ((u8)7) +#define CSR_WRRD_CNT ((u8)3) +#define CSR_RD_CNT ((u8)7) +#define CSR_MAX ((u32)0x3FFFF) +#define CSR_DEF ((u16)0x0000) +#define CSR_REAL_ADDR(val) ((unsigned int)val << 2) + +/* + * IDT 89HPESx basic register + * @IDT_VIDDID_CSR: PCIe VID and DID of IDT 89HPESx + * @IDT_VID_MASK: Mask of VID + */ +#define IDT_VIDDID_CSR ((u32)0x0000) +#define IDT_VID_MASK ((u32)0xFFFF) + +/* + * IDT 89HPESx can send NACK when new command is sent before previous one + * fininshed execution. In this case driver retries operation + * certain times. + * @RETRY_CNT: Number of retries before giving up and fail + * @idt_smb_safe: Generate a retry loop on corresponding SMBus method + */ +#define RETRY_CNT (128) +#define idt_smb_safe(ops, args...) ({ \ + int __retry = RETRY_CNT; \ + s32 __sts; \ + do { \ + __sts = i2c_smbus_ ## ops ## _data(args); \ + } while (__retry-- && __sts < 0); \ + __sts; \ +}) + +/*=========================================================================== + * i2c bus level IO-operations + *=========================================================================== + */ + +/* + * idt_smb_write_byte() - SMBus write method when I2C_SMBUS_BYTE_DATA operation + * is only available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_byte(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx; + + /* Loop over the supplied data sending byte one-by-one */ + for (idx = 0; idx < seq->bytecnt; idx++) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE; + if (idx == 0) + ccode |= CCODE_START; + if (idx == seq->bytecnt - 1) + ccode |= CCODE_END; + + /* Send data to the device */ + sts = idt_smb_safe(write_byte, pdev->client, ccode, + seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + return 0; +} + +/* + * idt_smb_read_byte() - SMBus read method when I2C_SMBUS_BYTE_DATA operation + * is only available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_byte(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx; + + /* Loop over the supplied buffer receiving byte one-by-one */ + for (idx = 0; idx < seq->bytecnt; idx++) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE; + if (idx == 0) + ccode |= CCODE_START; + if (idx == seq->bytecnt - 1) + ccode |= CCODE_END; + + /* Read data from the device */ + sts = idt_smb_safe(read_byte, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + seq->data[idx] = (u8)sts; + } + + return 0; +} + +/* + * idt_smb_write_word() - SMBus write method when I2C_SMBUS_BYTE_DATA and + * I2C_FUNC_SMBUS_WORD_DATA operations are available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_word(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx, evencnt; + + /* Calculate the even count of data to send */ + evencnt = seq->bytecnt - (seq->bytecnt % 2); + + /* Loop over the supplied data sending two bytes at a time */ + for (idx = 0; idx < evencnt; idx += 2) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_WORD; + if (idx == 0) + ccode |= CCODE_START; + if (idx == evencnt - 2) + ccode |= CCODE_END; + + /* Send word data to the device */ + sts = idt_smb_safe(write_word, pdev->client, ccode, + *(u16 *)&seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + /* If there is odd number of bytes then send just one last byte */ + if (seq->bytecnt != evencnt) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE | CCODE_END; + if (idx == 0) + ccode |= CCODE_START; + + /* Send byte data to the device */ + sts = idt_smb_safe(write_byte, pdev->client, ccode, + seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + return 0; +} + +/* + * idt_smb_read_word() - SMBus read method when I2C_SMBUS_BYTE_DATA and + * I2C_FUNC_SMBUS_WORD_DATA operations are available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_word(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx, evencnt; + + /* Calculate the even count of data to send */ + evencnt = seq->bytecnt - (seq->bytecnt % 2); + + /* Loop over the supplied data reading two bytes at a time */ + for (idx = 0; idx < evencnt; idx += 2) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_WORD; + if (idx == 0) + ccode |= CCODE_START; + if (idx == evencnt - 2) + ccode |= CCODE_END; + + /* Read word data from the device */ + sts = idt_smb_safe(read_word, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + *(u16 *)&seq->data[idx] = (u16)sts; + } + + /* If there is odd number of bytes then receive just one last byte */ + if (seq->bytecnt != evencnt) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE | CCODE_END; + if (idx == 0) + ccode |= CCODE_START; + + /* Read last data byte from the device */ + sts = idt_smb_safe(read_byte, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + seq->data[idx] = (u8)sts; + } + + return 0; +} + +/* + * idt_smb_write_block() - SMBus write method when I2C_SMBUS_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_block(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + u8 ccode; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Send block of data to the device */ + return idt_smb_safe(write_block, pdev->client, ccode, seq->bytecnt, + seq->data); +} + +/* + * idt_smb_read_block() - SMBus read method when I2C_SMBUS_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_block(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Read block of data from the device */ + sts = idt_smb_safe(read_block, pdev->client, ccode, seq->data); + if (sts != seq->bytecnt) + return (sts < 0 ? sts : -ENODATA); + + return 0; +} + +/* + * idt_smb_write_i2c_block() - SMBus write method when I2C_SMBUS_I2C_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + * + * NOTE It's usual SMBus write block operation, except the actual data length is + * sent as first byte of data + */ +static int idt_smb_write_i2c_block(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the data to send. Length byte must be added prior the data */ + buf[0] = seq->bytecnt; + memcpy(&buf[1], seq->data, seq->bytecnt); + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Send length and block of data to the device */ + return idt_smb_safe(write_i2c_block, pdev->client, ccode, + seq->bytecnt + 1, buf); +} + +/* + * idt_smb_read_i2c_block() - SMBus read method when I2C_SMBUS_I2C_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + * + * NOTE It's usual SMBus read block operation, except the actual data length is + * retrieved as first byte of data + */ +static int idt_smb_read_i2c_block(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; + s32 sts; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Read length and block of data from the device */ + sts = idt_smb_safe(read_i2c_block, pdev->client, ccode, + seq->bytecnt + 1, buf); + if (sts != seq->bytecnt + 1) + return (sts < 0 ? sts : -ENODATA); + if (buf[0] != seq->bytecnt) + return -ENODATA; + + /* Copy retrieved data to the output data buffer */ + memcpy(seq->data, &buf[1], seq->bytecnt); + + return 0; +} + +/*=========================================================================== + * EEPROM IO-operations + *=========================================================================== + */ + +/* + * idt_eeprom_read_byte() - read just one byte from EEPROM + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @data: Data to be written to EEPROM + */ +static int idt_eeprom_read_byte(struct idt_89hpesx_dev *pdev, u16 memaddr, + u8 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_eeprom_seq eeseq; + struct idt_smb_seq smbseq; + int ret, retry; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_EEPROM; + smbseq.data = (u8 *)&eeseq; + + /* + * Sometimes EEPROM may respond with NACK if it's busy with previous + * operation, so we need to perform a few attempts of read cycle + */ + retry = RETRY_CNT; + do { + /* Send EEPROM memory address to read data from */ + smbseq.bytecnt = EEPROM_WRRD_CNT; + eeseq.cmd = pdev->inieecmd | EEPROM_OP_READ; + eeseq.eeaddr = pdev->eeaddr; + eeseq.memaddr = cpu_to_le16(memaddr); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init eeprom addr 0x%02hhx", + memaddr); + break; + } + + /* Perform read operation */ + smbseq.bytecnt = EEPROM_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read eeprom data 0x%02hhx", + memaddr); + break; + } + + /* Restart read operation if the device is busy */ + if (retry && (eeseq.cmd & EEPROM_NAERR)) { + dev_dbg(dev, "EEPROM busy, retry reading after %d ms", + EEPROM_TOUT); + msleep(EEPROM_TOUT); + continue; + } + + /* Check whether IDT successfully read data from EEPROM */ + if (eeseq.cmd & (EEPROM_NAERR | EEPROM_LAERR | EEPROM_MSS)) { + dev_err(dev, + "Communication with eeprom failed, cmd 0x%hhx", + eeseq.cmd); + ret = -EREMOTEIO; + break; + } + + /* Save retrieved data and exit the loop */ + *data = eeseq.data; + break; + } while (retry--); + + /* Return the status of operation */ + return ret; +} + +/* + * idt_eeprom_write() - EEPROM write operation + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @len: Length of data to be written + * @data: Data to be written to EEPROM + */ +static int idt_eeprom_write(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, + const u8 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_eeprom_seq eeseq; + struct idt_smb_seq smbseq; + int ret; + u16 idx; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_EEPROM; + smbseq.data = (u8 *)&eeseq; + + /* Send data byte-by-byte, checking if it is successfully written */ + for (idx = 0; idx < len; idx++, memaddr++) { + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Perform write operation */ + smbseq.bytecnt = EEPROM_WR_CNT; + eeseq.cmd = pdev->inieecmd | EEPROM_OP_WRITE; + eeseq.eeaddr = pdev->eeaddr; + eeseq.memaddr = cpu_to_le16(memaddr); + eeseq.data = data[idx]; + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, + "Failed to write 0x%04hx:0x%02hhx to eeprom", + memaddr, data[idx]); + goto err_mutex_unlock; + } + + /* + * Check whether the data is successfully written by reading + * from the same EEPROM memory address. + */ + eeseq.data = ~data[idx]; + ret = idt_eeprom_read_byte(pdev, memaddr, &eeseq.data); + if (ret != 0) + goto err_mutex_unlock; + + /* Check whether the read byte is the same as written one */ + if (eeseq.data != data[idx]) { + dev_err(dev, "Values don't match 0x%02hhx != 0x%02hhx", + eeseq.data, data[idx]); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + if (ret != 0) + return ret; + } + + return 0; +} + +/* + * idt_eeprom_read() - EEPROM read operation + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @len: Length of data to read + * @buf: Buffer to read data to + */ +static int idt_eeprom_read(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, + u8 *buf) +{ + int ret; + u16 idx; + + /* Read data byte-by-byte, retrying if it wasn't successful */ + for (idx = 0; idx < len; idx++, memaddr++) { + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Just read the byte to the buffer */ + ret = idt_eeprom_read_byte(pdev, memaddr, &buf[idx]); + + /* Unlock IDT SMBus device */ + mutex_unlock(&pdev->smb_mtx); + + /* Return error if read operation failed */ + if (ret != 0) + return ret; + } + + return 0; +} + +/*=========================================================================== + * CSR IO-operations + *=========================================================================== + */ + +/* + * idt_csr_write() - CSR write operation + * @pdev: Pointer to the driver data + * @csraddr: CSR address (with no two LS bits) + * @data: Data to be written to CSR + */ +static int idt_csr_write(struct idt_89hpesx_dev *pdev, u16 csraddr, + const u32 data) +{ + struct device *dev = &pdev->client->dev; + struct idt_csr_seq csrseq; + struct idt_smb_seq smbseq; + int ret; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_CSR; + smbseq.data = (u8 *)&csrseq; + + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Perform write operation */ + smbseq.bytecnt = CSR_WR_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_WRITE; + csrseq.csraddr = cpu_to_le16(csraddr); + csrseq.data = cpu_to_le32(data); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to write 0x%04x: 0x%04x to csr", + CSR_REAL_ADDR(csraddr), data); + goto err_mutex_unlock; + } + + /* Send CSR address to read data from */ + smbseq.bytecnt = CSR_WRRD_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init csr address 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Perform read operation */ + smbseq.bytecnt = CSR_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read csr 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Check whether IDT successfully retrieved CSR data */ + if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { + dev_err(dev, "IDT failed to perform CSR r/w"); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + + return ret; +} + +/* + * idt_csr_read() - CSR read operation + * @pdev: Pointer to the driver data + * @csraddr: CSR address (with no two LS bits) + * @data: Data to be written to CSR + */ +static int idt_csr_read(struct idt_89hpesx_dev *pdev, u16 csraddr, u32 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_csr_seq csrseq; + struct idt_smb_seq smbseq; + int ret; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_CSR; + smbseq.data = (u8 *)&csrseq; + + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Send CSR register address before reading it */ + smbseq.bytecnt = CSR_WRRD_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; + csrseq.csraddr = cpu_to_le16(csraddr); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init csr address 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Perform read operation */ + smbseq.bytecnt = CSR_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read csr 0x%04hx", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Check whether IDT successfully retrieved CSR data */ + if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { + dev_err(dev, "IDT failed to perform CSR r/w"); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Save data retrieved from IDT */ + *data = le32_to_cpu(csrseq.data); + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + + return ret; +} + +/*=========================================================================== + * Sysfs/debugfs-nodes IO-operations + *=========================================================================== + */ + +/* + * eeprom_write() - EEPROM sysfs-node write callback + * @filep: Pointer to the file system node + * @kobj: Pointer to the kernel object related to the sysfs-node + * @attr: Attributes of the file + * @buf: Buffer to write data to + * @off: Offset at which data should be written to + * @count: Number of bytes to write + */ +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Retrieve driver data */ + pdev = dev_get_drvdata(kobj_to_dev(kobj)); + + /* Perform EEPROM write operation */ + ret = idt_eeprom_write(pdev, (u16)off, (u16)count, (u8 *)buf); + return (ret != 0 ? ret : count); +} + +/* + * eeprom_read() - EEPROM sysfs-node read callback + * @filep: Pointer to the file system node + * @kobj: Pointer to the kernel object related to the sysfs-node + * @attr: Attributes of the file + * @buf: Buffer to write data to + * @off: Offset at which data should be written to + * @count: Number of bytes to write + */ +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Retrieve driver data */ + pdev = dev_get_drvdata(kobj_to_dev(kobj)); + + /* Perform EEPROM read operation */ + ret = idt_eeprom_read(pdev, (u16)off, (u16)count, (u8 *)buf); + return (ret != 0 ? ret : count); +} + +/* + * idt_dbgfs_csr_write() - CSR debugfs-node write callback + * @filep: Pointer to the file system file descriptor + * @buf: Buffer to read data from + * @count: Size of the buffer + * @offp: Offset within the file + * + * It accepts either "0x:0x" for saving register address + * and writing value to specified DWORD register or "0x" for + * just saving register address in order to perform next read operation. + * + * WARNING No spaces are allowed. Incoming string must be strictly formated as: + * ":". Register address must be aligned within 4 bytes + * (one DWORD). + */ +static ssize_t idt_dbgfs_csr_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *offp) +{ + struct idt_89hpesx_dev *pdev = filep->private_data; + char *colon_ch, *csraddr_str, *csrval_str; + int ret, csraddr_len, csrval_len; + u32 csraddr, csrval; + char *buf; + + /* Copy data from User-space */ + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, offp, ubuf, count); + if (ret < 0) + goto free_buf; + buf[count] = 0; + + /* Find position of colon in the buffer */ + colon_ch = strnchr(buf, count, ':'); + + /* + * If there is colon passed then new CSR value should be parsed as + * well, so allocate buffer for CSR address substring. + * If no colon is found, then string must have just one number with + * no new CSR value + */ + if (colon_ch != NULL) { + csraddr_len = colon_ch - buf; + csraddr_str = + kmalloc(sizeof(char)*(csraddr_len + 1), GFP_KERNEL); + if (csraddr_str == NULL) + return -ENOMEM; + /* Copy the register address to the substring buffer */ + strncpy(csraddr_str, buf, csraddr_len); + csraddr_str[csraddr_len] = '\0'; + /* Register value must follow the colon */ + csrval_str = colon_ch + 1; + csrval_len = strnlen(csrval_str, count - csraddr_len); + } else /* if (str_colon == NULL) */ { + csraddr_str = (char *)buf; /* Just to shut warning up */ + csraddr_len = strnlen(csraddr_str, count); + csrval_str = NULL; + csrval_len = 0; + } + + /* Convert CSR address to u32 value */ + ret = kstrtou32(csraddr_str, 0, &csraddr); + if (ret != 0) + goto free_csraddr_str; + + /* Check whether passed register address is valid */ + if (csraddr > CSR_MAX || !IS_ALIGNED(csraddr, SZ_4)) { + ret = -EINVAL; + goto free_csraddr_str; + } + + /* Shift register address to the right so to have u16 address */ + pdev->csr = (csraddr >> 2); + + /* Parse new CSR value and send it to IDT, if colon has been found */ + if (colon_ch != NULL) { + ret = kstrtou32(csrval_str, 0, &csrval); + if (ret != 0) + goto free_csraddr_str; + + ret = idt_csr_write(pdev, pdev->csr, csrval); + if (ret != 0) + goto free_csraddr_str; + } + + /* Free memory only if colon has been found */ +free_csraddr_str: + if (colon_ch != NULL) + kfree(csraddr_str); + + /* Free buffer allocated for data retrieved from User-space */ +free_buf: + kfree(buf); + + return (ret != 0 ? ret : count); +} + +/* + * idt_dbgfs_csr_read() - CSR debugfs-node read callback + * @filep: Pointer to the file system file descriptor + * @buf: Buffer to write data to + * @count: Size of the buffer + * @offp: Offset within the file + * + * It just prints the pair "0x:0x" to passed buffer. + */ +#define CSRBUF_SIZE ((size_t)32) +static ssize_t idt_dbgfs_csr_read(struct file *filep, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct idt_89hpesx_dev *pdev = filep->private_data; + u32 csraddr, csrval; + char buf[CSRBUF_SIZE]; + int ret, size; + + /* Perform CSR read operation */ + ret = idt_csr_read(pdev, pdev->csr, &csrval); + if (ret != 0) + return ret; + + /* Shift register address to the left so to have real address */ + csraddr = ((u32)pdev->csr << 2); + + /* Print the "0x:0x" to buffer */ + size = snprintf(buf, CSRBUF_SIZE, "0x%05x:0x%08x\n", + (unsigned int)csraddr, (unsigned int)csrval); + + /* Copy data to User-space */ + return simple_read_from_buffer(ubuf, count, offp, buf, size); +} + +/* + * eeprom_attribute - EEPROM sysfs-node attributes + * + * NOTE Size will be changed in compliance with OF node. EEPROM attribute will + * be read-only as well if the corresponding flag is specified in OF node. + */ +static BIN_ATTR_RW(eeprom, EEPROM_DEF_SIZE); + +/* + * csr_dbgfs_ops - CSR debugfs-node read/write operations + */ +static const struct file_operations csr_dbgfs_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = idt_dbgfs_csr_write, + .read = idt_dbgfs_csr_read +}; + +/*=========================================================================== + * Driver init/deinit methods + *=========================================================================== + */ + +/* + * idt_set_defval() - disable EEPROM access by default + * @pdev: Pointer to the driver data + */ +static void idt_set_defval(struct idt_89hpesx_dev *pdev) +{ + /* If OF info is missing then use next values */ + pdev->eesize = 0; + pdev->eero = true; + pdev->inieecmd = 0; + pdev->eeaddr = 0; +} + +#ifdef CONFIG_OF +static const struct i2c_device_id ee_ids[]; +/* + * idt_ee_match_id() - check whether the node belongs to compatible EEPROMs + */ +static const struct i2c_device_id *idt_ee_match_id(struct device_node *node) +{ + const struct i2c_device_id *id = ee_ids; + char devname[I2C_NAME_SIZE]; + + /* Retrieve the device name without manufacturer name */ + if (of_modalias_node(node, devname, sizeof(devname))) + return NULL; + + /* Search through the device name */ + while (id->name[0]) { + if (strcmp(devname, id->name) == 0) + return id; + id++; + } + return NULL; +} + +/* + * idt_get_ofdata() - get IDT i2c-device parameters from device tree + * @pdev: Pointer to the driver data + */ +static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) +{ + const struct device_node *node = pdev->client->dev.of_node; + struct device *dev = &pdev->client->dev; + + /* Read dts node parameters */ + if (node) { + const struct i2c_device_id *ee_id = NULL; + struct device_node *child; + const __be32 *addr_be; + int len; + + /* Walk through all child nodes looking for compatible one */ + for_each_available_child_of_node(node, child) { + ee_id = idt_ee_match_id(child); + if (IS_ERR_OR_NULL(ee_id)) { + dev_warn(dev, "Skip unsupported child node %s", + child->full_name); + continue; + } else + break; + } + + /* If there is no child EEPROM device, then set zero size */ + if (!ee_id) { + idt_set_defval(pdev); + return; + } + + /* Retrieve EEPROM size */ + pdev->eesize = (u32)ee_id->driver_data; + + /* Get custom EEPROM address from 'reg' attribute */ + addr_be = of_get_property(child, "reg", &len); + if (!addr_be || (len < sizeof(*addr_be))) { + dev_warn(dev, "No reg on %s, use default address %d", + child->full_name, EEPROM_DEF_ADDR); + pdev->inieecmd = 0; + pdev->eeaddr = EEPROM_DEF_ADDR << 1; + } else { + pdev->inieecmd = EEPROM_USA; + pdev->eeaddr = be32_to_cpup(addr_be) << 1; + } + + /* Check EEPROM 'read-only' flag */ + if (of_get_property(child, "read-only", NULL)) + pdev->eero = true; + else /* if (!of_get_property(node, "read-only", NULL)) */ + pdev->eero = false; + + dev_dbg(dev, "EEPROM of %u bytes found by %hhu", + pdev->eesize, pdev->eeaddr); + } else { + dev_warn(dev, "No dts node, EEPROM access disabled"); + idt_set_defval(pdev); + } +} +#else +static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + + dev_warn(dev, "OF table is unsupported, EEPROM access disabled"); + + /* Nothing we can do, just set the default values */ + idt_set_defval(pdev); +} +#endif /* CONFIG_OF */ + +/* + * idt_create_pdev() - create and init data structure of the driver + * @client: i2c client of IDT PCIe-switch device + */ +static struct idt_89hpesx_dev *idt_create_pdev(struct i2c_client *client) +{ + struct idt_89hpesx_dev *pdev; + + /* Allocate memory for driver data */ + pdev = devm_kmalloc(&client->dev, sizeof(struct idt_89hpesx_dev), + GFP_KERNEL); + if (pdev == NULL) + return ERR_PTR(-ENOMEM); + + /* Initialize basic fields of the data */ + pdev->client = client; + i2c_set_clientdata(client, pdev); + + /* Read OF nodes information */ + idt_get_ofdata(pdev); + + /* Initialize basic CSR CMD field - use full DWORD-sized r/w ops */ + pdev->inicsrcmd = CSR_DWE; + pdev->csr = CSR_DEF; + + /* Enable Packet Error Checking if it's supported by adapter */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) { + pdev->iniccode = CCODE_PEC; + client->flags |= I2C_CLIENT_PEC; + } else /* PEC is unsupported */ { + pdev->iniccode = 0; + } + + return pdev; +} + +/* + * idt_free_pdev() - free data structure of the driver + * @pdev: Pointer to the driver data + */ +static void idt_free_pdev(struct idt_89hpesx_dev *pdev) +{ + /* Clear driver data from device private field */ + i2c_set_clientdata(pdev->client, NULL); + + /* Just free memory allocated for data */ + devm_kfree(&pdev->client->dev, pdev); +} + +/* + * idt_set_smbus_ops() - set supported SMBus operations + * @pdev: Pointer to the driver data + * Return status of smbus check operations + */ +static int idt_set_smbus_ops(struct idt_89hpesx_dev *pdev) +{ + struct i2c_adapter *adapter = pdev->client->adapter; + struct device *dev = &pdev->client->dev; + + /* Check i2c adapter read functionality */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + pdev->smb_read = idt_smb_read_block; + dev_dbg(dev, "SMBus block-read op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + pdev->smb_read = idt_smb_read_i2c_block; + dev_dbg(dev, "SMBus i2c-block-read op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA) && + i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + pdev->smb_read = idt_smb_read_word; + dev_warn(dev, "Use slow word/byte SMBus read ops"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + pdev->smb_read = idt_smb_read_byte; + dev_warn(dev, "Use slow byte SMBus read op"); + } else /* no supported smbus read operations */ { + dev_err(dev, "No supported SMBus read op"); + return -EPFNOSUPPORT; + } + + /* Check i2c adapter write functionality */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) { + pdev->smb_write = idt_smb_write_block; + dev_dbg(dev, "SMBus block-write op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { + pdev->smb_write = idt_smb_write_i2c_block; + dev_dbg(dev, "SMBus i2c-block-write op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) && + i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + pdev->smb_write = idt_smb_write_word; + dev_warn(dev, "Use slow word/byte SMBus write op"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + pdev->smb_write = idt_smb_write_byte; + dev_warn(dev, "Use slow byte SMBus write op"); + } else /* no supported smbus write operations */ { + dev_err(dev, "No supported SMBus write op"); + return -EPFNOSUPPORT; + } + + /* Initialize IDT SMBus slave interface mutex */ + mutex_init(&pdev->smb_mtx); + + return 0; +} + +/* + * idt_check_dev() - check whether it's really IDT 89HPESx device + * @pdev: Pointer to the driver data + * Return status of i2c adapter check operation + */ +static int idt_check_dev(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + u32 viddid; + int ret; + + /* Read VID and DID directly from IDT memory space */ + ret = idt_csr_read(pdev, IDT_VIDDID_CSR, &viddid); + if (ret != 0) { + dev_err(dev, "Failed to read VID/DID"); + return ret; + } + + /* Check whether it's IDT device */ + if ((viddid & IDT_VID_MASK) != PCI_VENDOR_ID_IDT) { + dev_err(dev, "Got unsupported VID/DID: 0x%08x", viddid); + return -ENODEV; + } + + dev_info(dev, "Found IDT 89HPES device VID:0x%04x, DID:0x%04x", + (viddid & IDT_VID_MASK), (viddid >> 16)); + + return 0; +} + +/* + * idt_create_sysfs_files() - create sysfs attribute files + * @pdev: Pointer to the driver data + * Return status of operation + */ +static int idt_create_sysfs_files(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + int ret; + + /* Don't do anything if EEPROM isn't accessible */ + if (pdev->eesize == 0) { + dev_dbg(dev, "Skip creating sysfs-files"); + return 0; + } + + /* Allocate memory for attribute file */ + pdev->ee_file = devm_kmalloc(dev, sizeof(*pdev->ee_file), GFP_KERNEL); + if (!pdev->ee_file) + return -ENOMEM; + + /* Copy the declared EEPROM attr structure to change some of fields */ + memcpy(pdev->ee_file, &bin_attr_eeprom, sizeof(*pdev->ee_file)); + + /* In case of read-only EEPROM get rid of write ability */ + if (pdev->eero) { + pdev->ee_file->attr.mode &= ~0200; + pdev->ee_file->write = NULL; + } + /* Create EEPROM sysfs file */ + pdev->ee_file->size = pdev->eesize; + ret = sysfs_create_bin_file(&dev->kobj, pdev->ee_file); + if (ret != 0) { + kfree(pdev->ee_file); + dev_err(dev, "Failed to create EEPROM sysfs-node"); + return ret; + } + + return 0; +} + +/* + * idt_remove_sysfs_files() - remove sysfs attribute files + * @pdev: Pointer to the driver data + */ +static void idt_remove_sysfs_files(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + + /* Don't do anything if EEPROM wasn't accessible */ + if (pdev->eesize == 0) + return; + + /* Remove EEPROM sysfs file */ + sysfs_remove_bin_file(&dev->kobj, pdev->ee_file); + + /* Free memory allocated for bin_attribute structure */ + kfree(pdev->ee_file); +} + +/* + * idt_create_dbgfs_files() - create debugfs files + * @pdev: Pointer to the driver data + */ +#define CSRNAME_LEN ((size_t)32) +static void idt_create_dbgfs_files(struct idt_89hpesx_dev *pdev) +{ + struct i2c_client *cli = pdev->client; + char fname[CSRNAME_LEN]; + + /* Create Debugfs directory for CSR file */ + snprintf(fname, CSRNAME_LEN, "%d-%04hx", cli->adapter->nr, cli->addr); + pdev->csr_dir = debugfs_create_dir(fname, csr_dbgdir); + + /* Create Debugfs file for CSR read/write operations */ + pdev->csr_file = debugfs_create_file(cli->name, 0600, + pdev->csr_dir, pdev, &csr_dbgfs_ops); +} + +/* + * idt_remove_dbgfs_files() - remove debugfs files + * @pdev: Pointer to the driver data + */ +static void idt_remove_dbgfs_files(struct idt_89hpesx_dev *pdev) +{ + /* Remove CSR directory and it sysfs-node */ + debugfs_remove_recursive(pdev->csr_dir); +} + +/* + * idt_probe() - IDT 89HPESx driver probe() callback method + */ +static int idt_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Create driver data */ + pdev = idt_create_pdev(client); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + /* Set SMBus operations */ + ret = idt_set_smbus_ops(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Check whether it is truly IDT 89HPESx device */ + ret = idt_check_dev(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Create sysfs files */ + ret = idt_create_sysfs_files(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Create debugfs files */ + idt_create_dbgfs_files(pdev); + + return 0; + +err_free_pdev: + idt_free_pdev(pdev); + + return ret; +} + +/* + * idt_remove() - IDT 89HPESx driver remove() callback method + */ +static int idt_remove(struct i2c_client *client) +{ + struct idt_89hpesx_dev *pdev = i2c_get_clientdata(client); + + /* Remove debugfs files first */ + idt_remove_dbgfs_files(pdev); + + /* Remove sysfs files */ + idt_remove_sysfs_files(pdev); + + /* Discard driver data structure */ + idt_free_pdev(pdev); + + return 0; +} + +/* + * ee_ids - array of supported EEPROMs + */ +static const struct i2c_device_id ee_ids[] = { + { "24c32", 4096}, + { "24c64", 8192}, + { "24c128", 16384}, + { "24c256", 32768}, + { "24c512", 65536}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ee_ids); + +/* + * idt_ids - supported IDT 89HPESx devices + */ +static const struct i2c_device_id idt_ids[] = { + { "89hpes8nt2", 0 }, + { "89hpes12nt3", 0 }, + + { "89hpes24nt6ag2", 0 }, + { "89hpes32nt8ag2", 0 }, + { "89hpes32nt8bg2", 0 }, + { "89hpes12nt12g2", 0 }, + { "89hpes16nt16g2", 0 }, + { "89hpes24nt24g2", 0 }, + { "89hpes32nt24ag2", 0 }, + { "89hpes32nt24bg2", 0 }, + + { "89hpes12n3", 0 }, + { "89hpes12n3a", 0 }, + { "89hpes24n3", 0 }, + { "89hpes24n3a", 0 }, + + { "89hpes32h8", 0 }, + { "89hpes32h8g2", 0 }, + { "89hpes48h12", 0 }, + { "89hpes48h12g2", 0 }, + { "89hpes48h12ag2", 0 }, + { "89hpes16h16", 0 }, + { "89hpes22h16", 0 }, + { "89hpes22h16g2", 0 }, + { "89hpes34h16", 0 }, + { "89hpes34h16g2", 0 }, + { "89hpes64h16", 0 }, + { "89hpes64h16g2", 0 }, + { "89hpes64h16ag2", 0 }, + + /* { "89hpes3t3", 0 }, // No SMBus-slave iface */ + { "89hpes12t3g2", 0 }, + { "89hpes24t3g2", 0 }, + /* { "89hpes4t4", 0 }, // No SMBus-slave iface */ + { "89hpes16t4", 0 }, + { "89hpes4t4g2", 0 }, + { "89hpes10t4g2", 0 }, + { "89hpes16t4g2", 0 }, + { "89hpes16t4ag2", 0 }, + { "89hpes5t5", 0 }, + { "89hpes6t5", 0 }, + { "89hpes8t5", 0 }, + { "89hpes8t5a", 0 }, + { "89hpes24t6", 0 }, + { "89hpes6t6g2", 0 }, + { "89hpes24t6g2", 0 }, + { "89hpes16t7", 0 }, + { "89hpes32t8", 0 }, + { "89hpes32t8g2", 0 }, + { "89hpes48t12", 0 }, + { "89hpes48t12g2", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, idt_ids); + +/* + * idt_driver - IDT 89HPESx driver structure + */ +static struct i2c_driver idt_driver = { + .driver = { + .name = IDT_NAME, + .owner = THIS_MODULE, + }, + .probe = idt_probe, + .remove = idt_remove, + .id_table = idt_ids, +}; + +/* + * idt_init() - IDT 89HPESx driver init() callback method + */ +static int __init idt_init(void) +{ + /* Create Debugfs directory first */ + if (debugfs_initialized()) + csr_dbgdir = debugfs_create_dir("idt_csr", NULL); + + /* Add new i2c-device driver */ + return i2c_add_driver(&idt_driver); +} +module_init(idt_init); + +/* + * idt_exit() - IDT 89HPESx driver exit() callback method + */ +static void __exit idt_exit(void) +{ + /* Discard debugfs directory and all files if any */ + debugfs_remove_recursive(csr_dbgdir); + + /* Unregister i2c-device driver */ + i2c_del_driver(&idt_driver); +} +module_exit(idt_exit); -- cgit v1.2.1 From 7a11a1d1b58873b2e5a6922dcdc23b6b339b14ba Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Jan 2017 15:56:44 +0100 Subject: lkdtm: hide stack overflow warning for corrupt-stack test After the latest change to make sure the compiler actually does a memset, it is now smart enough to flag the stack overflow at compile time, at least with gcc-7.0: drivers/misc/lkdtm_bugs.c: In function 'lkdtm_CORRUPT_STACK': drivers/misc/lkdtm_bugs.c:88:144: warning: 'memset' writing 64 bytes into a region of size 8 overflows the destination [-Wstringop-overflow=] To outsmart the compiler again, this moves the memset into a noinline function where (for now) it doesn't see that we intentionally write broken code here. Fixes: c55d240003ae ("lkdtm: Prevent the compiler from optimising lkdtm_CORRUPT_STACK()") Signed-off-by: Arnd Bergmann Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm_bugs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index 91edd0b55e5c..bb3bb8ef5f44 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -80,12 +80,17 @@ void lkdtm_OVERFLOW(void) (void) recursive_loop(recur_count); } +static noinline void __lkdtm_CORRUPT_STACK(void *stack) +{ + memset(stack, 'a', 64); +} + noinline void lkdtm_CORRUPT_STACK(void) { /* Use default char array length that triggers stack protection. */ char data[8]; + __lkdtm_CORRUPT_STACK(&data); - memset((void *)data, 'a', 64); pr_info("Corrupted stack with '%16s'...\n", data); } -- cgit v1.2.1 From 29fe7d59bdd8cea561187fca155dbf5cee918bb8 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 20 Jan 2017 02:17:16 +0200 Subject: mei: make mei_io_list_flush static mei_io_list_flush is used only in client.c so make it local to the file and mark static. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/client.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index b0395601c6ae..3aaa86235fe0 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -404,7 +404,7 @@ static void __mei_io_list_flush(struct mei_cl_cb *list, * @list: An instance of our list structure * @cl: host client */ -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) { __mei_io_list_flush(list, cl, false); } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index f2545af9be7b..dc80ad5c9fa2 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -93,7 +93,6 @@ static inline void mei_io_list_init(struct mei_cl_cb *list) { INIT_LIST_HEAD(&list->list); } -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl); /* * MEI Host Client Functions -- cgit v1.2.1 From 669c256cb9fb29a540ce4befb4328d88c665e523 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 20 Jan 2017 02:17:17 +0200 Subject: mei: make mei_cl_set_disconnected static mei_cl_set_disconnected is used only in client.c, so make it local to the file and mark static. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 2 +- drivers/misc/mei/client.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 3aaa86235fe0..da1c0703910a 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -756,7 +756,7 @@ static void mei_cl_wake_all(struct mei_cl *cl) * * @cl: host client */ -void mei_cl_set_disconnected(struct mei_cl *cl) +static void mei_cl_set_disconnected(struct mei_cl *cl) { struct mei_device *dev = cl->dev; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index dc80ad5c9fa2..ab92a542661b 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -208,7 +208,6 @@ static inline u8 mei_cl_host_addr(const struct mei_cl *cl) } int mei_cl_disconnect(struct mei_cl *cl); -void mei_cl_set_disconnected(struct mei_cl *cl); int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list); int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, -- cgit v1.2.1 From acf50ec773703f7043944aaf957b625a82fc9ce0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 24 Jan 2017 14:38:38 +0000 Subject: eeprom: fix memory leak on buf when failed allocation of csraddr_str The error return path When csraddr_str fails to free buf, causing a memory leak. Fix this by returning via the free_buf label that performs the necessary cleanup. Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 25d47d09e1cb..56bc5c2dfe60 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -965,8 +965,10 @@ static ssize_t idt_dbgfs_csr_write(struct file *filep, const char __user *ubuf, csraddr_len = colon_ch - buf; csraddr_str = kmalloc(sizeof(char)*(csraddr_len + 1), GFP_KERNEL); - if (csraddr_str == NULL) - return -ENOMEM; + if (csraddr_str == NULL) { + ret = -ENOMEM; + goto free_buf; + } /* Copy the register address to the substring buffer */ strncpy(csraddr_str, buf, csraddr_len); csraddr_str[csraddr_len] = '\0'; -- cgit v1.2.1 From 8ade6039b8404a28a7f76eac37a92c464b69499a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 20 Jan 2017 21:57:57 +0100 Subject: eeprom: fix platform_no_drv_owner.cocci warnings No need to set .owner here. The core will do it. Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: Serge Semin Signed-off-by: Julia Lawall Signed-off-by: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 56bc5c2dfe60..4db0d2cb08c6 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1554,7 +1554,6 @@ MODULE_DEVICE_TABLE(i2c, idt_ids); static struct i2c_driver idt_driver = { .driver = { .name = IDT_NAME, - .owner = THIS_MODULE, }, .probe = idt_probe, .remove = idt_remove, -- cgit v1.2.1 From 9ba60573638e2006170ebcc5489fb1e068afbc8f Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Thu, 19 Jan 2017 11:40:13 +0100 Subject: lkdtm: Fix Oops when unloading the module No jprobe is registered when the module is loaded without specifying a crashpoint that uses a jprobe. At the moment, we unconditionally try to unregister the jprobe on module unload which results in an Oops. Add a check to fix this. Signed-off-by: Juerg Haefliger Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index 7eeb71a75549..4d44084071d8 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -535,7 +535,9 @@ static void __exit lkdtm_module_exit(void) /* Handle test-specific clean-up. */ lkdtm_usercopy_exit(); - unregister_jprobe(lkdtm_jprobe); + if (lkdtm_jprobe != NULL) + unregister_jprobe(lkdtm_jprobe); + pr_info("Crash point unregistered\n"); } -- cgit v1.2.1 From cdd1737cba7a6453c9453c55c4537a15cc1f49b2 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Jan 2017 14:52:18 -0600 Subject: misc: sram: Split sram data structures into local header In preparation of a coming file split of the sram driver, move the common data structures into a local header file that can be shared between files related to the sram driver. Signed-off-by: Dave Gerlach Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 30 ++---------------------------- drivers/misc/sram.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 drivers/misc/sram.h (limited to 'drivers/misc') diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 07ec2a8a9343..5a6e001845c2 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -31,35 +31,9 @@ #include #include -#define SRAM_GRANULARITY 32 - -struct sram_partition { - void __iomem *base; - - struct gen_pool *pool; - struct bin_attribute battr; - struct mutex lock; -}; - -struct sram_dev { - struct device *dev; - void __iomem *virt_base; - - struct gen_pool *pool; - struct clk *clk; +#include "sram.h" - struct sram_partition *partition; - u32 partitions; -}; - -struct sram_reserve { - struct list_head list; - u32 start; - u32 size; - bool export; - bool pool; - const char *label; -}; +#define SRAM_GRANULARITY 32 static ssize_t sram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h new file mode 100644 index 000000000000..52501a84468c --- /dev/null +++ b/drivers/misc/sram.h @@ -0,0 +1,39 @@ +/* + * Defines for the SRAM driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __SRAM_H +#define __SRAM_H + +struct sram_partition { + void __iomem *base; + + struct gen_pool *pool; + struct bin_attribute battr; + struct mutex lock; + struct list_head list; +}; + +struct sram_dev { + struct device *dev; + void __iomem *virt_base; + + struct gen_pool *pool; + struct clk *clk; + + struct sram_partition *partition; + u32 partitions; +}; + +struct sram_reserve { + struct list_head list; + u32 start; + u32 size; + bool export; + bool pool; + const char *label; +}; +#endif /* __SRAM_H */ -- cgit v1.2.1 From 728bbe75c82fdc6bdf157652a4351856ed08afc4 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Jan 2017 14:52:19 -0600 Subject: misc: sram: Introduce support code for protect-exec sram type Some platforms, like many ARM SoCs, require the ability to run code from on-chip memory like SRAM for tasks like reconfiguring the SDRAM controller or entering low-power sleep modes. In order to do this we must be able to allocate memory that the code can be copied to but then change the mapping to be read-only and executable so that no memory is both writable and executable at the same time to avoid opening any unneccesary security holes. By using the existing "pool" partition type that the SRAM driver allows we can create a memory space that will already be exposed by the genalloc framework to allow for allocating memory but we must extend this to meet the executable requirements. By making use of various set_memory_* APIs we can change the attributes of pages to make them writable for code upload but then read-only and executable when we want to actually run code. Because SRAM is a shared resource we need a centralized manager of these set memory calls. Because the SRAM driver itself is responsible for allocating the memory we can introduce a sram_copy_exec API for the driver that works like memcpy but also manages the page attributes and locking to allow multiple users of the same SRAM space to all copy their code over independent of other each before starting execution. It is maintained in a separate file from the core SRAM driver to allow it to be selectively built depending on whether or not a platform has the appropriate set_memory_* APIs. A future patch will integrate it with the core SRAM driver. Signed-off-by: Dave Gerlach Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram-exec.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/sram.h | 18 ++++++++ 2 files changed, 123 insertions(+) create mode 100644 drivers/misc/sram-exec.c (limited to 'drivers/misc') diff --git a/drivers/misc/sram-exec.c b/drivers/misc/sram-exec.c new file mode 100644 index 000000000000..ac522417c462 --- /dev/null +++ b/drivers/misc/sram-exec.c @@ -0,0 +1,105 @@ +/* + * SRAM protect-exec region helper functions + * + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Dave Gerlach + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include + +#include "sram.h" + +static DEFINE_MUTEX(exec_pool_list_mutex); +static LIST_HEAD(exec_pool_list); + +int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block, + struct sram_partition *part) +{ + unsigned long base = (unsigned long)part->base; + unsigned long end = base + block->size; + + if (!PAGE_ALIGNED(base) || !PAGE_ALIGNED(end)) { + dev_err(sram->dev, + "SRAM pool marked with 'protect-exec' is not page aligned and will not be created.\n"); + return -ENOMEM; + } + + return 0; +} + +int sram_add_protect_exec(struct sram_partition *part) +{ + mutex_lock(&exec_pool_list_mutex); + list_add_tail(&part->list, &exec_pool_list); + mutex_unlock(&exec_pool_list_mutex); + + return 0; +} + +/** + * sram_exec_copy - copy data to a protected executable region of sram + * + * @pool: struct gen_pool retrieved that is part of this sram + * @dst: Destination address for the copy, that must be inside pool + * @src: Source address for the data to copy + * @size: Size of copy to perform, which starting from dst, must reside in pool + * + * This helper function allows sram driver to act as central control location + * of 'protect-exec' pools which are normal sram pools but are always set + * read-only and executable except when copying data to them, at which point + * they are set to read-write non-executable, to make sure no memory is + * writeable and executable at the same time. This region must be page-aligned + * and is checked during probe, otherwise page attribute manipulation would + * not be possible. + */ +int sram_exec_copy(struct gen_pool *pool, void *dst, void *src, + size_t size) +{ + struct sram_partition *part = NULL, *p; + unsigned long base; + int pages; + + mutex_lock(&exec_pool_list_mutex); + list_for_each_entry(p, &exec_pool_list, list) { + if (p->pool == pool) + part = p; + } + mutex_unlock(&exec_pool_list_mutex); + + if (!part) + return -EINVAL; + + if (!addr_in_gen_pool(pool, (unsigned long)dst, size)) + return -EINVAL; + + base = (unsigned long)part->base; + pages = PAGE_ALIGN(size) / PAGE_SIZE; + + mutex_lock(&part->lock); + + set_memory_nx((unsigned long)base, pages); + set_memory_rw((unsigned long)base, pages); + + memcpy(dst, src, size); + + set_memory_ro((unsigned long)base, pages); + set_memory_x((unsigned long)base, pages); + + mutex_unlock(&part->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(sram_exec_copy); diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h index 52501a84468c..b268cd3f55bb 100644 --- a/drivers/misc/sram.h +++ b/drivers/misc/sram.h @@ -36,4 +36,22 @@ struct sram_reserve { bool pool; const char *label; }; + +#ifdef CONFIG_SRAM_EXEC +int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block, + struct sram_partition *part); +int sram_add_protect_exec(struct sram_partition *part); +#else +static inline int sram_check_protect_exec(struct sram_dev *sram, + struct sram_reserve *block, + struct sram_partition *part) +{ + return -ENODEV; +} + +static inline int sram_add_protect_exec(struct sram_partition *part) +{ + return -ENODEV; +} +#endif /* CONFIG_SRAM_EXEC */ #endif /* __SRAM_H */ -- cgit v1.2.1 From 37afff0d87c9939843c664970af6c6d952f95712 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Jan 2017 14:52:20 -0600 Subject: misc: sram: Integrate protect-exec reserved sram area type Introduce a new "protect-exec" reserved sram area type which is makes use of the the existing functionality provided for the "pool" sram region type for use with the genalloc framework and with the added requirement that it be maintained as read-only and executable while allowing for an arbitrary number of drivers to share the space. This introduces a common way to maintain a region of sram as read-only and executable and also introduces a helper function, sram_exec_copy, which allows for copying data to this protected region while maintaining locking to avoid conflicts between multiple users of the same space. A region of memory that is marked with the "protect-exec" flag in the device tree also has the requirement of providing a page aligned block of memory so that the page attribute manipulation does not affect surrounding regions. Also, selectively enable this only for builds that support set_memory_* calls, for now just ARM, through the use of Kconfig. Signed-off-by: Dave Gerlach Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 4 ++++ drivers/misc/Makefile | 1 + drivers/misc/sram.c | 21 +++++++++++++++++++-- drivers/misc/sram.h | 1 + 4 files changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 64971baf11fa..0444a8f9b094 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -474,11 +474,15 @@ config SRAM bool "Generic on-chip SRAM driver" depends on HAS_IOMEM select GENERIC_ALLOCATOR + select SRAM_EXEC if ARM help This driver allows you to declare a memory region to be managed by the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. +config SRAM_EXEC + bool + config VEXPRESS_SYSCFG bool "Versatile Express System Configuration driver" depends on VEXPRESS_CONFIG diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 31983366090a..7a3ea89339b4 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o +obj-$(CONFIG_SRAM_EXEC) += sram-exec.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 5a6e001845c2..d1185b78cf9a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -122,6 +122,18 @@ static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block, if (ret) return ret; } + if (block->protect_exec) { + ret = sram_check_protect_exec(sram, block, part); + if (ret) + return ret; + + ret = sram_add_pool(sram, block, start, part); + if (ret) + return ret; + + sram_add_protect_exec(part); + } + sram->partitions++; return 0; @@ -207,7 +219,11 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) if (of_find_property(child, "pool", NULL)) block->pool = true; - if ((block->export || block->pool) && block->size) { + if (of_find_property(child, "protect-exec", NULL)) + block->protect_exec = true; + + if ((block->export || block->pool || block->protect_exec) && + block->size) { exports++; label = NULL; @@ -269,7 +285,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) goto err_chunks; } - if ((block->export || block->pool) && block->size) { + if ((block->export || block->pool || block->protect_exec) && + block->size) { ret = sram_add_partition(sram, block, res->start + block->start); if (ret) { diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h index b268cd3f55bb..c181ce4c8fca 100644 --- a/drivers/misc/sram.h +++ b/drivers/misc/sram.h @@ -34,6 +34,7 @@ struct sram_reserve { u32 size; bool export; bool pool; + bool protect_exec; const char *label; }; -- cgit v1.2.1 From aed74b140e10725caf8a982ea26610d1dd1f612c Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 26 Jan 2017 16:00:36 +0300 Subject: eeprom: idt_89hpesx: Discard memory freeing allocated by devm_kmalloc Indeed, the data structure is allocated by device resource manager, so the driver doesn't need to free anything on remove() callback. Reported-by: Julia Lawall Signed-off-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 4db0d2cb08c6..5d48aecbe208 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1229,9 +1229,6 @@ static void idt_free_pdev(struct idt_89hpesx_dev *pdev) { /* Clear driver data from device private field */ i2c_set_clientdata(pdev->client, NULL); - - /* Just free memory allocated for data */ - devm_kfree(&pdev->client->dev, pdev); } /* -- cgit v1.2.1 From f2d697604cc860a177b793f669b22ef9c13131b9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 25 Jan 2017 14:09:52 +0000 Subject: eeprom: idt_89hpesx: Drop kfree for memory allocated with devm_kzalloc It's not necessary to free memory allocated with devm_kzalloc and using kfree leads to a double free. Fixes: cfad6425382e ("eeprom: Add IDT 89HPESx EEPROM/CSR driver") Signed-off-by: Wei Yongjun Signed-off-by: Dan Carpenter Acked-by: Serge Semin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/idt_89hpesx.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 5d48aecbe208..4a22a1d99395 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1358,7 +1358,6 @@ static int idt_create_sysfs_files(struct idt_89hpesx_dev *pdev) pdev->ee_file->size = pdev->eesize; ret = sysfs_create_bin_file(&dev->kobj, pdev->ee_file); if (ret != 0) { - kfree(pdev->ee_file); dev_err(dev, "Failed to create EEPROM sysfs-node"); return ret; } @@ -1380,9 +1379,6 @@ static void idt_remove_sysfs_files(struct idt_89hpesx_dev *pdev) /* Remove EEPROM sysfs file */ sysfs_remove_bin_file(&dev->kobj, pdev->ee_file); - - /* Free memory allocated for bin_attribute structure */ - kfree(pdev->ee_file); } /* -- cgit v1.2.1 From 9ecb839f16a353e71175981a098842fda4ddc7f1 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:38 +0200 Subject: mei: bus: cancel and disable callback after release call A driver on the mei bus may rely on the availability of the receive callback during driver remove() call, e.g. mei_wdt. Move callbacks dismantling after the remove() call to unblock that scenario. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 2d9c5dd06e42..3bb1f1500f6b 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -665,6 +665,10 @@ static int mei_cl_device_remove(struct device *dev) if (!cldev || !dev->driver) return 0; + cldrv = to_mei_cl_driver(dev->driver); + if (cldrv->remove) + ret = cldrv->remove(cldev); + if (cldev->rx_cb) { cancel_work_sync(&cldev->rx_work); cldev->rx_cb = NULL; @@ -674,10 +678,6 @@ static int mei_cl_device_remove(struct device *dev) cldev->notif_cb = NULL; } - cldrv = to_mei_cl_driver(dev->driver); - if (cldrv->remove) - ret = cldrv->remove(cldev); - module_put(THIS_MODULE); dev->driver = NULL; return ret; -- cgit v1.2.1 From 5d88246090c5e9b178eb05bcfb52bbaad2ae48f3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:39 +0200 Subject: mei: bus: prevent hardware module unload if device on bus is active The hardware module should not be unloaded if the bus has active devices. Get get_/put_ bus parent module upon client device connection/disconnection, to prevent the hardware managing module to disappear underneath. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 31 +++++++++++++++++++++++++++++++ drivers/misc/mei/client.c | 10 ++++++++-- drivers/misc/mei/mei_dev.h | 2 ++ 3 files changed, 41 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 3bb1f1500f6b..b67f15b53067 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -541,6 +541,37 @@ out: } EXPORT_SYMBOL_GPL(mei_cldev_disable); +/** + * mei_cl_bus_module_get - acquire module of the underlying + * hw module. + * + * @cl: host client + * + * Return: true on success; false if the module was removed. + */ +bool mei_cl_bus_module_get(struct mei_cl *cl) +{ + struct mei_cl_device *cldev = cl->cldev; + + if (!cldev) + return true; + + return try_module_get(cldev->bus->dev->driver->owner); +} + +/** + * mei_cl_bus_module_put - release the underlying hw module. + * + * @cl: host client + */ +void mei_cl_bus_module_put(struct mei_cl *cl) +{ + struct mei_cl_device *cldev = cl->cldev; + + if (cldev) + module_put(cldev->bus->dev->driver->owner); +} + /** * mei_cl_device_find - find matching entry in the driver id table * diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index da1c0703910a..619b4702347f 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -774,6 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) cl->tx_flow_ctrl_creds = 0; cl->timer_count = 0; + mei_cl_bus_module_put(cl); + if (!cl->me_cl) return; @@ -1077,13 +1079,17 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, dev = cl->dev; + if (!mei_cl_bus_module_get(cl)) + return -ENODEV; + rets = mei_cl_set_connecting(cl, me_cl); if (rets) - return rets; + goto nortpm; if (mei_cl_is_fixed_address(cl)) { cl->state = MEI_FILE_CONNECTED; - return 0; + rets = 0; + goto nortpm; } rets = pm_runtime_get(dev->dev); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 8dadb98662a9..33ff4b7e356d 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -328,6 +328,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, bool mei_cl_bus_rx_event(struct mei_cl *cl); bool mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); +bool mei_cl_bus_module_get(struct mei_cl *cl); +void mei_cl_bus_module_put(struct mei_cl *cl); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); -- cgit v1.2.1 From 57080e88240a46c62cf7538de009545eb03d2568 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:40 +0200 Subject: mei: bus: unregister callbacks upon me client disable call Stop and unregister receive and notification callbacks from the disable function, to allow its later re-enablement. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index b67f15b53067..cb3e9e0ca049 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -498,6 +498,25 @@ out: } EXPORT_SYMBOL_GPL(mei_cldev_enable); +/** + * mei_cldev_unregister_callbacks - internal wrapper for unregistering + * callbacks. + * + * @cldev: client device + */ +static void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev) +{ + if (cldev->rx_cb) { + cancel_work_sync(&cldev->rx_work); + cldev->rx_cb = NULL; + } + + if (cldev->notif_cb) { + cancel_work_sync(&cldev->notif_work); + cldev->notif_cb = NULL; + } +} + /** * mei_cldev_disable - disable me client device * disconnect form the me client @@ -519,6 +538,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev) bus = cldev->bus; + mei_cldev_unregister_callbacks(cldev); + mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { @@ -700,14 +721,7 @@ static int mei_cl_device_remove(struct device *dev) if (cldrv->remove) ret = cldrv->remove(cldev); - if (cldev->rx_cb) { - cancel_work_sync(&cldev->rx_work); - cldev->rx_cb = NULL; - } - if (cldev->notif_cb) { - cancel_work_sync(&cldev->notif_work); - cldev->notif_cb = NULL; - } + mei_cldev_unregister_callbacks(cldev); module_put(THIS_MODULE); dev->driver = NULL; -- cgit v1.2.1 From 7c47d2ca0feca767479329da23523ed798acb854 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:41 +0200 Subject: mei: return error on notification request to a disconnected client Request for a notification from a disconnected client will be ignored silently by the FW but the caller should know that the operation hasn't succeeded. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 619b4702347f..ecfaef93457d 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1331,6 +1331,9 @@ int mei_cl_notify_request(struct mei_cl *cl, return -EOPNOTSUPP; } + if (!mei_cl_is_connected(cl)) + return -ENODEV; + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); -- cgit v1.2.1 From 6c0d6701a551ac344e63935535db0494110c5f4e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:42 +0200 Subject: mei: abort waiting for notification on unsupported HW On legacy HW, pre Skylake, the notifications are not supported, return -EOPNOTSUPP in mei_cl_notify_get and prevent waiting indefinitely. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index ecfaef93457d..923fad39535c 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1428,6 +1428,11 @@ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) dev = cl->dev; + if (!dev->hbm_f_ev_supported) { + cl_dbg(dev, cl, "notifications not supported\n"); + return -EOPNOTSUPP; + } + if (!mei_cl_is_connected(cl)) return -ENODEV; -- cgit v1.2.1 From 6537ae2f20412dd1c7464272dfff77d5afb50d25 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Jan 2017 16:32:43 +0200 Subject: mei: amthif: clean command queue upon disconnection In order to prevent memory leak clean up the amthif command queue upon disconnection. The issue may happen only on error path as the command queue is cleaned upon file descriptor close. And remove the cleanup from mei_cl_flush_queues as this code is never reached for amthif client. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 923fad39535c..d9e3e68bab89 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -546,7 +546,6 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) mei_io_list_free(&cl->dev->write_waiting_list, cl); mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); - mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); mei_cl_read_cb_flush(cl, fp); @@ -769,6 +768,7 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) mei_io_list_free(&dev->write_waiting_list, cl); mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_io_list_free(&dev->amthif_cmd_list, cl); mei_cl_wake_all(cl); cl->rx_flow_ctrl_creds = 0; cl->tx_flow_ctrl_creds = 0; -- cgit v1.2.1 From 9ecdbc58f96bd145669510822bc44eafde3c7351 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Jan 2017 16:32:44 +0200 Subject: mei: amthif: allow the read completion after close The amthif client connection is shared over multiple file descriptors. In case a file descriptor was closed immediately after a write, the read credits should be still available so the pending reads can be cleaned from the queue, hence we cannot drop the control read list, this is done only upon connection close. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 466afb2611c6..2d553cd6d4d0 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -353,9 +353,9 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) dev->iamthif_canceled = true; } + /* Don't clean ctrl_rd_list here, the reads has to be completed */ mei_clear_list(file, &dev->amthif_cmd_list.list); mei_clear_list(file, &cl->rd_completed); - mei_clear_list(file, &dev->ctrl_rd_list.list); return 0; } -- cgit v1.2.1 From 962ff7bcec243dc5ff6dd3cbad6ed585e3177556 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 27 Jan 2017 16:32:45 +0200 Subject: mei: replace callback structures used as list head by list_head mei_dev structure used struct mei_cl_cb type variables as for holding callbacks list heads. Replace them by the actual struct list_head as there is no other info that is handled. This slims down the mei_dev structure and mostly streamline the code. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 11 +++++----- drivers/misc/mei/client.c | 52 ++++++++++++++++++++++---------------------- drivers/misc/mei/client.h | 20 +++++------------ drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/hw-me.c | 10 ++++----- drivers/misc/mei/hw-txe.c | 10 ++++----- drivers/misc/mei/init.c | 22 +++++++++---------- drivers/misc/mei/interrupt.c | 36 +++++++++++++++--------------- drivers/misc/mei/mei_dev.h | 20 ++++++++--------- 9 files changed, 85 insertions(+), 98 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 2d553cd6d4d0..366ef1fe9b90 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -132,8 +132,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); - cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, - typeof(*cb), list); + cb = list_first_entry_or_null(&dev->amthif_cmd_list, typeof(*cb), list); if (!cb) { dev->iamthif_state = MEI_IAMTHIF_IDLE; cl->fp = NULL; @@ -167,7 +166,7 @@ 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.list); + list_add_tail(&cb->list, &dev->amthif_cmd_list); /* * The previous request is still in processing, queue this one. @@ -211,7 +210,7 @@ unsigned int mei_amthif_poll(struct file *file, poll_table *wait) * Return: 0, OK; otherwise, error. */ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { int ret; @@ -237,7 +236,7 @@ int mei_amthif_irq_write(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 mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev; int ret; @@ -354,7 +353,7 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) } /* Don't clean ctrl_rd_list here, the reads has to be completed */ - mei_clear_list(file, &dev->amthif_cmd_list.list); + mei_clear_list(file, &dev->amthif_cmd_list); mei_clear_list(file, &cl->rd_completed); return 0; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index d9e3e68bab89..838a4a349f9e 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -379,17 +379,17 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, /** * __mei_io_list_flush - removes and frees cbs belonging to cl. * - * @list: an instance of our list structure + * @head: an instance of our list structure * @cl: host client, can be NULL for flushing the whole list * @free: whether to free the cbs */ -static void __mei_io_list_flush(struct mei_cl_cb *list, +static void __mei_io_list_flush(struct list_head *head, struct mei_cl *cl, bool free) { struct mei_cl_cb *cb, *next; /* enable removing everything if no cl is specified */ - list_for_each_entry_safe(cb, next, &list->list, list) { + list_for_each_entry_safe(cb, next, head, list) { if (!cl || mei_cl_cmp_id(cl, cb->cl)) { list_del_init(&cb->list); if (free) @@ -401,23 +401,23 @@ static void __mei_io_list_flush(struct mei_cl_cb *list, /** * mei_io_list_flush - removes list entry belonging to cl. * - * @list: An instance of our list structure + * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +static inline void mei_io_list_flush(struct list_head *head, struct mei_cl *cl) { - __mei_io_list_flush(list, cl, false); + __mei_io_list_flush(head, cl, false); } /** * mei_io_list_free - removes cb belonging to cl and free them * - * @list: An instance of our list structure + * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) +static inline void mei_io_list_free(struct list_head *head, struct mei_cl *cl) { - __mei_io_list_flush(list, cl, true); + __mei_io_list_flush(head, cl, true); } /** @@ -479,7 +479,7 @@ struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, if (!cb) return NULL; - list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list); + list_add_tail(&cb->list, &cl->dev->ctrl_wr_list); return cb; } @@ -831,7 +831,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); cl->timer_count = MEI_CONNECT_TIMEOUT; mei_schedule_stall_timer(dev); @@ -849,7 +849,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) * Return: 0, OK; otherwise, error. */ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -864,7 +864,7 @@ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, ret = mei_cl_send_disconnect(cl, cb); if (ret) - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -986,7 +986,7 @@ static bool mei_cl_is_other_connecting(struct mei_cl *cl) dev = cl->dev; - list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) { + list_for_each_entry(cb, &dev->ctrl_rd_list, list) { if (cb->fop_type == MEI_FOP_CONNECT && mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) return true; @@ -1017,7 +1017,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); cl->timer_count = MEI_CONNECT_TIMEOUT; mei_schedule_stall_timer(dev); return 0; @@ -1033,7 +1033,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) * Return: 0, OK; otherwise, error. */ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -1051,7 +1051,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, rets = mei_cl_send_connect(cl, cb); if (rets) - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return rets; } @@ -1276,7 +1276,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) * Return: 0 on such and error otherwise. */ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -1294,11 +1294,11 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, ret = mei_hbm_cl_notify_req(dev, cl, request); if (ret) { cl->status = ret; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); return 0; } @@ -1353,7 +1353,7 @@ int mei_cl_notify_request(struct mei_cl *cl, rets = -ENODEV; goto out; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); } mutex_unlock(&dev->device_lock); @@ -1533,7 +1533,7 @@ nortpm: * Return: 0, OK; otherwise error. */ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev; struct mei_msg_data *buf; @@ -1605,13 +1605,13 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, } if (mei_hdr.msg_complete) - list_move_tail(&cb->list, &dev->write_waiting_list.list); + list_move_tail(&cb->list, &dev->write_waiting_list); return 0; err: cl->status = rets; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return rets; } @@ -1701,9 +1701,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) out: if (mei_hdr.msg_complete) - list_add_tail(&cb->list, &dev->write_waiting_list.list); + list_add_tail(&cb->list, &dev->write_waiting_list); else - list_add_tail(&cb->list, &dev->write_list.list); + list_add_tail(&cb->list, &dev->write_list); cb = NULL; if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index ab92a542661b..f48cfa026268 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -84,16 +84,6 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) */ void mei_io_cb_free(struct mei_cl_cb *priv_cb); -/** - * mei_io_list_init - Sets up a queue list. - * - * @list: An instance cl callback structure - */ -static inline void mei_io_list_init(struct mei_cl_cb *list) -{ - INIT_LIST_HEAD(&list->list); -} - /* * MEI Host Client Functions */ @@ -209,17 +199,17 @@ static inline u8 mei_cl_host_addr(const struct mei_cl *cl) int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, const struct file *file); int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + 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 mei_cl_cb *cmpl_list); + 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 mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); @@ -230,7 +220,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); int mei_cl_notify_request(struct mei_cl *cl, const struct file *file, u8 request); int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); void mei_cl_notify(struct mei_cl *cl); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 25b4a1ba522d..ba3a774c8d71 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -815,7 +815,7 @@ static void mei_hbm_cl_res(struct mei_device *dev, struct mei_cl_cb *cb, *next; cl = NULL; - list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) { cl = cb->cl; diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index a05375a3338a..06a579952e3f 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1189,7 +1189,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; - struct mei_cl_cb complete_list; + struct list_head cmpl_list; s32 slots; u32 hcsr; int rets = 0; @@ -1201,7 +1201,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) hcsr = mei_hcsr_read(dev); me_intr_clear(dev, hcsr); - mei_io_list_init(&complete_list); + INIT_LIST_HEAD(&cmpl_list); /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { @@ -1227,7 +1227,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) slots = mei_count_full_read_slots(dev); while (slots > 0) { dev_dbg(dev->dev, "slots to read = %08x\n", slots); - rets = mei_irq_read_handler(dev, &complete_list, &slots); + rets = mei_irq_read_handler(dev, &cmpl_list, &slots); /* There is a race between ME write and interrupt delivery: * Not all data is always available immediately after the * interrupt, so try to read again on the next interrupt. @@ -1252,11 +1252,11 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) */ if (dev->pg_event != MEI_PG_EVENT_WAIT && dev->pg_event != MEI_PG_EVENT_RECEIVED) { - rets = mei_irq_write_handler(dev, &complete_list); + rets = mei_irq_write_handler(dev, &cmpl_list); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } - mei_irq_compl_handler(dev, &complete_list); + mei_irq_compl_handler(dev, &cmpl_list); end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index e9f8c0aeec13..6259b11d440c 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -1057,7 +1057,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; struct mei_txe_hw *hw = to_txe_hw(dev); - struct mei_cl_cb complete_list; + struct list_head cmpl_list; s32 slots; int rets = 0; @@ -1069,7 +1069,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) /* initialize our complete list */ mutex_lock(&dev->device_lock); - mei_io_list_init(&complete_list); + INIT_LIST_HEAD(&cmpl_list); if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) mei_txe_check_and_ack_intrs(dev, true); @@ -1126,7 +1126,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) slots = mei_count_full_read_slots(dev); if (test_and_clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause)) { /* Read from TXE */ - rets = mei_irq_read_handler(dev, &complete_list, &slots); + rets = mei_irq_read_handler(dev, &cmpl_list, &slots); if (rets && dev->dev_state != MEI_DEV_RESETTING) { dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", rets); @@ -1144,14 +1144,14 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) if (hw->aliveness && dev->hbuf_is_ready) { /* get the real register value */ dev->hbuf_is_ready = mei_hbuf_is_ready(dev); - rets = mei_irq_write_handler(dev, &complete_list); + rets = mei_irq_write_handler(dev, &cmpl_list); if (rets && rets != -EMSGSIZE) dev_err(dev->dev, "mei_irq_write_handler ret = %d.\n", rets); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } - mei_irq_compl_handler(dev, &complete_list); + mei_irq_compl_handler(dev, &cmpl_list); end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 41e5760a6886..cfb1cdf176fa 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -349,16 +349,16 @@ EXPORT_SYMBOL_GPL(mei_stop); bool mei_write_is_idle(struct mei_device *dev) { bool idle = (dev->dev_state == MEI_DEV_ENABLED && - list_empty(&dev->ctrl_wr_list.list) && - list_empty(&dev->write_list.list) && - list_empty(&dev->write_waiting_list.list)); + list_empty(&dev->ctrl_wr_list) && + list_empty(&dev->write_list) && + list_empty(&dev->write_waiting_list)); dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n", idle, mei_dev_state_str(dev->dev_state), - list_empty(&dev->ctrl_wr_list.list), - list_empty(&dev->write_list.list), - list_empty(&dev->write_waiting_list.list)); + list_empty(&dev->ctrl_wr_list), + list_empty(&dev->write_list), + list_empty(&dev->write_waiting_list)); return idle; } @@ -388,17 +388,17 @@ void mei_device_init(struct mei_device *dev, dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; - mei_io_list_init(&dev->write_list); - mei_io_list_init(&dev->write_waiting_list); - mei_io_list_init(&dev->ctrl_wr_list); - mei_io_list_init(&dev->ctrl_rd_list); + INIT_LIST_HEAD(&dev->write_list); + INIT_LIST_HEAD(&dev->write_waiting_list); + INIT_LIST_HEAD(&dev->ctrl_wr_list); + INIT_LIST_HEAD(&dev->ctrl_rd_list); INIT_DELAYED_WORK(&dev->timer_work, mei_timer); 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); - mei_io_list_init(&dev->amthif_cmd_list); + 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 b584749bcc4a..406e9e2b2fff 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -35,14 +35,14 @@ * for the completed callbacks * * @dev: mei device - * @compl_list: list of completed cbs + * @cmpl_list: list of completed cbs */ -void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) +void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list) { struct mei_cl_cb *cb, *next; struct mei_cl *cl; - list_for_each_entry_safe(cb, next, &compl_list->list, list) { + list_for_each_entry_safe(cb, next, cmpl_list, list) { cl = cb->cl; list_del_init(&cb->list); @@ -92,13 +92,13 @@ void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) * * @cl: reading client * @mei_hdr: header of mei client message - * @complete_list: completion list + * @cmpl_list: completion list * * Return: always 0 */ int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *complete_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; @@ -144,7 +144,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (mei_hdr->msg_complete) { cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx); - list_move_tail(&cb->list, &complete_list->list); + list_move_tail(&cb->list, cmpl_list); } else { pm_runtime_mark_last_busy(dev->dev); pm_request_autosuspend(dev->dev); @@ -154,7 +154,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, discard: if (cb) - list_move_tail(&cb->list, &complete_list->list); + list_move_tail(&cb->list, cmpl_list); mei_irq_discard_msg(dev, mei_hdr); return 0; } @@ -169,7 +169,7 @@ discard: * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -183,7 +183,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, return -EMSGSIZE; ret = mei_hbm_cl_disconnect_rsp(dev, cl); - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -199,7 +199,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -219,7 +219,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, if (ret) { cl->status = ret; cb->buf_idx = 0; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -249,7 +249,7 @@ static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr) * Return: 0 on success, <0 on failure. */ int mei_irq_read_handler(struct mei_device *dev, - struct mei_cl_cb *cmpl_list, s32 *slots) + struct list_head *cmpl_list, s32 *slots) { struct mei_msg_hdr *mei_hdr; struct mei_cl *cl; @@ -347,12 +347,11 @@ EXPORT_SYMBOL_GPL(mei_irq_read_handler); * * Return: 0 on success, <0 on failure. */ -int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) +int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list) { struct mei_cl *cl; struct mei_cl_cb *cb, *next; - struct mei_cl_cb *list; s32 slots; int ret; @@ -367,19 +366,18 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) /* complete all waiting for write CB */ dev_dbg(dev->dev, "complete all waiting for write cb.\n"); - list = &dev->write_waiting_list; - list_for_each_entry_safe(cb, next, &list->list, list) { + list_for_each_entry_safe(cb, next, &dev->write_waiting_list, list) { cl = cb->cl; cl->status = 0; cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); cl->writing_state = MEI_WRITE_COMPLETE; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); } /* complete control write list CB */ dev_dbg(dev->dev, "complete control write list cb.\n"); - list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list, list) { cl = cb->cl; switch (cb->fop_type) { case MEI_FOP_DISCONNECT: @@ -423,7 +421,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete write list CB */ dev_dbg(dev->dev, "complete write list cb.\n"); - list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { + 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); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 33ff4b7e356d..d41aac53a2ac 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -441,10 +441,10 @@ struct mei_device { struct cdev cdev; int minor; - struct mei_cl_cb write_list; - struct mei_cl_cb write_waiting_list; - struct mei_cl_cb ctrl_wr_list; - struct mei_cl_cb ctrl_rd_list; + struct list_head write_list; + struct list_head write_waiting_list; + struct list_head ctrl_wr_list; + struct list_head ctrl_rd_list; struct list_head file_list; long open_handle_count; @@ -501,7 +501,7 @@ struct mei_device { bool override_fixed_address; /* amthif list for cmd waiting */ - struct mei_cl_cb amthif_cmd_list; + struct list_head amthif_cmd_list; struct mei_cl iamthif_cl; long iamthif_open_count; u32 iamthif_stall_timer; @@ -573,10 +573,10 @@ void mei_cancel_work(struct mei_device *dev); void mei_timer(struct work_struct *work); void mei_schedule_stall_timer(struct mei_device *dev); int mei_irq_read_handler(struct mei_device *dev, - struct mei_cl_cb *cmpl_list, s32 *slots); + struct list_head *cmpl_list, s32 *slots); -int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); -void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); +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 @@ -592,12 +592,12 @@ 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 mei_cl_cb *cmpl_list); + 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 mei_cl_cb *complete_list); + struct list_head *cmpl_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); /* -- cgit v1.2.1 From f046192d98c9a929c3eefdd65b885000d341d969 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 27 Jan 2017 16:32:46 +0200 Subject: mei: revamp io list cleanup function. Specify in function names by which object is the io list filtered: cl for a client and fp for file descriptor. In that course a code duplication is resolved by dropping mei_cl_read_cb_flush and mei_clear_list and using mei_io_list_free_fp function. Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 34 ++++--------------- drivers/misc/mei/client.c | 83 ++++++++++++++++++++++------------------------- drivers/misc/mei/client.h | 2 +- 3 files changed, 47 insertions(+), 72 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 366ef1fe9b90..0e7406ccb6dd 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -310,51 +310,31 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) } } -/** - * mei_clear_list - removes all callbacks associated with file - * from mei_cb_list - * - * @file: file structure - * @mei_cb_list: callbacks list - * - * mei_clear_list is called to clear resources associated with file - * when application calls close function or Ctrl-C was pressed - */ -static void mei_clear_list(const struct file *file, - struct list_head *mei_cb_list) -{ - struct mei_cl_cb *cb, *next; - - list_for_each_entry_safe(cb, next, mei_cb_list, list) - if (file == cb->fp) - mei_io_cb_free(cb); -} - /** * mei_amthif_release - the release function * * @dev: device structure -* @file: pointer to file structure +* @fp: pointer to file structure * * Return: 0 on success, <0 on error */ -int mei_amthif_release(struct mei_device *dev, struct file *file) +int mei_amthif_release(struct mei_device *dev, struct file *fp) { - struct mei_cl *cl = file->private_data; + struct mei_cl *cl = fp->private_data; if (dev->iamthif_open_count > 0) dev->iamthif_open_count--; - if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { + 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_state); dev->iamthif_canceled = true; } /* Don't clean ctrl_rd_list here, the reads has to be completed */ - mei_clear_list(file, &dev->amthif_cmd_list); - mei_clear_list(file, &cl->rd_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/client.c b/drivers/misc/mei/client.c index 838a4a349f9e..68fe37b5bc52 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -377,14 +377,14 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, } /** - * __mei_io_list_flush - removes and frees cbs belonging to cl. + * __mei_io_list_flush_cl - removes and frees cbs belonging to cl. * * @head: an instance of our list structure * @cl: host client, can be NULL for flushing the whole list * @free: whether to free the cbs */ -static void __mei_io_list_flush(struct list_head *head, - struct mei_cl *cl, bool free) +static void __mei_io_list_flush_cl(struct list_head *head, + const struct mei_cl *cl, bool free) { struct mei_cl_cb *cb, *next; @@ -399,25 +399,42 @@ static void __mei_io_list_flush(struct list_head *head, } /** - * mei_io_list_flush - removes list entry belonging to cl. + * mei_io_list_flush_cl - removes list entry belonging to cl. * * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_flush(struct list_head *head, struct mei_cl *cl) +static inline void mei_io_list_flush_cl(struct list_head *head, + const struct mei_cl *cl) { - __mei_io_list_flush(head, cl, false); + __mei_io_list_flush_cl(head, cl, false); } /** - * mei_io_list_free - removes cb belonging to cl and free them + * mei_io_list_free_cl - removes cb belonging to cl and free them * * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_free(struct list_head *head, struct mei_cl *cl) +static inline void mei_io_list_free_cl(struct list_head *head, + const struct mei_cl *cl) { - __mei_io_list_flush(head, cl, true); + __mei_io_list_flush_cl(head, cl, true); +} + +/** + * mei_io_list_free_fp - free cb from a list that matches file pointer + * + * @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) +{ + struct mei_cl_cb *cb, *next; + + list_for_each_entry_safe(cb, next, head, list) + if (!fp || fp == cb->fp) + mei_io_cb_free(cb); } /** @@ -503,27 +520,6 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) return NULL; } -/** - * mei_cl_read_cb_flush - free client's read pending and completed cbs - * for a specific file - * - * @cl: host client - * @fp: file pointer (matching cb file object), may be NULL - */ -void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) -{ - struct mei_cl_cb *cb, *next; - - list_for_each_entry_safe(cb, next, &cl->rd_completed, list) - if (!fp || fp == cb->fp) - mei_io_cb_free(cb); - - - list_for_each_entry_safe(cb, next, &cl->rd_pending, list) - if (!fp || fp == cb->fp) - mei_io_cb_free(cb); -} - /** * mei_cl_flush_queues - flushes queue lists belonging to cl. * @@ -542,17 +538,16 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) dev = cl->dev; cl_dbg(dev, cl, "remove list entry belonging to cl\n"); - mei_io_list_free(&cl->dev->write_list, cl); - mei_io_list_free(&cl->dev->write_waiting_list, cl); - mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); - mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); - - mei_cl_read_cb_flush(cl, fp); + mei_io_list_free_cl(&cl->dev->write_list, cl); + mei_io_list_free_cl(&cl->dev->write_waiting_list, cl); + mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl); + mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); + mei_io_list_free_fp(&cl->rd_pending, fp); + mei_io_list_free_fp(&cl->rd_completed, fp); return 0; } - /** * mei_cl_init - initializes cl. * @@ -764,11 +759,11 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) return; cl->state = MEI_FILE_DISCONNECTED; - mei_io_list_free(&dev->write_list, cl); - mei_io_list_free(&dev->write_waiting_list, cl); - mei_io_list_flush(&dev->ctrl_rd_list, cl); - mei_io_list_flush(&dev->ctrl_wr_list, cl); - mei_io_list_free(&dev->amthif_cmd_list, cl); + mei_io_list_free_cl(&dev->write_list, 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; @@ -1123,8 +1118,8 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, if (!mei_cl_is_connected(cl)) { if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { - mei_io_list_flush(&dev->ctrl_rd_list, cl); - mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); + mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); /* ignore disconnect return valuue; * in case of failure reset will be invoked */ diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index f48cfa026268..545ae319ba90 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -83,6 +83,7 @@ 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 @@ -99,7 +100,6 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev); struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp); -void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp); struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops type, const struct file *fp); -- cgit v1.2.1 From f8a096059fc5f719301d314e5d7451f1bab5032a Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 26 Jan 2017 17:16:26 +0200 Subject: mei: simplify error handling via devres function. Use devm_ and pcim_ functions to make error handling simpler and code smaller and tidier. Based on original patch by mei: me: use managed functions pcim_* and devm_* https://lkml.org/lkml/2016/2/1/339 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 6 ++-- drivers/misc/mei/hw-txe.c | 4 +-- drivers/misc/mei/hw-txe.h | 2 +- drivers/misc/mei/pci-me.c | 50 +++++++-------------------------- drivers/misc/mei/pci-txe.c | 69 +++++++--------------------------------------- 5 files changed, 26 insertions(+), 105 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 06a579952e3f..befeac52c349 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1389,7 +1389,7 @@ const struct mei_cfg mei_me_pch8_sps_cfg = { * @pdev: The pci device structure * @cfg: per device generation config * - * Return: The mei_device_device pointer on success, NULL on failure. + * Return: The mei_device pointer on success, NULL on failure. */ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, const struct mei_cfg *cfg) @@ -1397,8 +1397,8 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, struct mei_device *dev; struct mei_me_hw *hw; - dev = kzalloc(sizeof(struct mei_device) + - sizeof(struct mei_me_hw), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + + sizeof(struct mei_me_hw), GFP_KERNEL); if (!dev) return NULL; hw = to_me_hw(dev); diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 6259b11d440c..24e4a4c96606 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -1207,8 +1207,8 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) struct mei_device *dev; struct mei_txe_hw *hw; - dev = kzalloc(sizeof(struct mei_device) + - sizeof(struct mei_txe_hw), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + + sizeof(struct mei_txe_hw), GFP_KERNEL); if (!dev) return NULL; diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h index ce3ed0b88b0c..e1e8b66d7648 100644 --- a/drivers/misc/mei/hw-txe.h +++ b/drivers/misc/mei/hw-txe.h @@ -45,7 +45,7 @@ * @intr_cause: translated interrupt cause */ struct mei_txe_hw { - void __iomem *mem_addr[NUM_OF_MEM_BARS]; + void __iomem * const *mem_addr; u32 aliveness; u32 readiness; u32 slots; diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index f9c6ec4b98ab..0a668fdfbbe9 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -149,18 +149,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; /* enable pci dev */ - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(&pdev->dev, "failed to enable pci device.\n"); goto end; } /* set PCI host mastering */ pci_set_master(pdev); - /* pci request regions for mei driver */ - err = pci_request_regions(pdev, KBUILD_MODNAME); + /* pci request regions and mapping IO device memory for mei driver */ + err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); if (err) { dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; + goto end; } if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) || @@ -173,24 +173,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } if (err) { dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); - goto release_regions; + goto end; } - /* allocates and initializes the mei dev structure */ dev = mei_me_dev_init(pdev, cfg); if (!dev) { err = -ENOMEM; - goto release_regions; + goto end; } hw = to_me_hw(dev); - /* mapping IO device memory */ - hw->mem_addr = pci_iomap(pdev, 0, 0); - if (!hw->mem_addr) { - dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); - err = -ENOMEM; - goto free_device; - } + hw->mem_addr = pcim_iomap_table(pdev)[0]; + pci_enable_msi(pdev); /* request and enable interrupt */ @@ -203,7 +197,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) { dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", pdev->irq); - goto disable_msi; + goto end; } if (mei_start(dev)) { @@ -242,15 +236,6 @@ release_irq: mei_cancel_work(dev); mei_disable_interrupts(dev); free_irq(pdev->irq, dev); -disable_msi: - pci_disable_msi(pdev); - pci_iounmap(pdev, hw->mem_addr); -free_device: - kfree(dev); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); end: dev_err(&pdev->dev, "initialization failed.\n"); return err; @@ -267,7 +252,6 @@ end: static void mei_me_remove(struct pci_dev *pdev) { struct mei_device *dev; - struct mei_me_hw *hw; dev = pci_get_drvdata(pdev); if (!dev) @@ -276,33 +260,19 @@ static void mei_me_remove(struct pci_dev *pdev) if (mei_pg_is_enabled(dev)) pm_runtime_get_noresume(&pdev->dev); - hw = to_me_hw(dev); - - dev_dbg(&pdev->dev, "stop\n"); mei_stop(dev); if (!pci_dev_run_wake(pdev)) mei_me_unset_pm_domain(dev); - /* disable interrupts */ mei_disable_interrupts(dev); free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - - if (hw->mem_addr) - pci_iounmap(pdev, hw->mem_addr); mei_deregister(dev); - - kfree(dev); - - pci_release_regions(pdev); - pci_disable_device(pdev); - - } + #ifdef CONFIG_PM_SLEEP static int mei_me_pci_suspend(struct device *device) { diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 58ffd30dcc91..fe088b40daf9 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -52,17 +52,6 @@ static inline void mei_txe_set_pm_domain(struct mei_device *dev) {} static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM */ -static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) -{ - int i; - - for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { - if (hw->mem_addr[i]) { - pci_iounmap(pdev, hw->mem_addr[i]); - hw->mem_addr[i] = NULL; - } - } -} /** * mei_txe_probe - Device Initialization Routine * @@ -75,22 +64,22 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct mei_device *dev; struct mei_txe_hw *hw; + const int mask = BIT(SEC_BAR) | BIT(BRIDGE_BAR); int err; - int i; /* enable pci dev */ - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(&pdev->dev, "failed to enable pci device.\n"); goto end; } /* set PCI host mastering */ pci_set_master(pdev); - /* pci request regions for mei driver */ - err = pci_request_regions(pdev, KBUILD_MODNAME); + /* pci request regions and mapping IO device memory for mei driver */ + err = pcim_iomap_regions(pdev, mask, KBUILD_MODNAME); if (err) { dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; + goto end; } err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); @@ -98,7 +87,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { dev_err(&pdev->dev, "No suitable DMA available.\n"); - goto release_regions; + goto end; } } @@ -106,20 +95,10 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev = mei_txe_dev_init(pdev); if (!dev) { err = -ENOMEM; - goto release_regions; + goto end; } hw = to_txe_hw(dev); - - /* mapping IO device memory */ - for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { - hw->mem_addr[i] = pci_iomap(pdev, i, 0); - if (!hw->mem_addr[i]) { - dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); - err = -ENOMEM; - goto free_device; - } - } - + hw->mem_addr = pcim_iomap_table(pdev); pci_enable_msi(pdev); @@ -140,7 +119,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) { dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n", pdev->irq); - goto free_device; + goto end; } if (mei_start(dev)) { @@ -173,23 +152,9 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) stop: mei_stop(dev); release_irq: - mei_cancel_work(dev); - - /* disable interrupts */ mei_disable_interrupts(dev); - free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - -free_device: - mei_txe_pci_iounmap(pdev, hw); - - kfree(dev); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); end: dev_err(&pdev->dev, "initialization failed.\n"); return err; @@ -206,38 +171,24 @@ end: static void mei_txe_remove(struct pci_dev *pdev) { struct mei_device *dev; - struct mei_txe_hw *hw; dev = pci_get_drvdata(pdev); if (!dev) { - dev_err(&pdev->dev, "mei: dev =NULL\n"); + dev_err(&pdev->dev, "mei: dev == NULL\n"); return; } pm_runtime_get_noresume(&pdev->dev); - hw = to_txe_hw(dev); - mei_stop(dev); if (!pci_dev_run_wake(pdev)) mei_txe_unset_pm_domain(dev); - /* disable interrupts */ mei_disable_interrupts(dev); free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - - pci_set_drvdata(pdev, NULL); - - mei_txe_pci_iounmap(pdev, hw); mei_deregister(dev); - - kfree(dev); - - pci_release_regions(pdev); - pci_disable_device(pdev); } -- cgit v1.2.1 From 3bb434cdcc6af3d4e70ba041e6f596e465d11e14 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 1 Feb 2017 15:42:57 +0100 Subject: vmw_vmci: switch to pci_irq_alloc_vectors Cleans up the IRQ management code a lot, including removing a lot of state from the per-device structure. Signed-off-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_guest.c | 75 ++++++++++---------------------------- 1 file changed, 20 insertions(+), 55 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 189b32519748..9d659542a335 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -54,10 +54,7 @@ struct vmci_guest_device { struct device *dev; /* PCI device we are attached to */ void __iomem *iobase; - unsigned int irq; - unsigned int intr_type; bool exclusive_vectors; - struct msix_entry msix_entries[VMCI_MAX_INTRS]; struct tasklet_struct datagram_tasklet; struct tasklet_struct bm_tasklet; @@ -368,30 +365,6 @@ static void vmci_process_bitmap(unsigned long data) vmci_dbell_scan_notification_entries(dev->notification_bitmap); } -/* - * Enable MSI-X. Try exclusive vectors first, then shared vectors. - */ -static int vmci_enable_msix(struct pci_dev *pdev, - struct vmci_guest_device *vmci_dev) -{ - int i; - int result; - - for (i = 0; i < VMCI_MAX_INTRS; ++i) { - vmci_dev->msix_entries[i].entry = i; - vmci_dev->msix_entries[i].vector = i; - } - - result = pci_enable_msix_exact(pdev, - vmci_dev->msix_entries, VMCI_MAX_INTRS); - if (result == 0) - vmci_dev->exclusive_vectors = true; - else if (result == -ENOSPC) - result = pci_enable_msix_exact(pdev, vmci_dev->msix_entries, 1); - - return result; -} - /* * Interrupt handler for legacy or MSI interrupt, or for first MSI-X * interrupt (vector VMCI_INTR_DATAGRAM). @@ -406,7 +379,7 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev) * Otherwise we must read the ICR to determine what to do. */ - if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) { + if (dev->exclusive_vectors) { tasklet_schedule(&dev->datagram_tasklet); } else { unsigned int icr; @@ -491,7 +464,6 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, } vmci_dev->dev = &pdev->dev; - vmci_dev->intr_type = VMCI_INTR_TYPE_INTX; vmci_dev->exclusive_vectors = false; vmci_dev->iobase = iobase; @@ -592,26 +564,26 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * Enable interrupts. Try MSI-X first, then MSI, and then fallback on * legacy interrupts. */ - if (!vmci_disable_msix && !vmci_enable_msix(pdev, vmci_dev)) { - vmci_dev->intr_type = VMCI_INTR_TYPE_MSIX; - vmci_dev->irq = vmci_dev->msix_entries[0].vector; - } else if (!vmci_disable_msi && !pci_enable_msi(pdev)) { - vmci_dev->intr_type = VMCI_INTR_TYPE_MSI; - vmci_dev->irq = pdev->irq; + error = pci_alloc_irq_vectors(pdev, VMCI_MAX_INTRS, VMCI_MAX_INTRS, + PCI_IRQ_MSIX); + if (error) { + error = pci_alloc_irq_vectors(pdev, 1, 1, + PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY); + if (error) + goto err_remove_bitmap; } else { - vmci_dev->intr_type = VMCI_INTR_TYPE_INTX; - vmci_dev->irq = pdev->irq; + vmci_dev->exclusive_vectors = true; } /* * Request IRQ for legacy or MSI interrupts, or for first * MSI-X vector. */ - error = request_irq(vmci_dev->irq, vmci_interrupt, IRQF_SHARED, - KBUILD_MODNAME, vmci_dev); + error = request_irq(pci_irq_vector(pdev, 0), vmci_interrupt, + IRQF_SHARED, KBUILD_MODNAME, vmci_dev); if (error) { dev_err(&pdev->dev, "Irq %u in use: %d\n", - vmci_dev->irq, error); + pci_irq_vector(pdev, 0), error); goto err_disable_msi; } @@ -622,13 +594,13 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * between the vectors. */ if (vmci_dev->exclusive_vectors) { - error = request_irq(vmci_dev->msix_entries[1].vector, + error = request_irq(pci_irq_vector(pdev, 1), vmci_interrupt_bm, 0, KBUILD_MODNAME, vmci_dev); if (error) { dev_err(&pdev->dev, "Failed to allocate irq %u: %d\n", - vmci_dev->msix_entries[1].vector, error); + pci_irq_vector(pdev, 1), error); goto err_free_irq; } } @@ -651,15 +623,12 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, return 0; err_free_irq: - free_irq(vmci_dev->irq, vmci_dev); + free_irq(pci_irq_vector(pdev, 0), vmci_dev); tasklet_kill(&vmci_dev->datagram_tasklet); tasklet_kill(&vmci_dev->bm_tasklet); err_disable_msi: - if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) - pci_disable_msix(pdev); - else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) - pci_disable_msi(pdev); + pci_free_irq_vectors(pdev); vmci_err = vmci_event_unsubscribe(ctx_update_sub_id); if (vmci_err < VMCI_SUCCESS) @@ -719,14 +688,10 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) * MSI-X, we might have multiple vectors, each with their own * IRQ, which we must free too. */ - free_irq(vmci_dev->irq, vmci_dev); - if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) { - if (vmci_dev->exclusive_vectors) - free_irq(vmci_dev->msix_entries[1].vector, vmci_dev); - pci_disable_msix(pdev); - } else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) { - pci_disable_msi(pdev); - } + if (vmci_dev->exclusive_vectors) + free_irq(pci_irq_vector(pdev, 1), vmci_dev); + free_irq(pci_irq_vector(pdev, 0), vmci_dev); + pci_free_irq_vectors(pdev); tasklet_kill(&vmci_dev->datagram_tasklet); tasklet_kill(&vmci_dev->bm_tasklet); -- cgit v1.2.1 From e1ef962bf1255cea2f165f3a7374766a7ceb4308 Mon Sep 17 00:00:00 2001 From: Miguel Bernal Marin Date: Tue, 31 Jan 2017 18:02:16 -0600 Subject: misc: fix typo on Kconfig When panel driver was moved from staging to misc a new line was missing to be added on Kconfig file. Fixes: 305b37bd01c2 ("misc: Move panel driver out of staging") Signed-off-by: Miguel Bernal Marin Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0444a8f9b094..b4d6aac83824 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -491,6 +491,7 @@ config VEXPRESS_SYSCFG ARM Ltd. Versatile Express uses specialised platform configuration 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 -- cgit v1.2.1 From 9c7daa61d91647fba3b986a9941d3fb531b9b9ff Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 2 Feb 2017 11:26:53 +0200 Subject: mei: me: add a wrapper to set host generated data interrupt Consolidate setting H_IG, an interrupt from host towards hw, into a wrapper to eliminate code duplication. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index befeac52c349..23294b845c42 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -139,6 +139,19 @@ static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) mei_hcsr_write(dev, reg); } +/** + * mei_hcsr_set_hig - set host interrupt (set H_IG) + * + * @dev: the device structure + */ +static inline void mei_hcsr_set_hig(struct mei_device *dev) +{ + u32 hcsr; + + hcsr = mei_hcsr_read(dev) | H_IG; + mei_hcsr_set(dev, hcsr); +} + /** * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register * @@ -505,7 +518,6 @@ static int mei_me_hbuf_write(struct mei_device *dev, unsigned long rem; unsigned long length = header->length; u32 *reg_buf = (u32 *)buf; - u32 hcsr; u32 dw_cnt; int i; int empty_slots; @@ -532,8 +544,7 @@ static int mei_me_hbuf_write(struct mei_device *dev, mei_me_hcbww_write(dev, reg); } - hcsr = mei_hcsr_read(dev) | H_IG; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set_hig(dev); if (!mei_me_hw_is_ready(dev)) return -EIO; @@ -580,7 +591,6 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, unsigned long buffer_length) { u32 *reg_buf = (u32 *)buffer; - u32 hcsr; for (; buffer_length >= sizeof(u32); buffer_length -= sizeof(u32)) *reg_buf++ = mei_me_mecbrw_read(dev); @@ -591,8 +601,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, memcpy(reg_buf, ®, buffer_length); } - hcsr = mei_hcsr_read(dev) | H_IG; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set_hig(dev); return 0; } -- cgit v1.2.1 From 47f60a017169d1d3c6cb086ece272f272f110845 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 2 Feb 2017 11:26:54 +0200 Subject: mei: me: generate an interrupt if the hw indicates reset. In rare case the driver may lose connection with the device after device reset due to a missed interrupt. The driver will unlock the flow by generating an interrupt towards the firmware (HIG) when the device is in the resetting state. The FW is able to ignore the interrupt during orderly flow. The effected platforms are skylake and newer. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 23294b845c42..71216affcab1 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -393,6 +393,19 @@ static bool mei_me_hw_is_ready(struct mei_device *dev) return (mecsr & ME_RDY_HRA) == ME_RDY_HRA; } +/** + * mei_me_hw_is_resetting - check whether the me(hw) is in reset + * + * @dev: mei device + * Return: bool + */ +static bool mei_me_hw_is_resetting(struct mei_device *dev) +{ + u32 mecsr = mei_me_mecsr_read(dev); + + return (mecsr & ME_RST_HRA) == ME_RST_HRA; +} + /** * mei_me_hw_ready_wait - wait until the me(hw) has turned ready * or timeout is reached @@ -1219,6 +1232,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) goto end; } + if (mei_me_hw_is_resetting(dev)) + mei_hcsr_set_hig(dev); + mei_me_pg_intr(dev, me_intr_src(hcsr)); /* check if we need to start the dev */ -- cgit v1.2.1 From cb97fbbcac15982406e0c74cd5512a8b6fcf10b3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 8 Feb 2017 00:41:45 +0200 Subject: mei: remove support for broken parallel read Parallel reads from multiple threads on a file descriptor are not well defined and racy. It is safer to return to original behavior and simply fail the additional read. The solution is to remove request for next read credit. Cc: #4.9 Fixes: ff1586a7ea57 ("mei: enqueue consecutive reads") Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index e1bf54481fd6..9d0b7050c79a 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -182,32 +182,36 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (rets == -EBUSY && - !mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, file)) { - rets = -ENOMEM; - goto out; - } - do { - mutex_unlock(&dev->device_lock); - - if (wait_event_interruptible(cl->rx_wait, - (!list_empty(&cl->rd_completed)) || - (!mei_cl_is_connected(cl)))) { +again: + mutex_unlock(&dev->device_lock); + if (wait_event_interruptible(cl->rx_wait, + !list_empty(&cl->rd_completed) || + !mei_cl_is_connected(cl))) { + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + mutex_lock(&dev->device_lock); - if (signal_pending(current)) - return -EINTR; - return -ERESTARTSYS; - } + if (!mei_cl_is_connected(cl)) { + rets = -ENODEV; + goto out; + } - mutex_lock(&dev->device_lock); - if (!mei_cl_is_connected(cl)) { - rets = -ENODEV; - goto out; - } + 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; - cb = mei_cl_read_cb(cl, file); - } while (!cb); + rets = 0; + goto out; + } copy_buffer: /* now copy the data to user space */ -- cgit v1.2.1 From 84a1ed0407d209beff4d9cb41120d0f69a89d4b8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:03 +0100 Subject: misc: panel: Fix LCD_FLAG_F/LCD_FLAG_N exchange LCD_FLAG_F is the font flag, LCD_FLAG_N is the two-lines flag. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/panel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index 6030ac5b8c63..cac7ac62ce5b 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -1278,9 +1278,10 @@ static inline int handle_lcd_special_code(void) lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS | ((lcd.flags & LCD_FLAG_F) - ? LCD_CMD_TWO_LINES : 0) - | ((lcd.flags & LCD_FLAG_N) ? 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)) { -- cgit v1.2.1 From 30f468b2ea7a75aa7e212b7d89eb136a55462ee8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:04 +0100 Subject: misc: panel: Remove PANEL_VERSION Hardcoded driver versions are so pre-git. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/panel.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index cac7ac62ce5b..fa4c12768a16 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -64,8 +64,6 @@ #define LCD_MINOR 156 #define KEYPAD_MINOR 185 -#define PANEL_VERSION "0.9.5" - #define LCD_MAXBYTES 256 /* max burst write */ #define KEYPAD_BUFFER 64 @@ -1656,8 +1654,7 @@ static void lcd_init(void) 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 "\nPanel-" - PANEL_VERSION); + panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE); #endif lcd.addr.x = 0; lcd.addr.y = 0; @@ -2278,8 +2275,7 @@ static void panel_detach(struct parport *port) } if (lcd.enabled) { - panel_lcd_print("\x0cLCD driver " PANEL_VERSION - "\nunloaded.\x1b[Lc\x1b[Lb\x1b[L-"); + panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); misc_deregister(&lcd_dev); lcd.initialized = false; } @@ -2401,7 +2397,7 @@ static int __init panel_init_module(void) if (!lcd.enabled && !keypad.enabled) { /* no device enabled, let's exit */ - pr_err("driver version " PANEL_VERSION " disabled.\n"); + pr_err("panel driver disabled.\n"); return -ENODEV; } @@ -2412,12 +2408,10 @@ static int __init panel_init_module(void) } if (pprt) - pr_info("driver version " PANEL_VERSION - " registered on parport%d (io=0x%lx).\n", parport, - pprt->port->base); + pr_info("panel driver registered on parport%d (io=0x%lx).\n", + parport, pprt->port->base); else - pr_info("driver version " PANEL_VERSION - " not yet registered\n"); + pr_info("panel driver not yet registered\n"); return 0; } -- cgit v1.2.1 From e28fa714aa8bb682d07136ba3821bc883c78f6df Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:05 +0100 Subject: misc: panel: Remove unused LCD_FLAG_S and LCD_FLAG_ID These definitions were never used in any publicly available version since (at least) 2004. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/panel.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index fa4c12768a16..8af500ecaaaf 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -119,8 +119,6 @@ #define PIN_SELECP 17 #define PIN_NOT_SET 127 -#define LCD_FLAG_S 0x0001 -#define LCD_FLAG_ID 0x0002 #define LCD_FLAG_B 0x0004 /* blink on */ #define LCD_FLAG_C 0x0008 /* cursor on */ #define LCD_FLAG_D 0x0010 /* display on */ -- cgit v1.2.1 From 731fcec46980cb21fd429e9039f565b9dbe060ae Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:06 +0100 Subject: misc: panel: Remove reference to misc device support As of commit 7c5763b8453a9487 ("drivers: misc: Remove MISC_DEVICES config option"), misc device support no longer needs to be enabled manually. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b4d6aac83824..2687ecf3e9e6 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -499,10 +499,9 @@ config PANEL 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). Both require misc device to be - enabled. 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. + 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. config PANEL_PARPORT int "Default parallel port number (0=LPT1)" -- cgit v1.2.1 From 9db3cf1ccd5f78ad5c2a01a2fd5c5774d0ed0f6f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:07 +0100 Subject: misc: panel: Move all suboptions into a big if section All 18 suboptions related to the panel driver have individual dependencies on PANEL. Replace them by a single "if PANEL / endif # PANEL" section for easier dependency management. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2687ecf3e9e6..c290990d73ed 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -503,9 +503,10 @@ config PANEL 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)" - depends on PANEL range 0 255 default "0" ---help--- @@ -517,7 +518,6 @@ config PANEL_PARPORT config PANEL_PROFILE int "Default panel profile (0-5, 0=custom)" - depends on PANEL range 0 5 default "5" ---help--- @@ -538,7 +538,7 @@ config PANEL_PROFILE for experts. config PANEL_KEYPAD - depends on PANEL && PANEL_PROFILE="0" + 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 @@ -555,7 +555,7 @@ config PANEL_KEYPAD supports simultaneous keys pressed when the keypad supports them. config PANEL_LCD - depends on PANEL && PANEL_PROFILE="0" + 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 @@ -578,7 +578,7 @@ config PANEL_LCD that those values changed from the 2.4 driver for better consistency. config PANEL_LCD_HEIGHT - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Number of lines on the LCD (1-2)" range 1 2 default 2 @@ -587,7 +587,7 @@ config PANEL_LCD_HEIGHT It can either be 1 or 2. config PANEL_LCD_WIDTH - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Number of characters per line on the LCD (1-40)" range 1 40 default 40 @@ -596,7 +596,7 @@ config PANEL_LCD_WIDTH Common values are 16,20,24,40. config PANEL_LCD_BWIDTH - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Internal LCD line width (1-40, 40 by default)" range 1 40 default 40 @@ -612,7 +612,7 @@ config PANEL_LCD_BWIDTH If you don't know, put '40' here. config PANEL_LCD_HWIDTH - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Hardware LCD line width (1-64, 64 by default)" range 1 64 default 64 @@ -626,7 +626,7 @@ config PANEL_LCD_HWIDTH 64 here for a 2x40. config PANEL_LCD_CHARSET - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "LCD character set (0=normal, 1=KS0074)" range 0 1 default 0 @@ -642,7 +642,7 @@ config PANEL_LCD_CHARSET If you don't know, use the normal one (0). config PANEL_LCD_PROTO - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "LCD communication mode (0=parallel 8 bits, 1=serial)" range 0 1 default 0 @@ -655,7 +655,7 @@ config PANEL_LCD_PROTO parallel LCD, and 1 for a serial LCD. config PANEL_LCD_PIN_E - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + 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 @@ -670,7 +670,7 @@ config PANEL_LCD_PIN_E Default for the 'E' pin in custom profile is '14' (AUTOFEED). config PANEL_LCD_PIN_RS - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + 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 @@ -685,7 +685,7 @@ config PANEL_LCD_PIN_RS Default for the 'RS' pin in custom profile is '17' (SELECT IN). config PANEL_LCD_PIN_RW - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + 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 @@ -700,7 +700,7 @@ config PANEL_LCD_PIN_RW Default for the 'RW' pin in custom profile is '16' (INIT). config PANEL_LCD_PIN_SCL - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" + 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 @@ -715,7 +715,7 @@ config PANEL_LCD_PIN_SCL Default for the 'SCL' pin in custom profile is '1' (STROBE). config PANEL_LCD_PIN_SDA - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" + 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 @@ -730,7 +730,7 @@ config PANEL_LCD_PIN_SDA Default for the 'SDA' pin in custom profile is '2' (D0). config PANEL_LCD_PIN_BL - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + 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 @@ -745,7 +745,6 @@ config PANEL_LCD_PIN_BL Default for the 'BL' pin in custom profile is '0' (uncontrolled). config PANEL_CHANGE_MESSAGE - depends on PANEL bool "Change LCD initialization message ?" default "n" ---help--- @@ -758,7 +757,7 @@ config PANEL_CHANGE_MESSAGE say 'N' and keep the default message with the version. config PANEL_BOOT_MESSAGE - depends on PANEL && PANEL_CHANGE_MESSAGE="y" + depends on PANEL_CHANGE_MESSAGE="y" string "New initialization message" default "" ---help--- @@ -770,6 +769,8 @@ config PANEL_BOOT_MESSAGE 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 + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" -- cgit v1.2.1 From 3f77b43965054c17fbc3111650c4e90f294a7bbe Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:08 +0100 Subject: misc: panel: Remove always-true check from panel_detach() panel_detach() already verified that pptr is a valid pointer. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/panel.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index 8af500ecaaaf..ed19b3e399b0 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -2266,24 +2266,22 @@ static void panel_detach(struct parport *port) if (scan_timer.function) del_timer_sync(&scan_timer); - if (pprt) { - 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); - lcd.initialized = false; - } + if (keypad.enabled) { + misc_deregister(&keypad_dev); + keypad_initialized = 0; + } - /* TODO: free all input signals */ - parport_release(pprt); - parport_unregister_device(pprt); - pprt = NULL; - unregister_reboot_notifier(&panel_notifier); + if (lcd.enabled) { + panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); + misc_deregister(&lcd_dev); + 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 = { -- cgit v1.2.1 From 204a4f6d67a4d7e65571c21bd90039fdccc35b2c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:09 +0100 Subject: misc: panel: Add lcd_home() helper Add a helper function to move the cursor to the home position, so callers no longer need access to internal state. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/panel.c | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index ed19b3e399b0..9c7b00951c22 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -903,6 +903,13 @@ static void lcd_gotoxy(void) (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) { @@ -921,9 +928,7 @@ static void lcd_clear_fast_s(void) { int pos; - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { @@ -935,9 +940,7 @@ static void lcd_clear_fast_s(void) } spin_unlock_irq(&pprt_lock); - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); } /* fills the display with spaces and resets X/Y */ @@ -945,9 +948,7 @@ static void lcd_clear_fast_p8(void) { int pos; - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { @@ -973,9 +974,7 @@ static void lcd_clear_fast_p8(void) } spin_unlock_irq(&pprt_lock); - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); } /* fills the display with spaces and resets X/Y */ @@ -983,9 +982,7 @@ static void lcd_clear_fast_tilcd(void) { int pos; - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { @@ -996,9 +993,7 @@ static void lcd_clear_fast_tilcd(void) spin_unlock_irq(&pprt_lock); - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); } /* clears the display and resets X/Y */ @@ -1373,9 +1368,7 @@ static void lcd_write_char(char c) processed = 1; } else if (!strcmp(lcd.esc_seq.buf, "[H")) { /* cursor to home */ - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); processed = 1; } /* codes starting with ^[[L */ @@ -1654,11 +1647,9 @@ static void lcd_init(void) #else panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE); #endif - lcd.addr.x = 0; - lcd.addr.y = 0; /* clear the display on the next device opening */ lcd.must_clear = true; - lcd_gotoxy(); + lcd_home(); } /* -- cgit v1.2.1 From fda4ae1819ce3603857a6baceda53d2c46d5bbd9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 Feb 2017 15:38:10 +0100 Subject: misc: panel: Abstract temporary backlight handling Currently the periodic scan timer is used for three purposes, entangling keypad and display handling, which are both optional: 1. Scanning the keypad, 2. Flashing the backlight when a key is pressed, 3. Disabling temporary backlighting after a fixed period of time. Abstract the second purpose using a new lcd_poke() function. Make the non-periodic temporary backlight handling independent from keypad handling by converting it to a delayed workqueue. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/panel.c | 101 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 41 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index 9c7b00951c22..ef2ece0f26af 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -75,8 +76,8 @@ /* a key repeats this times INPUT_POLL_TIME */ #define KEYPAD_REP_DELAY (2) -/* keep the light on this times INPUT_POLL_TIME for each flash */ -#define FLASH_LIGHT_TEMPO (200) +/* 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) @@ -252,7 +253,10 @@ static struct { int hwidth; int charset; int proto; - int light_tempo; + + struct delayed_work bl_work; + struct mutex bl_tempo_lock; /* Protects access to bl_tempo */ + bool bl_tempo; /* TODO: use union here? */ struct { @@ -657,8 +661,6 @@ static void lcd_get_bits(unsigned int port, int *val) } } -static void init_scan_timer(void); - /* sets data port bits according to current signals values */ static int set_data_bits(void) { @@ -790,11 +792,8 @@ static void lcd_send_serial(int byte) } /* turn the backlight on or off */ -static void lcd_backlight(int on) +static void __lcd_backlight(int on) { - if (lcd.pins.bl == PIN_NONE) - return; - /* The backlight is activated by setting the AUTOFEED line to +5V */ spin_lock_irq(&pprt_lock); if (on) @@ -805,6 +804,44 @@ static void lcd_backlight(int on) spin_unlock_irq(&pprt_lock); } +static void lcd_backlight(int on) +{ + if (lcd.pins.bl == PIN_NONE) + return; + + mutex_lock(&lcd.bl_tempo_lock); + if (!lcd.bl_tempo) + __lcd_backlight(on); + mutex_unlock(&lcd.bl_tempo_lock); +} + +static void lcd_bl_off(struct work_struct *work) +{ + mutex_lock(&lcd.bl_tempo_lock); + if (lcd.bl_tempo) { + lcd.bl_tempo = false; + if (!(lcd.flags & LCD_FLAG_L)) + __lcd_backlight(0); + } + mutex_unlock(&lcd.bl_tempo_lock); +} + +/* turn the backlight on for a little while */ +static void lcd_poke(void) +{ + if (lcd.pins.bl == PIN_NONE) + return; + + cancel_delayed_work_sync(&lcd.bl_work); + + mutex_lock(&lcd.bl_tempo_lock); + if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L)) + __lcd_backlight(1); + lcd.bl_tempo = true; + schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ); + mutex_unlock(&lcd.bl_tempo_lock); +} + /* send a command to the LCD panel in serial mode */ static void lcd_write_cmd_s(int cmd) { @@ -1099,13 +1136,8 @@ static inline int handle_lcd_special_code(void) processed = 1; break; case '*': - /* flash back light using the keypad timer */ - if (scan_timer.function) { - if (lcd.light_tempo == 0 && - ((lcd.flags & LCD_FLAG_L) == 0)) - lcd_backlight(1); - lcd.light_tempo = FLASH_LIGHT_TEMPO; - } + /* flash back light */ + lcd_poke(); processed = 1; break; case 'f': /* Small Font */ @@ -1275,16 +1307,8 @@ static inline int handle_lcd_special_code(void) ? LCD_CMD_TWO_LINES : 0)); /* check whether L flag was changed */ - else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) { - if (lcd.flags & (LCD_FLAG_L)) - lcd_backlight(1); - else if (lcd.light_tempo == 0) - /* - * switch off the light only when the tempo - * lighting is gone - */ - lcd_backlight(0); - } + else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) + lcd_backlight(!!(lcd.flags & LCD_FLAG_L)); } return processed; @@ -1615,8 +1639,10 @@ static void lcd_init(void) else lcd_char_conv = NULL; - if (lcd.pins.bl != PIN_NONE) - init_scan_timer(); + 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]); @@ -1984,19 +2010,8 @@ static void panel_scan_timer(void) panel_process_inputs(); } - if (lcd.enabled && lcd.initialized) { - if (keypressed) { - if (lcd.light_tempo == 0 && - ((lcd.flags & LCD_FLAG_L) == 0)) - lcd_backlight(1); - lcd.light_tempo = FLASH_LIGHT_TEMPO; - } else if (lcd.light_tempo > 0) { - lcd.light_tempo--; - if (lcd.light_tempo == 0 && - ((lcd.flags & LCD_FLAG_L) == 0)) - lcd_backlight(0); - } - } + if (keypressed && lcd.enabled && lcd.initialized) + lcd_poke(); mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME); } @@ -2265,6 +2280,10 @@ static void panel_detach(struct parport *port) if (lcd.enabled) { panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); misc_deregister(&lcd_dev); + if (lcd.pins.bl != PIN_NONE) { + cancel_delayed_work_sync(&lcd.bl_work); + __lcd_backlight(0); + } lcd.initialized = false; } -- cgit v1.2.1