diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-19 15:55:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-19 15:55:08 -0700 |
commit | acc5965b9ff8a1889f5b51466562896d59c6e1b9 (patch) | |
tree | 8cd651e6594a9133f216d59a4fa1f18d8c63fb26 /drivers/misc | |
parent | 09ea8089abb5d851ce08a9b1a43706e42ef39db2 (diff) | |
parent | 5418e6dfc905b3ccc1e01bdad97d948697b20100 (diff) |
Merge tag 'char-misc-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc and other driver updates from Greg KH:
"Here is the "big" set of char/misc and other driver subsystem changes
for 6.11-rc1. Nothing major in here, just loads of new drivers and
updates. Included in here are:
- IIO api updates and new drivers added
- wait_interruptable_timeout() api cleanups for some drivers
- MODULE_DESCRIPTION() additions for loads of drivers
- parport out-of-bounds fix
- interconnect driver updates and additions
- mhi driver updates and additions
- w1 driver fixes
- binder speedups and fixes
- eeprom driver updates
- coresight driver updates
- counter driver update
- new misc driver additions
- other minor api updates
All of these, EXCEPT for the final Kconfig build fix for 32bit
systems, have been in linux-next for a while with no reported issues.
The Kconfig fixup went in 29 hours ago, so might have missed the
latest linux-next, but was acked by everyone involved"
* tag 'char-misc-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (330 commits)
misc: Kconfig: exclude mrvl-cn10k-dpi compilation for 32-bit systems
misc: delete Makefile.rej
binder: fix hang of unregistered readers
misc: Kconfig: add a new dependency for MARVELL_CN10K_DPI
virtio: add missing MODULE_DESCRIPTION() macro
agp: uninorth: add missing MODULE_DESCRIPTION() macro
spmi: add missing MODULE_DESCRIPTION() macros
dev/parport: fix the array out-of-bounds risk
samples: configfs: add missing MODULE_DESCRIPTION() macro
misc: mrvl-cn10k-dpi: add Octeon CN10K DPI administrative driver
misc: keba: Fix missing AUXILIARY_BUS dependency
slimbus: Fix struct and documentation alignment in stream.c
MAINTAINERS: CC dri-devel list on Qualcomm FastRPC patches
misc: fastrpc: use coherent pool for untranslated Compute Banks
misc: fastrpc: support complete DMA pool access to the DSP
misc: fastrpc: add missing MODULE_DESCRIPTION() macro
misc: fastrpc: Add missing dev_err newlines
misc: fastrpc: Use memdup_user()
nvmem: core: Implement force_ro sysfs attribute
nvmem: Use sysfs_emit() for type attribute
...
Diffstat (limited to 'drivers/misc')
27 files changed, 1502 insertions, 269 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index faf983680040..41c3d2821a78 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -585,6 +585,21 @@ config NSM To compile this driver as a module, choose M here. The module will be called nsm. +config MARVELL_CN10K_DPI + tristate "Octeon CN10K DPI driver" + depends on PCI + depends on ARCH_THUNDER || (COMPILE_TEST && 64BIT) + help + Enables Octeon CN10K DMA packet interface (DPI) driver which + intializes DPI hardware's physical function (PF) device's + global configuration and its virtual function (VFs) resource + configuration to enable DMA transfers. DPI PF device does not + have any data movement functionality, it only serves VF's + resource configuration requests. + + To compile this driver as a module, choose M here: the module + will be called mrvl_cn10k_dpi. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" @@ -602,4 +617,5 @@ source "drivers/misc/cardreader/Kconfig" source "drivers/misc/uacce/Kconfig" source "drivers/misc/pvpanic/Kconfig" source "drivers/misc/mchp_pci1xxxx/Kconfig" +source "drivers/misc/keba/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 153a3f4837e8..c2f990862d2b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -69,3 +69,5 @@ obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o obj-$(CONFIG_NSM) += nsm.o +obj-$(CONFIG_MARVELL_CN10K_DPI) += mrvl_cn10k_dpi.o +obj-y += keba/ diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index 693f0e539f37..6db4db975b9a 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -285,7 +285,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, #endif /* CONFIG_PM */ static const struct i2c_device_id apds9802als_id[] = { - { DRIVER_NAME, 0 }, + { DRIVER_NAME }, { } }; diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 92b92be91d60..6d4edd69db12 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -625,15 +625,15 @@ static ssize_t apds990x_lux_show(struct device *dev, struct apds990x_chip *chip = dev_get_drvdata(dev); ssize_t ret; u32 result; - long timeout; + long time_left; if (pm_runtime_suspended(dev)) return -EIO; - timeout = wait_event_interruptible_timeout(chip->wait, - !chip->lux_wait_fresh_res, - msecs_to_jiffies(APDS_TIMEOUT)); - if (!timeout) + time_left = wait_event_interruptible_timeout(chip->wait, + !chip->lux_wait_fresh_res, + msecs_to_jiffies(APDS_TIMEOUT)); + if (!time_left) return -EIO; mutex_lock(&chip->mutex); @@ -1253,7 +1253,7 @@ static int apds990x_runtime_resume(struct device *dev) #endif static const struct i2c_device_id apds990x_id[] = { - {"apds990x", 0 }, + { "apds990x" }, {} }; diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 1629b62fd052..0c052b05ab6a 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -680,15 +680,15 @@ static ssize_t bh1770_lux_result_show(struct device *dev, { struct bh1770_chip *chip = dev_get_drvdata(dev); ssize_t ret; - long timeout; + long time_left; if (pm_runtime_suspended(dev)) return -EIO; /* Chip is not enabled at all */ - timeout = wait_event_interruptible_timeout(chip->wait, - !chip->lux_wait_result, - msecs_to_jiffies(BH1770_TIMEOUT)); - if (!timeout) + time_left = wait_event_interruptible_timeout(chip->wait, + !chip->lux_wait_result, + msecs_to_jiffies(BH1770_TIMEOUT)); + if (!time_left) return -EIO; mutex_lock(&chip->mutex); @@ -1361,8 +1361,8 @@ static int bh1770_runtime_resume(struct device *dev) #endif static const struct i2c_device_id bh1770_id[] = { - {"bh1770glc", 0 }, - {"sfh7770", 0 }, + { "bh1770glc" }, + { "sfh7770" }, {} }; diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 5f8dcd0e3848..4175df7ef011 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -271,7 +271,7 @@ static void ds1682_remove(struct i2c_client *client) } static const struct i2c_device_id ds1682_id[] = { - { "ds1682", 0 }, + { "ds1682" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1682_id); diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 4e61ac18cc96..9df12399bda3 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -109,6 +109,8 @@ config EEPROM_IDT_89HPESX config EEPROM_EE1004 tristate "SPD EEPROMs on DDR4 memory modules" depends on I2C && SYSFS + select NVMEM + select NVMEM_SYSFS help Enable this driver to get read support to SPD EEPROMs following the JEDEC EE1004 standard. These are typically found on DDR4 diff --git a/drivers/misc/eeprom/digsy_mtc_eeprom.c b/drivers/misc/eeprom/digsy_mtc_eeprom.c index f1f766b70965..88888485e6f8 100644 --- a/drivers/misc/eeprom/digsy_mtc_eeprom.c +++ b/drivers/misc/eeprom/digsy_mtc_eeprom.c @@ -14,13 +14,12 @@ * and delete this driver. */ -#include <linux/gpio.h> #include <linux/gpio/machine.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/spi/spi.h> #include <linux/spi/spi_gpio.h> -#include <linux/eeprom_93xx46.h> #define GPIO_EEPROM_CLK 216 #define GPIO_EEPROM_CS 210 @@ -29,22 +28,13 @@ #define GPIO_EEPROM_OE 255 #define EE_SPI_BUS_NUM 1 -static void digsy_mtc_op_prepare(void *p) -{ - /* enable */ - gpio_set_value(GPIO_EEPROM_OE, 0); -} - -static void digsy_mtc_op_finish(void *p) -{ - /* disable */ - gpio_set_value(GPIO_EEPROM_OE, 1); -} +static const struct property_entry digsy_mtc_spi_properties[] = { + PROPERTY_ENTRY_U32("data-size", 8), + { } +}; -struct eeprom_93xx46_platform_data digsy_mtc_eeprom_data = { - .flags = EE_ADDR8, - .prepare = digsy_mtc_op_prepare, - .finish = digsy_mtc_op_finish, +static const struct software_node digsy_mtc_spi_node = { + .properties = digsy_mtc_spi_properties, }; static struct spi_gpio_platform_data eeprom_spi_gpio_data = { @@ -70,18 +60,19 @@ static struct gpiod_lookup_table eeprom_spi_gpiod_table = { "miso", GPIO_ACTIVE_HIGH), GPIO_LOOKUP("gpio@b00", GPIO_EEPROM_CS, "cs", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("gpio@b00", GPIO_EEPROM_OE, + "select", GPIO_ACTIVE_LOW), { }, }, }; static struct spi_board_info digsy_mtc_eeprom_info[] __initdata = { { - .modalias = "93xx46", + .modalias = "eeprom-93xx46", .max_speed_hz = 1000000, .bus_num = EE_SPI_BUS_NUM, .chip_select = 0, .mode = SPI_MODE_0, - .platform_data = &digsy_mtc_eeprom_data, }, }; @@ -89,15 +80,18 @@ static int __init digsy_mtc_eeprom_devices_init(void) { int ret; - ret = gpio_request_one(GPIO_EEPROM_OE, GPIOF_OUT_INIT_HIGH, - "93xx46 EEPROMs OE"); - if (ret) { - pr_err("can't request gpio %d\n", GPIO_EEPROM_OE); - return ret; - } gpiod_add_lookup_table(&eeprom_spi_gpiod_table); spi_register_board_info(digsy_mtc_eeprom_info, ARRAY_SIZE(digsy_mtc_eeprom_info)); - return platform_device_register(&digsy_mtc_eeprom); + + ret = device_add_software_node(&digsy_mtc_eeprom.dev, &digsy_mtc_spi_node); + if (ret) + return ret; + + ret = platform_device_register(&digsy_mtc_eeprom); + if (ret) + device_remove_software_node(&digsy_mtc_eeprom.dev); + + return ret; } device_initcall(digsy_mtc_eeprom_devices_init); diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c index 21feebc3044c..d4aeeb2b2169 100644 --- a/drivers/misc/eeprom/ee1004.c +++ b/drivers/misc/eeprom/ee1004.c @@ -9,12 +9,14 @@ * Copyright (C) 2008 Wolfram Sang, Pengutronix */ +#include <linux/device.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/nvmem-provider.h> /* * DDR4 memory modules use special EEPROMs following the Jedec EE1004 @@ -52,7 +54,7 @@ static struct ee1004_bus_data { } ee1004_bus_data[EE1004_MAX_BUSSES]; static const struct i2c_device_id ee1004_ids[] = { - { "ee1004", 0 }, + { "ee1004" }, { } }; MODULE_DEVICE_TABLE(i2c, ee1004_ids); @@ -144,13 +146,17 @@ static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf, return i2c_smbus_read_i2c_block_data_or_emulated(client, offset, count, buf); } -static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +static int ee1004_read(void *priv, unsigned int off, void *val, size_t count) { - struct i2c_client *client = kobj_to_i2c_client(kobj); - size_t requested = count; - int ret = 0; + struct i2c_client *client = priv; + char *buf = val; + int ret; + + if (unlikely(!count)) + return count; + + if (off + count > EE1004_EEPROM_SIZE) + return -EINVAL; /* * Read data from chip, protecting against concurrent access to @@ -160,42 +166,52 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, while (count) { ret = ee1004_eeprom_read(client, buf, off, count); - if (ret < 0) - goto out; + if (ret < 0) { + mutex_unlock(&ee1004_bus_lock); + return ret; + } buf += ret; off += ret; count -= ret; } -out: + mutex_unlock(&ee1004_bus_lock); - return ret < 0 ? ret : requested; + return 0; } -static BIN_ATTR_RO(eeprom, EE1004_EEPROM_SIZE); - -static struct bin_attribute *ee1004_attrs[] = { - &bin_attr_eeprom, - NULL -}; - -BIN_ATTRIBUTE_GROUPS(ee1004); - static void ee1004_probe_temp_sensor(struct i2c_client *client) { struct i2c_board_info info = { .type = "jc42" }; - u8 byte14; + unsigned short addr = 0x18 | (client->addr & 7); + unsigned short addr_list[] = { addr, I2C_CLIENT_END }; + u8 data[2]; int ret; /* byte 14, bit 7 is set if temp sensor is present */ - ret = ee1004_eeprom_read(client, &byte14, 14, 1); - if (ret != 1 || !(byte14 & BIT(7))) + ret = ee1004_eeprom_read(client, data, 14, 1); + if (ret != 1) return; - info.addr = 0x18 | (client->addr & 7); - - i2c_new_client_device(client->adapter, &info); + if (!(data[0] & BIT(7))) { + /* + * If the SPD data suggests that there is no temperature + * sensor, it may still be there for SPD revision 1.0. + * See SPD Annex L, Revision 1 and 2, for details. + * Check DIMM type and SPD revision; if it is a DDR4 + * with SPD revision 1.0, check the thermal sensor address + * and instantiate the jc42 driver if a chip is found at + * that address. + * It is not necessary to check if there is a chip at the + * temperature sensor address since i2c_new_scanned_device() + * will do that and return silently if no chip is found. + */ + ret = ee1004_eeprom_read(client, data, 1, 2); + if (ret != 2 || data[0] != 0x10 || data[1] != 0x0c) + return; + } + i2c_new_scanned_device(client->adapter, &info, addr_list, NULL); } static void ee1004_cleanup(int idx, struct ee1004_bus_data *bd) @@ -207,9 +223,36 @@ static void ee1004_cleanup(int idx, struct ee1004_bus_data *bd) } } +static void ee1004_cleanup_bus_data(void *data) +{ + struct ee1004_bus_data *bd = data; + + /* Remove page select clients if this is the last device */ + mutex_lock(&ee1004_bus_lock); + ee1004_cleanup(EE1004_NUM_PAGES, bd); + mutex_unlock(&ee1004_bus_lock); +} + static int ee1004_probe(struct i2c_client *client) { + struct nvmem_config config = { + .dev = &client->dev, + .name = dev_name(&client->dev), + .id = NVMEM_DEVID_NONE, + .owner = THIS_MODULE, + .type = NVMEM_TYPE_EEPROM, + .read_only = true, + .root_only = false, + .reg_read = ee1004_read, + .size = EE1004_EEPROM_SIZE, + .word_size = 1, + .stride = 1, + .priv = client, + .compat = true, + .base_dev = &client->dev, + }; struct ee1004_bus_data *bd; + struct nvmem_device *ndev; int err, cnr = 0; /* Make sure we can operate on this adapter */ @@ -228,6 +271,10 @@ static int ee1004_probe(struct i2c_client *client) "Only %d busses supported", EE1004_MAX_BUSSES); } + err = devm_add_action_or_reset(&client->dev, ee1004_cleanup_bus_data, bd); + if (err < 0) + return err; + i2c_set_clientdata(client, bd); if (++bd->dev_count == 1) { @@ -237,16 +284,18 @@ static int ee1004_probe(struct i2c_client *client) cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr); if (IS_ERR(cl)) { - err = PTR_ERR(cl); - goto err_clients; + mutex_unlock(&ee1004_bus_lock); + return PTR_ERR(cl); } bd->set_page[cnr] = cl; } /* Remember current page to avoid unneeded page select */ err = ee1004_get_current_page(bd); - if (err < 0) - goto err_clients; + if (err < 0) { + mutex_unlock(&ee1004_bus_lock); + return err; + } dev_dbg(&client->dev, "Currently selected page: %d\n", err); bd->current_page = err; } @@ -255,27 +304,15 @@ static int ee1004_probe(struct i2c_client *client) mutex_unlock(&ee1004_bus_lock); + ndev = devm_nvmem_register(&client->dev, &config); + if (IS_ERR(ndev)) + return PTR_ERR(ndev); + dev_info(&client->dev, "%u byte EE1004-compliant SPD EEPROM, read-only\n", EE1004_EEPROM_SIZE); return 0; - - err_clients: - ee1004_cleanup(cnr, bd); - mutex_unlock(&ee1004_bus_lock); - - return err; -} - -static void ee1004_remove(struct i2c_client *client) -{ - struct ee1004_bus_data *bd = i2c_get_clientdata(client); - - /* Remove page select clients if this is the last device */ - mutex_lock(&ee1004_bus_lock); - ee1004_cleanup(EE1004_NUM_PAGES, bd); - mutex_unlock(&ee1004_bus_lock); } /*-------------------------------------------------------------------------*/ @@ -283,10 +320,8 @@ static void ee1004_remove(struct i2c_client *client) static struct i2c_driver ee1004_driver = { .driver = { .name = "ee1004", - .dev_groups = ee1004_groups, }, .probe = ee1004_probe, - .remove = ee1004_remove, .id_table = ee1004_ids, }; module_i2c_driver(ee1004_driver); diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 45c8ae0db8f9..e2221be88445 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -5,19 +5,42 @@ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de> */ +#include <linux/array_size.h> +#include <linux/bits.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/gpio/consumer.h> -#include <linux/kernel.h> +#include <linux/kstrtox.h> #include <linux/log2.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/of.h> -#include <linux/of_device.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/spi/spi.h> +#include <linux/string_choices.h> + #include <linux/nvmem-provider.h> -#include <linux/eeprom_93xx46.h> + +struct eeprom_93xx46_platform_data { + unsigned char flags; +#define EE_ADDR8 0x01 /* 8 bit addr. cfg */ +#define EE_ADDR16 0x02 /* 16 bit addr. cfg */ +#define EE_READONLY 0x08 /* forbid writing */ +#define EE_SIZE1K 0x10 /* 1 kb of data, that is a 93xx46 */ +#define EE_SIZE2K 0x20 /* 2 kb of data, that is a 93xx56 */ +#define EE_SIZE4K 0x40 /* 4 kb of data, that is a 93xx66 */ + + unsigned int quirks; +/* Single word read transfers only; no sequential read. */ +#define EEPROM_93XX46_QUIRK_SINGLE_WORD_READ (1 << 0) +/* Instructions such as EWEN are (addrlen + 2) in length. */ +#define EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH (1 << 1) +/* Add extra cycle after address during a read */ +#define EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE BIT(2) + + struct gpio_desc *select; +}; #define OP_START 0x4 #define OP_WRITE (OP_START | 0x1) @@ -96,15 +119,14 @@ static int eeprom_93xx46_read(void *priv, unsigned int off, mutex_lock(&edev->lock); - if (edev->pdata->prepare) - edev->pdata->prepare(edev); + gpiod_set_value_cansleep(edev->pdata->select, 1); /* The opcode in front of the address is three bits. */ bits = edev->addrlen + 3; while (count) { struct spi_message m; - struct spi_transfer t[2] = { { 0 } }; + struct spi_transfer t[2] = {}; u16 cmd_addr = OP_READ << edev->addrlen; size_t nbytes = count; @@ -126,25 +148,23 @@ static int eeprom_93xx46_read(void *priv, unsigned int off, bits += 1; } - spi_message_init(&m); - t[0].tx_buf = (char *)&cmd_addr; t[0].len = 2; t[0].bits_per_word = bits; - spi_message_add_tail(&t[0], &m); t[1].rx_buf = buf; t[1].len = count; t[1].bits_per_word = 8; - spi_message_add_tail(&t[1], &m); + + spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); err = spi_sync(edev->spi, &m); /* have to wait at least Tcsl ns */ ndelay(250); if (err) { - dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n", - nbytes, (int)off, err); + dev_err(&edev->spi->dev, "read %zu bytes at %u: err. %d\n", + nbytes, off, err); break; } @@ -153,8 +173,7 @@ static int eeprom_93xx46_read(void *priv, unsigned int off, count -= nbytes; } - if (edev->pdata->finish) - edev->pdata->finish(edev); + gpiod_set_value_cansleep(edev->pdata->select, 0); mutex_unlock(&edev->lock); @@ -164,7 +183,7 @@ static int eeprom_93xx46_read(void *priv, unsigned int off, static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) { struct spi_message m; - struct spi_transfer t; + struct spi_transfer t = {}; int bits, ret; u16 cmd_addr; @@ -182,31 +201,27 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) bits += 2; } - dev_dbg(&edev->spi->dev, "ew%s cmd 0x%04x, %d bits\n", - is_on ? "en" : "ds", cmd_addr, bits); - - spi_message_init(&m); - memset(&t, 0, sizeof(t)); + dev_dbg(&edev->spi->dev, "ew %s cmd 0x%04x, %d bits\n", + str_enable_disable(is_on), cmd_addr, bits); t.tx_buf = &cmd_addr; t.len = 2; t.bits_per_word = bits; - spi_message_add_tail(&t, &m); + + spi_message_init_with_transfers(&m, &t, 1); mutex_lock(&edev->lock); - if (edev->pdata->prepare) - edev->pdata->prepare(edev); + gpiod_set_value_cansleep(edev->pdata->select, 1); ret = spi_sync(edev->spi, &m); /* have to wait at least Tcsl ns */ ndelay(250); if (ret) - dev_err(&edev->spi->dev, "erase/write %sable error %d\n", - is_on ? "en" : "dis", ret); + dev_err(&edev->spi->dev, "erase/write %s error %d\n", + str_enable_disable(is_on), ret); - if (edev->pdata->finish) - edev->pdata->finish(edev); + gpiod_set_value_cansleep(edev->pdata->select, 0); mutex_unlock(&edev->lock); return ret; @@ -217,7 +232,7 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, const char *buf, unsigned off) { struct spi_message m; - struct spi_transfer t[2]; + struct spi_transfer t[2] = {}; int bits, data_len, ret; u16 cmd_addr; @@ -239,18 +254,15 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr); - spi_message_init(&m); - memset(t, 0, sizeof(t)); - t[0].tx_buf = (char *)&cmd_addr; t[0].len = 2; t[0].bits_per_word = bits; - spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; t[1].len = data_len; t[1].bits_per_word = 8; - spi_message_add_tail(&t[1], &m); + + spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); ret = spi_sync(edev->spi, &m); /* have to wait program cycle time Twc ms */ @@ -263,7 +275,8 @@ static int eeprom_93xx46_write(void *priv, unsigned int off, { struct eeprom_93xx46_dev *edev = priv; char *buf = val; - int i, ret, step = 1; + int ret, step = 1; + unsigned int i; if (unlikely(off >= edev->size)) return -EFBIG; @@ -285,20 +298,17 @@ static int eeprom_93xx46_write(void *priv, unsigned int off, mutex_lock(&edev->lock); - if (edev->pdata->prepare) - edev->pdata->prepare(edev); + gpiod_set_value_cansleep(edev->pdata->select, 1); for (i = 0; i < count; i += step) { ret = eeprom_93xx46_write_word(edev, &buf[i], off + i); if (ret) { - dev_err(&edev->spi->dev, "write failed at %d: %d\n", - (int)off + i, ret); + dev_err(&edev->spi->dev, "write failed at %u: %d\n", off + i, ret); break; } } - if (edev->pdata->finish) - edev->pdata->finish(edev); + gpiod_set_value_cansleep(edev->pdata->select, 0); mutex_unlock(&edev->lock); @@ -309,9 +319,8 @@ static int eeprom_93xx46_write(void *priv, unsigned int off, static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) { - struct eeprom_93xx46_platform_data *pd = edev->pdata; struct spi_message m; - struct spi_transfer t; + struct spi_transfer t = {}; int bits, ret; u16 cmd_addr; @@ -331,18 +340,15 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits); - spi_message_init(&m); - memset(&t, 0, sizeof(t)); - t.tx_buf = &cmd_addr; t.len = 2; t.bits_per_word = bits; - spi_message_add_tail(&t, &m); + + spi_message_init_with_transfers(&m, &t, 1); mutex_lock(&edev->lock); - if (edev->pdata->prepare) - edev->pdata->prepare(edev); + gpiod_set_value_cansleep(edev->pdata->select, 1); ret = spi_sync(edev->spi, &m); if (ret) @@ -350,21 +356,23 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) /* have to wait erase cycle time Tec ms */ mdelay(6); - if (pd->finish) - pd->finish(edev); + gpiod_set_value_cansleep(edev->pdata->select, 0); mutex_unlock(&edev->lock); return ret; } -static ssize_t eeprom_93xx46_store_erase(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t erase_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev); - int erase = 0, ret; + bool erase; + int ret; + + ret = kstrtobool(buf, &erase); + if (ret) + return ret; - sscanf(buf, "%d", &erase); if (erase) { ret = eeprom_93xx46_ew(edev, 1); if (ret) @@ -378,21 +386,7 @@ static ssize_t eeprom_93xx46_store_erase(struct device *dev, } return count; } -static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); - -static void select_assert(void *context) -{ - struct eeprom_93xx46_dev *edev = context; - - gpiod_set_value_cansleep(edev->pdata->select, 1); -} - -static void select_deassert(void *context) -{ - struct eeprom_93xx46_dev *edev = context; - - gpiod_set_value_cansleep(edev->pdata->select, 0); -} +static DEVICE_ATTR_WO(erase); static const struct of_device_id eeprom_93xx46_of_table[] = { { .compatible = "eeprom-93xx46", .data = &at93c46_data, }, @@ -422,22 +416,20 @@ static const struct spi_device_id eeprom_93xx46_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, eeprom_93xx46_spi_ids); -static int eeprom_93xx46_probe_dt(struct spi_device *spi) +static int eeprom_93xx46_probe_fw(struct device *dev) { - const struct of_device_id *of_id = - of_match_device(eeprom_93xx46_of_table, &spi->dev); - struct device_node *np = spi->dev.of_node; + const struct eeprom_93xx46_devtype_data *data; struct eeprom_93xx46_platform_data *pd; u32 tmp; int ret; - pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); if (!pd) return -ENOMEM; - ret = of_property_read_u32(np, "data-size", &tmp); + ret = device_property_read_u32(dev, "data-size", &tmp); if (ret < 0) { - dev_err(&spi->dev, "data-size property not found\n"); + dev_err(dev, "data-size property not found\n"); return ret; } @@ -446,30 +438,25 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) } else if (tmp == 16) { pd->flags |= EE_ADDR16; } else { - dev_err(&spi->dev, "invalid data-size (%d)\n", tmp); + dev_err(dev, "invalid data-size (%d)\n", tmp); return -EINVAL; } - if (of_property_read_bool(np, "read-only")) + if (device_property_read_bool(dev, "read-only")) pd->flags |= EE_READONLY; - pd->select = devm_gpiod_get_optional(&spi->dev, "select", - GPIOD_OUT_LOW); + pd->select = devm_gpiod_get_optional(dev, "select", GPIOD_OUT_LOW); if (IS_ERR(pd->select)) return PTR_ERR(pd->select); + gpiod_set_consumer_name(pd->select, "93xx46 EEPROMs OE"); - pd->prepare = select_assert; - pd->finish = select_deassert; - gpiod_direction_output(pd->select, 0); - - if (of_id->data) { - const struct eeprom_93xx46_devtype_data *data = of_id->data; - + data = spi_get_device_match_data(to_spi_device(dev)); + if (data) { pd->quirks = data->quirks; pd->flags |= data->flags; } - spi->dev.platform_data = pd; + dev->platform_data = pd; return 0; } @@ -478,13 +465,12 @@ static int eeprom_93xx46_probe(struct spi_device *spi) { struct eeprom_93xx46_platform_data *pd; struct eeprom_93xx46_dev *edev; + struct device *dev = &spi->dev; int err; - if (spi->dev.of_node) { - err = eeprom_93xx46_probe_dt(spi); - if (err < 0) - return err; - } + err = eeprom_93xx46_probe_fw(dev); + if (err < 0) + return err; pd = spi->dev.platform_data; if (!pd) { @@ -565,7 +551,7 @@ static void eeprom_93xx46_remove(struct spi_device *spi) static struct spi_driver eeprom_93xx46_driver = { .driver = { .name = "93xx46", - .of_match_table = of_match_ptr(eeprom_93xx46_of_table), + .of_match_table = eeprom_93xx46_of_table, }, .probe = eeprom_93xx46_probe, .remove = eeprom_93xx46_remove, diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 327afb866b21..43421fe37d33 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1426,58 +1426,58 @@ MODULE_DEVICE_TABLE(i2c, ee_ids); * idt_ids - supported IDT 89HPESx devices */ static const struct i2c_device_id idt_ids[] = { - { "89hpes8nt2", 0 }, - { "89hpes12nt3", 0 }, - - { "89hpes24nt6ag2", 0 }, - { "89hpes32nt8ag2", 0 }, - { "89hpes32nt8bg2", 0 }, - { "89hpes12nt12g2", 0 }, - { "89hpes16nt16g2", 0 }, - { "89hpes24nt24g2", 0 }, - { "89hpes32nt24ag2", 0 }, - { "89hpes32nt24bg2", 0 }, - - { "89hpes12n3", 0 }, - { "89hpes12n3a", 0 }, - { "89hpes24n3", 0 }, - { "89hpes24n3a", 0 }, - - { "89hpes32h8", 0 }, - { "89hpes32h8g2", 0 }, - { "89hpes48h12", 0 }, - { "89hpes48h12g2", 0 }, - { "89hpes48h12ag2", 0 }, - { "89hpes16h16", 0 }, - { "89hpes22h16", 0 }, - { "89hpes22h16g2", 0 }, - { "89hpes34h16", 0 }, - { "89hpes34h16g2", 0 }, - { "89hpes64h16", 0 }, - { "89hpes64h16g2", 0 }, - { "89hpes64h16ag2", 0 }, - - /* { "89hpes3t3", 0 }, // No SMBus-slave iface */ - { "89hpes12t3g2", 0 }, - { "89hpes24t3g2", 0 }, - /* { "89hpes4t4", 0 }, // No SMBus-slave iface */ - { "89hpes16t4", 0 }, - { "89hpes4t4g2", 0 }, - { "89hpes10t4g2", 0 }, - { "89hpes16t4g2", 0 }, - { "89hpes16t4ag2", 0 }, - { "89hpes5t5", 0 }, - { "89hpes6t5", 0 }, - { "89hpes8t5", 0 }, - { "89hpes8t5a", 0 }, - { "89hpes24t6", 0 }, - { "89hpes6t6g2", 0 }, - { "89hpes24t6g2", 0 }, - { "89hpes16t7", 0 }, - { "89hpes32t8", 0 }, - { "89hpes32t8g2", 0 }, - { "89hpes48t12", 0 }, - { "89hpes48t12g2", 0 }, + { "89hpes8nt2" }, + { "89hpes12nt3" }, + + { "89hpes24nt6ag2" }, + { "89hpes32nt8ag2" }, + { "89hpes32nt8bg2" }, + { "89hpes12nt12g2" }, + { "89hpes16nt16g2" }, + { "89hpes24nt24g2" }, + { "89hpes32nt24ag2" }, + { "89hpes32nt24bg2" }, + + { "89hpes12n3" }, + { "89hpes12n3a" }, + { "89hpes24n3" }, + { "89hpes24n3a" }, + + { "89hpes32h8" }, + { "89hpes32h8g2" }, + { "89hpes48h12" }, + { "89hpes48h12g2" }, + { "89hpes48h12ag2" }, + { "89hpes16h16" }, + { "89hpes22h16" }, + { "89hpes22h16g2" }, + { "89hpes34h16" }, + { "89hpes34h16g2" }, + { "89hpes64h16" }, + { "89hpes64h16g2" }, + { "89hpes64h16ag2" }, + + /* { "89hpes3t3" }, // No SMBus-slave iface */ + { "89hpes12t3g2" }, + { "89hpes24t3g2" }, + /* { "89hpes4t4" }, // No SMBus-slave iface */ + { "89hpes16t4" }, + { "89hpes4t4g2" }, + { "89hpes10t4g2" }, + { "89hpes16t4g2" }, + { "89hpes16t4ag2" }, + { "89hpes5t5" }, + { "89hpes6t5" }, + { "89hpes8t5" }, + { "89hpes8t5a" }, + { "89hpes24t6" }, + { "89hpes6t6g2" }, + { "89hpes24t6g2" }, + { "89hpes16t7" }, + { "89hpes32t8" }, + { "89hpes32t8g2" }, + { "89hpes48t12" }, + { "89hpes48t12g2" }, { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, idt_ids); diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index cb6b1efeafe0..6fab2ffa736b 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -183,7 +183,7 @@ static void max6875_remove(struct i2c_client *client) } static const struct i2c_device_id max6875_id[] = { - { "max6875", 0 }, + { "max6875" }, { } }; MODULE_DEVICE_TABLE(i2c, max6875_id); diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index a7a2bcedb37e..5204fda51da3 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -325,7 +325,7 @@ static void fastrpc_free_map(struct kref *ref) err = qcom_scm_assign_mem(map->phys, map->size, &src_perms, &perm, 1); if (err) { - dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d", + dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d\n", map->phys, map->size, err); return; } @@ -816,7 +816,7 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd, map->attr = attr; err = qcom_scm_assign_mem(map->phys, (u64)map->size, &src_perms, dst_perms, 2); if (err) { - dev_err(sess->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d", + dev_err(sess->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d\n", map->phys, map->size, err); goto map_err; } @@ -953,7 +953,10 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx) ctx->msg_sz = pkt_size; - err = fastrpc_buf_alloc(ctx->fl, dev, pkt_size, &ctx->buf); + if (ctx->fl->sctx->sid) + err = fastrpc_buf_alloc(ctx->fl, dev, pkt_size, &ctx->buf); + else + err = fastrpc_remote_heap_alloc(ctx->fl, dev, pkt_size, &ctx->buf); if (err) return err; @@ -1222,7 +1225,7 @@ static bool is_session_rejected(struct fastrpc_user *fl, bool unsigned_pd_reques * that does not support unsigned PD offload */ if (!fl->cctx->unsigned_support || !unsigned_pd_request) { - dev_err(&fl->cctx->rpdev->dev, "Error: Untrusted application trying to offload to signed PD"); + dev_err(&fl->cctx->rpdev->dev, "Error: Untrusted application trying to offload to signed PD\n"); return true; } } @@ -1260,17 +1263,12 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, goto err; } - name = kzalloc(init.namelen, GFP_KERNEL); - if (!name) { - err = -ENOMEM; + name = memdup_user(u64_to_user_ptr(init.name), init.namelen); + if (IS_ERR(name)) { + err = PTR_ERR(name); goto err; } - if (copy_from_user(name, (void __user *)(uintptr_t)init.name, init.namelen)) { - err = -EFAULT; - goto err_name; - } - if (!fl->cctx->remote_heap) { err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen, &fl->cctx->remote_heap); @@ -1286,7 +1284,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, &src_perms, fl->cctx->vmperms, fl->cctx->vmcount); if (err) { - dev_err(fl->sctx->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d", + dev_err(fl->sctx->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d\n", fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err); goto err_map; } @@ -1340,7 +1338,7 @@ err_invoke: (u64)fl->cctx->remote_heap->size, &src_perms, &dst_perms, 1); if (err) - dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d", + dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d\n", fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err); } err_map: @@ -2278,6 +2276,8 @@ 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); @@ -2320,6 +2320,23 @@ 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) { + u64 src_perms; + + rmem = of_reserved_mem_lookup(rmem_node); + if (!rmem) { + err = -EINVAL; + goto fdev_error; + } + + src_perms = BIT(QCOM_SCM_VMID_HLOS); + + qcom_scm_assign_mem(rmem->base, rmem->size, &src_perms, + data->vmperms, data->vmcount); + + } + secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain")); data->secure = secure_dsp; @@ -2501,5 +2518,6 @@ static void fastrpc_exit(void) } module_exit(fastrpc_exit); +MODULE_DESCRIPTION("Qualcomm FastRPC"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS(DMA_BUF); diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c index 759eaeb64307..ff92c6edff6b 100644 --- a/drivers/misc/hmc6352.c +++ b/drivers/misc/hmc6352.c @@ -121,7 +121,7 @@ static void hmc6352_remove(struct i2c_client *client) } static const struct i2c_device_id hmc6352_id[] = { - { "hmc6352", 0 }, + { "hmc6352" }, { } }; diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c index ee6296b98078..4cdb1087838f 100644 --- a/drivers/misc/ics932s401.c +++ b/drivers/misc/ics932s401.c @@ -95,7 +95,7 @@ static int ics932s401_detect(struct i2c_client *client, static void ics932s401_remove(struct i2c_client *client); static const struct i2c_device_id ics932s401_id[] = { - { "ics932s401", 0 }, + { "ics932s401" }, { } }; MODULE_DEVICE_TABLE(i2c, ics932s401_id); diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index ebf0635aee64..9f26db467a81 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -449,7 +449,7 @@ static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id isl29003_id[] = { - { "isl29003", 0 }, + { "isl29003" }, {} }; MODULE_DEVICE_TABLE(i2c, isl29003_id); diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index c5976fa8c825..1643ba2ff964 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -177,7 +177,7 @@ static void isl29020_remove(struct i2c_client *client) } static const struct i2c_device_id isl29020_id[] = { - { "isl29020", 0 }, + { "isl29020" }, { } }; diff --git a/drivers/misc/keba/Kconfig b/drivers/misc/keba/Kconfig new file mode 100644 index 000000000000..5fbcbc2252ac --- /dev/null +++ b/drivers/misc/keba/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +config KEBA_CP500 + tristate "KEBA CP500 system FPGA support" + depends on PCI + select AUXILIARY_BUS + help + This driver supports the KEBA CP500 system FPGA, which is used in + KEBA CP500 devices. It registers all sub devices present on the CP500 + system FPGA as separate devices. A driver is needed for each sub + device. + + This driver can also be built as a module. If so, the module will be + called cp500. diff --git a/drivers/misc/keba/Makefile b/drivers/misc/keba/Makefile new file mode 100644 index 000000000000..0a8b846cda7d --- /dev/null +++ b/drivers/misc/keba/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_KEBA_CP500) += cp500.o diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c new file mode 100644 index 000000000000..9ba46f0f9392 --- /dev/null +++ b/drivers/misc/keba/cp500.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) KEBA Industrial Automation Gmbh 2024 + * + * Driver for KEBA system FPGA + * + * The KEBA system FPGA implements various devices. This driver registers + * auxiliary devices for every device within the FPGA. + */ + +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/misc/keba.h> +#include <linux/module.h> +#include <linux/pci.h> + +#define CP500 "cp500" + +#define PCI_VENDOR_ID_KEBA 0xCEBA +#define PCI_DEVICE_ID_KEBA_CP035 0x2706 +#define PCI_DEVICE_ID_KEBA_CP505 0x2703 +#define PCI_DEVICE_ID_KEBA_CP520 0x2696 + +#define CP500_SYS_BAR 0 +#define CP500_ECM_BAR 1 + +/* BAR 0 registers */ +#define CP500_VERSION_REG 0x00 +#define CP500_RECONFIG_REG 0x11 /* upper 8-bits of STARTUP register */ +#define CP500_AXI_REG 0x40 + +/* Bits in BUILD_REG */ +#define CP500_BUILD_TEST 0x8000 /* FPGA test version */ + +/* Bits in RECONFIG_REG */ +#define CP500_RECFG_REQ 0x01 /* reconfigure FPGA on next reset */ + +/* MSIX */ +#define CP500_AXI_MSIX 3 +#define CP500_NUM_MSIX 8 +#define CP500_NUM_MSIX_NO_MMI 2 +#define CP500_NUM_MSIX_NO_AXI 3 + +/* EEPROM */ +#define CP500_HW_CPU_EEPROM_NAME "cp500_cpu_eeprom" + +#define CP500_IS_CP035(dev) ((dev)->pci_dev->device == PCI_DEVICE_ID_KEBA_CP035) +#define CP500_IS_CP505(dev) ((dev)->pci_dev->device == PCI_DEVICE_ID_KEBA_CP505) +#define CP500_IS_CP520(dev) ((dev)->pci_dev->device == PCI_DEVICE_ID_KEBA_CP520) + +struct cp500_dev_info { + off_t offset; + size_t size; +}; + +struct cp500_devs { + struct cp500_dev_info startup; + struct cp500_dev_info i2c; +}; + +/* list of devices within FPGA of CP035 family (CP035, CP056, CP057) */ +static struct cp500_devs cp035_devices = { + .startup = { 0x0000, SZ_4K }, + .i2c = { 0x4000, SZ_4K }, +}; + +/* list of devices within FPGA of CP505 family (CP503, CP505, CP507) */ +static struct cp500_devs cp505_devices = { + .startup = { 0x0000, SZ_4K }, + .i2c = { 0x5000, SZ_4K }, +}; + +/* list of devices within FPGA of CP520 family (CP520, CP530) */ +static struct cp500_devs cp520_devices = { + .startup = { 0x0000, SZ_4K }, + .i2c = { 0x5000, SZ_4K }, +}; + +struct cp500 { + struct pci_dev *pci_dev; + struct cp500_devs *devs; + int msix_num; + struct { + int major; + int minor; + int build; + } version; + + /* system FPGA BAR */ + resource_size_t sys_hwbase; + struct keba_i2c_auxdev *i2c; + + /* ECM EtherCAT BAR */ + resource_size_t ecm_hwbase; + + void __iomem *system_startup_addr; +}; + +/* I2C devices */ +static struct i2c_board_info cp500_i2c_info[] = { + { /* temperature sensor */ + I2C_BOARD_INFO("emc1403", 0x4c), + }, + { /* + * CPU EEPROM + * CP035 family: CPU board + * CP505 family: bridge board + * CP520 family: carrier board + */ + I2C_BOARD_INFO("24c32", 0x50), + .dev_name = CP500_HW_CPU_EEPROM_NAME, + }, + { /* interface board EEPROM */ + I2C_BOARD_INFO("24c32", 0x51), + }, + { /* + * EEPROM (optional) + * CP505 family: CPU board + * CP520 family: MMI board + */ + I2C_BOARD_INFO("24c32", 0x52), + }, + { /* extension module 0 EEPROM (optional) */ + I2C_BOARD_INFO("24c32", 0x53), + }, + { /* extension module 1 EEPROM (optional) */ + I2C_BOARD_INFO("24c32", 0x54), + }, + { /* extension module 2 EEPROM (optional) */ + I2C_BOARD_INFO("24c32", 0x55), + }, + { /* extension module 3 EEPROM (optional) */ + I2C_BOARD_INFO("24c32", 0x56), + } +}; + +static ssize_t cp500_get_fpga_version(struct cp500 *cp500, char *buf, + size_t max_len) +{ + int n; + + if (CP500_IS_CP035(cp500)) + n = scnprintf(buf, max_len, "CP035"); + else if (CP500_IS_CP505(cp500)) + n = scnprintf(buf, max_len, "CP505"); + else + n = scnprintf(buf, max_len, "CP500"); + + n += scnprintf(buf + n, max_len - n, "_FPGA_%d.%02d", + cp500->version.major, cp500->version.minor); + + /* test versions have test bit set */ + if (cp500->version.build & CP500_BUILD_TEST) + n += scnprintf(buf + n, max_len - n, "Test%d", + cp500->version.build & ~CP500_BUILD_TEST); + + n += scnprintf(buf + n, max_len - n, "\n"); + + return n; +} + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cp500 *cp500 = dev_get_drvdata(dev); + + return cp500_get_fpga_version(cp500, buf, PAGE_SIZE); +} +static DEVICE_ATTR_RO(version); + +static ssize_t keep_cfg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct cp500 *cp500 = dev_get_drvdata(dev); + unsigned long keep_cfg = 1; + + /* + * FPGA configuration stream is kept during reset when RECONFIG bit is + * zero + */ + if (ioread8(cp500->system_startup_addr + CP500_RECONFIG_REG) & + CP500_RECFG_REQ) + keep_cfg = 0; + + return sysfs_emit(buf, "%lu\n", keep_cfg); +} + +static ssize_t keep_cfg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cp500 *cp500 = dev_get_drvdata(dev); + unsigned long keep_cfg; + + if (kstrtoul(buf, 10, &keep_cfg) < 0) + return -EINVAL; + + /* + * In normal operation "keep_cfg" is "1". This means that the FPGA keeps + * its configuration stream during a reset. + * In case of a firmware update of the FPGA, the configuration stream + * needs to be reloaded. This can be done without a powercycle by + * writing a "0" into the "keep_cfg" attribute. After a reset/reboot th + * new configuration stream will be loaded. + */ + if (keep_cfg) + iowrite8(0, cp500->system_startup_addr + CP500_RECONFIG_REG); + else + iowrite8(CP500_RECFG_REQ, + cp500->system_startup_addr + CP500_RECONFIG_REG); + + return count; +} +static DEVICE_ATTR_RW(keep_cfg); + +static struct attribute *attrs[] = { + &dev_attr_version.attr, + &dev_attr_keep_cfg.attr, + NULL +}; +static const struct attribute_group attrs_group = { .attrs = attrs }; + +static void cp500_i2c_release(struct device *dev) +{ + struct keba_i2c_auxdev *i2c = + container_of(dev, struct keba_i2c_auxdev, auxdev.dev); + + kfree(i2c); +} + +static int cp500_register_i2c(struct cp500 *cp500) +{ + int retval; + + cp500->i2c = kzalloc(sizeof(*cp500->i2c), GFP_KERNEL); + if (!cp500->i2c) + return -ENOMEM; + + cp500->i2c->auxdev.name = "i2c"; + cp500->i2c->auxdev.id = 0; + cp500->i2c->auxdev.dev.release = cp500_i2c_release; + cp500->i2c->auxdev.dev.parent = &cp500->pci_dev->dev; + cp500->i2c->io = (struct resource) { + /* I2C register area */ + .start = (resource_size_t) cp500->sys_hwbase + + cp500->devs->i2c.offset, + .end = (resource_size_t) cp500->sys_hwbase + + cp500->devs->i2c.offset + + cp500->devs->i2c.size - 1, + .flags = IORESOURCE_MEM, + }; + cp500->i2c->info_size = ARRAY_SIZE(cp500_i2c_info); + cp500->i2c->info = cp500_i2c_info; + + retval = auxiliary_device_init(&cp500->i2c->auxdev); + if (retval) { + kfree(cp500->i2c); + cp500->i2c = NULL; + + return retval; + } + retval = __auxiliary_device_add(&cp500->i2c->auxdev, "keba"); + if (retval) { + auxiliary_device_uninit(&cp500->i2c->auxdev); + cp500->i2c = NULL; + + return retval; + } + + return 0; +} + +static void cp500_register_auxiliary_devs(struct cp500 *cp500) +{ + struct device *dev = &cp500->pci_dev->dev; + + if (cp500_register_i2c(cp500)) + dev_warn(dev, "Failed to register i2c!\n"); +} + +static void cp500_unregister_dev(struct auxiliary_device *auxdev) +{ + auxiliary_device_delete(auxdev); + auxiliary_device_uninit(auxdev); +} + +static void cp500_unregister_auxiliary_devs(struct cp500 *cp500) +{ + + if (cp500->i2c) { + cp500_unregister_dev(&cp500->i2c->auxdev); + cp500->i2c = NULL; + } +} + +static irqreturn_t cp500_axi_handler(int irq, void *dev) +{ + struct cp500 *cp500 = dev; + u32 axi_address = ioread32(cp500->system_startup_addr + CP500_AXI_REG); + + /* + * FPGA signals AXI response error, print AXI address to indicate which + * IP core was affected + */ + dev_err(&cp500->pci_dev->dev, "AXI response error at 0x%08x\n", + axi_address); + + return IRQ_HANDLED; +} + +static int cp500_enable(struct cp500 *cp500) +{ + int axi_irq = -1; + int ret; + + if (cp500->msix_num > CP500_NUM_MSIX_NO_AXI) { + axi_irq = pci_irq_vector(cp500->pci_dev, CP500_AXI_MSIX); + ret = request_irq(axi_irq, cp500_axi_handler, 0, + CP500, cp500); + if (ret != 0) { + dev_err(&cp500->pci_dev->dev, + "Failed to register AXI response error!\n"); + return ret; + } + } + + return 0; +} + +static void cp500_disable(struct cp500 *cp500) +{ + int axi_irq; + + if (cp500->msix_num > CP500_NUM_MSIX_NO_AXI) { + axi_irq = pci_irq_vector(cp500->pci_dev, CP500_AXI_MSIX); + free_irq(axi_irq, cp500); + } +} + +static int cp500_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +{ + struct device *dev = &pci_dev->dev; + struct resource startup; + struct cp500 *cp500; + u32 cp500_vers; + char buf[64]; + int ret; + + cp500 = devm_kzalloc(dev, sizeof(*cp500), GFP_KERNEL); + if (!cp500) + return -ENOMEM; + cp500->pci_dev = pci_dev; + cp500->sys_hwbase = pci_resource_start(pci_dev, CP500_SYS_BAR); + cp500->ecm_hwbase = pci_resource_start(pci_dev, CP500_ECM_BAR); + if (!cp500->sys_hwbase || !cp500->ecm_hwbase) + return -ENODEV; + + if (CP500_IS_CP035(cp500)) + cp500->devs = &cp035_devices; + else if (CP500_IS_CP505(cp500)) + cp500->devs = &cp505_devices; + else if (CP500_IS_CP520(cp500)) + cp500->devs = &cp520_devices; + else + return -ENODEV; + + ret = pci_enable_device(pci_dev); + if (ret) + return ret; + pci_set_master(pci_dev); + + startup = *pci_resource_n(pci_dev, CP500_SYS_BAR); + startup.end = startup.start + cp500->devs->startup.size - 1; + cp500->system_startup_addr = devm_ioremap_resource(&pci_dev->dev, + &startup); + if (IS_ERR(cp500->system_startup_addr)) { + ret = PTR_ERR(cp500->system_startup_addr); + goto out_disable; + } + + cp500->msix_num = pci_alloc_irq_vectors(pci_dev, CP500_NUM_MSIX_NO_MMI, + CP500_NUM_MSIX, PCI_IRQ_MSIX); + if (cp500->msix_num < CP500_NUM_MSIX_NO_MMI) { + dev_err(&pci_dev->dev, + "Hardware does not support enough MSI-X interrupts\n"); + ret = -ENODEV; + goto out_disable; + } + + cp500_vers = ioread32(cp500->system_startup_addr + CP500_VERSION_REG); + cp500->version.major = (cp500_vers & 0xff); + cp500->version.minor = (cp500_vers >> 8) & 0xff; + cp500->version.build = (cp500_vers >> 16) & 0xffff; + cp500_get_fpga_version(cp500, buf, sizeof(buf)); + + dev_info(&pci_dev->dev, "FPGA version %s", buf); + + pci_set_drvdata(pci_dev, cp500); + + ret = sysfs_create_group(&pci_dev->dev.kobj, &attrs_group); + if (ret != 0) + goto out_free_irq; + + ret = cp500_enable(cp500); + if (ret != 0) + goto out_remove_group; + + cp500_register_auxiliary_devs(cp500); + + return 0; + +out_remove_group: + sysfs_remove_group(&pci_dev->dev.kobj, &attrs_group); +out_free_irq: + pci_free_irq_vectors(pci_dev); +out_disable: + pci_clear_master(pci_dev); + pci_disable_device(pci_dev); + + return ret; +} + +static void cp500_remove(struct pci_dev *pci_dev) +{ + struct cp500 *cp500 = pci_get_drvdata(pci_dev); + + cp500_unregister_auxiliary_devs(cp500); + + cp500_disable(cp500); + + sysfs_remove_group(&pci_dev->dev.kobj, &attrs_group); + + pci_set_drvdata(pci_dev, 0); + + pci_free_irq_vectors(pci_dev); + + pci_clear_master(pci_dev); + pci_disable_device(pci_dev); +} + +static struct pci_device_id cp500_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_KEBA, PCI_DEVICE_ID_KEBA_CP035) }, + { PCI_DEVICE(PCI_VENDOR_ID_KEBA, PCI_DEVICE_ID_KEBA_CP505) }, + { PCI_DEVICE(PCI_VENDOR_ID_KEBA, PCI_DEVICE_ID_KEBA_CP520) }, + { } +}; +MODULE_DEVICE_TABLE(pci, cp500_ids); + +static struct pci_driver cp500_driver = { + .name = CP500, + .id_table = cp500_ids, + .probe = cp500_probe, + .remove = cp500_remove, +}; +module_pci_driver(cp500_driver); + +MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>"); +MODULE_DESCRIPTION("KEBA CP500 system FPGA driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 2733070acf39..9eebeffcd8fd 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -80,6 +80,8 @@ static void whitelist(struct mei_cl_device *cldev) cldev->do_match = 1; } +#define MKHI_SEND_MAX_TIMEOUT_MSEC 4000 + #define OSTYPE_LINUX 2 struct mei_os_ver { __le16 build; @@ -128,7 +130,7 @@ static int mei_osver(struct mei_cl_device *cldev) os_ver = (struct mei_os_ver *)fwcaps->data; os_ver->os_type = OSTYPE_LINUX; - return __mei_cl_send(cldev->cl, buf, size, 0, mode); + return __mei_cl_send_timeout(cldev->cl, buf, size, 0, mode, MKHI_SEND_MAX_TIMEOUT_MSEC); } #define MKHI_FWVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \ @@ -148,8 +150,8 @@ static int mei_fwver(struct mei_cl_device *cldev) req.hdr.group_id = MKHI_GEN_GROUP_ID; req.hdr.command = MKHI_GEN_GET_FW_VERSION_CMD; - ret = __mei_cl_send(cldev->cl, (u8 *)&req, sizeof(req), 0, - MEI_CL_IO_TX_BLOCKING); + ret = __mei_cl_send_timeout(cldev->cl, (u8 *)&req, sizeof(req), 0, + MEI_CL_IO_TX_BLOCKING, MKHI_SEND_MAX_TIMEOUT_MSEC); if (ret < 0) { dev_info(&cldev->dev, "Could not send ReqFWVersion cmd ret = %d\n", ret); return ret; diff --git a/drivers/misc/mrvl_cn10k_dpi.c b/drivers/misc/mrvl_cn10k_dpi.c new file mode 100644 index 000000000000..7d5433121ff6 --- /dev/null +++ b/drivers/misc/mrvl_cn10k_dpi.c @@ -0,0 +1,676 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell Octeon CN10K DPI driver + * + * Copyright (C) 2024 Marvell. + * + */ + +#include <linux/bitfield.h> +#include <linux/compat.h> +#include <linux/delay.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/irq.h> +#include <linux/interrupt.h> + +#include <uapi/misc/mrvl_cn10k_dpi.h> + +/* PCI device IDs */ +#define PCI_DEVID_MRVL_CN10K_DPI_PF 0xA080 +#define PCI_SUBDEVID_MRVL_CN10K_DPI_PF 0xB900 + +/* PCI BAR Number */ +#define PCI_DPI_CFG_BAR 0 + +/* MSI-X interrupts */ +#define DPI_MAX_REQQ_INT 0x20 +#define DPI_MAX_CC_INT 0x40 + +/* MBOX MSI-X interrupt vector index */ +#define DPI_MBOX_PF_VF_INT_IDX 0x75 + +#define DPI_MAX_IRQS (DPI_MBOX_PF_VF_INT_IDX + 1) + +#define DPI_MAX_VFS 0x20 + +#define DPI_MAX_ENG_FIFO_SZ 0x20 +#define DPI_MAX_ENG_MOLR 0x400 + +#define DPI_DMA_IDS_DMA_NPA_PF_FUNC(x) FIELD_PREP(GENMASK_ULL(31, 16), x) +#define DPI_DMA_IDS_INST_STRM(x) FIELD_PREP(GENMASK_ULL(47, 40), x) +#define DPI_DMA_IDS_DMA_STRM(x) FIELD_PREP(GENMASK_ULL(39, 32), x) +#define DPI_DMA_ENG_EN_MOLR(x) FIELD_PREP(GENMASK_ULL(41, 32), x) +#define DPI_EBUS_PORTX_CFG_MPS(x) FIELD_PREP(GENMASK(6, 4), x) +#define DPI_DMA_IDS_DMA_SSO_PF_FUNC(x) FIELD_PREP(GENMASK(15, 0), x) +#define DPI_DMA_IDS2_INST_AURA(x) FIELD_PREP(GENMASK(19, 0), x) +#define DPI_DMA_IBUFF_CSIZE_CSIZE(x) FIELD_PREP(GENMASK(13, 0), x) +#define DPI_EBUS_PORTX_CFG_MRRS(x) FIELD_PREP(GENMASK(2, 0), x) +#define DPI_ENG_BUF_BLKS(x) FIELD_PREP(GENMASK(5, 0), x) +#define DPI_DMA_CONTROL_DMA_ENB GENMASK_ULL(53, 48) + +#define DPI_DMA_CONTROL_O_MODE BIT_ULL(14) +#define DPI_DMA_CONTROL_LDWB BIT_ULL(32) +#define DPI_DMA_CONTROL_WQECSMODE1 BIT_ULL(37) +#define DPI_DMA_CONTROL_ZBWCSEN BIT_ULL(39) +#define DPI_DMA_CONTROL_WQECSOFF(ofst) (((u64)ofst) << 40) +#define DPI_DMA_CONTROL_WQECSDIS BIT_ULL(47) +#define DPI_DMA_CONTROL_PKT_EN BIT_ULL(56) +#define DPI_DMA_IBUFF_CSIZE_NPA_FREE BIT(16) + +#define DPI_CTL_EN BIT_ULL(0) +#define DPI_DMA_CC_INT BIT_ULL(0) +#define DPI_DMA_QRST BIT_ULL(0) + +#define DPI_REQQ_INT_INSTRFLT BIT_ULL(0) +#define DPI_REQQ_INT_RDFLT BIT_ULL(1) +#define DPI_REQQ_INT_WRFLT BIT_ULL(2) +#define DPI_REQQ_INT_CSFLT BIT_ULL(3) +#define DPI_REQQ_INT_INST_DBO BIT_ULL(4) +#define DPI_REQQ_INT_INST_ADDR_NULL BIT_ULL(5) +#define DPI_REQQ_INT_INST_FILL_INVAL BIT_ULL(6) +#define DPI_REQQ_INT_INSTR_PSN BIT_ULL(7) + +#define DPI_REQQ_INT \ + (DPI_REQQ_INT_INSTRFLT | \ + DPI_REQQ_INT_RDFLT | \ + DPI_REQQ_INT_WRFLT | \ + DPI_REQQ_INT_CSFLT | \ + DPI_REQQ_INT_INST_DBO | \ + DPI_REQQ_INT_INST_ADDR_NULL | \ + DPI_REQQ_INT_INST_FILL_INVAL | \ + DPI_REQQ_INT_INSTR_PSN) + +#define DPI_PF_RAS_EBI_DAT_PSN BIT_ULL(0) +#define DPI_PF_RAS_NCB_DAT_PSN BIT_ULL(1) +#define DPI_PF_RAS_NCB_CMD_PSN BIT_ULL(2) + +#define DPI_PF_RAS_INT \ + (DPI_PF_RAS_EBI_DAT_PSN | \ + DPI_PF_RAS_NCB_DAT_PSN | \ + DPI_PF_RAS_NCB_CMD_PSN) + +/* Message fields in word_l of DPI mailbox structure */ +#define DPI_MBOX_VFID(msg) FIELD_GET(GENMASK_ULL(7, 0), msg) +#define DPI_MBOX_CMD(msg) FIELD_GET(GENMASK_ULL(11, 8), msg) +#define DPI_MBOX_CBUF_SIZE(msg) FIELD_GET(GENMASK_ULL(27, 12), msg) +#define DPI_MBOX_CBUF_AURA(msg) FIELD_GET(GENMASK_ULL(47, 28), msg) +#define DPI_MBOX_SSO_PFFUNC(msg) FIELD_GET(GENMASK_ULL(63, 48), msg) + +/* Message fields in word_h of DPI mailbox structure */ +#define DPI_MBOX_NPA_PFFUNC(msg) FIELD_GET(GENMASK_ULL(15, 0), msg) +#define DPI_MBOX_WQES_COMPL(msg) FIELD_GET(GENMASK_ULL(16, 16), msg) +#define DPI_MBOX_WQES_OFFSET(msg) FIELD_GET(GENMASK_ULL(23, 17), msg) + +#define DPI_DMAX_IBUFF_CSIZE(x) (0x0ULL | ((x) << 11)) +#define DPI_DMAX_IDS(x) (0x18ULL | ((x) << 11)) +#define DPI_DMAX_IDS2(x) (0x20ULL | ((x) << 11)) +#define DPI_DMAX_QRST(x) (0x30ULL | ((x) << 11)) + +#define DPI_CTL 0x10010ULL +#define DPI_DMA_CONTROL 0x10018ULL +#define DPI_PF_RAS 0x10308ULL +#define DPI_PF_RAS_ENA_W1C 0x10318ULL +#define DPI_MBOX_VF_PF_INT 0x16300ULL +#define DPI_MBOX_VF_PF_INT_W1S 0x16308ULL +#define DPI_MBOX_VF_PF_INT_ENA_W1C 0x16310ULL +#define DPI_MBOX_VF_PF_INT_ENA_W1S 0x16318ULL + +#define DPI_DMA_ENGX_EN(x) (0x10040ULL | ((x) << 3)) +#define DPI_ENGX_BUF(x) (0x100C0ULL | ((x) << 3)) +#define DPI_EBUS_PORTX_CFG(x) (0x10100ULL | ((x) << 3)) +#define DPI_DMA_CCX_INT(x) (0x11000ULL | ((x) << 3)) +#define DPI_DMA_CCX_INT_ENA_W1C(x) (0x11800ULL | ((x) << 3)) +#define DPI_REQQX_INT(x) (0x12C00ULL | ((x) << 5)) +#define DPI_REQQX_INT_ENA_W1C(x) (0x13800ULL | ((x) << 5)) +#define DPI_MBOX_PF_VF_DATA0(x) (0x16000ULL | ((x) << 4)) +#define DPI_MBOX_PF_VF_DATA1(x) (0x16008ULL | ((x) << 4)) + +#define DPI_WCTL_FIF_THR 0x17008ULL + +#define DPI_EBUS_MAX_PORTS 2 + +#define DPI_EBUS_MRRS_MIN 128 +#define DPI_EBUS_MRRS_MAX 1024 +#define DPI_EBUS_MPS_MIN 128 +#define DPI_EBUS_MPS_MAX 1024 +#define DPI_WCTL_FIFO_THRESHOLD 0x30 + +#define DPI_QUEUE_OPEN 0x1 +#define DPI_QUEUE_CLOSE 0x2 +#define DPI_REG_DUMP 0x3 +#define DPI_GET_REG_CFG 0x4 +#define DPI_QUEUE_OPEN_V2 0x5 + +enum dpi_mbox_rsp_type { + DPI_MBOX_TYPE_CMD, + DPI_MBOX_TYPE_RSP_ACK, + DPI_MBOX_TYPE_RSP_NACK, +}; + +struct dpivf_config { + u32 aura; + u16 csize; + u16 sso_pf_func; + u16 npa_pf_func; +}; + +struct dpipf_vf { + struct dpivf_config vf_config; + bool setup_done; + u8 this_vfid; +}; + +/* DPI device mailbox */ +struct dpi_mbox { + struct work_struct work; + /* lock to serialize mbox requests */ + struct mutex lock; + struct dpipf *pf; + u8 __iomem *pf_vf_data_reg; + u8 __iomem *vf_pf_data_reg; +}; + +struct dpipf { + struct miscdevice miscdev; + void __iomem *reg_base; + struct pci_dev *pdev; + struct dpipf_vf vf[DPI_MAX_VFS]; + /* Mailbox to talk to VFs */ + struct dpi_mbox *mbox[DPI_MAX_VFS]; +}; + +struct dpi_mbox_message { + uint64_t word_l; + uint64_t word_h; +}; + +static inline void dpi_reg_write(struct dpipf *dpi, u64 offset, u64 val) +{ + writeq(val, dpi->reg_base + offset); +} + +static inline u64 dpi_reg_read(struct dpipf *dpi, u64 offset) +{ + return readq(dpi->reg_base + offset); +} + +static void dpi_wqe_cs_offset(struct dpipf *dpi, u8 offset) +{ + u64 reg; + + reg = dpi_reg_read(dpi, DPI_DMA_CONTROL); + reg &= ~DPI_DMA_CONTROL_WQECSDIS; + reg |= DPI_DMA_CONTROL_ZBWCSEN | DPI_DMA_CONTROL_WQECSMODE1; + reg |= DPI_DMA_CONTROL_WQECSOFF(offset); + dpi_reg_write(dpi, DPI_DMA_CONTROL, reg); +} + +static int dpi_queue_init(struct dpipf *dpi, struct dpipf_vf *dpivf, u8 vf) +{ + u16 sso_pf_func = dpivf->vf_config.sso_pf_func; + u16 npa_pf_func = dpivf->vf_config.npa_pf_func; + u16 csize = dpivf->vf_config.csize; + u32 aura = dpivf->vf_config.aura; + unsigned long timeout; + u64 reg; + + dpi_reg_write(dpi, DPI_DMAX_QRST(vf), DPI_DMA_QRST); + + /* Wait for a maximum of 3 sec */ + timeout = jiffies + msecs_to_jiffies(3000); + while (!time_after(jiffies, timeout)) { + reg = dpi_reg_read(dpi, DPI_DMAX_QRST(vf)); + if (!(reg & DPI_DMA_QRST)) + break; + + /* Reset would take time for the request cache to drain */ + usleep_range(500, 1000); + } + + if (reg & DPI_DMA_QRST) { + dev_err(&dpi->pdev->dev, "Queue reset failed\n"); + return -EBUSY; + } + + dpi_reg_write(dpi, DPI_DMAX_IDS2(vf), 0); + dpi_reg_write(dpi, DPI_DMAX_IDS(vf), 0); + + reg = DPI_DMA_IBUFF_CSIZE_CSIZE(csize) | DPI_DMA_IBUFF_CSIZE_NPA_FREE; + dpi_reg_write(dpi, DPI_DMAX_IBUFF_CSIZE(vf), reg); + + reg = dpi_reg_read(dpi, DPI_DMAX_IDS2(vf)); + reg |= DPI_DMA_IDS2_INST_AURA(aura); + dpi_reg_write(dpi, DPI_DMAX_IDS2(vf), reg); + + reg = dpi_reg_read(dpi, DPI_DMAX_IDS(vf)); + reg |= DPI_DMA_IDS_DMA_NPA_PF_FUNC(npa_pf_func); + reg |= DPI_DMA_IDS_DMA_SSO_PF_FUNC(sso_pf_func); + reg |= DPI_DMA_IDS_DMA_STRM(vf + 1); + reg |= DPI_DMA_IDS_INST_STRM(vf + 1); + dpi_reg_write(dpi, DPI_DMAX_IDS(vf), reg); + + return 0; +} + +static void dpi_queue_fini(struct dpipf *dpi, u8 vf) +{ + dpi_reg_write(dpi, DPI_DMAX_QRST(vf), DPI_DMA_QRST); + + /* Reset IDS and IDS2 registers */ + dpi_reg_write(dpi, DPI_DMAX_IDS2(vf), 0); + dpi_reg_write(dpi, DPI_DMAX_IDS(vf), 0); +} + +static irqreturn_t dpi_mbox_intr_handler(int irq, void *data) +{ + struct dpipf *dpi = data; + u64 reg; + u32 vf; + + reg = dpi_reg_read(dpi, DPI_MBOX_VF_PF_INT); + if (reg) { + for (vf = 0; vf < pci_num_vf(dpi->pdev); vf++) { + if (reg & BIT_ULL(vf)) + schedule_work(&dpi->mbox[vf]->work); + } + dpi_reg_write(dpi, DPI_MBOX_VF_PF_INT, reg); + } + + return IRQ_HANDLED; +} + +static int queue_config(struct dpipf *dpi, struct dpipf_vf *dpivf, struct dpi_mbox_message *msg) +{ + int ret = 0; + + switch (DPI_MBOX_CMD(msg->word_l)) { + case DPI_QUEUE_OPEN: + case DPI_QUEUE_OPEN_V2: + dpivf->vf_config.aura = DPI_MBOX_CBUF_AURA(msg->word_l); + dpivf->vf_config.csize = DPI_MBOX_CMD(msg->word_l) == DPI_QUEUE_OPEN ? + DPI_MBOX_CBUF_SIZE(msg->word_l) >> 3 : + DPI_MBOX_CBUF_SIZE(msg->word_l); + dpivf->vf_config.sso_pf_func = DPI_MBOX_SSO_PFFUNC(msg->word_l); + dpivf->vf_config.npa_pf_func = DPI_MBOX_NPA_PFFUNC(msg->word_h); + ret = dpi_queue_init(dpi, dpivf, DPI_MBOX_VFID(msg->word_l)); + if (!ret) { + if (DPI_MBOX_WQES_COMPL(msg->word_h)) + dpi_wqe_cs_offset(dpi, DPI_MBOX_WQES_OFFSET(msg->word_h)); + dpivf->setup_done = true; + } + break; + case DPI_QUEUE_CLOSE: + memset(&dpivf->vf_config, 0, sizeof(struct dpivf_config)); + dpi_queue_fini(dpi, DPI_MBOX_VFID(msg->word_l)); + dpivf->setup_done = false; + break; + default: + return -EINVAL; + } + + return ret; +} + +static void dpi_pfvf_mbox_work(struct work_struct *work) +{ + struct dpi_mbox *mbox = container_of(work, struct dpi_mbox, work); + struct dpi_mbox_message msg; + struct dpipf_vf *dpivf; + struct dpipf *dpi; + int vfid, ret; + + dpi = mbox->pf; + memset(&msg, 0, sizeof(msg)); + + mutex_lock(&mbox->lock); + msg.word_l = readq(mbox->vf_pf_data_reg); + if (msg.word_l == (u64)-1) + goto exit; + + vfid = DPI_MBOX_VFID(msg.word_l); + if (vfid >= pci_num_vf(dpi->pdev)) + goto exit; + + dpivf = &dpi->vf[vfid]; + msg.word_h = readq(mbox->pf_vf_data_reg); + + ret = queue_config(dpi, dpivf, &msg); + if (ret < 0) + writeq(DPI_MBOX_TYPE_RSP_NACK, mbox->pf_vf_data_reg); + else + writeq(DPI_MBOX_TYPE_RSP_ACK, mbox->pf_vf_data_reg); +exit: + mutex_unlock(&mbox->lock); +} + +/* Setup registers for a PF mailbox */ +static void dpi_setup_mbox_regs(struct dpipf *dpi, int vf) +{ + struct dpi_mbox *mbox = dpi->mbox[vf]; + + mbox->pf_vf_data_reg = dpi->reg_base + DPI_MBOX_PF_VF_DATA0(vf); + mbox->vf_pf_data_reg = dpi->reg_base + DPI_MBOX_PF_VF_DATA1(vf); +} + +static int dpi_pfvf_mbox_setup(struct dpipf *dpi) +{ + int vf; + + for (vf = 0; vf < DPI_MAX_VFS; vf++) { + dpi->mbox[vf] = devm_kzalloc(&dpi->pdev->dev, sizeof(*dpi->mbox[vf]), GFP_KERNEL); + + if (!dpi->mbox[vf]) + return -ENOMEM; + + mutex_init(&dpi->mbox[vf]->lock); + INIT_WORK(&dpi->mbox[vf]->work, dpi_pfvf_mbox_work); + dpi->mbox[vf]->pf = dpi; + dpi_setup_mbox_regs(dpi, vf); + } + + return 0; +} + +static void dpi_pfvf_mbox_destroy(struct dpipf *dpi) +{ + unsigned int vf; + + for (vf = 0; vf < DPI_MAX_VFS; vf++) { + if (work_pending(&dpi->mbox[vf]->work)) + cancel_work_sync(&dpi->mbox[vf]->work); + + dpi->mbox[vf] = NULL; + } +} + +static void dpi_init(struct dpipf *dpi) +{ + unsigned int engine, port; + u8 mrrs_val, mps_val; + u64 reg; + + for (engine = 0; engine < DPI_MAX_ENGINES; engine++) { + if (engine == 4 || engine == 5) + reg = DPI_ENG_BUF_BLKS(16); + else + reg = DPI_ENG_BUF_BLKS(8); + + dpi_reg_write(dpi, DPI_ENGX_BUF(engine), reg); + } + + reg = DPI_DMA_CONTROL_ZBWCSEN | DPI_DMA_CONTROL_PKT_EN | DPI_DMA_CONTROL_LDWB | + DPI_DMA_CONTROL_O_MODE | DPI_DMA_CONTROL_DMA_ENB; + + dpi_reg_write(dpi, DPI_DMA_CONTROL, reg); + dpi_reg_write(dpi, DPI_CTL, DPI_CTL_EN); + + mrrs_val = 2; /* 512B */ + mps_val = 1; /* 256B */ + + for (port = 0; port < DPI_EBUS_MAX_PORTS; port++) { + reg = dpi_reg_read(dpi, DPI_EBUS_PORTX_CFG(port)); + reg &= ~(DPI_EBUS_PORTX_CFG_MRRS(7) | DPI_EBUS_PORTX_CFG_MPS(7)); + reg |= DPI_EBUS_PORTX_CFG_MPS(mps_val) | DPI_EBUS_PORTX_CFG_MRRS(mrrs_val); + dpi_reg_write(dpi, DPI_EBUS_PORTX_CFG(port), reg); + } + + dpi_reg_write(dpi, DPI_WCTL_FIF_THR, DPI_WCTL_FIFO_THRESHOLD); +} + +static void dpi_fini(struct dpipf *dpi) +{ + unsigned int engine; + + for (engine = 0; engine < DPI_MAX_ENGINES; engine++) + dpi_reg_write(dpi, DPI_ENGX_BUF(engine), 0); + + dpi_reg_write(dpi, DPI_DMA_CONTROL, 0); + dpi_reg_write(dpi, DPI_CTL, 0); +} + +static void dpi_free_irq_vectors(void *pdev) +{ + pci_free_irq_vectors((struct pci_dev *)pdev); +} + +static int dpi_irq_init(struct dpipf *dpi) +{ + struct pci_dev *pdev = dpi->pdev; + struct device *dev = &pdev->dev; + int i, ret; + + /* Clear all RAS interrupts */ + dpi_reg_write(dpi, DPI_PF_RAS, DPI_PF_RAS_INT); + + /* Clear all RAS interrupt enable bits */ + dpi_reg_write(dpi, DPI_PF_RAS_ENA_W1C, DPI_PF_RAS_INT); + + for (i = 0; i < DPI_MAX_REQQ_INT; i++) { + dpi_reg_write(dpi, DPI_REQQX_INT(i), DPI_REQQ_INT); + dpi_reg_write(dpi, DPI_REQQX_INT_ENA_W1C(i), DPI_REQQ_INT); + } + + for (i = 0; i < DPI_MAX_CC_INT; i++) { + dpi_reg_write(dpi, DPI_DMA_CCX_INT(i), DPI_DMA_CC_INT); + dpi_reg_write(dpi, DPI_DMA_CCX_INT_ENA_W1C(i), DPI_DMA_CC_INT); + } + + ret = pci_alloc_irq_vectors(pdev, DPI_MAX_IRQS, DPI_MAX_IRQS, PCI_IRQ_MSIX); + if (ret != DPI_MAX_IRQS) { + dev_err(dev, "DPI: Failed to alloc %d msix irqs\n", DPI_MAX_IRQS); + return ret; + } + + ret = devm_add_action_or_reset(dev, dpi_free_irq_vectors, pdev); + if (ret) { + dev_err(dev, "DPI: Failed to add irq free action\n"); + return ret; + } + + ret = devm_request_irq(dev, pci_irq_vector(pdev, DPI_MBOX_PF_VF_INT_IDX), + dpi_mbox_intr_handler, 0, "dpi-mbox", dpi); + if (ret) { + dev_err(dev, "DPI: request_irq failed for mbox; err=%d\n", ret); + return ret; + } + + dpi_reg_write(dpi, DPI_MBOX_VF_PF_INT_ENA_W1S, GENMASK_ULL(31, 0)); + + return 0; +} + +static int dpi_mps_mrrs_config(struct dpipf *dpi, void __user *arg) +{ + struct dpi_mps_mrrs_cfg cfg; + u8 mrrs_val, mps_val; + u64 reg; + + if (copy_from_user(&cfg, arg, sizeof(struct dpi_mps_mrrs_cfg))) + return -EFAULT; + + if (cfg.max_read_req_sz < DPI_EBUS_MRRS_MIN || cfg.max_read_req_sz > DPI_EBUS_MRRS_MAX || + !is_power_of_2(cfg.max_read_req_sz)) + return -EINVAL; + + if (cfg.max_payload_sz < DPI_EBUS_MPS_MIN || cfg.max_payload_sz > DPI_EBUS_MPS_MAX || + !is_power_of_2(cfg.max_payload_sz)) + return -EINVAL; + + if (cfg.port >= DPI_EBUS_MAX_PORTS) + return -EINVAL; + + /* Make sure reserved fields are set to 0 */ + if (cfg.reserved) + return -EINVAL; + + mrrs_val = fls(cfg.max_read_req_sz >> 8); + mps_val = fls(cfg.max_payload_sz >> 8); + + reg = dpi_reg_read(dpi, DPI_EBUS_PORTX_CFG(cfg.port)); + reg &= ~(DPI_EBUS_PORTX_CFG_MRRS(0x7) | DPI_EBUS_PORTX_CFG_MPS(0x7)); + reg |= DPI_EBUS_PORTX_CFG_MPS(mps_val) | DPI_EBUS_PORTX_CFG_MRRS(mrrs_val); + dpi_reg_write(dpi, DPI_EBUS_PORTX_CFG(cfg.port), reg); + + return 0; +} + +static int dpi_engine_config(struct dpipf *dpi, void __user *arg) +{ + struct dpi_engine_cfg cfg; + unsigned int engine; + u8 *eng_buf; + u64 reg; + + if (copy_from_user(&cfg, arg, sizeof(struct dpi_engine_cfg))) + return -EFAULT; + + /* Make sure reserved fields are set to 0 */ + if (cfg.reserved) + return -EINVAL; + + eng_buf = (u8 *)&cfg.fifo_mask; + + for (engine = 0; engine < DPI_MAX_ENGINES; engine++) { + if (eng_buf[engine] > DPI_MAX_ENG_FIFO_SZ) + return -EINVAL; + dpi_reg_write(dpi, DPI_ENGX_BUF(engine), eng_buf[engine]); + + if (cfg.update_molr) { + if (cfg.molr[engine] > DPI_MAX_ENG_MOLR) + return -EINVAL; + reg = DPI_DMA_ENG_EN_MOLR(cfg.molr[engine]); + dpi_reg_write(dpi, DPI_DMA_ENGX_EN(engine), reg); + } else { + /* Make sure unused fields are set to 0 */ + if (cfg.molr[engine]) + return -EINVAL; + } + } + + return 0; +} + +static long dpi_dev_ioctl(struct file *fptr, unsigned int cmd, unsigned long data) +{ + void __user *arg = (void __user *)data; + struct dpipf *dpi; + int ret; + + dpi = container_of(fptr->private_data, struct dpipf, miscdev); + + switch (cmd) { + case DPI_MPS_MRRS_CFG: + ret = dpi_mps_mrrs_config(dpi, arg); + break; + case DPI_ENGINE_CFG: + ret = dpi_engine_config(dpi, arg); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +static const struct file_operations dpi_device_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dpi_dev_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +static int dpi_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct dpipf *dpi; + int ret; + + dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL); + if (!dpi) + return -ENOMEM; + + dpi->pdev = pdev; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(dev, "DPI: Failed to enable PCI device\n"); + return ret; + } + + ret = pcim_iomap_regions(pdev, BIT(0) | BIT(4), KBUILD_MODNAME); + if (ret) { + dev_err(dev, "DPI: Failed to request MMIO region\n"); + return ret; + } + + dpi->reg_base = pcim_iomap_table(pdev)[PCI_DPI_CFG_BAR]; + + /* Initialize global PF registers */ + dpi_init(dpi); + + /* Setup PF-VF mailbox */ + ret = dpi_pfvf_mbox_setup(dpi); + if (ret) { + dev_err(dev, "DPI: Failed to setup pf-vf mbox\n"); + goto err_dpi_fini; + } + + /* Register interrupts */ + ret = dpi_irq_init(dpi); + if (ret) { + dev_err(dev, "DPI: Failed to initialize irq vectors\n"); + goto err_dpi_mbox_free; + } + + pci_set_drvdata(pdev, dpi); + dpi->miscdev.minor = MISC_DYNAMIC_MINOR; + dpi->miscdev.name = KBUILD_MODNAME; + dpi->miscdev.fops = &dpi_device_fops; + dpi->miscdev.parent = dev; + + ret = misc_register(&dpi->miscdev); + if (ret) { + dev_err(dev, "DPI: Failed to register misc device\n"); + goto err_dpi_mbox_free; + } + + return 0; + +err_dpi_mbox_free: + dpi_pfvf_mbox_destroy(dpi); +err_dpi_fini: + dpi_fini(dpi); + return ret; +} + +static void dpi_remove(struct pci_dev *pdev) +{ + struct dpipf *dpi = pci_get_drvdata(pdev); + + misc_deregister(&dpi->miscdev); + pci_sriov_configure_simple(pdev, 0); + dpi_pfvf_mbox_destroy(dpi); + dpi_fini(dpi); + pci_set_drvdata(pdev, NULL); +} + +static const struct pci_device_id dpi_id_table[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_MRVL_CN10K_DPI_PF, + PCI_VENDOR_ID_CAVIUM, PCI_SUBDEVID_MRVL_CN10K_DPI_PF) }, + { 0, } /* end of table */ +}; + +static struct pci_driver dpi_driver = { + .name = KBUILD_MODNAME, + .id_table = dpi_id_table, + .probe = dpi_probe, + .remove = dpi_remove, + .sriov_configure = pci_sriov_configure_simple, +}; + +module_pci_driver(dpi_driver); +MODULE_DEVICE_TABLE(pci, dpi_id_table); +MODULE_AUTHOR("Marvell."); +MODULE_DESCRIPTION("Marvell Octeon CN10K DPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/open-dice.c b/drivers/misc/open-dice.c index 1e3eb2aa44d9..e6a61e6d9427 100644 --- a/drivers/misc/open-dice.c +++ b/drivers/misc/open-dice.c @@ -201,5 +201,6 @@ static void __exit open_dice_exit(void) module_init(open_dice_init); module_exit(open_dice_exit); +MODULE_DESCRIPTION("Driver for Open Profile for DICE."); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>"); diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index c4f963cf96f2..ff172cf4614d 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -198,7 +198,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) { unsigned short version = 0, chip = 0, min_ver = 0, maj_ver = 0; static const char read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 }; - long timeout; + long time_left; pr_debug("%s", __func__); @@ -208,11 +208,11 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) return -EIO; } - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME)); - if (timeout <= 0) { + if (time_left <= 0) { pr_err(" waiting for ver info- timed out or received signal"); - return timeout ? -ERESTARTSYS : -ETIMEDOUT; + return time_left ? -ERESTARTSYS : -ETIMEDOUT; } reinit_completion(&kim_gdata->kim_rcvd); /* diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 7dd86a9858ab..1d54680d6ed2 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -229,7 +229,7 @@ static int __maybe_unused tifm_7xx1_resume(struct device *dev_d) struct pci_dev *dev = to_pci_dev(dev_d); struct tifm_adapter *fm = pci_get_drvdata(dev); int rc; - unsigned long timeout; + unsigned long time_left; unsigned int good_sockets = 0, bad_sockets = 0; unsigned long flags; /* Maximum number of entries is 4 */ @@ -265,8 +265,8 @@ static int __maybe_unused tifm_7xx1_resume(struct device *dev_d) if (good_sockets) { fm->finish_me = &finish_resume; spin_unlock_irqrestore(&fm->lock, flags); - timeout = wait_for_completion_timeout(&finish_resume, HZ); - dev_dbg(&dev->dev, "wait returned %lu\n", timeout); + time_left = wait_for_completion_timeout(&finish_resume, HZ); + dev_dbg(&dev->dev, "wait returned %lu\n", time_left); writel(TIFM_IRQ_FIFOMASK(good_sockets) | TIFM_IRQ_CARDMASK(good_sockets), fm->addr + FM_CLEAR_INTERRUPT_ENABLE); diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index a3bc2823143e..2ad4387c9837 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -420,7 +420,7 @@ static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id tsl2550_id[] = { - { "tsl2550", 0 }, + { "tsl2550" }, { } }; MODULE_DEVICE_TABLE(i2c, tsl2550_id); diff --git a/drivers/misc/vcpu_stall_detector.c b/drivers/misc/vcpu_stall_detector.c index e2015c87f03f..41b8c2119e20 100644 --- a/drivers/misc/vcpu_stall_detector.c +++ b/drivers/misc/vcpu_stall_detector.c @@ -32,6 +32,7 @@ struct vcpu_stall_detect_config { u32 clock_freq_hz; u32 stall_timeout_sec; + int ppi_irq; void __iomem *membase; struct platform_device *dev; @@ -77,6 +78,12 @@ vcpu_stall_detect_timer_fn(struct hrtimer *hrtimer) return HRTIMER_RESTART; } +static irqreturn_t vcpu_stall_detector_irq(int irq, void *dev) +{ + panic("vCPU stall detector"); + return IRQ_HANDLED; +} + static int start_stall_detector_cpu(unsigned int cpu) { u32 ticks, ping_timeout_ms; @@ -132,7 +139,7 @@ static int stop_stall_detector_cpu(unsigned int cpu) static int vcpu_stall_detect_probe(struct platform_device *pdev) { - int ret; + int ret, irq; struct resource *r; void __iomem *membase; u32 clock_freq_hz = VCPU_STALL_DEFAULT_CLOCK_HZ; @@ -169,9 +176,22 @@ static int vcpu_stall_detect_probe(struct platform_device *pdev) vcpu_stall_config = (struct vcpu_stall_detect_config) { .membase = membase, .clock_freq_hz = clock_freq_hz, - .stall_timeout_sec = stall_timeout_sec + .stall_timeout_sec = stall_timeout_sec, + .ppi_irq = -1, }; + irq = platform_get_irq_optional(pdev, 0); + if (irq > 0) { + ret = request_percpu_irq(irq, + vcpu_stall_detector_irq, + "vcpu_stall_detector", + vcpu_stall_detectors); + if (ret) + goto err; + + vcpu_stall_config.ppi_irq = irq; + } + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "virt/vcpu_stall_detector:online", start_stall_detector_cpu, @@ -184,6 +204,9 @@ static int vcpu_stall_detect_probe(struct platform_device *pdev) vcpu_stall_config.hp_online = ret; return 0; err: + if (vcpu_stall_config.ppi_irq > 0) + free_percpu_irq(vcpu_stall_config.ppi_irq, + vcpu_stall_detectors); return ret; } @@ -193,6 +216,10 @@ static void vcpu_stall_detect_remove(struct platform_device *pdev) cpuhp_remove_state(vcpu_stall_config.hp_online); + if (vcpu_stall_config.ppi_irq > 0) + free_percpu_irq(vcpu_stall_config.ppi_irq, + vcpu_stall_detectors); + for_each_possible_cpu(cpu) stop_stall_detector_cpu(cpu); } |