diff options
Diffstat (limited to 'drivers/misc')
39 files changed, 1412 insertions, 612 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b9ca56930003..b9c11f67315f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -660,4 +660,5 @@ source "drivers/misc/pvpanic/Kconfig" source "drivers/misc/mchp_pci1xxxx/Kconfig" source "drivers/misc/keba/Kconfig" source "drivers/misc/amd-sbi/Kconfig" +source "drivers/misc/rp1/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 917b9a7183aa..e2e66f5f4fb8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -75,3 +75,4 @@ lan966x-pci-objs += lan966x_pci.dtbo.o obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ +obj-$(CONFIG_MISC_RP1) += rp1/ diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c index b653a21a909e..3dec2fc00124 100644 --- a/drivers/misc/amd-sbi/rmi-core.c +++ b/drivers/misc/amd-sbi/rmi-core.c @@ -42,7 +42,6 @@ #define RD_MCA_CMD 0x86 /* CPUID MCAMSR mask & index */ -#define CPUID_MCA_THRD_MASK GENMASK(15, 0) #define CPUID_MCA_THRD_INDEX 32 #define CPUID_MCA_FUNC_MASK GENMASK(31, 0) #define CPUID_EXT_FUNC_INDEX 56 @@ -129,7 +128,7 @@ static int rmi_cpuid_read(struct sbrmi_data *data, goto exit_unlock; } - thread = msg->cpu_in_out << CPUID_MCA_THRD_INDEX & CPUID_MCA_THRD_MASK; + thread = msg->cpu_in_out >> CPUID_MCA_THRD_INDEX; /* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */ if (thread > 127) { @@ -210,7 +209,7 @@ static int rmi_mca_msr_read(struct sbrmi_data *data, goto exit_unlock; } - thread = msg->mcamsr_in_out << CPUID_MCA_THRD_INDEX & CPUID_MCA_THRD_MASK; + thread = msg->mcamsr_in_out >> CPUID_MCA_THRD_INDEX; /* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */ if (thread > 127) { @@ -321,6 +320,10 @@ int rmi_mailbox_xfer(struct sbrmi_data *data, ret = regmap_read(data->regmap, SBRMI_OUTBNDMSG7, &ec); if (ret || ec) goto exit_clear_alert; + + /* Clear the input value before updating the output data */ + msg->mb_in_out = 0; + /* * For a read operation, the initiator (BMC) reads the firmware * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1] @@ -373,7 +376,8 @@ static int apml_rmi_reg_xfer(struct sbrmi_data *data, mutex_unlock(&data->lock); if (msg.rflag && !ret) - return copy_to_user(arg, &msg, sizeof(struct apml_reg_xfer_msg)); + if (copy_to_user(arg, &msg, sizeof(struct apml_reg_xfer_msg))) + return -EFAULT; return ret; } @@ -391,7 +395,9 @@ static int apml_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg __use if (ret && ret != -EPROTOTYPE) return ret; - return copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg)); + if (copy_to_user(arg, &msg, sizeof(struct apml_mbox_msg))) + return -EFAULT; + return ret; } static int apml_cpuid_xfer(struct sbrmi_data *data, struct apml_cpuid_msg __user *arg) @@ -408,7 +414,9 @@ static int apml_cpuid_xfer(struct sbrmi_data *data, struct apml_cpuid_msg __user if (ret && ret != -EPROTOTYPE) return ret; - return copy_to_user(arg, &msg, sizeof(struct apml_cpuid_msg)); + if (copy_to_user(arg, &msg, sizeof(struct apml_cpuid_msg))) + return -EFAULT; + return ret; } static int apml_mcamsr_xfer(struct sbrmi_data *data, struct apml_mcamsr_msg __user *arg) @@ -425,7 +433,9 @@ static int apml_mcamsr_xfer(struct sbrmi_data *data, struct apml_mcamsr_msg __us if (ret && ret != -EPROTOTYPE) return ret; - return copy_to_user(arg, &msg, sizeof(struct apml_mcamsr_msg)); + if (copy_to_user(arg, &msg, sizeof(struct apml_mcamsr_msg))) + return -EFAULT; + return ret; } static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index fc64474b8241..babdb60cc46c 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -888,7 +888,7 @@ static size_t c2port_bin_attr_size(struct kobject *kobj, static const struct attribute_group c2port_group = { .attrs = c2port_attrs, - .bin_attrs_new = c2port_bin_attrs, + .bin_attrs = c2port_bin_attrs, .bin_size = c2port_bin_attr_size, }; diff --git a/drivers/misc/cardreader/rts5264.c b/drivers/misc/cardreader/rts5264.c index 06d7a8a95fd6..d050c9fff7ac 100644 --- a/drivers/misc/cardreader/rts5264.c +++ b/drivers/misc/cardreader/rts5264.c @@ -413,8 +413,8 @@ static void rts5264_init_from_hw(struct rtsx_pcr *pcr) { struct pci_dev *pdev = pcr->pci; u32 lval1, lval2, i; - u16 setting_reg1, setting_reg2; - u8 valid, efuse_valid, tmp; + u16 setting_reg1, setting_reg2, phy_val; + u8 valid, efuse_valid, tmp, efuse_len; rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, REG_EFUSE_POR | REG_EFUSE_POWER_MASK, @@ -433,6 +433,8 @@ static void rts5264_init_from_hw(struct rtsx_pcr *pcr) break; } rtsx_pci_read_register(pcr, RTS5264_EFUSE_READ_DATA, &tmp); + efuse_len = ((tmp & 0x70) >> 4); + pcr_dbg(pcr, "Load efuse len: 0x%x\n", efuse_len); efuse_valid = ((tmp & 0x0C) >> 2); pcr_dbg(pcr, "Load efuse valid: 0x%x\n", efuse_valid); @@ -445,6 +447,58 @@ static void rts5264_init_from_hw(struct rtsx_pcr *pcr) REG_EFUSE_POR, 0); pcr_dbg(pcr, "Disable efuse por!\n"); + if (is_version(pcr, PID_5264, RTS5264_IC_VER_B)) { + pci_write_config_dword(pdev, 0x718, 0x0007C000); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE, 0xFF, 0x88); + rtsx_pci_read_phy_register(pcr, _PHY_REV0, &phy_val); + phy_val &= 0xFFFD; + + if (efuse_len == 0) { + rtsx_pci_write_register(pcr, RTS5264_FW_CFG_INFO2, 0x0F, 0x0F); + rtsx_pci_write_register(pcr, 0xFF14, 0xFF, 0x79); + rtsx_pci_write_register(pcr, 0xFF15, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF16, 0xFF, 0x3D); + rtsx_pci_write_register(pcr, 0xFF17, 0xFF, 0xFE); + + rtsx_pci_write_register(pcr, 0xFF18, 0xFF, 0x5B); + rtsx_pci_write_register(pcr, 0xFF19, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF1A, 0xFF, 0x3E); + rtsx_pci_write_register(pcr, 0xFF1B, 0xFF, 0xFE); + + rtsx_pci_write_register(pcr, 0xFF1C, 0xFF, 0x00); + rtsx_pci_write_register(pcr, 0xFF1D, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF1E, 0xFF, 0x3F); + rtsx_pci_write_register(pcr, 0xFF1F, 0xFF, 0xFE); + + rtsx_pci_write_register(pcr, 0xFF20, 0xFF, 0x81); + rtsx_pci_write_register(pcr, 0xFF21, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF22, 0xFF, 0x3C); + rtsx_pci_write_register(pcr, 0xFF23, 0xFF, 0xFE); + } + + rtsx_pci_write_register(pcr, 0xFF24, 0xFF, 0x79); + rtsx_pci_write_register(pcr, 0xFF25, 0xFF, 0x5B); + rtsx_pci_write_register(pcr, 0xFF26, 0xFF, 0x00); + rtsx_pci_write_register(pcr, 0xFF27, 0xFF, 0x40); + + rtsx_pci_write_register(pcr, 0xFF28, 0xFF, (u8)phy_val); + rtsx_pci_write_register(pcr, 0xFF29, 0xFF, (u8)(phy_val >> 8)); + rtsx_pci_write_register(pcr, 0xFF2A, 0xFF, 0x19); + rtsx_pci_write_register(pcr, 0xFF2B, 0xFF, 0x40); + + rtsx_pci_write_register(pcr, 0xFF2C, 0xFF, 0x20); + rtsx_pci_write_register(pcr, 0xFF2D, 0xFF, 0xDA); + rtsx_pci_write_register(pcr, 0xFF2E, 0xFF, 0x0A); + rtsx_pci_write_register(pcr, 0xFF2F, 0xFF, 0x40); + + rtsx_pci_write_register(pcr, 0xFF30, 0xFF, 0x20); + rtsx_pci_write_register(pcr, 0xFF31, 0xFF, 0xD2); + rtsx_pci_write_register(pcr, 0xFF32, 0xFF, 0x0A); + rtsx_pci_write_register(pcr, 0xFF33, 0xFF, 0x40); + } else { + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE, 0x80, 0x80); + } + if (efuse_valid == 2 || efuse_valid == 3) { if (valid == 3) { /* Bypass efuse */ @@ -618,6 +672,9 @@ static int rts5264_optimize_phy(struct rtsx_pcr *pcr) rtsx_pci_update_phy(pcr, _PHY_REV0, 0x1FF, 0x3800); } + if (is_version(pcr, PID_5264, RTS5264_IC_VER_B)) + rtsx_pci_write_phy_register(pcr, 0x00, 0x5B79); + return 0; } @@ -820,7 +877,7 @@ int rts5264_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, SSC_DEPTH_MASK, ssc_depth); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); - if (is_version(pcr, 0x5264, IC_VER_A)) { + if (is_version(pcr, PID_5264, RTS5264_IC_VER_A)) { rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS5264_CARD_CLK_SRC2, RTS5264_REG_BIG_KVCO_A, 0); diff --git a/drivers/misc/cardreader/rts5264.h b/drivers/misc/cardreader/rts5264.h index e3cbbf2fe1a4..f3e81daa708d 100644 --- a/drivers/misc/cardreader/rts5264.h +++ b/drivers/misc/cardreader/rts5264.h @@ -61,6 +61,8 @@ /* DMACTL 0xFE2C */ #define RTS5264_DMA_PACK_SIZE_MASK 0x70 +#define RTS5264_FW_CFG_INFO2 0xFF52 + #define RTS5264_FW_CFG1 0xFF55 #define RTS5264_SYS_CLK_SEL_MCU_CLK (0x01<<7) #define RTS5264_CRC_CLK_SEL_MCU_CLK (0x01<<6) @@ -272,6 +274,10 @@ #define SD_LUN 1 #define SD_EXPRESS_LUN 2 +#define RTS5264_IC_VER_A 0 +#define RTS5264_IC_VER_B 2 +#define RTS5264_IC_VER_C 3 + int rts5264_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index a7b066c48740..f9952d76d6ed 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1236,7 +1236,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) else if (PCI_PID(pcr) == PID_5228) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, RTS5228_SSC_DEPTH_2M); - else if (is_version(pcr, 0x5264, IC_VER_A)) + else if (is_version(pcr, PID_5264, RTS5264_IC_VER_A)) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); else if (PCI_PID(pcr) == PID_5264) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c index 148107a4547c..d007a4455ce5 100644 --- a/drivers/misc/cardreader/rtsx_usb.c +++ b/drivers/misc/cardreader/rtsx_usb.c @@ -698,6 +698,12 @@ static void rtsx_usb_disconnect(struct usb_interface *intf) } #ifdef CONFIG_PM +static int rtsx_usb_resume_child(struct device *dev, void *data) +{ + pm_request_resume(dev); + return 0; +} + static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct rtsx_ucr *ucr = @@ -713,8 +719,10 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) mutex_unlock(&ucr->dev_mutex); /* Defer the autosuspend if card exists */ - if (val & (SD_CD | MS_CD)) + if (val & (SD_CD | MS_CD)) { + device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); return -EAGAIN; + } } else { /* There is an ongoing operation*/ return -EAGAIN; @@ -724,12 +732,6 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -static int rtsx_usb_resume_child(struct device *dev, void *data) -{ - pm_request_resume(dev); - return 0; -} - static int rtsx_usb_resume(struct usb_interface *intf) { device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 5d5a70a62e98..cb09e056531a 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -194,8 +194,8 @@ static const struct bin_attribute ds1682_eeprom_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = DS1682_EEPROM_SIZE, - .read_new = ds1682_eeprom_read, - .write_new = ds1682_eeprom_write, + .read = ds1682_eeprom_read, + .write = ds1682_eeprom_write, }; static int ds1682_nvmem_read(void *priv, unsigned int offset, void *val, diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index cb1c4b8e7fd3..0bef5b93bd6d 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -37,6 +37,7 @@ config EEPROM_AT25 depends on SPI && SYSFS select NVMEM select NVMEM_SYSFS + select SPI_MEM help Enable this driver to get read/write support to most SPI EEPROMs and Cypress FRAMs, diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 595ceb9a7126..2d0492867054 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -7,8 +7,10 @@ */ #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/property.h> @@ -17,6 +19,7 @@ #include <linux/spi/eeprom.h> #include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> #include <linux/nvmem-provider.h> @@ -35,13 +38,12 @@ struct at25_data { struct spi_eeprom chip; - struct spi_device *spi; + struct spi_mem *spimem; struct mutex lock; unsigned addrlen; struct nvmem_config nvmem_config; struct nvmem_device *nvmem; u8 sernum[FM25_SN_LEN]; - u8 command[EE_MAXADDRLEN + 1]; }; #define AT25_WREN 0x06 /* latch the write enable */ @@ -74,20 +76,29 @@ struct at25_data { #define io_limit PAGE_SIZE /* bytes */ +/* Handle the address MSB as part of instruction byte */ +static u8 at25_instr(struct at25_data *at25, u8 instr, unsigned int off) +{ + if (!(at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)) + return instr; + if (off < BIT(at25->addrlen * 8)) + return instr; + return instr | AT25_INSTR_BIT3; +} + static int at25_ee_read(void *priv, unsigned int offset, void *val, size_t count) { + u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL); struct at25_data *at25 = priv; char *buf = val; - size_t max_chunk = spi_max_transfer_size(at25->spi); unsigned int msg_offset = offset; size_t bytes_left = count; size_t segment; - u8 *cp; - ssize_t status; - struct spi_transfer t[2]; - struct spi_message m; - u8 instr; + int status; + + if (!bounce) + return -ENOMEM; if (unlikely(offset >= at25->chip.byte_len)) return -EINVAL; @@ -97,87 +108,67 @@ static int at25_ee_read(void *priv, unsigned int offset, return -EINVAL; do { - segment = min(bytes_left, max_chunk); - cp = at25->command; - - instr = AT25_READ; - if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) - if (msg_offset >= BIT(at25->addrlen * 8)) - instr |= AT25_INSTR_BIT3; - - mutex_lock(&at25->lock); + struct spi_mem_op op; - *cp++ = instr; + segment = min(bytes_left, io_limit); - /* 8/16/24-bit address is written MSB first */ - switch (at25->addrlen) { - default: /* case 3 */ - *cp++ = msg_offset >> 16; - fallthrough; - case 2: - *cp++ = msg_offset >> 8; - fallthrough; - case 1: - case 0: /* can't happen: for better code generation */ - *cp++ = msg_offset >> 0; - } - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = at25->command; - t[0].len = at25->addrlen + 1; - spi_message_add_tail(&t[0], &m); + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_READ, + msg_offset), 1), + SPI_MEM_OP_ADDR(at25->addrlen, msg_offset, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(segment, bounce, 1)); - t[1].rx_buf = buf; - t[1].len = segment; - spi_message_add_tail(&t[1], &m); - - status = spi_sync(at25->spi, &m); + status = spi_mem_adjust_op_size(at25->spimem, &op); + if (status) + return status; + segment = op.data.nbytes; + mutex_lock(&at25->lock); + status = spi_mem_exec_op(at25->spimem, &op); mutex_unlock(&at25->lock); - if (status) return status; + memcpy(buf, bounce, segment); msg_offset += segment; buf += segment; bytes_left -= segment; } while (bytes_left > 0); - dev_dbg(&at25->spi->dev, "read %zu bytes at %d\n", + dev_dbg(&at25->spimem->spi->dev, "read %zu bytes at %d\n", count, offset); return 0; } -/* Read extra registers as ID or serial number */ +/* + * Read extra registers as ID or serial number + * + * Allow for the callers to provide @buf on stack (not necessary DMA-capable) + * by allocating a bounce buffer internally. + */ static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, int len) { + u8 *bounce __free(kfree) = kmalloc(len, GFP_KERNEL); + struct spi_mem_op op; int status; - struct spi_transfer t[2]; - struct spi_message m; - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = at25->command; - t[0].len = 1; - spi_message_add_tail(&t[0], &m); - t[1].rx_buf = buf; - t[1].len = len; - spi_message_add_tail(&t[1], &m); + if (!bounce) + return -ENOMEM; - mutex_lock(&at25->lock); + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(command, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(len, bounce, 1)); - at25->command[0] = command; + status = spi_mem_exec_op(at25->spimem, &op); + dev_dbg(&at25->spimem->spi->dev, "read %d aux bytes --> %d\n", len, status); + if (status) + return status; - status = spi_sync(at25->spi, &m); - dev_dbg(&at25->spi->dev, "read %d aux bytes --> %d\n", len, status); + memcpy(buf, bounce, len); - mutex_unlock(&at25->lock); - return status; + return 0; } static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -195,14 +186,47 @@ static struct attribute *sernum_attrs[] = { }; ATTRIBUTE_GROUPS(sernum); +/* + * Poll Read Status Register with timeout + * + * Return: + * 0, if the chip is ready + * [positive] Status Register value as-is, if the chip is busy + * [negative] error code in case of read failure + */ +static int at25_wait_ready(struct at25_data *at25) +{ + u8 *bounce __free(kfree) = kmalloc(1, GFP_KERNEL); + struct spi_mem_op op; + int status; + + if (!bounce) + return -ENOMEM; + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_RDSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, bounce, 1)); + + read_poll_timeout(spi_mem_exec_op, status, + status || !(bounce[0] & AT25_SR_nRDY), false, + USEC_PER_MSEC, USEC_PER_MSEC * EE_TIMEOUT, + at25->spimem, &op); + if (status < 0) + return status; + if (!(bounce[0] & AT25_SR_nRDY)) + return 0; + + return bounce[0]; +} + static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) { + u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL); struct at25_data *at25 = priv; - size_t maxsz = spi_max_transfer_size(at25->spi); const char *buf = val; - int status = 0; - unsigned buf_size; - u8 *bounce; + unsigned int buf_size; + int status; if (unlikely(off >= at25->chip.byte_len)) return -EFBIG; @@ -211,11 +235,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) if (unlikely(!count)) return -EINVAL; - /* Temp buffer starts with command and address */ buf_size = at25->chip.page_size; - if (buf_size > io_limit) - buf_size = io_limit; - bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL); + if (!bounce) return -ENOMEM; @@ -223,85 +244,64 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) * For write, rollover is within the page ... so we write at * most one page, then manually roll over to the next page. */ - mutex_lock(&at25->lock); + guard(mutex)(&at25->lock); do { - unsigned long timeout, retries; - unsigned segment; - unsigned offset = off; - u8 *cp = bounce; - int sr; - u8 instr; - - *cp = AT25_WREN; - status = spi_write(at25->spi, cp, 1); - if (status < 0) { - dev_dbg(&at25->spi->dev, "WREN --> %d\n", status); - break; - } - - instr = AT25_WRITE; - if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) - if (offset >= BIT(at25->addrlen * 8)) - instr |= AT25_INSTR_BIT3; - *cp++ = instr; + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_WREN, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + unsigned int segment; - /* 8/16/24-bit address is written MSB first */ - switch (at25->addrlen) { - default: /* case 3 */ - *cp++ = offset >> 16; - fallthrough; - case 2: - *cp++ = offset >> 8; - fallthrough; - case 1: - case 0: /* can't happen: for better code generation */ - *cp++ = offset >> 0; + status = spi_mem_exec_op(at25->spimem, &op); + if (status < 0) { + dev_dbg(&at25->spimem->spi->dev, "WREN --> %d\n", status); + return status; } /* Write as much of a page as we can */ - segment = buf_size - (offset % buf_size); + segment = buf_size - (off % buf_size); if (segment > count) segment = count; - if (segment > maxsz) - segment = maxsz; - memcpy(cp, buf, segment); - status = spi_write(at25->spi, bounce, - segment + at25->addrlen + 1); - dev_dbg(&at25->spi->dev, "write %u bytes at %u --> %d\n", - segment, offset, status); - if (status < 0) - break; + if (segment > io_limit) + segment = io_limit; + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_WRITE, off), + 1), + SPI_MEM_OP_ADDR(at25->addrlen, off, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(segment, bounce, 1)); + + status = spi_mem_adjust_op_size(at25->spimem, &op); + if (status) + return status; + segment = op.data.nbytes; + + memcpy(bounce, buf, segment); + + status = spi_mem_exec_op(at25->spimem, &op); + dev_dbg(&at25->spimem->spi->dev, "write %u bytes at %u --> %d\n", + segment, off, status); + if (status) + return status; /* * REVISIT this should detect (or prevent) failed writes * to read-only sections of the EEPROM... */ - /* Wait for non-busy status */ - timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT); - retries = 0; - do { - - sr = spi_w8r8(at25->spi, AT25_RDSR); - if (sr < 0 || (sr & AT25_SR_nRDY)) { - dev_dbg(&at25->spi->dev, - "rdsr --> %d (%02x)\n", sr, sr); - /* at HZ=100, this is sloooow */ - msleep(1); - continue; - } - if (!(sr & AT25_SR_nRDY)) - break; - } while (retries++ < 3 || time_before_eq(jiffies, timeout)); - - if ((sr < 0) || (sr & AT25_SR_nRDY)) { - dev_err(&at25->spi->dev, + status = at25_wait_ready(at25); + if (status < 0) { + dev_err_probe(&at25->spimem->spi->dev, status, + "Read Status Redister command failed\n"); + return status; + } + if (status) { + dev_dbg(&at25->spimem->spi->dev, + "Status %02x\n", status); + dev_err(&at25->spimem->spi->dev, "write %u bytes offset %u, timeout after %u msecs\n", - segment, offset, - jiffies_to_msecs(jiffies - - (timeout - EE_TIMEOUT))); - status = -ETIMEDOUT; - break; + segment, off, EE_TIMEOUT); + return -ETIMEDOUT; } off += segment; @@ -310,9 +310,6 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) } while (count > 0); - mutex_unlock(&at25->lock); - - kfree(bounce); return status; } @@ -388,17 +385,33 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) /* Get ID of chip */ fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); + /* There are inside-out FRAM variations, detect them and reverse the ID bytes */ + if (id[6] == 0x7f && id[2] == 0xc2) + for (i = 0; i < ARRAY_SIZE(id) / 2; i++) { + u8 tmp = id[i]; + int j = ARRAY_SIZE(id) - i - 1; + + id[i] = id[j]; + id[j] = tmp; + } if (id[6] != 0xc2) { dev_err(dev, "Error: no Cypress FRAM (id %02x)\n", id[6]); return -ENODEV; } - /* Set size found in ID */ - if (id[7] < 0x21 || id[7] > 0x26) { + + switch (id[7]) { + case 0x21 ... 0x26: + chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024; + break; + case 0x2a ... 0x30: + /* CY15B116QN ... CY15B116QN */ + chip->byte_len = BIT(((id[7] >> 1) & 0xf) + 13); + break; + default: dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]); return -ENODEV; } - chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024; if (chip->byte_len > 64 * 1024) chip->flags |= EE_ADDR3; else @@ -429,31 +442,33 @@ static const struct spi_device_id at25_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, at25_spi_ids); -static int at25_probe(struct spi_device *spi) +static int at25_probe(struct spi_mem *mem) { - struct at25_data *at25 = NULL; - int err; - int sr; + struct spi_device *spi = mem->spi; struct spi_eeprom *pdata; + struct at25_data *at25; bool is_fram; + int err; + + at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL); + if (!at25) + return -ENOMEM; + + at25->spimem = mem; /* * Ping the chip ... the status register is pretty portable, - * unlike probing manufacturer IDs. We do expect that system - * firmware didn't write it in the past few milliseconds! + * unlike probing manufacturer IDs. */ - sr = spi_w8r8(spi, AT25_RDSR); - if (sr < 0 || sr & AT25_SR_nRDY) { - dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr); + err = at25_wait_ready(at25); + if (err < 0) + return dev_err_probe(&spi->dev, err, "Read Status Register command failed\n"); + if (err) { + dev_err(&spi->dev, "Not ready (%02x)\n", err); return -ENXIO; } - at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL); - if (!at25) - return -ENOMEM; - mutex_init(&at25->lock); - at25->spi = spi; spi_set_drvdata(spi, at25); is_fram = fwnode_device_is_compatible(dev_fwnode(&spi->dev), "cypress,fm25"); @@ -514,17 +529,19 @@ static int at25_probe(struct spi_device *spi) /*-------------------------------------------------------------------------*/ -static struct spi_driver at25_driver = { - .driver = { - .name = "at25", - .of_match_table = at25_of_match, - .dev_groups = sernum_groups, +static struct spi_mem_driver at25_driver = { + .spidrv = { + .driver = { + .name = "at25", + .of_match_table = at25_of_match, + .dev_groups = sernum_groups, + }, + .id_table = at25_spi_ids, }, .probe = at25_probe, - .id_table = at25_spi_ids, }; -module_spi_driver(at25_driver); +module_spi_mem_driver(at25_driver); MODULE_DESCRIPTION("Driver for most SPI EEPROMs"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 1c36ad153e78..a3e4cada3b51 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -127,7 +127,7 @@ static const struct bin_attribute user_eeprom_attr = { .mode = S_IRUGO, }, .size = USER_EEPROM_SIZE, - .read_new = max6875_read, + .read = max6875_read, }; static int max6875_probe(struct i2c_client *client) diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 76511d279aff..ca4c420e4a2f 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <linux/string_choices.h> static LIST_HEAD(container_list); static DEFINE_MUTEX(container_list_lock); @@ -592,7 +593,7 @@ static ssize_t get_component_power_status(struct device *cdev, if (ecomp->power_status == -1) return (edev->cb->get_power_status) ? -EIO : -ENOTTY; - return sysfs_emit(buf, "%s\n", ecomp->power_status ? "on" : "off"); + return sysfs_emit(buf, "%s\n", str_on_off(ecomp->power_status)); } static ssize_t set_component_power_status(struct device *cdev, diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 378923594f02..53e88a1bc430 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2262,8 +2262,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) int i, err, domain_id = -1, vmcount; const char *domain; bool secure_dsp; - struct device_node *rmem_node; - struct reserved_mem *rmem; unsigned int vmids[FASTRPC_MAX_VMIDS]; err = of_property_read_string(rdev->of_node, "label", &domain); @@ -2306,20 +2304,17 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) } } - rmem_node = of_parse_phandle(rdev->of_node, "memory-region", 0); - if (domain_id == SDSP_DOMAIN_ID && rmem_node) { + if (domain_id == SDSP_DOMAIN_ID) { + struct resource res; u64 src_perms; - rmem = of_reserved_mem_lookup(rmem_node); - if (!rmem) { - err = -EINVAL; - goto err_free_data; - } + err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res); + if (!err) { + src_perms = BIT(QCOM_SCM_VMID_HLOS); - src_perms = BIT(QCOM_SCM_VMID_HLOS); - - qcom_scm_assign_mem(rmem->base, rmem->size, &src_perms, + qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, data->vmperms, data->vmcount); + } } diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c index 187c5bc91e31..5ba40222eb12 100644 --- a/drivers/misc/hi6421v600-irq.c +++ b/drivers/misc/hi6421v600-irq.c @@ -214,7 +214,6 @@ static void hi6421v600_irq_init(struct hi6421v600_irq *priv) static int hi6421v600_irq_probe(struct platform_device *pdev) { struct device *pmic_dev = pdev->dev.parent; - struct device_node *np = pmic_dev->of_node; struct platform_device *pmic_pdev; struct device *dev = &pdev->dev; struct hi6421v600_irq *priv; @@ -254,8 +253,7 @@ static int hi6421v600_irq_probe(struct platform_device *pdev) if (!priv->irqs) return -ENOMEM; - priv->domain = irq_domain_create_simple(of_fwnode_handle(np), - PMIC_IRQ_LIST_MAX, 0, + priv->domain = irq_domain_create_simple(dev_fwnode(pmic_dev), PMIC_IRQ_LIST_MAX, 0, &hi6421v600_domain_ops, priv); if (!priv->domain) { dev_err(dev, "Failed to create IRQ domain\n"); diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index 39468bd27b85..03ebe33185f9 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile @@ -8,7 +8,7 @@ lkdtm-$(CONFIG_LKDTM) += perms.o lkdtm-$(CONFIG_LKDTM) += refcount.o lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o lkdtm-$(CONFIG_LKDTM) += usercopy.o -lkdtm-$(CONFIG_LKDTM) += stackleak.o +lkdtm-$(CONFIG_LKDTM) += kstack_erase.o lkdtm-$(CONFIG_LKDTM) += cfi.o lkdtm-$(CONFIG_LKDTM) += fortify.o lkdtm-$(CONFIG_PPC_64S_HASH_MMU) += powerpc.o diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/kstack_erase.c index f1d022160913..4fd9b0bfb874 100644 --- a/drivers/misc/lkdtm/stackleak.c +++ b/drivers/misc/lkdtm/kstack_erase.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * This code tests that the current task stack is properly erased (filled - * with STACKLEAK_POISON). + * with KSTACK_ERASE_POISON). * * Authors: * Alexander Popov <alex.popov@linux.com> @@ -9,9 +9,9 @@ */ #include "lkdtm.h" -#include <linux/stackleak.h> +#include <linux/kstack_erase.h> -#if defined(CONFIG_GCC_PLUGIN_STACKLEAK) +#if defined(CONFIG_KSTACK_ERASE) /* * Check that stackleak tracks the lowest stack pointer and erases the stack * below this as expected. @@ -85,7 +85,7 @@ static void noinstr check_stackleak_irqoff(void) while (poison_low > task_stack_low) { poison_low -= sizeof(unsigned long); - if (*(unsigned long *)poison_low == STACKLEAK_POISON) + if (*(unsigned long *)poison_low == KSTACK_ERASE_POISON) continue; instrumentation_begin(); @@ -96,7 +96,7 @@ static void noinstr check_stackleak_irqoff(void) } instrumentation_begin(); - pr_info("stackleak stack usage:\n" + pr_info("kstack erase stack usage:\n" " high offset: %lu bytes\n" " current: %lu bytes\n" " lowest: %lu bytes\n" @@ -121,7 +121,7 @@ out: instrumentation_end(); } -static void lkdtm_STACKLEAK_ERASING(void) +static void lkdtm_KSTACK_ERASE(void) { unsigned long flags; @@ -129,19 +129,19 @@ static void lkdtm_STACKLEAK_ERASING(void) check_stackleak_irqoff(); local_irq_restore(flags); } -#else /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */ -static void lkdtm_STACKLEAK_ERASING(void) +#else /* defined(CONFIG_KSTACK_ERASE) */ +static void lkdtm_KSTACK_ERASE(void) { - if (IS_ENABLED(CONFIG_HAVE_ARCH_STACKLEAK)) { - pr_err("XFAIL: stackleak is not enabled (CONFIG_GCC_PLUGIN_STACKLEAK=n)\n"); + if (IS_ENABLED(CONFIG_HAVE_ARCH_KSTACK_ERASE)) { + pr_err("XFAIL: stackleak is not enabled (CONFIG_KSTACK_ERASE=n)\n"); } else { - pr_err("XFAIL: stackleak is not supported on this arch (HAVE_ARCH_STACKLEAK=n)\n"); + pr_err("XFAIL: stackleak is not supported on this arch (HAVE_ARCH_KSTACK_ERASE=n)\n"); } } -#endif /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */ +#endif /* defined(CONFIG_KSTACK_ERASE) */ static struct crashtype crashtypes[] = { - CRASHTYPE(STACKLEAK_ERASING), + CRASHTYPE(KSTACK_ERASE), }; struct crashtype_category stackleak_crashtypes = { diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 9eebeffcd8fd..90dba20b2de7 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -463,14 +463,14 @@ static void mei_nfc(struct mei_cl_device *cldev) if (IS_ERR(cl)) { ret = PTR_ERR(cl); cl = NULL; - dev_err(bus->dev, "nfc hook alloc failed %d\n", ret); + dev_err(&cldev->dev, "nfc hook alloc failed %d\n", ret); goto out; } me_cl = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid); if (!me_cl) { ret = -ENOTTY; - dev_err(bus->dev, "Cannot find nfc info %d\n", ret); + dev_err(&cldev->dev, "Cannot find nfc info %d\n", ret); goto out; } @@ -496,13 +496,13 @@ static void mei_nfc(struct mei_cl_device *cldev) goto disconnect; } - dev_dbg(bus->dev, "nfc radio %s\n", radio_name); + dev_dbg(&cldev->dev, "nfc radio %s\n", radio_name); strscpy(cldev->name, radio_name, sizeof(cldev->name)); disconnect: mutex_lock(&bus->device_lock); if (mei_cl_disconnect(cl) < 0) - dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n"); + dev_err(&cldev->dev, "Can't disconnect the NFC INFO ME\n"); mei_cl_flush_queues(cl, NULL); @@ -515,7 +515,7 @@ out: if (ret) cldev->do_match = 0; - dev_dbg(bus->dev, "end of fixup match = %d\n", cldev->do_match); + dev_dbg(&cldev->dev, "end of fixup match = %d\n", cldev->do_match); } /** diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 67176caf5416..5cc3ad07d5be 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -875,14 +875,14 @@ int mei_cldev_disable(struct mei_cl_device *cldev) mei_cl_bus_vtag_free(cldev); if (!mei_cl_is_connected(cl)) { - dev_dbg(bus->dev, "Already disconnected\n"); + dev_dbg(&cldev->dev, "Already disconnected\n"); err = 0; goto out; } err = mei_cl_disconnect(cl); if (err < 0) - dev_err(bus->dev, "Could not disconnect from the ME client\n"); + dev_err(&cldev->dev, "Could not disconnect from the ME client\n"); out: /* Flush queues and remove any pending read unless we have mapped DMA */ @@ -935,7 +935,7 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, cl = cldev->cl; bus = cldev->bus; - dev_dbg(bus->dev, "client_id %u, fence_id %u\n", client_id, fence_id); + dev_dbg(&cldev->dev, "client_id %u, fence_id %u\n", client_id, fence_id); if (!bus->hbm_f_gsc_supported) return -EOPNOTSUPP; @@ -983,11 +983,11 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, /* send the message to GSC */ ret = __mei_cl_send(cl, (u8 *)ext_hdr, buf_sz, 0, MEI_CL_IO_SGL); if (ret < 0) { - dev_err(bus->dev, "__mei_cl_send failed, returned %zd\n", ret); + dev_err(&cldev->dev, "__mei_cl_send failed, returned %zd\n", ret); goto end; } if (ret != buf_sz) { - dev_err(bus->dev, "__mei_cl_send returned %zd instead of expected %zd\n", + dev_err(&cldev->dev, "__mei_cl_send returned %zd instead of expected %zd\n", ret, buf_sz); ret = -EIO; goto end; @@ -997,7 +997,7 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, ret = __mei_cl_recv(cl, (u8 *)&rx_msg, sizeof(rx_msg), NULL, MEI_CL_IO_SGL, 0); if (ret != sizeof(rx_msg)) { - dev_err(bus->dev, "__mei_cl_recv returned %zd instead of expected %zd\n", + dev_err(&cldev->dev, "__mei_cl_recv returned %zd instead of expected %zd\n", ret, sizeof(rx_msg)); if (ret >= 0) ret = -EIO; @@ -1006,13 +1006,13 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, /* check rx_msg.client_id and rx_msg.fence_id match the ones we send */ if (rx_msg.client_id != client_id || rx_msg.fence_id != fence_id) { - dev_err(bus->dev, "received client_id/fence_id %u/%u instead of %u/%u sent\n", + dev_err(&cldev->dev, "received client_id/fence_id %u/%u instead of %u/%u sent\n", rx_msg.client_id, rx_msg.fence_id, client_id, fence_id); ret = -EFAULT; goto end; } - dev_dbg(bus->dev, "gsc command: successfully written %u bytes\n", rx_msg.written); + dev_dbg(&cldev->dev, "gsc command: successfully written %u bytes\n", rx_msg.written); ret = rx_msg.written; end: @@ -1156,7 +1156,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *a, { struct mei_cl_device *cldev = to_mei_cl_device(dev); - return scnprintf(buf, PAGE_SIZE, "%s", cldev->name); + return sysfs_emit(buf, "%s", cldev->name); } static DEVICE_ATTR_RO(name); @@ -1166,7 +1166,7 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); - return sprintf(buf, "%pUl", uuid); + return sysfs_emit(buf, "%pUl", uuid); } static DEVICE_ATTR_RO(uuid); @@ -1176,7 +1176,7 @@ static ssize_t version_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u8 version = mei_me_cl_ver(cldev->me_cl); - return sprintf(buf, "%02X", version); + return sysfs_emit(buf, "%02X", version); } static DEVICE_ATTR_RO(version); @@ -1187,8 +1187,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); u8 version = mei_me_cl_ver(cldev->me_cl); - return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:%02X:", - cldev->name, uuid, version); + return sysfs_emit(buf, "mei:%s:%pUl:%02X:", cldev->name, uuid, version); } static DEVICE_ATTR_RO(modalias); @@ -1198,7 +1197,7 @@ static ssize_t max_conn_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u8 maxconn = mei_me_cl_max_conn(cldev->me_cl); - return sprintf(buf, "%d", maxconn); + return sysfs_emit(buf, "%d", maxconn); } static DEVICE_ATTR_RO(max_conn); @@ -1208,7 +1207,7 @@ static ssize_t fixed_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u8 fixed = mei_me_cl_fixed(cldev->me_cl); - return sprintf(buf, "%d", fixed); + return sysfs_emit(buf, "%d", fixed); } static DEVICE_ATTR_RO(fixed); @@ -1218,7 +1217,7 @@ static ssize_t vtag_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); bool vt = mei_me_cl_vt(cldev->me_cl); - return sprintf(buf, "%d", vt); + return sysfs_emit(buf, "%d", vt); } static DEVICE_ATTR_RO(vtag); @@ -1228,7 +1227,7 @@ static ssize_t max_len_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u32 maxlen = mei_me_cl_max_len(cldev->me_cl); - return sprintf(buf, "%u", maxlen); + return sysfs_emit(buf, "%u", maxlen); } static DEVICE_ATTR_RO(max_len); @@ -1301,10 +1300,16 @@ static void mei_dev_bus_put(struct mei_device *bus) static void mei_cl_bus_dev_release(struct device *dev) { struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_device *mdev = cldev->cl->dev; + struct mei_cl *cl; mei_cl_flush_queues(cldev->cl, NULL); mei_me_cl_put(cldev->me_cl); mei_dev_bus_put(cldev->bus); + + list_for_each_entry(cl, &mdev->file_list, link) + WARN_ON(cl == cldev->cl); + kfree(cldev->cl); kfree(cldev); } @@ -1399,7 +1404,7 @@ static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) { int ret; - dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", + dev_dbg(&cldev->dev, "adding %pUL:%02X\n", mei_me_cl_uuid(cldev->me_cl), mei_me_cl_ver(cldev->me_cl)); ret = device_add(&cldev->dev); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 026b1f686c16..4fe9a2752d43 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -510,7 +510,7 @@ int mei_hbm_cl_notify_req(struct mei_device *dev, ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) - dev_err(dev->dev, "notify request failed: ret = %d\n", ret); + cl_err(dev, cl, "notify request failed: ret = %d\n", ret); return ret; } @@ -626,7 +626,7 @@ int mei_hbm_cl_dma_map_req(struct mei_device *dev, struct mei_cl *cl) ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) - dev_err(dev->dev, "dma map request failed: ret = %d\n", ret); + cl_err(dev, cl, "dma map request failed: ret = %d\n", ret); return ret; } @@ -654,7 +654,7 @@ int mei_hbm_cl_dma_unmap_req(struct mei_device *dev, struct mei_cl *cl) ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) - dev_err(dev->dev, "dma unmap request failed: ret = %d\n", ret); + cl_err(dev, cl, "dma unmap request failed: ret = %d\n", ret); return ret; } @@ -679,10 +679,10 @@ static void mei_hbm_cl_dma_map_res(struct mei_device *dev, return; if (res->status) { - dev_err(dev->dev, "cl dma map failed %d\n", res->status); + cl_err(dev, cl, "cl dma map failed %d\n", res->status); cl->status = -EFAULT; } else { - dev_dbg(dev->dev, "cl dma map succeeded\n"); + cl_dbg(dev, cl, "cl dma map succeeded\n"); cl->dma_mapped = 1; cl->status = 0; } @@ -709,10 +709,10 @@ static void mei_hbm_cl_dma_unmap_res(struct mei_device *dev, return; if (res->status) { - dev_err(dev->dev, "cl dma unmap failed %d\n", res->status); + cl_err(dev, cl, "cl dma unmap failed %d\n", res->status); cl->status = -EFAULT; } else { - dev_dbg(dev->dev, "cl dma unmap succeeded\n"); + cl_dbg(dev, cl, "cl dma unmap succeeded\n"); cl->dma_mapped = 0; cl->status = 0; } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index c484f416fae4..d472f6bbe767 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -35,7 +35,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list) cl = cb->cl; list_del_init(&cb->list); - dev_dbg(dev->dev, "completing call back.\n"); + cl_dbg(dev, cl, "completing call back.\n"); mei_cl_complete(cl, cb); } } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 1f5aaf16e300..8a149a15b861 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -256,7 +256,7 @@ copy_buffer: length = min_t(size_t, length, cb->buf_idx - *offset); if (copy_to_user(ubuf, cb->buf.data + *offset, length)) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto free; } @@ -379,7 +379,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = copy_from_user(cb->buf.data, ubuf, length); if (rets) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; mei_io_cb_free(cb); goto out; @@ -421,7 +421,7 @@ static int mei_ioctl_connect_client(struct file *file, /* find ME client we're trying to connect to */ me_cl = mei_me_cl_by_uuid(dev, in_client_uuid); if (!me_cl) { - dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", + cl_dbg(dev, cl, "Cannot connect to FW Client UUID = %pUl\n", in_client_uuid); rets = -ENOTTY; goto end; @@ -431,24 +431,21 @@ static int mei_ioctl_connect_client(struct file *file, bool forbidden = dev->override_fixed_address ? !dev->allow_fixed_address : !dev->hbm_f_fa_supported; if (forbidden) { - dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n", + cl_dbg(dev, cl, "Connection forbidden to FW Client UUID = %pUl\n", in_client_uuid); rets = -ENOTTY; goto end; } } - dev_dbg(dev->dev, "Connect to FW Client ID = %d\n", - me_cl->client_id); - dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n", - me_cl->props.protocol_version); - dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n", - me_cl->props.max_msg_length); + cl_dbg(dev, cl, "Connect to FW Client ID = %d\n", me_cl->client_id); + cl_dbg(dev, cl, "FW Client - Protocol Version = %d\n", me_cl->props.protocol_version); + cl_dbg(dev, cl, "FW Client - Max Msg Len = %d\n", me_cl->props.max_msg_length); /* prepare the output buffer */ client->max_msg_length = me_cl->props.max_msg_length; client->protocol_version = me_cl->props.protocol_version; - dev_dbg(dev->dev, "Can connect?\n"); + cl_dbg(dev, cl, "Can connect?\n"); rets = mei_cl_connect(cl, me_cl, file); @@ -515,19 +512,19 @@ static int mei_ioctl_connect_vtag(struct file *file, cl = file->private_data; dev = cl->dev; - dev_dbg(dev->dev, "FW Client %pUl vtag %d\n", in_client_uuid, vtag); + cl_dbg(dev, cl, "FW Client %pUl vtag %d\n", in_client_uuid, vtag); switch (cl->state) { case MEI_FILE_DISCONNECTED: if (mei_cl_vtag_by_fp(cl, file) != vtag) { - dev_err(dev->dev, "reconnect with different vtag\n"); + cl_err(dev, cl, "reconnect with different vtag\n"); return -EINVAL; } break; case MEI_FILE_INITIALIZING: /* malicious connect from another thread may push vtag */ if (!IS_ERR(mei_cl_fp_by_vtag(cl, vtag))) { - dev_err(dev->dev, "vtag already filled\n"); + cl_err(dev, cl, "vtag already filled\n"); return -EINVAL; } @@ -546,7 +543,7 @@ static int mei_ioctl_connect_vtag(struct file *file, continue; /* replace cl with acquired one */ - dev_dbg(dev->dev, "replacing with existing cl\n"); + cl_dbg(dev, cl, "replacing with existing cl\n"); mei_cl_unlink(cl); kfree(cl); file->private_data = pos; @@ -656,7 +653,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) dev = cl->dev; - dev_dbg(dev->dev, "IOCTL cmd = 0x%x", cmd); + cl_dbg(dev, cl, "IOCTL cmd = 0x%x", cmd); mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { @@ -666,9 +663,9 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) switch (cmd) { case IOCTL_MEI_CONNECT_CLIENT: - dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); + cl_dbg(dev, cl, "IOCTL_MEI_CONNECT_CLIENT\n"); if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -689,7 +686,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) /* if all is ok, copying the data back to user. */ if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto out; } @@ -697,10 +694,10 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; case IOCTL_MEI_CONNECT_CLIENT_VTAG: - dev_dbg(dev->dev, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n"); + cl_dbg(dev, cl, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n"); if (copy_from_user(&conn_vtag, (char __user *)data, sizeof(conn_vtag))) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -711,13 +708,13 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) rets = mei_vt_support_check(dev, cl_uuid); if (rets == -EOPNOTSUPP) - dev_dbg(dev->dev, "FW Client %pUl does not support vtags\n", + cl_dbg(dev, cl, "FW Client %pUl does not support vtags\n", cl_uuid); if (rets) goto out; if (!vtag) { - dev_dbg(dev->dev, "vtag can't be zero\n"); + cl_dbg(dev, cl, "vtag can't be zero\n"); rets = -EINVAL; goto out; } @@ -729,7 +726,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) /* if all is ok, copying the data back to user. */ if (copy_to_user((char __user *)data, &conn_vtag, sizeof(conn_vtag))) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto out; } @@ -737,10 +734,10 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; case IOCTL_MEI_NOTIFY_SET: - dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_SET.\n"); + cl_dbg(dev, cl, "IOCTL_MEI_NOTIFY_SET\n"); if (copy_from_user(¬ify_req, (char __user *)data, sizeof(notify_req))) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -748,15 +745,15 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; case IOCTL_MEI_NOTIFY_GET: - dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_GET.\n"); + cl_dbg(dev, cl, "IOCTL_MEI_NOTIFY_GET\n"); rets = mei_ioctl_client_notify_get(file, ¬ify_get); if (rets) goto out; - dev_dbg(dev->dev, "copy connect data to user\n"); + cl_dbg(dev, cl, "copy connect data to user\n"); if (copy_to_user((char __user *)data, ¬ify_get, sizeof(notify_get))) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto out; diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 435760b1e86f..b2b5a20ae3fa 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -256,6 +256,9 @@ static int mei_vsc_hw_reset(struct mei_device *mei_dev, bool intr_enable) vsc_tp_reset(hw->tp); + if (!intr_enable) + return 0; + return vsc_tp_init(hw->tp, mei_dev->dev); } @@ -377,6 +380,8 @@ err_stop: err_cancel: mei_cancel_work(mei_dev); + vsc_tp_register_event_cb(tp, NULL, NULL); + mei_disable_interrupts(mei_dev); return ret; @@ -385,11 +390,14 @@ err_cancel: static void mei_vsc_remove(struct platform_device *pdev) { struct mei_device *mei_dev = platform_get_drvdata(pdev); + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); pm_runtime_disable(mei_dev->dev); mei_stop(mei_dev); + vsc_tp_register_event_cb(hw->tp, NULL, NULL); + mei_disable_interrupts(mei_dev); mei_deregister(mei_dev); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c index 267d0de5fade..5ecf99883996 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/types.h> +#include <linux/workqueue.h> #include "vsc-tp.h" @@ -76,12 +77,12 @@ struct vsc_tp { atomic_t assert_cnt; wait_queue_head_t xfer_wait; + struct work_struct event_work; vsc_tp_event_cb_t event_notify; void *event_notify_context; - - /* used to protect command download */ - struct mutex mutex; + struct mutex event_notify_mutex; /* protects event_notify + context */ + struct mutex mutex; /* protects command download */ }; /* GPIO resources */ @@ -106,17 +107,19 @@ static irqreturn_t vsc_tp_isr(int irq, void *data) wake_up(&tp->xfer_wait); - return IRQ_WAKE_THREAD; + schedule_work(&tp->event_work); + + return IRQ_HANDLED; } -static irqreturn_t vsc_tp_thread_isr(int irq, void *data) +static void vsc_tp_event_work(struct work_struct *work) { - struct vsc_tp *tp = data; + struct vsc_tp *tp = container_of(work, struct vsc_tp, event_work); + + guard(mutex)(&tp->event_notify_mutex); if (tp->event_notify) tp->event_notify(tp->event_notify_context); - - return IRQ_HANDLED; } /* wakeup firmware and wait for response */ @@ -399,6 +402,8 @@ EXPORT_SYMBOL_NS_GPL(vsc_tp_need_read, "VSC_TP"); int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, void *context) { + guard(mutex)(&tp->event_notify_mutex); + tp->event_notify = event_cb; tp->event_notify_context = context; @@ -407,37 +412,6 @@ int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, EXPORT_SYMBOL_NS_GPL(vsc_tp_register_event_cb, "VSC_TP"); /** - * vsc_tp_request_irq - request irq for vsc_tp device - * @tp: vsc_tp device handle - */ -int vsc_tp_request_irq(struct vsc_tp *tp) -{ - struct spi_device *spi = tp->spi; - struct device *dev = &spi->dev; - int ret; - - irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY); - ret = request_threaded_irq(spi->irq, vsc_tp_isr, vsc_tp_thread_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), tp); - if (ret) - return ret; - - return 0; -} -EXPORT_SYMBOL_NS_GPL(vsc_tp_request_irq, "VSC_TP"); - -/** - * vsc_tp_free_irq - free irq for vsc_tp device - * @tp: vsc_tp device handle - */ -void vsc_tp_free_irq(struct vsc_tp *tp) -{ - free_irq(tp->spi->irq, tp); -} -EXPORT_SYMBOL_NS_GPL(vsc_tp_free_irq, "VSC_TP"); - -/** * vsc_tp_intr_synchronize - synchronize vsc_tp interrupt * @tp: vsc_tp device handle */ @@ -523,13 +497,15 @@ static int vsc_tp_probe(struct spi_device *spi) tp->spi = spi; irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY); - ret = request_threaded_irq(spi->irq, vsc_tp_isr, vsc_tp_thread_isr, + ret = request_threaded_irq(spi->irq, NULL, vsc_tp_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(dev), tp); if (ret) return ret; mutex_init(&tp->mutex); + mutex_init(&tp->event_notify_mutex); + INIT_WORK(&tp->event_work, vsc_tp_event_work); /* only one child acpi device */ ret = acpi_dev_for_each_child(ACPI_COMPANION(dev), @@ -552,35 +528,27 @@ static int vsc_tp_probe(struct spi_device *spi) return 0; err_destroy_lock: - mutex_destroy(&tp->mutex); - free_irq(spi->irq, tp); + cancel_work_sync(&tp->event_work); + mutex_destroy(&tp->event_notify_mutex); + mutex_destroy(&tp->mutex); + return ret; } +/* Note this is also used for shutdown */ static void vsc_tp_remove(struct spi_device *spi) { struct vsc_tp *tp = spi_get_drvdata(spi); platform_device_unregister(tp->pdev); - mutex_destroy(&tp->mutex); - free_irq(spi->irq, tp); -} - -static void vsc_tp_shutdown(struct spi_device *spi) -{ - struct vsc_tp *tp = spi_get_drvdata(spi); - - platform_device_unregister(tp->pdev); + cancel_work_sync(&tp->event_work); + mutex_destroy(&tp->event_notify_mutex); mutex_destroy(&tp->mutex); - - vsc_tp_reset(tp); - - free_irq(spi->irq, tp); } static const struct acpi_device_id vsc_tp_acpi_ids[] = { @@ -595,7 +563,7 @@ MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); static struct spi_driver vsc_tp_driver = { .probe = vsc_tp_probe, .remove = vsc_tp_remove, - .shutdown = vsc_tp_shutdown, + .shutdown = vsc_tp_remove, .driver = { .name = "vsc-tp", .acpi_match_table = vsc_tp_acpi_ids, diff --git a/drivers/misc/mei/vsc-tp.h b/drivers/misc/mei/vsc-tp.h index 14ca195cbddc..f9513ddc3e40 100644 --- a/drivers/misc/mei/vsc-tp.h +++ b/drivers/misc/mei/vsc-tp.h @@ -37,9 +37,6 @@ int vsc_tp_xfer(struct vsc_tp *tp, u8 cmd, const void *obuf, size_t olen, int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, void *context); -int vsc_tp_request_irq(struct vsc_tp *tp); -void vsc_tp_free_irq(struct vsc_tp *tp); - void vsc_tp_intr_enable(struct vsc_tp *tp); void vsc_tp_intr_disable(struct vsc_tp *tp); void vsc_tp_intr_synchronize(struct vsc_tp *tp); diff --git a/drivers/misc/misc_minor_kunit.c b/drivers/misc/misc_minor_kunit.c index 293e0fb7e43e..30eceac5f1b6 100644 --- a/drivers/misc/misc_minor_kunit.c +++ b/drivers/misc/misc_minor_kunit.c @@ -3,6 +3,9 @@ #include <kunit/test-bug.h> #include <linux/module.h> #include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/init_syscalls.h> /* dynamic minor (2) */ static struct miscdevice dev_dynamic_minor = { @@ -51,19 +54,601 @@ static void kunit_misc_dynamic_minor(struct kunit *test) misc_deregister(&dev_misc_dynamic_minor); } +struct miscdev_test_case { + const char *str; + int minor; +}; + +static struct miscdev_test_case miscdev_test_ranges[] = { + { + .str = "lower static range, top", + .minor = 15, + }, + { + .str = "upper static range, bottom", + .minor = 130, + }, + { + .str = "lower static range, bottom", + .minor = 0, + }, + { + .str = "upper static range, top", + .minor = MISC_DYNAMIC_MINOR - 1, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str); + +static int miscdev_find_minors(struct kunit_suite *suite) +{ + int ret; + struct miscdevice miscstat = { + .name = "miscstat", + }; + int i; + + for (i = 15; i >= 0; i--) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[0].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = 128; i < MISC_DYNAMIC_MINOR; i++) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[1].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = 0; i < miscdev_test_ranges[0].minor; i++) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[2].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[3].minor = miscstat.minor; + misc_deregister(&miscstat); + } + + return ret; +} + +static bool is_valid_dynamic_minor(int minor) +{ + if (minor < 0) + return false; + if (minor == MISC_DYNAMIC_MINOR) + return false; + if (minor >= 0 && minor <= 15) + return false; + if (minor >= 128 && minor < MISC_DYNAMIC_MINOR) + return false; + return true; +} + +static int miscdev_test_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations miscdev_test_fops = { + .open = miscdev_test_open, +}; + +static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc) +{ + int ret; + struct file *filp; + char *devname; + + devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name); + ret = init_mknod(devname, S_IFCHR | 0600, + new_encode_dev(MKDEV(MISC_MAJOR, misc->minor))); + if (ret != 0) + KUNIT_FAIL(test, "failed to create node\n"); + + filp = filp_open(devname, O_RDONLY, 0); + if (IS_ERR_OR_NULL(filp)) + KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp)); + else + fput(filp); + + init_unlink(devname); + kfree(devname); +} + +static void __init miscdev_test_static_basic(struct kunit *test) +{ + struct miscdevice misc_test = { + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc_test.minor = params->minor; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + + if (ret == 0) { + miscdev_test_can_open(test, &misc_test); + misc_deregister(&misc_test); + } +} + +static void __init miscdev_test_dynamic_basic(struct kunit *test) +{ + struct miscdevice misc_test = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor)); + + if (ret == 0) { + miscdev_test_can_open(test, &misc_test); + misc_deregister(&misc_test); + } +} + +static void miscdev_test_twice(struct kunit *test) +{ + struct miscdevice misc_test = { + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc_test.minor = params->minor; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret == 0) + misc_deregister(&misc_test); + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret == 0) + misc_deregister(&misc_test); +} + +static void miscdev_test_duplicate_minor(struct kunit *test) +{ + struct miscdevice misc1 = { + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .name = "misc2", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc1.minor = params->minor; + misc2.minor = params->minor; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc1.minor, params->minor); + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + if (ret == 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +static void miscdev_test_duplicate_name(struct kunit *test) +{ + struct miscdevice misc1 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + int ret; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +/* + * Test that after a duplicate name failure, the reserved minor number is + * freed to be allocated next. + */ +static void miscdev_test_duplicate_name_leak(struct kunit *test) +{ + struct miscdevice misc1 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc3 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc3", + .fops = &miscdev_test_fops, + }; + int ret; + int dyn_minor; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + /* + * Find out what is the next minor number available. + */ + ret = misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + dyn_minor = misc3.minor; + misc_deregister(&misc3); + misc3.minor = MISC_DYNAMIC_MINOR; + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&misc2); + + /* + * Now check that we can still get the same minor we found before. + */ + ret = misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor); + misc_deregister(&misc3); + + misc_deregister(&misc1); +} + +/* + * Try to register a static minor with a duplicate name. That might not + * deallocate the minor, preventing it from being used again. + */ +static void miscdev_test_duplicate_error(struct kunit *test) +{ + struct miscdevice miscdyn = { + .minor = MISC_DYNAMIC_MINOR, + .name = "name1", + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "name1", + .fops = &miscdev_test_fops, + }; + struct miscdevice miscnew = { + .name = "name2", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + miscstat.minor = params->minor; + miscnew.minor = params->minor; + + ret = misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&miscstat); + + ret = misc_register(&miscnew); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor); + if (ret == 0) + misc_deregister(&miscnew); + + misc_deregister(&miscdyn); +} + +static void __init miscdev_test_dynamic_only_range(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + /* + * This is the bug we are looking for! + * We asked for a dynamic minor and got a minor in the static range space. + */ + if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) { + KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor); + i++; + break; + } + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void __init miscdev_test_collision(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + miscstat.minor = miscdev_test_ranges[0].minor; + ret = misc_register(&miscstat); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + misc_deregister(&miscstat); + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void __init miscdev_test_collision_reverse(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + KUNIT_EXPECT_EQ(test, ret, 0); + + miscstat.minor = miscdev_test_ranges[0].minor; + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + if (ret == 0) + misc_deregister(&miscstat); + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } +} + +static void __init miscdev_test_conflict(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn = { + .name = "miscdyn", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + + ret = misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + /* + * Try to register a static minor with the same minor as the + * dynamic one. + */ + miscstat.minor = miscdyn.minor; + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + if (ret == 0) + misc_deregister(&miscstat); + + miscdev_test_can_open(test, &miscdyn); + + misc_deregister(&miscdyn); +} + +static void __init miscdev_test_conflict_reverse(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn = { + .name = "miscdyn", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + + /* + * Find the first available dynamic minor to use it as a static + * minor later on. + */ + ret = misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + miscstat.minor = miscdyn.minor; + misc_deregister(&miscdyn); + + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdyn.minor); + + /* + * Try to register a dynamic minor after registering a static minor + * within the dynamic range. It should work but get a different + * minor. + */ + miscdyn.minor = MISC_DYNAMIC_MINOR; + ret = misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_NE(test, miscdyn.minor, miscstat.minor); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + if (ret == 0) + misc_deregister(&miscdyn); + + miscdev_test_can_open(test, &miscstat); + + misc_deregister(&miscstat); +} + static struct kunit_case test_cases[] = { KUNIT_CASE(kunit_dynamic_minor), KUNIT_CASE(kunit_static_minor), KUNIT_CASE(kunit_misc_dynamic_minor), + KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params), + KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params), + KUNIT_CASE(miscdev_test_duplicate_name), + KUNIT_CASE(miscdev_test_duplicate_name_leak), + KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params), {} }; static struct kunit_suite test_suite = { - .name = "misc_minor_test", + .name = "miscdev", + .suite_init = miscdev_find_minors, .test_cases = test_cases, }; kunit_test_suite(test_suite); +static struct kunit_case __refdata test_init_cases[] = { + KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params), + KUNIT_CASE(miscdev_test_dynamic_basic), + KUNIT_CASE(miscdev_test_dynamic_only_range), + KUNIT_CASE(miscdev_test_collision), + KUNIT_CASE(miscdev_test_collision_reverse), + KUNIT_CASE(miscdev_test_conflict), + KUNIT_CASE(miscdev_test_conflict_reverse), + {} +}; + +static struct kunit_suite test_init_suite = { + .name = "miscdev_init", + .suite_init = miscdev_find_minors, + .test_cases = test_init_cases, +}; +kunit_test_init_section_suite(test_init_suite); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vimal Agrawal"); -MODULE_DESCRIPTION("misc minor testing"); +MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>"); +MODULE_DESCRIPTION("Test module for misc character devices"); diff --git a/drivers/misc/ocxl/sysfs.c b/drivers/misc/ocxl/sysfs.c index e849641687a0..1b6a86f17b6c 100644 --- a/drivers/misc/ocxl/sysfs.c +++ b/drivers/misc/ocxl/sysfs.c @@ -16,7 +16,7 @@ static ssize_t global_mmio_size_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", afu->config.global_mmio_size); } @@ -26,7 +26,7 @@ static ssize_t pp_mmio_size_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", afu->config.pp_mmio_stride); } @@ -36,7 +36,7 @@ static ssize_t afu_version_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%hhu:%hhu\n", + return sysfs_emit(buf, "%hhu:%hhu\n", afu->config.version_major, afu->config.version_minor); } @@ -47,7 +47,7 @@ static ssize_t contexts_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%d/%d\n", + return sysfs_emit(buf, "%d/%d\n", afu->pasid_count, afu->pasid_max); } @@ -61,9 +61,9 @@ static ssize_t reload_on_reset_show(struct device *device, int val; if (ocxl_config_get_reset_reload(pci_dev, &val)) - return scnprintf(buf, PAGE_SIZE, "unavailable\n"); + return sysfs_emit(buf, "unavailable\n"); - return scnprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t reload_on_reset_store(struct device *device, @@ -155,7 +155,7 @@ int ocxl_sysfs_register_afu(struct ocxl_file_info *info) info->attr_global_mmio.attr.name = "global_mmio_area"; info->attr_global_mmio.attr.mode = 0600; info->attr_global_mmio.size = info->afu->config.global_mmio_size; - info->attr_global_mmio.read_new = global_mmio_read; + info->attr_global_mmio.read = global_mmio_read; info->attr_global_mmio.mmap = global_mmio_mmap; rc = device_create_bin_file(&info->dev, &info->attr_global_mmio); if (rc) { diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 6121c0940cd1..7bee179841bc 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -655,8 +655,8 @@ static const struct bin_attribute pch_bin_attr = { .mode = S_IRUGO | S_IWUSR, }, .size = PCH_PHUB_OROM_SIZE + 1, - .read_new = pch_phub_bin_read, - .write_new = pch_phub_bin_write, + .read = pch_phub_bin_read, + .write = pch_phub_bin_write, }; static int pch_phub_probe(struct pci_dev *pdev, diff --git a/drivers/misc/rp1/Kconfig b/drivers/misc/rp1/Kconfig new file mode 100644 index 000000000000..5232e70d3079 --- /dev/null +++ b/drivers/misc/rp1/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# RaspberryPi RP1 misc device +# + +config MISC_RP1 + tristate "RaspberryPi RP1 misc device" + depends on OF_IRQ && OF_OVERLAY && PCI_MSI && PCI_QUIRKS + select PCI_DYNAMIC_OF_NODES + help + Support the RP1 peripheral chip found on Raspberry Pi 5 board. + + This device supports several sub-devices including e.g. Ethernet + controller, USB controller, I2C, SPI and UART. + + The driver is responsible for enabling the DT node once the PCIe + endpoint has been configured, and handling interrupts. + + This driver uses an overlay to load other drivers to support for + RP1 internal sub-devices. diff --git a/drivers/misc/rp1/Makefile b/drivers/misc/rp1/Makefile new file mode 100644 index 000000000000..508b4cb05627 --- /dev/null +++ b/drivers/misc/rp1/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MISC_RP1) += rp1-pci.o +rp1-pci-objs := rp1_pci.o rp1-pci.dtbo.o diff --git a/drivers/misc/rp1/rp1-pci.dtso b/drivers/misc/rp1/rp1-pci.dtso new file mode 100644 index 000000000000..eea826b36e02 --- /dev/null +++ b/drivers/misc/rp1/rp1-pci.dtso @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/* + * The dts overlay is included from the dts directory so + * it can be possible to check it with CHECK_DTBS while + * also compile it from the driver source directory. + */ + +/dts-v1/; +/plugin/; + +/ { + fragment@0 { + target-path=""; + __overlay__ { + compatible = "pci1de4,1"; + #address-cells = <3>; + #size-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + + #include "arm64/broadcom/rp1-common.dtsi" + }; + }; +}; diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c new file mode 100644 index 000000000000..803832006ec8 --- /dev/null +++ b/drivers/misc/rp1/rp1_pci.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2025 Raspberry Pi Ltd. + * + * All rights reserved. + */ + +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +#define RP1_HW_IRQ_MASK GENMASK(5, 0) + +#define REG_SET 0x800 +#define REG_CLR 0xc00 + +/* MSI-X CFG registers start at 0x8 */ +#define MSIX_CFG(x) (0x8 + (4 * (x))) + +#define MSIX_CFG_IACK_EN BIT(3) +#define MSIX_CFG_IACK BIT(2) +#define MSIX_CFG_ENABLE BIT(0) + +/* Address map */ +#define RP1_PCIE_APBS_BASE 0x108000 + +/* Interrupts */ +#define RP1_INT_END 61 + +/* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.lib */ +extern char __dtbo_rp1_pci_begin[]; +extern char __dtbo_rp1_pci_end[]; + +struct rp1_dev { + struct pci_dev *pdev; + struct irq_domain *domain; + struct irq_data *pcie_irqds[64]; + void __iomem *bar1; + int ovcs_id; /* overlay changeset id */ + bool level_triggered_irq[RP1_INT_END]; +}; + +static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value) +{ + iowrite32(value, rp1->bar1 + RP1_PCIE_APBS_BASE + REG_SET + MSIX_CFG(hwirq)); +} + +static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value) +{ + iowrite32(value, rp1->bar1 + RP1_PCIE_APBS_BASE + REG_CLR + MSIX_CFG(hwirq)); +} + +static void rp1_mask_irq(struct irq_data *irqd) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + + pci_msi_mask_irq(pcie_irqd); +} + +static void rp1_unmask_irq(struct irq_data *irqd) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq]; + + pci_msi_unmask_irq(pcie_irqd); +} + +static int rp1_irq_set_type(struct irq_data *irqd, unsigned int type) +{ + struct rp1_dev *rp1 = irqd->domain->host_data; + unsigned int hwirq = (unsigned int)irqd->hwirq; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + dev_dbg(&rp1->pdev->dev, "MSIX IACK EN for IRQ %u\n", hwirq); + msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK_EN); + rp1->level_triggered_irq[hwirq] = true; + break; + case IRQ_TYPE_EDGE_RISING: + msix_cfg_clr(rp1, hwirq, MSIX_CFG_IACK_EN); + rp1->level_triggered_irq[hwirq] = false; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct irq_chip rp1_irq_chip = { + .name = "rp1_irq_chip", + .irq_mask = rp1_mask_irq, + .irq_unmask = rp1_unmask_irq, + .irq_set_type = rp1_irq_set_type, +}; + +static void rp1_chained_handle_irq(struct irq_desc *desc) +{ + unsigned int hwirq = desc->irq_data.hwirq & RP1_HW_IRQ_MASK; + struct rp1_dev *rp1 = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int virq; + + chained_irq_enter(chip, desc); + + virq = irq_find_mapping(rp1->domain, hwirq); + generic_handle_irq(virq); + if (rp1->level_triggered_irq[hwirq]) + msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK); + + chained_irq_exit(chip, desc); +} + +static int rp1_irq_xlate(struct irq_domain *d, struct device_node *node, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + struct rp1_dev *rp1 = d->host_data; + struct irq_data *pcie_irqd; + unsigned long hwirq; + int pcie_irq; + int ret; + + ret = irq_domain_xlate_twocell(d, node, intspec, intsize, + &hwirq, out_type); + if (ret) + return ret; + + pcie_irq = pci_irq_vector(rp1->pdev, hwirq); + pcie_irqd = irq_get_irq_data(pcie_irq); + rp1->pcie_irqds[hwirq] = pcie_irqd; + *out_hwirq = hwirq; + + return 0; +} + +static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, + bool reserve) +{ + struct rp1_dev *rp1 = d->host_data; + + msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); + + return 0; +} + +static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd) +{ + struct rp1_dev *rp1 = d->host_data; + + msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); +} + +static const struct irq_domain_ops rp1_domain_ops = { + .xlate = rp1_irq_xlate, + .activate = rp1_irq_activate, + .deactivate = rp1_irq_deactivate, +}; + +static void rp1_unregister_interrupts(struct pci_dev *pdev) +{ + struct rp1_dev *rp1 = pci_get_drvdata(pdev); + int irq, i; + + if (rp1->domain) { + for (i = 0; i < RP1_INT_END; i++) { + irq = irq_find_mapping(rp1->domain, i); + irq_dispose_mapping(irq); + } + + irq_domain_remove(rp1->domain); + } + + pci_free_irq_vectors(pdev); +} + +static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + u32 dtbo_size = __dtbo_rp1_pci_end - __dtbo_rp1_pci_begin; + void *dtbo_start = __dtbo_rp1_pci_begin; + struct device *dev = &pdev->dev; + struct device_node *rp1_node; + bool skip_ovl = true; + struct rp1_dev *rp1; + int err = 0; + int i; + + /* + * Either use rp1_nexus node if already present in DT, or + * set a flag to load it from overlay at runtime + */ + rp1_node = of_find_node_by_name(NULL, "rp1_nexus"); + if (!rp1_node) { + rp1_node = dev_of_node(dev); + skip_ovl = false; + } + + if (!rp1_node) { + dev_err(dev, "Missing of_node for device\n"); + err = -EINVAL; + goto err_put_node; + } + + rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL); + if (!rp1) { + err = -ENOMEM; + goto err_put_node; + } + + rp1->pdev = pdev; + + if (pci_resource_len(pdev, 1) <= 0x10000) { + dev_err(&pdev->dev, + "Not initialized - is the firmware running?\n"); + err = -EINVAL; + goto err_put_node; + } + + err = pcim_enable_device(pdev); + if (err < 0) { + err = dev_err_probe(&pdev->dev, err, + "Enabling PCI device has failed"); + goto err_put_node; + } + + rp1->bar1 = pcim_iomap(pdev, 1, 0); + if (!rp1->bar1) { + dev_err(&pdev->dev, "Cannot map PCI BAR\n"); + err = -EIO; + goto err_put_node; + } + + pci_set_master(pdev); + + err = pci_alloc_irq_vectors(pdev, RP1_INT_END, RP1_INT_END, + PCI_IRQ_MSIX); + if (err < 0) { + err = dev_err_probe(&pdev->dev, err, + "Failed to allocate MSI-X vectors\n"); + goto err_put_node; + } else if (err != RP1_INT_END) { + dev_err(&pdev->dev, "Cannot allocate enough interrupts\n"); + err = -EINVAL; + goto err_put_node; + } + + pci_set_drvdata(pdev, rp1); + rp1->domain = irq_domain_add_linear(rp1_node, RP1_INT_END, + &rp1_domain_ops, rp1); + if (!rp1->domain) { + dev_err(&pdev->dev, "Error creating IRQ domain\n"); + err = -ENOMEM; + goto err_unregister_interrupts; + } + + for (i = 0; i < RP1_INT_END; i++) { + unsigned int irq = irq_create_mapping(rp1->domain, i); + + if (!irq) { + dev_err(&pdev->dev, "Failed to create IRQ mapping\n"); + err = -EINVAL; + goto err_unregister_interrupts; + } + + irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq); + irq_set_probe(irq); + irq_set_chained_handler_and_data(pci_irq_vector(pdev, i), + rp1_chained_handle_irq, rp1); + } + + if (!skip_ovl) { + err = of_overlay_fdt_apply(dtbo_start, dtbo_size, &rp1->ovcs_id, + rp1_node); + if (err) + goto err_unregister_interrupts; + } + + err = of_platform_default_populate(rp1_node, NULL, dev); + if (err) { + dev_err_probe(&pdev->dev, err, "Error populating devicetree\n"); + goto err_unload_overlay; + } + + return 0; + +err_unload_overlay: + of_overlay_remove(&rp1->ovcs_id); +err_unregister_interrupts: + rp1_unregister_interrupts(pdev); +err_put_node: + if (skip_ovl) + of_node_put(rp1_node); + + return err; +} + +static void rp1_remove(struct pci_dev *pdev) +{ + struct rp1_dev *rp1 = pci_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + of_platform_depopulate(dev); + of_overlay_remove(&rp1->ovcs_id); + rp1_unregister_interrupts(pdev); +} + +static const struct pci_device_id dev_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RPI_RP1_C0), }, + { } +}; +MODULE_DEVICE_TABLE(pci, dev_id_table); + +static struct pci_driver rp1_driver = { + .name = KBUILD_MODNAME, + .id_table = dev_id_table, + .probe = rp1_probe, + .remove = rp1_remove, +}; + +module_pci_driver(rp1_driver); + +MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>"); +MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>"); +MODULE_DESCRIPTION("RaspberryPi RP1 misc device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index e5069882457e..c98ff8aa221c 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -28,7 +28,8 @@ static ssize_t sram_read(struct file *filp, struct kobject *kobj, { struct sram_partition *part; - part = container_of(attr, struct sram_partition, battr); + /* Cast away the const as the attribute is part of a larger structure */ + part = (struct sram_partition *)container_of(attr, struct sram_partition, battr); mutex_lock(&part->lock); memcpy_fromio(buf, part->base + pos, count); @@ -43,7 +44,8 @@ static ssize_t sram_write(struct file *filp, struct kobject *kobj, { struct sram_partition *part; - part = container_of(attr, struct sram_partition, battr); + /* Cast away the const as the attribute is part of a larger structure */ + part = (struct sram_partition *)container_of(attr, struct sram_partition, battr); mutex_lock(&part->lock); memcpy_toio(part->base + pos, buf, count); @@ -83,8 +85,8 @@ static int sram_add_export(struct sram_dev *sram, struct sram_reserve *block, return -ENOMEM; part->battr.attr.mode = S_IRUSR | S_IWUSR; - part->battr.read_new = sram_read; - part->battr.write_new = sram_write; + part->battr.read = sram_read; + part->battr.write = sram_write; part->battr.size = block->size; return device_create_bin_file(sram->dev, &part->battr); @@ -164,8 +166,8 @@ static void sram_free_partitions(struct sram_dev *sram) static int sram_reserve_cmp(void *priv, const struct list_head *a, const struct list_head *b) { - struct sram_reserve *ra = list_entry(a, struct sram_reserve, list); - struct sram_reserve *rb = list_entry(b, struct sram_reserve, list); + const struct sram_reserve *ra = list_entry(a, struct sram_reserve, list); + const struct sram_reserve *rb = list_entry(b, struct sram_reserve, list); return ra->start - rb->start; } diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index f7cde245ac95..0b1a6350c02b 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -118,20 +118,17 @@ static void fpc202_set_enable(struct fpc202_priv *priv, int enable) gpiod_set_value(priv->en_gpio, enable); } -static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct fpc202_priv *priv = gpiochip_get_data(chip); int ret; u8 val; - if (fpc202_gpio_get_dir(offset) == GPIO_LINE_DIRECTION_IN) - return; - ret = fpc202_read(priv, FPC202_REG_OUT_A_OUT_B_VAL); if (ret < 0) { dev_err(&priv->client->dev, "Failed to set GPIO %d value! err %d\n", offset, ret); - return; + return ret; } val = (u8)ret; @@ -141,7 +138,7 @@ static void fpc202_gpio_set(struct gpio_chip *chip, unsigned int offset, else val &= ~BIT(offset - FPC202_GPIO_P0_S0_OUT_A); - fpc202_write(priv, FPC202_REG_OUT_A_OUT_B_VAL, val); + return fpc202_write(priv, FPC202_REG_OUT_A_OUT_B_VAL, val); } static int fpc202_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -284,7 +281,7 @@ static int fpc202_probe_port(struct fpc202_priv *priv, struct device_node *i2c_h desc.chan_id = port_id; desc.parent = dev; - desc.bus_handle = of_node_to_fwnode(i2c_handle); + desc.bus_handle = of_fwnode_handle(i2c_handle); desc.num_aliases = FPC202_ALIASES_PER_PORT; fpc202_fill_alias_table(priv->client, aliases, port_id); @@ -336,7 +333,7 @@ static int fpc202_probe(struct i2c_client *client) priv->gpio.base = -1; priv->gpio.direction_input = fpc202_gpio_direction_input; priv->gpio.direction_output = fpc202_gpio_direction_output; - priv->gpio.set = fpc202_gpio_set; + priv->gpio.set_rv = fpc202_gpio_set; priv->gpio.get = fpc202_gpio_get; priv->gpio.ngpio = FPC202_GPIO_COUNT; priv->gpio.parent = dev; diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c index 6db1c9d48f8f..44fa81d6cec2 100644 --- a/drivers/misc/tps6594-pfsm.c +++ b/drivers/misc/tps6594-pfsm.c @@ -1,6 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PFSM (Pre-configurable Finite State Machine) driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * PFSM (Pre-configurable Finite State Machine) driver for the following + * PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6594 + * - TPS6593 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -141,7 +147,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a switch (cmd) { case PMIC_GOTO_STANDBY: /* Disable LP mode on TPS6594 Family PMIC */ - if (pfsm->chip_id != TPS65224) { + if (pfsm->chip_id != TPS65224 && pfsm->chip_id != TPS652G1) { ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2, TPS6594_BIT_LP_STANDBY_SEL); @@ -154,8 +160,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0)); break; case PMIC_GOTO_LP_STANDBY: - /* TPS65224 does not support LP STANDBY */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support LP STANDBY */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; /* Enable LP mode */ @@ -179,8 +185,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_NSLEEP1B | TPS6594_BIT_NSLEEP2B); break; case PMIC_SET_MCU_ONLY_STATE: - /* TPS65224 does not support MCU_ONLY_STATE */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support MCU_ONLY_STATE */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; if (copy_from_user(&state_opt, argp, sizeof(state_opt))) @@ -206,7 +212,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return -EFAULT; /* Configure wake-up destination */ - if (pfsm->chip_id == TPS65224) { + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) { regmap_reg = TPS65224_REG_STARTUP_CTRL; mask = TPS65224_MASK_STARTUP_DEST; } else { @@ -230,9 +236,14 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return ret; /* Modify NSLEEP1-2 bits */ - ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS, - pfsm->chip_id == TPS65224 ? - TPS6594_BIT_NSLEEP1B : TPS6594_BIT_NSLEEP2B); + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP1B); + else + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP2B); break; } diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c index f22b44827e92..8069d271ed81 100644 --- a/drivers/misc/vmw_vmci/vmci_context.c +++ b/drivers/misc/vmw_vmci/vmci_context.c @@ -251,6 +251,8 @@ static int ctx_fire_notification(u32 context_id, u32 priv_flags) ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, VMCI_CONTEXT_RESOURCE_ID); ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr); + memset((char*)&ev + sizeof(ev.msg.hdr), 0, + ev.msg.hdr.payload_size); ev.msg.event_data.event = VMCI_EVENT_CTX_REMOVED; ev.payload.context_id = context_id; @@ -269,28 +271,6 @@ static int ctx_fire_notification(u32 context_id, u32 priv_flags) } /* - * Returns the current number of pending datagrams. The call may - * also serve as a synchronization point for the datagram queue, - * as no enqueue operations can occur concurrently. - */ -int vmci_ctx_pending_datagrams(u32 cid, u32 *pending) -{ - struct vmci_ctx *context; - - context = vmci_ctx_get(cid); - if (context == NULL) - return VMCI_ERROR_INVALID_ARGS; - - spin_lock(&context->lock); - if (pending) - *pending = context->pending_datagrams; - spin_unlock(&context->lock); - vmci_ctx_put(context); - - return VMCI_SUCCESS; -} - -/* * Queues a VMCI datagram for the appropriate target VM context. */ int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg) @@ -992,38 +972,6 @@ int vmci_ctx_dbell_destroy(u32 context_id, struct vmci_handle handle) } /* - * Unregisters all doorbell handles that were previously - * registered with vmci_ctx_dbell_create. - */ -int vmci_ctx_dbell_destroy_all(u32 context_id) -{ - struct vmci_ctx *context; - struct vmci_handle handle; - - if (context_id == VMCI_INVALID_ID) - return VMCI_ERROR_INVALID_ARGS; - - context = vmci_ctx_get(context_id); - if (context == NULL) - return VMCI_ERROR_NOT_FOUND; - - spin_lock(&context->lock); - do { - struct vmci_handle_arr *arr = context->doorbell_array; - handle = vmci_handle_arr_remove_tail(arr); - } while (!vmci_handle_is_invalid(handle)); - do { - struct vmci_handle_arr *arr = context->pending_doorbell_array; - handle = vmci_handle_arr_remove_tail(arr); - } while (!vmci_handle_is_invalid(handle)); - spin_unlock(&context->lock); - - vmci_ctx_put(context); - - return VMCI_SUCCESS; -} - -/* * Registers a notification of a doorbell handle initiated by the * specified source context. The notification of doorbells are * subject to the same isolation rules as datagram delivery. To diff --git a/drivers/misc/vmw_vmci/vmci_context.h b/drivers/misc/vmw_vmci/vmci_context.h index 4db8701c9781..980fdece0f7d 100644 --- a/drivers/misc/vmw_vmci/vmci_context.h +++ b/drivers/misc/vmw_vmci/vmci_context.h @@ -132,7 +132,6 @@ bool vmci_ctx_supports_host_qp(struct vmci_ctx *context); int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg); int vmci_ctx_dequeue_datagram(struct vmci_ctx *context, size_t *max_size, struct vmci_datagram **dg); -int vmci_ctx_pending_datagrams(u32 cid, u32 *pending); struct vmci_ctx *vmci_ctx_get(u32 cid); void vmci_ctx_put(struct vmci_ctx *context); bool vmci_ctx_exists(u32 cid); @@ -153,7 +152,6 @@ void vmci_ctx_unset_notify(struct vmci_ctx *context); int vmci_ctx_dbell_create(u32 context_id, struct vmci_handle handle); int vmci_ctx_dbell_destroy(u32 context_id, struct vmci_handle handle); -int vmci_ctx_dbell_destroy_all(u32 context_id); int vmci_ctx_notify_dbell(u32 cid, struct vmci_handle handle, u32 src_priv_flags); diff --git a/drivers/misc/vmw_vmci/vmci_doorbell.c b/drivers/misc/vmw_vmci/vmci_doorbell.c index fa8a7fce4481..53eeb9e6cb56 100644 --- a/drivers/misc/vmw_vmci/vmci_doorbell.c +++ b/drivers/misc/vmw_vmci/vmci_doorbell.c @@ -258,23 +258,6 @@ static int dbell_unlink(struct vmci_handle handle) } /* - * Notify another guest or the host. We send a datagram down to the - * host via the hypervisor with the notification info. - */ -static int dbell_notify_as_guest(struct vmci_handle handle, u32 priv_flags) -{ - struct vmci_doorbell_notify_msg notify_msg; - - notify_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, - VMCI_DOORBELL_NOTIFY); - notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE; - notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE; - notify_msg.handle = handle; - - return vmci_send_datagram(¬ify_msg.hdr); -} - -/* * Calls the specified callback in a delayed context. */ static void dbell_delayed_dispatch(struct work_struct *work) @@ -566,39 +549,3 @@ int vmci_doorbell_destroy(struct vmci_handle handle) return VMCI_SUCCESS; } EXPORT_SYMBOL_GPL(vmci_doorbell_destroy); - -/* - * vmci_doorbell_notify() - Ring the doorbell (and hide in the bushes). - * @dst: The handlle identifying the doorbell resource - * @priv_flags: Priviledge flags. - * - * Generates a notification on the doorbell identified by the - * handle. For host side generation of notifications, the caller - * can specify what the privilege of the calling side is. - */ -int vmci_doorbell_notify(struct vmci_handle dst, u32 priv_flags) -{ - int retval; - enum vmci_route route; - struct vmci_handle src; - - if (vmci_handle_is_invalid(dst) || - (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)) - return VMCI_ERROR_INVALID_ARGS; - - src = VMCI_INVALID_HANDLE; - retval = vmci_route(&src, &dst, false, &route); - if (retval < VMCI_SUCCESS) - return retval; - - if (VMCI_ROUTE_AS_HOST == route) - return vmci_ctx_notify_dbell(VMCI_HOST_CONTEXT_ID, - dst, priv_flags); - - if (VMCI_ROUTE_AS_GUEST == route) - return dbell_notify_as_guest(dst, priv_flags); - - pr_warn("Unknown route (%d) for doorbell\n", route); - return VMCI_ERROR_DST_UNREACHABLE; -} -EXPORT_SYMBOL_GPL(vmci_doorbell_notify); diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 73d71c4ec139..b88ac144ad32 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -3023,139 +3023,6 @@ s64 vmci_qpair_consume_buf_ready(const struct vmci_qp *qpair) EXPORT_SYMBOL_GPL(vmci_qpair_consume_buf_ready); /* - * vmci_qpair_enqueue() - Throw data on the queue. - * @qpair: Pointer to the queue pair struct. - * @buf: Pointer to buffer containing data - * @buf_size: Length of buffer. - * @buf_type: Buffer type (Unused). - * - * This is the client interface for enqueueing data into the queue. - * Returns number of bytes enqueued or < 0 on error. - */ -ssize_t vmci_qpair_enqueue(struct vmci_qp *qpair, - const void *buf, - size_t buf_size, - int buf_type) -{ - ssize_t result; - struct iov_iter from; - struct kvec v = {.iov_base = (void *)buf, .iov_len = buf_size}; - - if (!qpair || !buf) - return VMCI_ERROR_INVALID_ARGS; - - iov_iter_kvec(&from, ITER_SOURCE, &v, 1, buf_size); - - qp_lock(qpair); - - do { - result = qp_enqueue_locked(qpair->produce_q, - qpair->consume_q, - qpair->produce_q_size, - &from); - - if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && - !qp_wait_for_ready_queue(qpair)) - result = VMCI_ERROR_WOULD_BLOCK; - - } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); - - qp_unlock(qpair); - - return result; -} -EXPORT_SYMBOL_GPL(vmci_qpair_enqueue); - -/* - * vmci_qpair_dequeue() - Get data from the queue. - * @qpair: Pointer to the queue pair struct. - * @buf: Pointer to buffer for the data - * @buf_size: Length of buffer. - * @buf_type: Buffer type (Unused). - * - * This is the client interface for dequeueing data from the queue. - * Returns number of bytes dequeued or < 0 on error. - */ -ssize_t vmci_qpair_dequeue(struct vmci_qp *qpair, - void *buf, - size_t buf_size, - int buf_type) -{ - ssize_t result; - struct iov_iter to; - struct kvec v = {.iov_base = buf, .iov_len = buf_size}; - - if (!qpair || !buf) - return VMCI_ERROR_INVALID_ARGS; - - iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); - - qp_lock(qpair); - - do { - result = qp_dequeue_locked(qpair->produce_q, - qpair->consume_q, - qpair->consume_q_size, - &to, true); - - if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && - !qp_wait_for_ready_queue(qpair)) - result = VMCI_ERROR_WOULD_BLOCK; - - } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); - - qp_unlock(qpair); - - return result; -} -EXPORT_SYMBOL_GPL(vmci_qpair_dequeue); - -/* - * vmci_qpair_peek() - Peek at the data in the queue. - * @qpair: Pointer to the queue pair struct. - * @buf: Pointer to buffer for the data - * @buf_size: Length of buffer. - * @buf_type: Buffer type (Unused on Linux). - * - * This is the client interface for peeking into a queue. (I.e., - * copy data from the queue without updating the head pointer.) - * Returns number of bytes dequeued or < 0 on error. - */ -ssize_t vmci_qpair_peek(struct vmci_qp *qpair, - void *buf, - size_t buf_size, - int buf_type) -{ - struct iov_iter to; - struct kvec v = {.iov_base = buf, .iov_len = buf_size}; - ssize_t result; - - if (!qpair || !buf) - return VMCI_ERROR_INVALID_ARGS; - - iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); - - qp_lock(qpair); - - do { - result = qp_dequeue_locked(qpair->produce_q, - qpair->consume_q, - qpair->consume_q_size, - &to, false); - - if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && - !qp_wait_for_ready_queue(qpair)) - result = VMCI_ERROR_WOULD_BLOCK; - - } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); - - qp_unlock(qpair); - - return result; -} -EXPORT_SYMBOL_GPL(vmci_qpair_peek); - -/* * vmci_qpair_enquev() - Throw data on the queue using iov. * @qpair: Pointer to the queue pair struct. * @iov: Pointer to buffer containing data |