diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/misc/Makefile | 20 | ||||
-rw-r--r-- | drivers/misc/aspeed-lpc-ctrl.c | 44 | ||||
-rw-r--r-- | drivers/misc/cardreader/rts5260.c | 12 | ||||
-rw-r--r-- | drivers/misc/eeprom/at24.c | 293 | ||||
-rw-r--r-- | drivers/misc/eeprom/at25.c | 2 | ||||
-rw-r--r-- | drivers/misc/lkdtm/Makefile | 20 | ||||
-rw-r--r-- | drivers/misc/lkdtm/bugs.c (renamed from drivers/misc/lkdtm_bugs.c) | 0 | ||||
-rw-r--r-- | drivers/misc/lkdtm/core.c (renamed from drivers/misc/lkdtm_core.c) | 0 | ||||
-rw-r--r-- | drivers/misc/lkdtm/heap.c (renamed from drivers/misc/lkdtm_heap.c) | 0 | ||||
-rw-r--r-- | drivers/misc/lkdtm/lkdtm.h (renamed from drivers/misc/lkdtm.h) | 0 | ||||
-rw-r--r-- | drivers/misc/lkdtm/perms.c (renamed from drivers/misc/lkdtm_perms.c) | 0 | ||||
-rw-r--r-- | drivers/misc/lkdtm/refcount.c (renamed from drivers/misc/lkdtm_refcount.c) | 1 | ||||
-rw-r--r-- | drivers/misc/lkdtm/rodata.c (renamed from drivers/misc/lkdtm_rodata.c) | 0 | ||||
-rw-r--r-- | drivers/misc/lkdtm/usercopy.c (renamed from drivers/misc/lkdtm_usercopy.c) | 0 | ||||
-rw-r--r-- | drivers/misc/mei/bus.c | 83 | ||||
-rw-r--r-- | drivers/misc/mei/client.c | 87 | ||||
-rw-r--r-- | drivers/misc/mei/debugfs.c | 7 | ||||
-rw-r--r-- | drivers/misc/mei/init.c | 1 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 66 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 10 | ||||
-rw-r--r-- | drivers/misc/mic/bus/vop_bus.c | 6 | ||||
-rw-r--r-- | drivers/misc/ocxl/pci.c | 2 |
23 files changed, 408 insertions, 247 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 03605f8fc0dc..5d713008749b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -75,7 +75,6 @@ config ATMEL_TCB_CLKSRC config ATMEL_TCB_CLKSRC_BLOCK int depends on ATMEL_TCB_CLKSRC - prompt "TC Block" if CPU_AT32AP700X default 0 range 0 1 help diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c3c8624f4d95..20be70c3f118 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o obj-$(CONFIG_ICS932S401) += ics932s401.o -obj-$(CONFIG_LKDTM) += lkdtm.o +obj-$(CONFIG_LKDTM) += lkdtm/ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o @@ -57,21 +57,3 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ - -lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o -lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o -lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o -lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o -lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o -lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o -lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o - -KCOV_INSTRUMENT_lkdtm_rodata.o := n - -OBJCOPYFLAGS := -OBJCOPYFLAGS_lkdtm_rodata_objcopy.o := \ - --set-section-flags .text=alloc,readonly \ - --rename-section .text=.rodata -targets += lkdtm_rodata.o lkdtm_rodata_objcopy.o -$(obj)/lkdtm_rodata_objcopy.o: $(obj)/lkdtm_rodata.o FORCE - $(call if_changed,objcopy) diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c index b5439643f54b..a024f8042259 100644 --- a/drivers/misc/aspeed-lpc-ctrl.c +++ b/drivers/misc/aspeed-lpc-ctrl.c @@ -7,6 +7,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/clk.h> #include <linux/mfd/syscon.h> #include <linux/miscdevice.h> #include <linux/mm.h> @@ -20,12 +21,17 @@ #define DEVICE_NAME "aspeed-lpc-ctrl" +#define HICR5 0x0 +#define HICR5_ENL2H BIT(8) +#define HICR5_ENFWH BIT(10) + #define HICR7 0x8 #define HICR8 0xc struct aspeed_lpc_ctrl { struct miscdevice miscdev; struct regmap *regmap; + struct clk *clk; phys_addr_t mem_base; resource_size_t mem_size; u32 pnor_size; @@ -153,8 +159,18 @@ static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd, if (rc) return rc; - return regmap_write(lpc_ctrl->regmap, HICR8, - (~(map.size - 1)) | ((map.size >> 16) - 1)); + rc = regmap_write(lpc_ctrl->regmap, HICR8, + (~(map.size - 1)) | ((map.size >> 16) - 1)); + if (rc) + return rc; + + /* + * Enable LPC FHW cycles. This is required for the host to + * access the regions specified. + */ + return regmap_update_bits(lpc_ctrl->regmap, HICR5, + HICR5_ENFWH | HICR5_ENL2H, + HICR5_ENFWH | HICR5_ENL2H); } return -EINVAL; @@ -221,16 +237,33 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) return -ENODEV; } + lpc_ctrl->clk = devm_clk_get(dev, NULL); + if (IS_ERR(lpc_ctrl->clk)) { + dev_err(dev, "couldn't get clock\n"); + return PTR_ERR(lpc_ctrl->clk); + } + rc = clk_prepare_enable(lpc_ctrl->clk); + if (rc) { + dev_err(dev, "couldn't enable clock\n"); + return rc; + } + lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; lpc_ctrl->miscdev.name = DEVICE_NAME; lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; lpc_ctrl->miscdev.parent = dev; rc = misc_register(&lpc_ctrl->miscdev); - if (rc) + if (rc) { dev_err(dev, "Unable to register device\n"); - else - dev_info(dev, "Loaded at %pr\n", &resm); + goto err; + } + + dev_info(dev, "Loaded at %pr\n", &resm); + + return 0; +err: + clk_disable_unprepare(lpc_ctrl->clk); return rc; } @@ -239,6 +272,7 @@ static int aspeed_lpc_ctrl_remove(struct platform_device *pdev) struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev); misc_deregister(&lpc_ctrl->miscdev); + clk_disable_unprepare(lpc_ctrl->clk); return 0; } diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c index 07cb93abf685..a493b01c5bc6 100644 --- a/drivers/misc/cardreader/rts5260.c +++ b/drivers/misc/cardreader/rts5260.c @@ -388,17 +388,17 @@ static void rts5260_disable_ocp(struct rtsx_pcr *pcr) OC_POWER_DOWN); } -int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val) +static int rts5260_get_ocpstat(struct rtsx_pcr *pcr, u8 *val) { return rtsx_pci_read_register(pcr, REG_OCPSTAT, val); } -int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val) +static int rts5260_get_ocpstat2(struct rtsx_pcr *pcr, u8 *val) { return rtsx_pci_read_register(pcr, REG_DV3318_OCPSTAT, val); } -void rts5260_clear_ocpstat(struct rtsx_pcr *pcr) +static void rts5260_clear_ocpstat(struct rtsx_pcr *pcr) { u8 mask = 0; u8 val = 0; @@ -418,7 +418,7 @@ void rts5260_clear_ocpstat(struct rtsx_pcr *pcr) DV3318_OCP_INT_CLR | DV3318_OCP_CLR, 0); } -void rts5260_process_ocp(struct rtsx_pcr *pcr) +static void rts5260_process_ocp(struct rtsx_pcr *pcr) { if (!pcr->option.ocp_en) return; @@ -449,7 +449,7 @@ void rts5260_process_ocp(struct rtsx_pcr *pcr) } } -int rts5260_init_hw(struct rtsx_pcr *pcr) +static int rts5260_init_hw(struct rtsx_pcr *pcr) { int err; @@ -620,7 +620,7 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr) return 0; } -void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable) +static void rts5260_set_aspm(struct rtsx_pcr *pcr, bool enable) { struct rtsx_cr_option *option = &pcr->option; u8 val = 0; diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 01f9c4921c50..0c125f207aea 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -1,14 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * at24.c - handle most I2C EEPROMs * * Copyright (C) 2005-2007 David Brownell * Copyright (C) 2008 Wolfram Sang, Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ + #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> @@ -63,8 +60,6 @@ struct at24_client { }; struct at24_data { - struct at24_platform_data chip; - /* * Lock protects against activities from other Linux tasks, * but not from changes by other I2C masters. @@ -75,7 +70,10 @@ struct at24_data { unsigned int num_addresses; unsigned int offset_adj; - struct nvmem_config nvmem_config; + u32 byte_len; + u16 page_size; + u8 flags; + struct nvmem_device *nvmem; struct gpio_desc *wp_gpio; @@ -239,8 +237,6 @@ static const struct acpi_device_id at24_acpi_ids[] = { }; MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); -/*-------------------------------------------------------------------------*/ - /* * This routine supports chips which consume multiple I2C addresses. It * computes the addressing information to be used for a given r/w request. @@ -255,7 +251,7 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24, { unsigned int i; - if (at24->chip.flags & AT24_FLAG_ADDR16) { + if (at24->flags & AT24_FLAG_ADDR16) { i = *offset >> 16; *offset &= 0xffff; } else { @@ -266,6 +262,11 @@ static struct at24_client *at24_translate_offset(struct at24_data *at24, return &at24->client[i]; } +static struct device *at24_base_client_dev(struct at24_data *at24) +{ + return &at24->client[0].client->dev; +} + static size_t at24_adjust_read_count(struct at24_data *at24, unsigned int offset, size_t count) { @@ -277,8 +278,8 @@ static size_t at24_adjust_read_count(struct at24_data *at24, * the next slave address: truncate the count to the slave boundary, * so that the read never straddles slaves. */ - if (at24->chip.flags & AT24_FLAG_NO_RDROL) { - bits = (at24->chip.flags & AT24_FLAG_ADDR16) ? 16 : 8; + if (at24->flags & AT24_FLAG_NO_RDROL) { + bits = (at24->flags & AT24_FLAG_ADDR16) ? 16 : 8; remainder = BIT(bits) - offset; if (count > remainder) count = remainder; @@ -337,7 +338,7 @@ static size_t at24_adjust_write_count(struct at24_data *at24, count = at24->write_max; /* Never roll over backwards, to the start of this page */ - next_page = roundup(offset + 1, at24->chip.page_size); + next_page = roundup(offset + 1, at24->page_size); if (offset + count > next_page) count = next_page - offset; @@ -371,15 +372,18 @@ static ssize_t at24_regmap_write(struct at24_data *at24, const char *buf, static int at24_read(void *priv, unsigned int off, void *val, size_t count) { - struct at24_data *at24 = priv; - struct device *dev = &at24->client[0].client->dev; + struct at24_data *at24; + struct device *dev; char *buf = val; int ret; + at24 = priv; + dev = at24_base_client_dev(at24); + if (unlikely(!count)) return count; - if (off + count > at24->chip.byte_len) + if (off + count > at24->byte_len) return -EINVAL; ret = pm_runtime_get_sync(dev); @@ -395,17 +399,15 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) mutex_lock(&at24->lock); while (count) { - int status; - - status = at24_regmap_read(at24, buf, off, count); - if (status < 0) { + ret = at24_regmap_read(at24, buf, off, count); + if (ret < 0) { mutex_unlock(&at24->lock); pm_runtime_put(dev); - return status; + return ret; } - buf += status; - off += status; - count -= status; + buf += ret; + off += ret; + count -= ret; } mutex_unlock(&at24->lock); @@ -417,15 +419,18 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count) static int at24_write(void *priv, unsigned int off, void *val, size_t count) { - struct at24_data *at24 = priv; - struct device *dev = &at24->client[0].client->dev; + struct at24_data *at24; + struct device *dev; char *buf = val; int ret; + at24 = priv; + dev = at24_base_client_dev(at24); + if (unlikely(!count)) return -EINVAL; - if (off + count > at24->chip.byte_len) + if (off + count > at24->byte_len) return -EINVAL; ret = pm_runtime_get_sync(dev); @@ -442,18 +447,16 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) gpiod_set_value_cansleep(at24->wp_gpio, 0); while (count) { - int status; - - status = at24_regmap_write(at24, buf, off, count); - if (status < 0) { + ret = at24_regmap_write(at24, buf, off, count); + if (ret < 0) { gpiod_set_value_cansleep(at24->wp_gpio, 1); mutex_unlock(&at24->lock); pm_runtime_put(dev); - return status; + return ret; } - buf += status; - off += status; - count -= status; + buf += ret; + off += ret; + count -= ret; } gpiod_set_value_cansleep(at24->wp_gpio, 1); @@ -464,7 +467,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) return 0; } -static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) +static void at24_properties_to_pdata(struct device *dev, + struct at24_platform_data *chip) { int err; u32 val; @@ -491,6 +495,43 @@ static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) } } +static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata) +{ + struct device_node *of_node = dev->of_node; + const struct at24_chip_data *cdata; + const struct i2c_device_id *id; + struct at24_platform_data *pd; + + pd = dev_get_platdata(dev); + if (pd) { + memcpy(pdata, pd, sizeof(*pdata)); + return 0; + } + + id = i2c_match_id(at24_ids, to_i2c_client(dev)); + + /* + * The I2C core allows OF nodes compatibles to match against the + * I2C device ID table as a fallback, so check not only if an OF + * node is present but also if it matches an OF device ID entry. + */ + if (of_node && of_match_device(at24_of_match, dev)) + cdata = of_device_get_match_data(dev); + else if (id) + cdata = (void *)&id->driver_data; + else + cdata = acpi_device_get_match_data(dev); + + if (!cdata) + return -ENODEV; + + pdata->byte_len = cdata->byte_len; + pdata->flags = cdata->flags; + at24_properties_to_pdata(dev, pdata); + + return 0; +} + static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) { if (flags & AT24_FLAG_MAC) { @@ -514,102 +555,83 @@ static unsigned int at24_get_offset_adj(u8 flags, unsigned int byte_len) } } -static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int at24_probe(struct i2c_client *client) { - struct at24_platform_data chip = { 0 }; - const struct at24_chip_data *cd = NULL; - bool writable; - struct at24_data *at24; - int err; - unsigned int i, num_addresses; struct regmap_config regmap_config = { }; + struct nvmem_config nvmem_config = { }; + struct at24_platform_data pdata = { }; + struct device *dev = &client->dev; + bool i2c_fn_i2c, i2c_fn_block; + unsigned int i, num_addresses; + struct at24_data *at24; + struct regmap *regmap; + size_t at24_size; + bool writable; u8 test_byte; + int err; - if (client->dev.platform_data) { - chip = *(struct at24_platform_data *)client->dev.platform_data; - } else { - /* - * The I2C core allows OF nodes compatibles to match against the - * I2C device ID table as a fallback, so check not only if an OF - * node is present but also if it matches an OF device ID entry. - */ - if (client->dev.of_node && - of_match_device(at24_of_match, &client->dev)) { - cd = of_device_get_match_data(&client->dev); - } else if (id) { - cd = (void *)id->driver_data; - } else { - const struct acpi_device_id *aid; - - aid = acpi_match_device(at24_acpi_ids, &client->dev); - if (aid) - cd = (void *)aid->driver_data; - } - if (!cd) - return -ENODEV; + i2c_fn_i2c = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); + i2c_fn_block = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK); - chip.byte_len = cd->byte_len; - chip.flags = cd->flags; - at24_get_pdata(&client->dev, &chip); - } + err = at24_get_pdata(dev, &pdata); + if (err) + return err; + + if (!i2c_fn_i2c && !i2c_fn_block) + pdata.page_size = 1; - if (!is_power_of_2(chip.byte_len)) - dev_warn(&client->dev, - "byte_len looks suspicious (no power of 2)!\n"); - if (!chip.page_size) { - dev_err(&client->dev, "page_size must not be 0!\n"); + if (!pdata.page_size) { + dev_err(dev, "page_size must not be 0!\n"); return -EINVAL; } - if (!is_power_of_2(chip.page_size)) - dev_warn(&client->dev, - "page_size looks suspicious (no power of 2)!\n"); - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && - !i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) - chip.page_size = 1; + if (!is_power_of_2(pdata.page_size)) + dev_warn(dev, "page_size looks suspicious (no power of 2)!\n"); - if (chip.flags & AT24_FLAG_TAKE8ADDR) + if (pdata.flags & AT24_FLAG_TAKE8ADDR) num_addresses = 8; else - num_addresses = DIV_ROUND_UP(chip.byte_len, - (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + num_addresses = DIV_ROUND_UP(pdata.byte_len, + (pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + + if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) { + dev_err(dev, + "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); + return -EINVAL; + } regmap_config.val_bits = 8; - regmap_config.reg_bits = (chip.flags & AT24_FLAG_ADDR16) ? 16 : 8; + regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8; + regmap_config.disable_locking = true; - at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + - num_addresses * sizeof(struct at24_client), GFP_KERNEL); + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + at24_size = sizeof(*at24) + num_addresses * sizeof(struct at24_client); + at24 = devm_kzalloc(dev, at24_size, GFP_KERNEL); if (!at24) return -ENOMEM; mutex_init(&at24->lock); - at24->chip = chip; + at24->byte_len = pdata.byte_len; + at24->page_size = pdata.page_size; + at24->flags = pdata.flags; at24->num_addresses = num_addresses; - at24->offset_adj = at24_get_offset_adj(chip.flags, chip.byte_len); + at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len); + at24->client[0].client = client; + at24->client[0].regmap = regmap; - at24->wp_gpio = devm_gpiod_get_optional(&client->dev, - "wp", GPIOD_OUT_HIGH); + at24->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_HIGH); if (IS_ERR(at24->wp_gpio)) return PTR_ERR(at24->wp_gpio); - at24->client[0].client = client; - at24->client[0].regmap = devm_regmap_init_i2c(client, ®map_config); - if (IS_ERR(at24->client[0].regmap)) - return PTR_ERR(at24->client[0].regmap); - - if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) { - dev_err(&client->dev, - "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); - return -EINVAL; - } - - writable = !(chip.flags & AT24_FLAG_READONLY); + writable = !(pdata.flags & AT24_FLAG_READONLY); if (writable) { at24->write_max = min_t(unsigned int, - chip.page_size, at24_io_limit); - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) && - at24->write_max > I2C_SMBUS_BLOCK_MAX) + pdata.page_size, at24_io_limit); + if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX) at24->write_max = I2C_SMBUS_BLOCK_MAX; } @@ -618,8 +640,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->client[i].client = i2c_new_dummy(client->adapter, client->addr + i); if (!at24->client[i].client) { - dev_err(&client->dev, "address 0x%02x unavailable\n", - client->addr + i); + dev_err(dev, "address 0x%02x unavailable\n", + client->addr + i); err = -EADDRINUSE; goto err_clients; } @@ -635,48 +657,47 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) i2c_set_clientdata(client, at24); /* enable runtime pm */ - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); /* * Perform a one-byte test read to verify that the * chip is functional. */ err = at24_read(at24, 0, &test_byte, 1); - pm_runtime_idle(&client->dev); + pm_runtime_idle(dev); if (err) { err = -ENODEV; goto err_clients; } - at24->nvmem_config.name = dev_name(&client->dev); - at24->nvmem_config.dev = &client->dev; - at24->nvmem_config.read_only = !writable; - at24->nvmem_config.root_only = true; - at24->nvmem_config.owner = THIS_MODULE; - at24->nvmem_config.compat = true; - at24->nvmem_config.base_dev = &client->dev; - at24->nvmem_config.reg_read = at24_read; - at24->nvmem_config.reg_write = at24_write; - at24->nvmem_config.priv = at24; - at24->nvmem_config.stride = 1; - at24->nvmem_config.word_size = 1; - at24->nvmem_config.size = chip.byte_len; - - at24->nvmem = nvmem_register(&at24->nvmem_config); - + nvmem_config.name = dev_name(dev); + nvmem_config.dev = dev; + nvmem_config.read_only = !writable; + nvmem_config.root_only = true; + nvmem_config.owner = THIS_MODULE; + nvmem_config.compat = true; + nvmem_config.base_dev = dev; + nvmem_config.reg_read = at24_read; + nvmem_config.reg_write = at24_write; + nvmem_config.priv = at24; + nvmem_config.stride = 1; + nvmem_config.word_size = 1; + nvmem_config.size = pdata.byte_len; + + at24->nvmem = nvmem_register(&nvmem_config); if (IS_ERR(at24->nvmem)) { err = PTR_ERR(at24->nvmem); goto err_clients; } - dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n", - chip.byte_len, client->name, - writable ? "writable" : "read-only", at24->write_max); + dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n", + pdata.byte_len, client->name, + writable ? "writable" : "read-only", at24->write_max); /* export data to kernel code */ - if (chip.setup) - chip.setup(at24->nvmem, chip.context); + if (pdata.setup) + pdata.setup(at24->nvmem, pdata.context); return 0; @@ -685,7 +706,7 @@ err_clients: if (at24->client[i].client) i2c_unregister_device(at24->client[i].client); - pm_runtime_disable(&client->dev); + pm_runtime_disable(dev); return err; } @@ -708,15 +729,13 @@ static int at24_remove(struct i2c_client *client) return 0; } -/*-------------------------------------------------------------------------*/ - static struct i2c_driver at24_driver = { .driver = { .name = "at24", .of_match_table = at24_of_match, .acpi_match_table = ACPI_PTR(at24_acpi_ids), }, - .probe = at24_probe, + .probe_new = at24_probe, .remove = at24_remove, .id_table = at24_ids, }; diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 9282ffd607ff..6a7d4a2ad514 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -102,7 +102,7 @@ static int at25_ee_read(void *priv, unsigned int offset, } spi_message_init(&m); - memset(t, 0, sizeof t); + memset(t, 0, sizeof(t)); t[0].tx_buf = command; t[0].len = at25->addrlen + 1; diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile new file mode 100644 index 000000000000..3370a4138e94 --- /dev/null +++ b/drivers/misc/lkdtm/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_LKDTM) += lkdtm.o + +lkdtm-$(CONFIG_LKDTM) += core.o +lkdtm-$(CONFIG_LKDTM) += bugs.o +lkdtm-$(CONFIG_LKDTM) += heap.o +lkdtm-$(CONFIG_LKDTM) += perms.o +lkdtm-$(CONFIG_LKDTM) += refcount.o +lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o +lkdtm-$(CONFIG_LKDTM) += usercopy.o + +KCOV_INSTRUMENT_rodata.o := n + +OBJCOPYFLAGS := +OBJCOPYFLAGS_rodata_objcopy.o := \ + --set-section-flags .text=alloc,readonly \ + --rename-section .text=.rodata +targets += rodata.o rodata_objcopy.o +$(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE + $(call if_changed,objcopy) diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm/bugs.c index 7eebbdfbcacd..7eebbdfbcacd 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm/bugs.c diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm/core.c index 2154d1bfd18b..2154d1bfd18b 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm/core.c diff --git a/drivers/misc/lkdtm_heap.c b/drivers/misc/lkdtm/heap.c index 65026d7de130..65026d7de130 100644 --- a/drivers/misc/lkdtm_heap.c +++ b/drivers/misc/lkdtm/heap.c diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index 9e513dcfd809..9e513dcfd809 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h diff --git a/drivers/misc/lkdtm_perms.c b/drivers/misc/lkdtm/perms.c index 53b85c9d16b8..53b85c9d16b8 100644 --- a/drivers/misc/lkdtm_perms.c +++ b/drivers/misc/lkdtm/perms.c diff --git a/drivers/misc/lkdtm_refcount.c b/drivers/misc/lkdtm/refcount.c index 2b99d448e7fd..0a146b32da13 100644 --- a/drivers/misc/lkdtm_refcount.c +++ b/drivers/misc/lkdtm/refcount.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * This is for all the tests related to refcount bugs (e.g. overflow, * underflow, reaching zero untested, etc). diff --git a/drivers/misc/lkdtm_rodata.c b/drivers/misc/lkdtm/rodata.c index 58d180af72cf..58d180af72cf 100644 --- a/drivers/misc/lkdtm_rodata.c +++ b/drivers/misc/lkdtm/rodata.c diff --git a/drivers/misc/lkdtm_usercopy.c b/drivers/misc/lkdtm/usercopy.c index 9725aed305bb..9725aed305bb 100644 --- a/drivers/misc/lkdtm_usercopy.c +++ b/drivers/misc/lkdtm/usercopy.c diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 772d02922529..b1133739fb4b 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -74,6 +74,23 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, goto out; } + while (cl->tx_cb_queued >= bus->tx_queue_limit) { + mutex_unlock(&bus->device_lock); + rets = wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE || + (!mei_cl_is_connected(cl))); + mutex_lock(&bus->device_lock); + if (rets) { + if (signal_pending(current)) + rets = -EINTR; + goto out; + } + if (!mei_cl_is_connected(cl)) { + rets = -ENODEV; + goto out; + } + } + cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); if (!cb) { rets = -ENOMEM; @@ -450,6 +467,29 @@ bool mei_cldev_enabled(struct mei_cl_device *cldev) EXPORT_SYMBOL_GPL(mei_cldev_enabled); /** + * mei_cl_bus_module_get - acquire module of the underlying + * hw driver. + * + * @cldev: mei client device + * + * Return: true on success; false if the module was removed. + */ +static bool mei_cl_bus_module_get(struct mei_cl_device *cldev) +{ + return try_module_get(cldev->bus->dev->driver->owner); +} + +/** + * mei_cl_bus_module_put - release the underlying hw module. + * + * @cldev: mei client device + */ +static void mei_cl_bus_module_put(struct mei_cl_device *cldev) +{ + module_put(cldev->bus->dev->driver->owner); +} + +/** * mei_cldev_enable - enable me client device * create connection with me client * @@ -487,9 +527,17 @@ int mei_cldev_enable(struct mei_cl_device *cldev) goto out; } + if (!mei_cl_bus_module_get(cldev)) { + dev_err(&cldev->dev, "get hw module failed"); + ret = -ENODEV; + goto out; + } + ret = mei_cl_connect(cl, cldev->me_cl, NULL); - if (ret < 0) + if (ret < 0) { dev_err(&cldev->dev, "cannot connect\n"); + mei_cl_bus_module_put(cldev); + } out: mutex_unlock(&bus->device_lock); @@ -553,6 +601,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev) dev_err(bus->dev, "Could not disconnect from the ME client\n"); out: + mei_cl_bus_module_put(cldev); + /* Flush queues and remove any pending read */ mei_cl_flush_queues(cl, NULL); mei_cl_unlink(cl); @@ -563,37 +613,6 @@ out: EXPORT_SYMBOL_GPL(mei_cldev_disable); /** - * mei_cl_bus_module_get - acquire module of the underlying - * hw module. - * - * @cl: host client - * - * Return: true on success; false if the module was removed. - */ -bool mei_cl_bus_module_get(struct mei_cl *cl) -{ - struct mei_cl_device *cldev = cl->cldev; - - if (!cldev) - return true; - - return try_module_get(cldev->bus->dev->driver->owner); -} - -/** - * mei_cl_bus_module_put - release the underlying hw module. - * - * @cl: host client - */ -void mei_cl_bus_module_put(struct mei_cl *cl) -{ - struct mei_cl_device *cldev = cl->cldev; - - if (cldev) - module_put(cldev->bus->dev->driver->owner); -} - -/** * mei_cl_device_find - find matching entry in the driver id table * * @cldev: me client device diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 7e60c1817c31..8d6197a88b54 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -350,6 +350,36 @@ void mei_io_cb_free(struct mei_cl_cb *cb) } /** + * mei_tx_cb_queue - queue tx callback + * + * Locking: called under "dev->device_lock" lock + * + * @cb: mei callback struct + * @head: an instance of list to queue on + */ +static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb, + struct list_head *head) +{ + list_add_tail(&cb->list, head); + cb->cl->tx_cb_queued++; +} + +/** + * mei_tx_cb_dequeue - dequeue tx callback + * + * Locking: called under "dev->device_lock" lock + * + * @cb: mei callback struct to dequeue and free + */ +static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb) +{ + if (!WARN_ON(cb->cl->tx_cb_queued == 0)) + cb->cl->tx_cb_queued--; + + mei_io_cb_free(cb); +} + +/** * mei_io_cb_init - allocate and initialize io callback * * @cl: mei client @@ -377,49 +407,37 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, } /** - * __mei_io_list_flush_cl - removes and frees cbs belonging to cl. + * mei_io_list_flush_cl - removes cbs belonging to the cl. * * @head: an instance of our list structure - * @cl: host client, can be NULL for flushing the whole list - * @free: whether to free the cbs + * @cl: host client */ -static void __mei_io_list_flush_cl(struct list_head *head, - const struct mei_cl *cl, bool free) +static void mei_io_list_flush_cl(struct list_head *head, + const struct mei_cl *cl) { struct mei_cl_cb *cb, *next; - /* enable removing everything if no cl is specified */ list_for_each_entry_safe(cb, next, head, list) { - if (!cl || mei_cl_cmp_id(cl, cb->cl)) { + if (mei_cl_cmp_id(cl, cb->cl)) list_del_init(&cb->list); - if (free) - mei_io_cb_free(cb); - } } } /** - * mei_io_list_flush_cl - removes list entry belonging to cl. + * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them * * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_flush_cl(struct list_head *head, - const struct mei_cl *cl) +static void mei_io_tx_list_free_cl(struct list_head *head, + const struct mei_cl *cl) { - __mei_io_list_flush_cl(head, cl, false); -} + struct mei_cl_cb *cb, *next; -/** - * mei_io_list_free_cl - removes cb belonging to cl and free them - * - * @head: An instance of our list structure - * @cl: host client - */ -static inline void mei_io_list_free_cl(struct list_head *head, - const struct mei_cl *cl) -{ - __mei_io_list_flush_cl(head, cl, true); + list_for_each_entry_safe(cb, next, head, list) { + if (mei_cl_cmp_id(cl, cb->cl)) + mei_tx_cb_dequeue(cb); + } } /** @@ -538,8 +556,8 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) dev = cl->dev; cl_dbg(dev, cl, "remove list entry belonging to cl\n"); - mei_io_list_free_cl(&cl->dev->write_list, cl); - mei_io_list_free_cl(&cl->dev->write_waiting_list, cl); + mei_io_tx_list_free_cl(&cl->dev->write_list, cl); + mei_io_tx_list_free_cl(&cl->dev->write_waiting_list, cl); mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); mei_io_list_free_fp(&cl->rd_pending, fp); @@ -756,8 +774,8 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) return; cl->state = MEI_FILE_DISCONNECTED; - mei_io_list_free_cl(&dev->write_list, cl); - mei_io_list_free_cl(&dev->write_waiting_list, cl); + mei_io_tx_list_free_cl(&dev->write_list, cl); + mei_io_tx_list_free_cl(&dev->write_waiting_list, cl); mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); mei_cl_wake_all(cl); @@ -765,8 +783,6 @@ static void mei_cl_set_disconnected(struct mei_cl *cl) cl->tx_flow_ctrl_creds = 0; cl->timer_count = 0; - mei_cl_bus_module_put(cl); - if (!cl->me_cl) return; @@ -1076,9 +1092,6 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, dev = cl->dev; - if (!mei_cl_bus_module_get(cl)) - return -ENODEV; - rets = mei_cl_set_connecting(cl, me_cl); if (rets) goto nortpm; @@ -1698,9 +1711,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) out: if (mei_hdr.msg_complete) - list_add_tail(&cb->list, &dev->write_waiting_list); + mei_tx_cb_enqueue(cb, &dev->write_waiting_list); else - list_add_tail(&cb->list, &dev->write_list); + mei_tx_cb_enqueue(cb, &dev->write_list); cb = NULL; if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { @@ -1746,7 +1759,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) switch (cb->fop_type) { case MEI_FOP_WRITE: - mei_io_cb_free(cb); + mei_tx_cb_dequeue(cb); cl->writing_state = MEI_WRITE_COMPLETE; if (waitqueue_active(&cl->tx_wait)) { wake_up_interruptible(&cl->tx_wait); diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index a617aa5a3ad8..c815da91089c 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -97,7 +97,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, int pos = 0; int ret; -#define HDR " |me|host|state|rd|wr|\n" +#define HDR " |me|host|state|rd|wr|wrq\n" if (!dev) return -ENODEV; @@ -130,9 +130,10 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, list_for_each_entry(cl, &dev->file_list, link) { pos += scnprintf(buf + pos, bufsz - pos, - "%3d|%2d|%4d|%5d|%2d|%2d|\n", + "%3d|%2d|%4d|%5d|%2d|%2d|%3u\n", i, mei_cl_me_id(cl), cl->host_client_id, cl->state, - !list_empty(&cl->rd_completed), cl->writing_state); + !list_empty(&cl->rd_completed), cl->writing_state, + cl->tx_cb_queued); i++; } out: diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index c46f6e99a55e..4888ebc076b7 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -383,6 +383,7 @@ void mei_device_init(struct mei_device *dev, INIT_LIST_HEAD(&dev->write_waiting_list); INIT_LIST_HEAD(&dev->ctrl_wr_list); INIT_LIST_HEAD(&dev->ctrl_rd_list); + dev->tx_queue_limit = MEI_TX_QUEUE_LIMIT_DEFAULT; INIT_DELAYED_WORK(&dev->timer_work, mei_timer); INIT_WORK(&dev->reset_work, mei_reset_work); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 758dc73602d5..7465f17e1559 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -291,6 +291,27 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } + while (cl->tx_cb_queued >= dev->tx_queue_limit) { + if (file->f_flags & O_NONBLOCK) { + rets = -EAGAIN; + goto out; + } + mutex_unlock(&dev->device_lock); + rets = wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE || + (!mei_cl_is_connected(cl))); + mutex_lock(&dev->device_lock); + if (rets) { + if (signal_pending(current)) + rets = -EINTR; + goto out; + } + if (!mei_cl_is_connected(cl)) { + rets = -ENODEV; + goto out; + } + } + *offset = 0; cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file); if (!cb) { @@ -507,7 +528,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; default: - dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd); rets = -ENOIOCTLCMD; } @@ -580,6 +600,12 @@ static __poll_t mei_poll(struct file *file, poll_table *wait) mei_cl_read_start(cl, mei_cl_mtu(cl), file); } + if (req_events & (POLLOUT | POLLWRNORM)) { + poll_wait(file, &cl->tx_wait, wait); + if (cl->tx_cb_queued < dev->tx_queue_limit) + mask |= POLLOUT | POLLWRNORM; + } + out: mutex_unlock(&dev->device_lock); return mask; @@ -749,10 +775,48 @@ static ssize_t hbm_ver_drv_show(struct device *device, } static DEVICE_ATTR_RO(hbm_ver_drv); +static ssize_t tx_queue_limit_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct mei_device *dev = dev_get_drvdata(device); + u8 size = 0; + + mutex_lock(&dev->device_lock); + size = dev->tx_queue_limit; + mutex_unlock(&dev->device_lock); + + return snprintf(buf, PAGE_SIZE, "%u\n", size); +} + +static ssize_t tx_queue_limit_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mei_device *dev = dev_get_drvdata(device); + u8 limit; + unsigned int inp; + int err; + + err = kstrtouint(buf, 10, &inp); + if (err) + return err; + if (inp > MEI_TX_QUEUE_LIMIT_MAX || inp < MEI_TX_QUEUE_LIMIT_MIN) + return -EINVAL; + limit = inp; + + mutex_lock(&dev->device_lock); + dev->tx_queue_limit = limit; + mutex_unlock(&dev->device_lock); + + return count; +} +static DEVICE_ATTR_RW(tx_queue_limit); + static struct attribute *mei_attrs[] = { &dev_attr_fw_status.attr, &dev_attr_hbm_ver.attr, &dev_attr_hbm_ver_drv.attr, + &dev_attr_tx_queue_limit.attr, NULL }; ATTRIBUTE_GROUPS(mei); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index ebcd5132e447..be9c48415da9 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -210,6 +210,7 @@ struct mei_cl_cb { * @timer_count: watchdog timer for operation completion * @notify_en: notification - enabled/disabled * @notify_ev: pending notification event + * @tx_cb_queued: number of tx callbacks in queue * @writing_state: state of the tx * @rd_pending: pending read credits * @rd_completed: completed read @@ -234,6 +235,7 @@ struct mei_cl { u8 timer_count; u8 notify_en; u8 notify_ev; + u8 tx_cb_queued; enum mei_file_transaction_states writing_state; struct list_head rd_pending; struct list_head rd_completed; @@ -241,6 +243,10 @@ struct mei_cl { struct mei_cl_device *cldev; }; +#define MEI_TX_QUEUE_LIMIT_DEFAULT 50 +#define MEI_TX_QUEUE_LIMIT_MAX 255 +#define MEI_TX_QUEUE_LIMIT_MIN 30 + /** * struct mei_hw_ops - hw specific ops * @@ -315,8 +321,6 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, bool mei_cl_bus_rx_event(struct mei_cl *cl); bool mei_cl_bus_notify_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); -bool mei_cl_bus_module_get(struct mei_cl *cl); -void mei_cl_bus_module_put(struct mei_cl *cl); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); @@ -361,6 +365,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @write_waiting_list : write completion list * @ctrl_wr_list : pending control write list * @ctrl_rd_list : pending control read list + * @tx_queue_limit: tx queues per client linit * * @file_list : list of opened handles * @open_handle_count: number of opened handles @@ -425,6 +430,7 @@ struct mei_device { struct list_head write_waiting_list; struct list_head ctrl_wr_list; struct list_head ctrl_rd_list; + u8 tx_queue_limit; struct list_head file_list; long open_handle_count; diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c index fd7f2a6049f8..e5bb9c749b5d 100644 --- a/drivers/misc/mic/bus/vop_bus.c +++ b/drivers/misc/mic/bus/vop_bus.c @@ -135,7 +135,9 @@ EXPORT_SYMBOL_GPL(vop_unregister_driver); static void vop_release_dev(struct device *d) { - put_device(d); + struct vop_device *dev = dev_to_vop(d); + + kfree(dev); } struct vop_device * @@ -174,7 +176,7 @@ vop_register_device(struct device *pdev, int id, goto free_vdev; return vdev; free_vdev: - kfree(vdev); + put_device(&vdev->dev); return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(vop_register_device); diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c index 0051d9ec76cc..21f425472a82 100644 --- a/drivers/misc/ocxl/pci.c +++ b/drivers/misc/ocxl/pci.c @@ -519,7 +519,7 @@ static struct ocxl_fn *init_function(struct pci_dev *dev) rc = device_register(&fn->dev); if (rc) { deconfigure_function(fn); - device_unregister(&fn->dev); + put_device(&fn->dev); return ERR_PTR(rc); } return fn; |