diff options
Diffstat (limited to 'drivers/misc')
82 files changed, 3253 insertions, 857 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 64971baf11fa..c290990d73ed 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -474,11 +474,15 @@ config SRAM bool "Generic on-chip SRAM driver" depends on HAS_IOMEM select GENERIC_ALLOCATOR + select SRAM_EXEC if ARM help This driver allows you to declare a memory region to be managed by the genalloc API. It is supposed to be used for small on-chip SRAM areas found on many SoCs. +config SRAM_EXEC + bool + config VEXPRESS_SYSCFG bool "Versatile Express System Configuration driver" depends on VEXPRESS_CONFIG @@ -487,6 +491,7 @@ config VEXPRESS_SYSCFG ARM Ltd. Versatile Express uses specialised platform configuration bus. System Configuration interface is one of the possible means of generating transactions on this bus. + config PANEL tristate "Parallel port LCD/Keypad Panel support" depends on PARPORT @@ -494,14 +499,14 @@ config PANEL Say Y here if you have an HD44780 or KS-0074 LCD connected to your parallel port. This driver also features 4 and 6-key keypads. The LCD is accessible through the /dev/lcd char device (10, 156), and the - keypad through /dev/keypad (10, 185). Both require misc device to be - enabled. This code can either be compiled as a module, or linked into - the kernel and started at boot. If you don't understand what all this - is about, say N. + keypad through /dev/keypad (10, 185). This code can either be + compiled as a module, or linked into the kernel and started at boot. + If you don't understand what all this is about, say N. + +if PANEL config PANEL_PARPORT int "Default parallel port number (0=LPT1)" - depends on PANEL range 0 255 default "0" ---help--- @@ -513,7 +518,6 @@ config PANEL_PARPORT config PANEL_PROFILE int "Default panel profile (0-5, 0=custom)" - depends on PANEL range 0 5 default "5" ---help--- @@ -534,7 +538,7 @@ config PANEL_PROFILE for experts. config PANEL_KEYPAD - depends on PANEL && PANEL_PROFILE="0" + depends on PANEL_PROFILE="0" int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)" range 0 3 default 0 @@ -551,7 +555,7 @@ config PANEL_KEYPAD supports simultaneous keys pressed when the keypad supports them. config PANEL_LCD - depends on PANEL && PANEL_PROFILE="0" + depends on PANEL_PROFILE="0" int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)" range 0 5 default 0 @@ -574,7 +578,7 @@ config PANEL_LCD that those values changed from the 2.4 driver for better consistency. config PANEL_LCD_HEIGHT - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Number of lines on the LCD (1-2)" range 1 2 default 2 @@ -583,7 +587,7 @@ config PANEL_LCD_HEIGHT It can either be 1 or 2. config PANEL_LCD_WIDTH - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Number of characters per line on the LCD (1-40)" range 1 40 default 40 @@ -592,7 +596,7 @@ config PANEL_LCD_WIDTH Common values are 16,20,24,40. config PANEL_LCD_BWIDTH - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Internal LCD line width (1-40, 40 by default)" range 1 40 default 40 @@ -608,7 +612,7 @@ config PANEL_LCD_BWIDTH If you don't know, put '40' here. config PANEL_LCD_HWIDTH - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Hardware LCD line width (1-64, 64 by default)" range 1 64 default 64 @@ -622,7 +626,7 @@ config PANEL_LCD_HWIDTH 64 here for a 2x40. config PANEL_LCD_CHARSET - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "LCD character set (0=normal, 1=KS0074)" range 0 1 default 0 @@ -638,7 +642,7 @@ config PANEL_LCD_CHARSET If you don't know, use the normal one (0). config PANEL_LCD_PROTO - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "LCD communication mode (0=parallel 8 bits, 1=serial)" range 0 1 default 0 @@ -651,7 +655,7 @@ config PANEL_LCD_PROTO parallel LCD, and 1 for a serial LCD. config PANEL_LCD_PIN_E - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) " range -17 17 default 14 @@ -666,7 +670,7 @@ config PANEL_LCD_PIN_E Default for the 'E' pin in custom profile is '14' (AUTOFEED). config PANEL_LCD_PIN_RS - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) " range -17 17 default 17 @@ -681,7 +685,7 @@ config PANEL_LCD_PIN_RS Default for the 'RS' pin in custom profile is '17' (SELECT IN). config PANEL_LCD_PIN_RW - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0" int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) " range -17 17 default 16 @@ -696,7 +700,7 @@ config PANEL_LCD_PIN_RW Default for the 'RW' pin in custom profile is '16' (INIT). config PANEL_LCD_PIN_SCL - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) " range -17 17 default 1 @@ -711,7 +715,7 @@ config PANEL_LCD_PIN_SCL Default for the 'SCL' pin in custom profile is '1' (STROBE). config PANEL_LCD_PIN_SDA - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0" int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) " range -17 17 default 2 @@ -726,7 +730,7 @@ config PANEL_LCD_PIN_SDA Default for the 'SDA' pin in custom profile is '2' (D0). config PANEL_LCD_PIN_BL - depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" + depends on PANEL_PROFILE="0" && PANEL_LCD="1" int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) " range -17 17 default 0 @@ -741,7 +745,6 @@ config PANEL_LCD_PIN_BL Default for the 'BL' pin in custom profile is '0' (uncontrolled). config PANEL_CHANGE_MESSAGE - depends on PANEL bool "Change LCD initialization message ?" default "n" ---help--- @@ -754,7 +757,7 @@ config PANEL_CHANGE_MESSAGE say 'N' and keep the default message with the version. config PANEL_BOOT_MESSAGE - depends on PANEL && PANEL_CHANGE_MESSAGE="y" + depends on PANEL_CHANGE_MESSAGE="y" string "New initialization message" default "" ---help--- @@ -766,6 +769,8 @@ config PANEL_BOOT_MESSAGE An empty message will only clear the display at driver init time. Any other printf()-formatted message is valid with newline and escape codes. +endif # PANEL + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 31983366090a..7a3ea89339b4 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o +obj-$(CONFIG_SRAM_EXEC) += sram-exec.o obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 0516ecda54d3..b2a0340f277e 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -20,6 +20,8 @@ #include <linux/of.h> +#include "../../sound/soc/atmel/atmel_ssc_dai.h" + /* Serialize access to ssc_list and user count */ static DEFINE_SPINLOCK(user_lock); static LIST_HEAD(ssc_list); @@ -145,6 +147,49 @@ static inline const struct atmel_ssc_platform_data * __init platform_get_device_id(pdev)->driver_data; } +#ifdef CONFIG_SND_ATMEL_SOC_SSC +static int ssc_sound_dai_probe(struct ssc_device *ssc) +{ + struct device_node *np = ssc->pdev->dev.of_node; + int ret; + int id; + + ssc->sound_dai = false; + + if (!of_property_read_bool(np, "#sound-dai-cells")) + return 0; + + id = of_alias_get_id(np, "ssc"); + if (id < 0) + return id; + + ret = atmel_ssc_set_audio(id); + ssc->sound_dai = !ret; + + return ret; +} + +static void ssc_sound_dai_remove(struct ssc_device *ssc) +{ + if (!ssc->sound_dai) + return; + + atmel_ssc_put_audio(of_alias_get_id(ssc->pdev->dev.of_node, "ssc")); +} +#else +static inline int ssc_sound_dai_probe(struct ssc_device *ssc) +{ + if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells")) + return -ENOTSUPP; + + return 0; +} + +static inline void ssc_sound_dai_remove(struct ssc_device *ssc) +{ +} +#endif + static int ssc_probe(struct platform_device *pdev) { struct resource *regs; @@ -204,6 +249,9 @@ static int ssc_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n", ssc->regs, ssc->irq); + if (ssc_sound_dai_probe(ssc)) + dev_err(&pdev->dev, "failed to auto-setup ssc for audio\n"); + return 0; } @@ -211,6 +259,8 @@ static int ssc_remove(struct platform_device *pdev) { struct ssc_device *ssc = platform_get_drvdata(pdev); + ssc_sound_dai_remove(ssc); + spin_lock(&user_lock); list_del(&ssc->list); spin_unlock(&user_lock); diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile index 56e9a4732ef0..c14fd6b65b5a 100644 --- a/drivers/misc/cxl/Makefile +++ b/drivers/misc/cxl/Makefile @@ -2,9 +2,10 @@ ccflags-y := $(call cc-disable-warning, unused-const-variable) ccflags-$(CONFIG_PPC_WERROR) += -Werror cxl-y += main.o file.o irq.o fault.o native.o -cxl-y += context.o sysfs.o debugfs.o pci.o trace.o +cxl-y += context.o sysfs.o pci.o trace.o cxl-y += vphb.o phb.o api.o cxl-$(CONFIG_PPC_PSERIES) += flash.o guest.o of.o hcalls.o +cxl-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_CXL) += cxl.o obj-$(CONFIG_CXL_BASE) += base.o diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index 2e5233b60971..bcc030eacab7 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c @@ -9,18 +9,118 @@ #include <linux/pci.h> #include <linux/slab.h> -#include <linux/anon_inodes.h> #include <linux/file.h> #include <misc/cxl.h> -#include <linux/fs.h> -#include <asm/pnv-pci.h> #include <linux/msi.h> +#include <linux/module.h> +#include <linux/mount.h> #include "cxl.h" +/* + * Since we want to track memory mappings to be able to force-unmap + * when the AFU is no longer reachable, we need an inode. For devices + * opened through the cxl user API, this is not a problem, but a + * userland process can also get a cxl fd through the cxl_get_fd() + * API, which is used by the cxlflash driver. + * + * Therefore we implement our own simple pseudo-filesystem and inode + * allocator. We don't use the anonymous inode, as we need the + * meta-data associated with it (address_space) and it is shared by + * other drivers/processes, so it could lead to cxl unmapping VMAs + * from random processes. + */ + +#define CXL_PSEUDO_FS_MAGIC 0x1697697f + +static int cxl_fs_cnt; +static struct vfsmount *cxl_vfs_mount; + +static const struct dentry_operations cxl_fs_dops = { + .d_dname = simple_dname, +}; + +static struct dentry *cxl_fs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) +{ + return mount_pseudo(fs_type, "cxl:", NULL, &cxl_fs_dops, + CXL_PSEUDO_FS_MAGIC); +} + +static struct file_system_type cxl_fs_type = { + .name = "cxl", + .owner = THIS_MODULE, + .mount = cxl_fs_mount, + .kill_sb = kill_anon_super, +}; + + +void cxl_release_mapping(struct cxl_context *ctx) +{ + if (ctx->kernelapi && ctx->mapping) + simple_release_fs(&cxl_vfs_mount, &cxl_fs_cnt); +} + +static struct file *cxl_getfile(const char *name, + const struct file_operations *fops, + void *priv, int flags) +{ + struct qstr this; + struct path path; + struct file *file; + struct inode *inode = NULL; + int rc; + + /* strongly inspired by anon_inode_getfile() */ + + if (fops->owner && !try_module_get(fops->owner)) + return ERR_PTR(-ENOENT); + + rc = simple_pin_fs(&cxl_fs_type, &cxl_vfs_mount, &cxl_fs_cnt); + if (rc < 0) { + pr_err("Cannot mount cxl pseudo filesystem: %d\n", rc); + file = ERR_PTR(rc); + goto err_module; + } + + inode = alloc_anon_inode(cxl_vfs_mount->mnt_sb); + if (IS_ERR(inode)) { + file = ERR_CAST(inode); + goto err_fs; + } + + file = ERR_PTR(-ENOMEM); + this.name = name; + this.len = strlen(name); + this.hash = 0; + path.dentry = d_alloc_pseudo(cxl_vfs_mount->mnt_sb, &this); + if (!path.dentry) + goto err_inode; + + path.mnt = mntget(cxl_vfs_mount); + d_instantiate(path.dentry, inode); + + file = alloc_file(&path, OPEN_FMODE(flags), fops); + if (IS_ERR(file)) + goto err_dput; + file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); + file->private_data = priv; + + return file; + +err_dput: + path_put(&path); +err_inode: + iput(inode); +err_fs: + simple_release_fs(&cxl_vfs_mount, &cxl_fs_cnt); +err_module: + module_put(fops->owner); + return file; +} + struct cxl_context *cxl_dev_context_init(struct pci_dev *dev) { - struct address_space *mapping; struct cxl_afu *afu; struct cxl_context *ctx; int rc; @@ -30,38 +130,20 @@ struct cxl_context *cxl_dev_context_init(struct pci_dev *dev) return ERR_CAST(afu); ctx = cxl_context_alloc(); - if (IS_ERR(ctx)) { - rc = PTR_ERR(ctx); - goto err_dev; - } + if (!ctx) + return ERR_PTR(-ENOMEM); ctx->kernelapi = true; - /* - * Make our own address space since we won't have one from the - * filesystem like the user api has, and even if we do associate a file - * with this context we don't want to use the global anonymous inode's - * address space as that can invalidate unrelated users: - */ - mapping = kmalloc(sizeof(struct address_space), GFP_KERNEL); - if (!mapping) { - rc = -ENOMEM; - goto err_ctx; - } - address_space_init_once(mapping); - /* Make it a slave context. We can promote it later? */ - rc = cxl_context_init(ctx, afu, false, mapping); + rc = cxl_context_init(ctx, afu, false); if (rc) - goto err_mapping; + goto err_ctx; return ctx; -err_mapping: - kfree(mapping); err_ctx: kfree(ctx); -err_dev: return ERR_PTR(rc); } EXPORT_SYMBOL_GPL(cxl_dev_context_init); @@ -340,6 +422,11 @@ struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops, { struct file *file; int rc, flags, fdtmp; + char *name = NULL; + + /* only allow one per context */ + if (ctx->mapping) + return ERR_PTR(-EEXIST); flags = O_RDWR | O_CLOEXEC; @@ -363,12 +450,13 @@ struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops, } else /* use default ops */ fops = (struct file_operations *)&afu_fops; - file = anon_inode_getfile("cxl", fops, ctx, flags); + name = kasprintf(GFP_KERNEL, "cxl:%d", ctx->pe); + file = cxl_getfile(name, fops, ctx, flags); + kfree(name); if (IS_ERR(file)) goto err_fd; - file->f_mapping = ctx->mapping; - + cxl_context_set_mapping(ctx, file->f_mapping); *fd = fdtmp; return file; @@ -541,7 +629,7 @@ int _cxl_cx4_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) if (remaining > 0) { new_ctx = cxl_dev_context_init(pdev); - if (!new_ctx) { + if (IS_ERR(new_ctx)) { pr_warn("%s: Failed to allocate enough contexts for MSIs\n", pci_name(pdev)); return -ENOSPC; } diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 5e506c19108a..062bf6ca2625 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -34,8 +34,7 @@ struct cxl_context *cxl_context_alloc(void) /* * Initialises a CXL context. */ -int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master, - struct address_space *mapping) +int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master) { int i; @@ -44,7 +43,7 @@ int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master, ctx->master = master; ctx->pid = ctx->glpid = NULL; /* Set in start work ioctl */ mutex_init(&ctx->mapping_lock); - ctx->mapping = mapping; + ctx->mapping = NULL; /* * Allocate the segment table before we put it in the IDR so that we @@ -114,16 +113,24 @@ int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master, return 0; } -static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +void cxl_context_set_mapping(struct cxl_context *ctx, + struct address_space *mapping) +{ + mutex_lock(&ctx->mapping_lock); + ctx->mapping = mapping; + mutex_unlock(&ctx->mapping_lock); +} + +static int cxl_mmap_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct cxl_context *ctx = vma->vm_file->private_data; - unsigned long address = (unsigned long)vmf->virtual_address; u64 area, offset; offset = vmf->pgoff << PAGE_SHIFT; pr_devel("%s: pe: %i address: 0x%lx offset: 0x%llx\n", - __func__, ctx->pe, address, offset); + __func__, ctx->pe, vmf->address, offset); if (ctx->afu->current_mode == CXL_MODE_DEDICATED) { area = ctx->afu->psn_phys; @@ -155,7 +162,7 @@ static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; } - vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); + vm_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT); mutex_unlock(&ctx->status_mutex); @@ -300,8 +307,6 @@ static void reclaim_ctx(struct rcu_head *rcu) if (ctx->ff_page) __free_page(ctx->ff_page); ctx->sstp = NULL; - if (ctx->kernelapi) - kfree(ctx->mapping); kfree(ctx->irq_bitmap); @@ -313,6 +318,8 @@ static void reclaim_ctx(struct rcu_head *rcu) void cxl_context_free(struct cxl_context *ctx) { + if (ctx->kernelapi && ctx->mapping) + cxl_release_mapping(ctx); mutex_lock(&ctx->afu->contexts_lock); idr_remove(&ctx->afu->contexts_idr, ctx->pe); mutex_unlock(&ctx->afu->contexts_lock); diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index a144073593fa..79e60ec70bd3 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -419,6 +419,9 @@ struct cxl_afu { struct mutex contexts_lock; spinlock_t afu_cntl_lock; + /* -1: AFU deconfigured/locked, >= 0: number of readers */ + atomic_t configured_state; + /* AFU error buffer fields and bin attribute for sysfs */ u64 eb_len, eb_offset; struct bin_attribute attr_eb; @@ -800,12 +803,67 @@ int afu_register_irqs(struct cxl_context *ctx, u32 count); void afu_release_irqs(struct cxl_context *ctx, void *cookie); void afu_irq_name_free(struct cxl_context *ctx); +#ifdef CONFIG_DEBUG_FS + int cxl_debugfs_init(void); void cxl_debugfs_exit(void); int cxl_debugfs_adapter_add(struct cxl *adapter); void cxl_debugfs_adapter_remove(struct cxl *adapter); int cxl_debugfs_afu_add(struct cxl_afu *afu); void cxl_debugfs_afu_remove(struct cxl_afu *afu); +void cxl_stop_trace(struct cxl *cxl); +void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, struct dentry *dir); +void cxl_debugfs_add_adapter_xsl_regs(struct cxl *adapter, struct dentry *dir); +void cxl_debugfs_add_afu_psl_regs(struct cxl_afu *afu, struct dentry *dir); + +#else /* CONFIG_DEBUG_FS */ + +static inline int __init cxl_debugfs_init(void) +{ + return 0; +} + +static inline void cxl_debugfs_exit(void) +{ +} + +static inline int cxl_debugfs_adapter_add(struct cxl *adapter) +{ + return 0; +} + +static inline void cxl_debugfs_adapter_remove(struct cxl *adapter) +{ +} + +static inline int cxl_debugfs_afu_add(struct cxl_afu *afu) +{ + return 0; +} + +static inline void cxl_debugfs_afu_remove(struct cxl_afu *afu) +{ +} + +static inline void cxl_stop_trace(struct cxl *cxl) +{ +} + +static inline void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, + struct dentry *dir) +{ +} + +static inline void cxl_debugfs_add_adapter_xsl_regs(struct cxl *adapter, + struct dentry *dir) +{ +} + +static inline void cxl_debugfs_add_afu_psl_regs(struct cxl_afu *afu, struct dentry *dir) +{ +} + +#endif /* CONFIG_DEBUG_FS */ void cxl_handle_fault(struct work_struct *work); void cxl_prefault(struct cxl_context *ctx, u64 wed); @@ -817,8 +875,9 @@ void cxl_dump_debug_buffer(void *addr, size_t size); void init_cxl_native(void); struct cxl_context *cxl_context_alloc(void); -int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master, - struct address_space *mapping); +int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master); +void cxl_context_set_mapping(struct cxl_context *ctx, + struct address_space *mapping); void cxl_context_free(struct cxl_context *ctx); int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma); unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq, @@ -869,14 +928,11 @@ int cxl_data_cache_flush(struct cxl *adapter); int cxl_afu_disable(struct cxl_afu *afu); int cxl_psl_purge(struct cxl_afu *afu); -void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, struct dentry *dir); -void cxl_debugfs_add_adapter_xsl_regs(struct cxl *adapter, struct dentry *dir); -void cxl_debugfs_add_afu_psl_regs(struct cxl_afu *afu, struct dentry *dir); void cxl_native_psl_irq_dump_regs(struct cxl_context *ctx); void cxl_native_err_irq_dump_regs(struct cxl *adapter); -void cxl_stop_trace(struct cxl *cxl); int cxl_pci_vphb_add(struct cxl_afu *afu); void cxl_pci_vphb_remove(struct cxl_afu *afu); +void cxl_release_mapping(struct cxl_context *ctx); extern struct pci_driver cxl_pci_driver; extern struct platform_driver cxl_of_driver; diff --git a/drivers/misc/cxl/debugfs.c b/drivers/misc/cxl/debugfs.c index ec7b8a017439..9c06ac8fa5ac 100644 --- a/drivers/misc/cxl/debugfs.c +++ b/drivers/misc/cxl/debugfs.c @@ -43,12 +43,14 @@ static int debugfs_io_u64_set(void *data, u64 val) out_be64((u64 __iomem *)data, val); return 0; } -DEFINE_SIMPLE_ATTRIBUTE(fops_io_x64, debugfs_io_u64_get, debugfs_io_u64_set, "0x%016llx\n"); +DEFINE_DEBUGFS_ATTRIBUTE(fops_io_x64, debugfs_io_u64_get, debugfs_io_u64_set, + "0x%016llx\n"); static struct dentry *debugfs_create_io_x64(const char *name, umode_t mode, struct dentry *parent, u64 __iomem *value) { - return debugfs_create_file(name, mode, parent, (void __force *)value, &fops_io_x64); + return debugfs_create_file_unsafe(name, mode, parent, + (void __force *)value, &fops_io_x64); } void cxl_debugfs_add_adapter_psl_regs(struct cxl *adapter, struct dentry *dir) diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c index 377e650a2a1d..2fa015c05561 100644 --- a/drivers/misc/cxl/fault.c +++ b/drivers/misc/cxl/fault.c @@ -8,7 +8,8 @@ */ #include <linux/workqueue.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/mm.h> #include <linux/pid.h> #include <linux/mm.h> #include <linux/moduleparam.h> diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index 77080cc5fa0a..e7139c76f961 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -12,7 +12,7 @@ #include <linux/export.h> #include <linux/kernel.h> #include <linux/bitmap.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/poll.h> #include <linux/pid.h> #include <linux/fs.h> @@ -86,9 +86,12 @@ static int __afu_open(struct inode *inode, struct file *file, bool master) goto err_put_afu; } - if ((rc = cxl_context_init(ctx, afu, master, inode->i_mapping))) + rc = cxl_context_init(ctx, afu, master); + if (rc) goto err_put_afu; + cxl_context_set_mapping(ctx, inode->i_mapping); + pr_devel("afu_open pe: %i\n", ctx->pe); file->private_data = ctx; cxl_ctx_get(); diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c index 3e102cd6ed91..e04bc4ddfd74 100644 --- a/drivers/misc/cxl/guest.c +++ b/drivers/misc/cxl/guest.c @@ -887,7 +887,7 @@ static void afu_handle_errstate(struct work_struct *work) afu_guest->previous_state == H_STATE_PERM_UNAVAILABLE) return; - if (afu_guest->handle_err == true) + if (afu_guest->handle_err) schedule_delayed_work(&afu_guest->work_err, msecs_to_jiffies(3000)); } diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index dec60f58a767..1a402bbed687 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -104,7 +104,7 @@ irqreturn_t cxl_irq(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_i } else { spin_lock(&ctx->lock); ctx->afu_err = irq_info->afu_err; - ctx->pending_afu_err = 1; + ctx->pending_afu_err = true; spin_unlock(&ctx->lock); wake_up_all(&ctx->wq); diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c index 62e0dfb5f15b..b0b6ed31918e 100644 --- a/drivers/misc/cxl/main.c +++ b/drivers/misc/cxl/main.c @@ -19,6 +19,8 @@ #include <linux/slab.h> #include <linux/idr.h> #include <linux/pci.h> +#include <linux/sched/task.h> + #include <asm/cputable.h> #include <misc/cxl-base.h> @@ -268,7 +270,7 @@ struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int slice) idr_init(&afu->contexts_idr); mutex_init(&afu->contexts_lock); spin_lock_init(&afu->afu_cntl_lock); - + atomic_set(&afu->configured_state, -1); afu->prefault_mode = CXL_PREFAULT_NONE; afu->irqs_max = afu->adapter->user_irqs; diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index a217a74ccc98..7ae710585267 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -9,8 +9,8 @@ #include <linux/spinlock.h> #include <linux/sched.h> +#include <linux/sched/clock.h> #include <linux/slab.h> -#include <linux/sched.h> #include <linux/mutex.h> #include <linux/mm.h> #include <linux/uaccess.h> @@ -54,7 +54,7 @@ static int afu_control(struct cxl_afu *afu, u64 command, u64 clear, AFU_Cntl | command); cpu_relax(); AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); - }; + } if (AFU_Cntl & CXL_AFU_Cntl_An_RA) { /* @@ -167,7 +167,7 @@ int cxl_psl_purge(struct cxl_afu *afu) cpu_relax(); } PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An); - }; + } end = local_clock(); pr_devel("PSL purged in %lld ns\n", end - start); @@ -931,9 +931,18 @@ static irqreturn_t native_irq_multiplexed(int irq, void *data) struct cxl_afu *afu = data; struct cxl_context *ctx; struct cxl_irq_info irq_info; - int ph = cxl_p2n_read(afu, CXL_PSL_PEHandle_An) & 0xffff; - int ret; - + u64 phreg = cxl_p2n_read(afu, CXL_PSL_PEHandle_An); + int ph, ret; + + /* check if eeh kicked in while the interrupt was in flight */ + if (unlikely(phreg == ~0ULL)) { + dev_warn(&afu->dev, + "Ignoring slice interrupt(%d) due to fenced card", + irq); + return IRQ_HANDLED; + } + /* Mask the pe-handle from register value */ + ph = phreg & 0xffff; if ((ret = native_get_irq_info(afu, &irq_info))) { WARN(1, "Unable to get CXL IRQ Info: %i\n", ret); return fail_psl_irq(afu, &irq_info); diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index e96be9ca4e60..b27ea98b781f 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -1129,6 +1129,7 @@ static int pci_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pc if ((rc = cxl_native_register_psl_irq(afu))) goto err2; + atomic_set(&afu->configured_state, 0); return 0; err2: @@ -1141,6 +1142,14 @@ err1: static void pci_deconfigure_afu(struct cxl_afu *afu) { + /* + * It's okay to deconfigure when AFU is already locked, otherwise wait + * until there are no readers + */ + if (atomic_read(&afu->configured_state) != -1) { + while (atomic_cmpxchg(&afu->configured_state, 0, -1) != -1) + schedule(); + } cxl_native_release_psl_irq(afu); if (afu->adapter->native->sl_ops->release_serr_irq) afu->adapter->native->sl_ops->release_serr_irq(afu); @@ -1610,6 +1619,9 @@ static void cxl_pci_remove_adapter(struct cxl *adapter) cxl_sysfs_adapter_remove(adapter); cxl_debugfs_adapter_remove(adapter); + /* Flush adapter datacache as its about to be removed */ + cxl_data_cache_flush(adapter); + cxl_deconfigure_adapter(adapter); device_unregister(&adapter->dev); @@ -1780,15 +1792,14 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev, /* If we're permanently dead, give up. */ if (state == pci_channel_io_perm_failure) { - /* Tell the AFU drivers; but we don't care what they - * say, we're going away. - */ for (i = 0; i < adapter->slices; i++) { afu = adapter->afu[i]; - /* Only participate in EEH if we are on a virtual PHB */ - if (afu->phb == NULL) - return PCI_ERS_RESULT_NONE; - cxl_vphb_error_detected(afu, state); + /* + * Tell the AFU drivers; but we don't care what they + * say, we're going away. + */ + if (afu->phb != NULL) + cxl_vphb_error_detected(afu, state); } return PCI_ERS_RESULT_DISCONNECT; } @@ -1921,7 +1932,7 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev) goto err; ctx = cxl_dev_context_init(afu_dev); - if (!ctx) + if (IS_ERR(ctx)) goto err; afu_dev->dev.archdata.cxl_ctx = ctx; diff --git a/drivers/misc/cxl/phb.c b/drivers/misc/cxl/phb.c index 0935d44c1770..6ec69ada19f4 100644 --- a/drivers/misc/cxl/phb.c +++ b/drivers/misc/cxl/phb.c @@ -20,7 +20,7 @@ bool _cxl_pci_associate_default_context(struct pci_dev *dev, struct cxl_afu *afu * in the virtual phb, we'll need a default context to attach them to. */ ctx = cxl_dev_context_init(dev); - if (!ctx) + if (IS_ERR(ctx)) return false; dev->dev.archdata.cxl_ctx = ctx; diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c index 3519acebfdab..512a4897dbf6 100644 --- a/drivers/misc/cxl/vphb.c +++ b/drivers/misc/cxl/vphb.c @@ -76,23 +76,32 @@ static int cxl_pcie_cfg_record(u8 bus, u8 devfn) return (bus << 8) + devfn; } -static int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn, - struct cxl_afu **_afu, int *_record) +static inline struct cxl_afu *pci_bus_to_afu(struct pci_bus *bus) { - struct pci_controller *phb; - struct cxl_afu *afu; - int record; + struct pci_controller *phb = bus ? pci_bus_to_host(bus) : NULL; - phb = pci_bus_to_host(bus); - if (phb == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; + return phb ? phb->private_data : NULL; +} + +static void cxl_afu_configured_put(struct cxl_afu *afu) +{ + atomic_dec_if_positive(&afu->configured_state); +} + +static bool cxl_afu_configured_get(struct cxl_afu *afu) +{ + return atomic_inc_unless_negative(&afu->configured_state); +} + +static inline int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn, + struct cxl_afu *afu, int *_record) +{ + int record; - afu = (struct cxl_afu *)phb->private_data; record = cxl_pcie_cfg_record(bus->number, devfn); if (record > afu->crs_num) return PCIBIOS_DEVICE_NOT_FOUND; - *_afu = afu; *_record = record; return 0; } @@ -106,9 +115,14 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, u16 val16; u32 val32; - rc = cxl_pcie_config_info(bus, devfn, &afu, &record); + afu = pci_bus_to_afu(bus); + /* Grab a reader lock on afu. */ + if (afu == NULL || !cxl_afu_configured_get(afu)) + return PCIBIOS_DEVICE_NOT_FOUND; + + rc = cxl_pcie_config_info(bus, devfn, afu, &record); if (rc) - return rc; + goto out; switch (len) { case 1: @@ -127,10 +141,9 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, WARN_ON(1); } - if (rc) - return PCIBIOS_DEVICE_NOT_FOUND; - - return PCIBIOS_SUCCESSFUL; +out: + cxl_afu_configured_put(afu); + return rc ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; } static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, @@ -139,9 +152,14 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, int rc, record; struct cxl_afu *afu; - rc = cxl_pcie_config_info(bus, devfn, &afu, &record); + afu = pci_bus_to_afu(bus); + /* Grab a reader lock on afu. */ + if (afu == NULL || !cxl_afu_configured_get(afu)) + return PCIBIOS_DEVICE_NOT_FOUND; + + rc = cxl_pcie_config_info(bus, devfn, afu, &record); if (rc) - return rc; + goto out; switch (len) { case 1: @@ -157,10 +175,9 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, WARN_ON(1); } - if (rc) - return PCIBIOS_SET_FAILED; - - return PCIBIOS_SUCCESSFUL; +out: + cxl_afu_configured_put(afu); + return rc ? PCIBIOS_SET_FAILED : PCIBIOS_SUCCESSFUL; } static struct pci_ops cxl_pcie_pci_ops = diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index c4e41c26649e..de58762097c4 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -100,4 +100,14 @@ config EEPROM_DIGSY_MTC_CFG If unsure, say N. +config EEPROM_IDT_89HPESX + tristate "IDT 89HPESx PCIe-swtiches EEPROM / CSR support" + depends on I2C && SYSFS + help + Enable this driver to get read/write access to EEPROM / CSRs + over IDT PCIe-swtich i2c-slave interface. + + This driver can also be built as a module. If so, the module + will be called idt_89hpesx. + endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index fc1e81d29267..90a52624ddeb 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o +obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 051b14766ef9..764ff5df0dbc 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -19,7 +19,7 @@ #include <linux/log2.h> #include <linux/bitops.h> #include <linux/jiffies.h> -#include <linux/of.h> +#include <linux/property.h> #include <linux/acpi.h> #include <linux/i2c.h> #include <linux/nvmem-provider.h> @@ -562,26 +562,26 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count) return 0; } -#ifdef CONFIG_OF -static void at24_get_ofdata(struct i2c_client *client, - struct at24_platform_data *chip) +static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) { - const __be32 *val; - struct device_node *node = client->dev.of_node; - - if (node) { - if (of_get_property(node, "read-only", NULL)) - chip->flags |= AT24_FLAG_READONLY; - val = of_get_property(node, "pagesize", NULL); - if (val) - chip->page_size = be32_to_cpup(val); + int err; + u32 val; + + if (device_property_present(dev, "read-only")) + chip->flags |= AT24_FLAG_READONLY; + + err = device_property_read_u32(dev, "pagesize", &val); + if (!err) { + chip->page_size = val; + } else { + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip->page_size = 1; } } -#else -static void at24_get_ofdata(struct i2c_client *client, - struct at24_platform_data *chip) -{ } -#endif /* CONFIG_OF */ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -613,15 +613,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); magic >>= AT24_SIZE_BYTELEN; chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); - /* - * This is slow, but we can't know all eeproms, so we better - * play safe. Specifying custom eeprom-types via platform_data - * is recommended anyhow. - */ - chip.page_size = 1; - /* update chipdata if OF is present */ - at24_get_ofdata(client, &chip); + at24_get_pdata(&client->dev, &chip); chip.setup = NULL; chip.context = NULL; diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 3d1d55157e5f..2fad790db3bf 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/capability.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/mutex.h> diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c new file mode 100644 index 000000000000..4a22a1d99395 --- /dev/null +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -0,0 +1,1581 @@ +/* + * This file is provided under a GPLv2 license. When using or + * redistributing this file, you may do so under that license. + * + * GPL LICENSE SUMMARY + * + * Copyright (C) 2016 T-Platforms. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, it can be found <http://www.gnu.org/licenses/>. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * IDT PCIe-switch NTB Linux driver + * + * Contact Information: + * Serge Semin <fancer.lancer@gmail.com>, <Sergey.Semin@t-platforms.ru> + */ +/* + * NOTE of the IDT 89HPESx SMBus-slave interface driver + * This driver primarily is developed to have an access to EEPROM device of + * IDT PCIe-switches. IDT provides a simple SMBus interface to perform IO- + * operations from/to EEPROM, which is located at private (so called Master) + * SMBus of switches. Using that interface this the driver creates a simple + * binary sysfs-file in the device directory: + * /sys/bus/i2c/devices/<bus>-<devaddr>/eeprom + * In case if read-only flag is specified in the dts-node of device desription, + * User-space applications won't be able to write to the EEPROM sysfs-node. + * Additionally IDT 89HPESx SMBus interface has an ability to write/read + * data of device CSRs. This driver exposes debugf-file to perform simple IO + * operations using that ability for just basic debug purpose. Particularly + * next file is created in the specific debugfs-directory: + * /sys/kernel/debug/idt_csr/ + * Format of the debugfs-node is: + * $ cat /sys/kernel/debug/idt_csr/<bus>-<devaddr>/<devname>; + * <CSR address>:<CSR value> + * So reading the content of the file gives current CSR address and it value. + * If User-space application wishes to change current CSR address, + * it can just write a proper value to the sysfs-file: + * $ echo "<CSR address>" > /sys/kernel/debug/idt_csr/<bus>-<devaddr>/<devname> + * If it wants to change the CSR value as well, the format of the write + * operation is: + * $ echo "<CSR address>:<CSR value>" > \ + * /sys/kernel/debug/idt_csr/<bus>-<devaddr>/<devname>; + * CSR address and value can be any of hexadecimal, decimal or octal format. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/sysfs.h> +#include <linux/debugfs.h> +#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/i2c.h> +#include <linux/pci_ids.h> +#include <linux/delay.h> + +#define IDT_NAME "89hpesx" +#define IDT_89HPESX_DESC "IDT 89HPESx SMBus-slave interface driver" +#define IDT_89HPESX_VER "1.0" + +MODULE_DESCRIPTION(IDT_89HPESX_DESC); +MODULE_VERSION(IDT_89HPESX_VER); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("T-platforms"); + +/* + * csr_dbgdir - CSR read/write operations Debugfs directory + */ +static struct dentry *csr_dbgdir; + +/* + * struct idt_89hpesx_dev - IDT 89HPESx device data structure + * @eesize: Size of EEPROM in bytes (calculated from "idt,eecompatible") + * @eero: EEPROM Read-only flag + * @eeaddr: EEPROM custom address + * + * @inieecmd: Initial cmd value for EEPROM read/write operations + * @inicsrcmd: Initial cmd value for CSR read/write operations + * @iniccode: Initialial command code value for IO-operations + * + * @csr: CSR address to perform read operation + * + * @smb_write: SMBus write method + * @smb_read: SMBus read method + * @smb_mtx: SMBus mutex + * + * @client: i2c client used to perform IO operations + * + * @ee_file: EEPROM read/write sysfs-file + * @csr_file: CSR read/write debugfs-node + */ +struct idt_smb_seq; +struct idt_89hpesx_dev { + u32 eesize; + bool eero; + u8 eeaddr; + + u8 inieecmd; + u8 inicsrcmd; + u8 iniccode; + + u16 csr; + + int (*smb_write)(struct idt_89hpesx_dev *, const struct idt_smb_seq *); + int (*smb_read)(struct idt_89hpesx_dev *, struct idt_smb_seq *); + struct mutex smb_mtx; + + struct i2c_client *client; + + struct bin_attribute *ee_file; + struct dentry *csr_dir; + struct dentry *csr_file; +}; + +/* + * struct idt_smb_seq - sequence of data to be read/written from/to IDT 89HPESx + * @ccode: SMBus command code + * @bytecnt: Byte count of operation + * @data: Data to by written + */ +struct idt_smb_seq { + u8 ccode; + u8 bytecnt; + u8 *data; +}; + +/* + * struct idt_eeprom_seq - sequence of data to be read/written from/to EEPROM + * @cmd: Transaction CMD + * @eeaddr: EEPROM custom address + * @memaddr: Internal memory address of EEPROM + * @data: Data to be written at the memory address + */ +struct idt_eeprom_seq { + u8 cmd; + u8 eeaddr; + u16 memaddr; + u8 data; +} __packed; + +/* + * struct idt_csr_seq - sequence of data to be read/written from/to CSR + * @cmd: Transaction CMD + * @csraddr: Internal IDT device CSR address + * @data: Data to be read/written from/to the CSR address + */ +struct idt_csr_seq { + u8 cmd; + u16 csraddr; + u32 data; +} __packed; + +/* + * SMBus command code macros + * @CCODE_END: Indicates the end of transaction + * @CCODE_START: Indicates the start of transaction + * @CCODE_CSR: CSR read/write transaction + * @CCODE_EEPROM: EEPROM read/write transaction + * @CCODE_BYTE: Supplied data has BYTE length + * @CCODE_WORD: Supplied data has WORD length + * @CCODE_BLOCK: Supplied data has variable length passed in bytecnt + * byte right following CCODE byte + */ +#define CCODE_END ((u8)0x01) +#define CCODE_START ((u8)0x02) +#define CCODE_CSR ((u8)0x00) +#define CCODE_EEPROM ((u8)0x04) +#define CCODE_BYTE ((u8)0x00) +#define CCODE_WORD ((u8)0x20) +#define CCODE_BLOCK ((u8)0x40) +#define CCODE_PEC ((u8)0x80) + +/* + * EEPROM command macros + * @EEPROM_OP_WRITE: EEPROM write operation + * @EEPROM_OP_READ: EEPROM read operation + * @EEPROM_USA: Use specified address of EEPROM + * @EEPROM_NAERR: EEPROM device is not ready to respond + * @EEPROM_LAERR: EEPROM arbitration loss error + * @EEPROM_MSS: EEPROM misplace start & stop bits error + * @EEPROM_WR_CNT: Bytes count to perform write operation + * @EEPROM_WRRD_CNT: Bytes count to write before reading + * @EEPROM_RD_CNT: Bytes count to perform read operation + * @EEPROM_DEF_SIZE: Fall back size of EEPROM + * @EEPROM_DEF_ADDR: Defatul EEPROM address + * @EEPROM_TOUT: Timeout before retry read operation if eeprom is busy + */ +#define EEPROM_OP_WRITE ((u8)0x00) +#define EEPROM_OP_READ ((u8)0x01) +#define EEPROM_USA ((u8)0x02) +#define EEPROM_NAERR ((u8)0x08) +#define EEPROM_LAERR ((u8)0x10) +#define EEPROM_MSS ((u8)0x20) +#define EEPROM_WR_CNT ((u8)5) +#define EEPROM_WRRD_CNT ((u8)4) +#define EEPROM_RD_CNT ((u8)5) +#define EEPROM_DEF_SIZE ((u16)4096) +#define EEPROM_DEF_ADDR ((u8)0x50) +#define EEPROM_TOUT (100) + +/* + * CSR command macros + * @CSR_DWE: Enable all four bytes of the operation + * @CSR_OP_WRITE: CSR write operation + * @CSR_OP_READ: CSR read operation + * @CSR_RERR: Read operation error + * @CSR_WERR: Write operation error + * @CSR_WR_CNT: Bytes count to perform write operation + * @CSR_WRRD_CNT: Bytes count to write before reading + * @CSR_RD_CNT: Bytes count to perform read operation + * @CSR_MAX: Maximum CSR address + * @CSR_DEF: Default CSR address + * @CSR_REAL_ADDR: CSR real unshifted address + */ +#define CSR_DWE ((u8)0x0F) +#define CSR_OP_WRITE ((u8)0x00) +#define CSR_OP_READ ((u8)0x10) +#define CSR_RERR ((u8)0x40) +#define CSR_WERR ((u8)0x80) +#define CSR_WR_CNT ((u8)7) +#define CSR_WRRD_CNT ((u8)3) +#define CSR_RD_CNT ((u8)7) +#define CSR_MAX ((u32)0x3FFFF) +#define CSR_DEF ((u16)0x0000) +#define CSR_REAL_ADDR(val) ((unsigned int)val << 2) + +/* + * IDT 89HPESx basic register + * @IDT_VIDDID_CSR: PCIe VID and DID of IDT 89HPESx + * @IDT_VID_MASK: Mask of VID + */ +#define IDT_VIDDID_CSR ((u32)0x0000) +#define IDT_VID_MASK ((u32)0xFFFF) + +/* + * IDT 89HPESx can send NACK when new command is sent before previous one + * fininshed execution. In this case driver retries operation + * certain times. + * @RETRY_CNT: Number of retries before giving up and fail + * @idt_smb_safe: Generate a retry loop on corresponding SMBus method + */ +#define RETRY_CNT (128) +#define idt_smb_safe(ops, args...) ({ \ + int __retry = RETRY_CNT; \ + s32 __sts; \ + do { \ + __sts = i2c_smbus_ ## ops ## _data(args); \ + } while (__retry-- && __sts < 0); \ + __sts; \ +}) + +/*=========================================================================== + * i2c bus level IO-operations + *=========================================================================== + */ + +/* + * idt_smb_write_byte() - SMBus write method when I2C_SMBUS_BYTE_DATA operation + * is only available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_byte(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx; + + /* Loop over the supplied data sending byte one-by-one */ + for (idx = 0; idx < seq->bytecnt; idx++) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE; + if (idx == 0) + ccode |= CCODE_START; + if (idx == seq->bytecnt - 1) + ccode |= CCODE_END; + + /* Send data to the device */ + sts = idt_smb_safe(write_byte, pdev->client, ccode, + seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + return 0; +} + +/* + * idt_smb_read_byte() - SMBus read method when I2C_SMBUS_BYTE_DATA operation + * is only available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_byte(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx; + + /* Loop over the supplied buffer receiving byte one-by-one */ + for (idx = 0; idx < seq->bytecnt; idx++) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE; + if (idx == 0) + ccode |= CCODE_START; + if (idx == seq->bytecnt - 1) + ccode |= CCODE_END; + + /* Read data from the device */ + sts = idt_smb_safe(read_byte, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + seq->data[idx] = (u8)sts; + } + + return 0; +} + +/* + * idt_smb_write_word() - SMBus write method when I2C_SMBUS_BYTE_DATA and + * I2C_FUNC_SMBUS_WORD_DATA operations are available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_word(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx, evencnt; + + /* Calculate the even count of data to send */ + evencnt = seq->bytecnt - (seq->bytecnt % 2); + + /* Loop over the supplied data sending two bytes at a time */ + for (idx = 0; idx < evencnt; idx += 2) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_WORD; + if (idx == 0) + ccode |= CCODE_START; + if (idx == evencnt - 2) + ccode |= CCODE_END; + + /* Send word data to the device */ + sts = idt_smb_safe(write_word, pdev->client, ccode, + *(u16 *)&seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + /* If there is odd number of bytes then send just one last byte */ + if (seq->bytecnt != evencnt) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE | CCODE_END; + if (idx == 0) + ccode |= CCODE_START; + + /* Send byte data to the device */ + sts = idt_smb_safe(write_byte, pdev->client, ccode, + seq->data[idx]); + if (sts != 0) + return (int)sts; + } + + return 0; +} + +/* + * idt_smb_read_word() - SMBus read method when I2C_SMBUS_BYTE_DATA and + * I2C_FUNC_SMBUS_WORD_DATA operations are available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_word(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + int idx, evencnt; + + /* Calculate the even count of data to send */ + evencnt = seq->bytecnt - (seq->bytecnt % 2); + + /* Loop over the supplied data reading two bytes at a time */ + for (idx = 0; idx < evencnt; idx += 2) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_WORD; + if (idx == 0) + ccode |= CCODE_START; + if (idx == evencnt - 2) + ccode |= CCODE_END; + + /* Read word data from the device */ + sts = idt_smb_safe(read_word, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + *(u16 *)&seq->data[idx] = (u16)sts; + } + + /* If there is odd number of bytes then receive just one last byte */ + if (seq->bytecnt != evencnt) { + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BYTE | CCODE_END; + if (idx == 0) + ccode |= CCODE_START; + + /* Read last data byte from the device */ + sts = idt_smb_safe(read_byte, pdev->client, ccode); + if (sts < 0) + return (int)sts; + + seq->data[idx] = (u8)sts; + } + + return 0; +} + +/* + * idt_smb_write_block() - SMBus write method when I2C_SMBUS_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + */ +static int idt_smb_write_block(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + u8 ccode; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Send block of data to the device */ + return idt_smb_safe(write_block, pdev->client, ccode, seq->bytecnt, + seq->data); +} + +/* + * idt_smb_read_block() - SMBus read method when I2C_SMBUS_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + */ +static int idt_smb_read_block(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + s32 sts; + u8 ccode; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Read block of data from the device */ + sts = idt_smb_safe(read_block, pdev->client, ccode, seq->data); + if (sts != seq->bytecnt) + return (sts < 0 ? sts : -ENODATA); + + return 0; +} + +/* + * idt_smb_write_i2c_block() - SMBus write method when I2C_SMBUS_I2C_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Sequence of data to be written + * + * NOTE It's usual SMBus write block operation, except the actual data length is + * sent as first byte of data + */ +static int idt_smb_write_i2c_block(struct idt_89hpesx_dev *pdev, + const struct idt_smb_seq *seq) +{ + u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the data to send. Length byte must be added prior the data */ + buf[0] = seq->bytecnt; + memcpy(&buf[1], seq->data, seq->bytecnt); + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Send length and block of data to the device */ + return idt_smb_safe(write_i2c_block, pdev->client, ccode, + seq->bytecnt + 1, buf); +} + +/* + * idt_smb_read_i2c_block() - SMBus read method when I2C_SMBUS_I2C_BLOCK_DATA + * operation is available + * @pdev: Pointer to the driver data + * @seq: Buffer to read data to + * + * NOTE It's usual SMBus read block operation, except the actual data length is + * retrieved as first byte of data + */ +static int idt_smb_read_i2c_block(struct idt_89hpesx_dev *pdev, + struct idt_smb_seq *seq) +{ + u8 ccode, buf[I2C_SMBUS_BLOCK_MAX + 1]; + s32 sts; + + /* Return error if too much data passed to send */ + if (seq->bytecnt > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + + /* Collect the command code byte */ + ccode = seq->ccode | CCODE_BLOCK | CCODE_START | CCODE_END; + + /* Read length and block of data from the device */ + sts = idt_smb_safe(read_i2c_block, pdev->client, ccode, + seq->bytecnt + 1, buf); + if (sts != seq->bytecnt + 1) + return (sts < 0 ? sts : -ENODATA); + if (buf[0] != seq->bytecnt) + return -ENODATA; + + /* Copy retrieved data to the output data buffer */ + memcpy(seq->data, &buf[1], seq->bytecnt); + + return 0; +} + +/*=========================================================================== + * EEPROM IO-operations + *=========================================================================== + */ + +/* + * idt_eeprom_read_byte() - read just one byte from EEPROM + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @data: Data to be written to EEPROM + */ +static int idt_eeprom_read_byte(struct idt_89hpesx_dev *pdev, u16 memaddr, + u8 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_eeprom_seq eeseq; + struct idt_smb_seq smbseq; + int ret, retry; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_EEPROM; + smbseq.data = (u8 *)&eeseq; + + /* + * Sometimes EEPROM may respond with NACK if it's busy with previous + * operation, so we need to perform a few attempts of read cycle + */ + retry = RETRY_CNT; + do { + /* Send EEPROM memory address to read data from */ + smbseq.bytecnt = EEPROM_WRRD_CNT; + eeseq.cmd = pdev->inieecmd | EEPROM_OP_READ; + eeseq.eeaddr = pdev->eeaddr; + eeseq.memaddr = cpu_to_le16(memaddr); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init eeprom addr 0x%02hhx", + memaddr); + break; + } + + /* Perform read operation */ + smbseq.bytecnt = EEPROM_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read eeprom data 0x%02hhx", + memaddr); + break; + } + + /* Restart read operation if the device is busy */ + if (retry && (eeseq.cmd & EEPROM_NAERR)) { + dev_dbg(dev, "EEPROM busy, retry reading after %d ms", + EEPROM_TOUT); + msleep(EEPROM_TOUT); + continue; + } + + /* Check whether IDT successfully read data from EEPROM */ + if (eeseq.cmd & (EEPROM_NAERR | EEPROM_LAERR | EEPROM_MSS)) { + dev_err(dev, + "Communication with eeprom failed, cmd 0x%hhx", + eeseq.cmd); + ret = -EREMOTEIO; + break; + } + + /* Save retrieved data and exit the loop */ + *data = eeseq.data; + break; + } while (retry--); + + /* Return the status of operation */ + return ret; +} + +/* + * idt_eeprom_write() - EEPROM write operation + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @len: Length of data to be written + * @data: Data to be written to EEPROM + */ +static int idt_eeprom_write(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, + const u8 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_eeprom_seq eeseq; + struct idt_smb_seq smbseq; + int ret; + u16 idx; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_EEPROM; + smbseq.data = (u8 *)&eeseq; + + /* Send data byte-by-byte, checking if it is successfully written */ + for (idx = 0; idx < len; idx++, memaddr++) { + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Perform write operation */ + smbseq.bytecnt = EEPROM_WR_CNT; + eeseq.cmd = pdev->inieecmd | EEPROM_OP_WRITE; + eeseq.eeaddr = pdev->eeaddr; + eeseq.memaddr = cpu_to_le16(memaddr); + eeseq.data = data[idx]; + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, + "Failed to write 0x%04hx:0x%02hhx to eeprom", + memaddr, data[idx]); + goto err_mutex_unlock; + } + + /* + * Check whether the data is successfully written by reading + * from the same EEPROM memory address. + */ + eeseq.data = ~data[idx]; + ret = idt_eeprom_read_byte(pdev, memaddr, &eeseq.data); + if (ret != 0) + goto err_mutex_unlock; + + /* Check whether the read byte is the same as written one */ + if (eeseq.data != data[idx]) { + dev_err(dev, "Values don't match 0x%02hhx != 0x%02hhx", + eeseq.data, data[idx]); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + if (ret != 0) + return ret; + } + + return 0; +} + +/* + * idt_eeprom_read() - EEPROM read operation + * @pdev: Pointer to the driver data + * @memaddr: Start EEPROM memory address + * @len: Length of data to read + * @buf: Buffer to read data to + */ +static int idt_eeprom_read(struct idt_89hpesx_dev *pdev, u16 memaddr, u16 len, + u8 *buf) +{ + int ret; + u16 idx; + + /* Read data byte-by-byte, retrying if it wasn't successful */ + for (idx = 0; idx < len; idx++, memaddr++) { + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Just read the byte to the buffer */ + ret = idt_eeprom_read_byte(pdev, memaddr, &buf[idx]); + + /* Unlock IDT SMBus device */ + mutex_unlock(&pdev->smb_mtx); + + /* Return error if read operation failed */ + if (ret != 0) + return ret; + } + + return 0; +} + +/*=========================================================================== + * CSR IO-operations + *=========================================================================== + */ + +/* + * idt_csr_write() - CSR write operation + * @pdev: Pointer to the driver data + * @csraddr: CSR address (with no two LS bits) + * @data: Data to be written to CSR + */ +static int idt_csr_write(struct idt_89hpesx_dev *pdev, u16 csraddr, + const u32 data) +{ + struct device *dev = &pdev->client->dev; + struct idt_csr_seq csrseq; + struct idt_smb_seq smbseq; + int ret; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_CSR; + smbseq.data = (u8 *)&csrseq; + + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Perform write operation */ + smbseq.bytecnt = CSR_WR_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_WRITE; + csrseq.csraddr = cpu_to_le16(csraddr); + csrseq.data = cpu_to_le32(data); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to write 0x%04x: 0x%04x to csr", + CSR_REAL_ADDR(csraddr), data); + goto err_mutex_unlock; + } + + /* Send CSR address to read data from */ + smbseq.bytecnt = CSR_WRRD_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init csr address 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Perform read operation */ + smbseq.bytecnt = CSR_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read csr 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Check whether IDT successfully retrieved CSR data */ + if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { + dev_err(dev, "IDT failed to perform CSR r/w"); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + + return ret; +} + +/* + * idt_csr_read() - CSR read operation + * @pdev: Pointer to the driver data + * @csraddr: CSR address (with no two LS bits) + * @data: Data to be written to CSR + */ +static int idt_csr_read(struct idt_89hpesx_dev *pdev, u16 csraddr, u32 *data) +{ + struct device *dev = &pdev->client->dev; + struct idt_csr_seq csrseq; + struct idt_smb_seq smbseq; + int ret; + + /* Initialize SMBus sequence fields */ + smbseq.ccode = pdev->iniccode | CCODE_CSR; + smbseq.data = (u8 *)&csrseq; + + /* Lock IDT SMBus device */ + mutex_lock(&pdev->smb_mtx); + + /* Send CSR register address before reading it */ + smbseq.bytecnt = CSR_WRRD_CNT; + csrseq.cmd = pdev->inicsrcmd | CSR_OP_READ; + csrseq.csraddr = cpu_to_le16(csraddr); + ret = pdev->smb_write(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to init csr address 0x%04x", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Perform read operation */ + smbseq.bytecnt = CSR_RD_CNT; + ret = pdev->smb_read(pdev, &smbseq); + if (ret != 0) { + dev_err(dev, "Failed to read csr 0x%04hx", + CSR_REAL_ADDR(csraddr)); + goto err_mutex_unlock; + } + + /* Check whether IDT successfully retrieved CSR data */ + if (csrseq.cmd & (CSR_RERR | CSR_WERR)) { + dev_err(dev, "IDT failed to perform CSR r/w"); + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* Save data retrieved from IDT */ + *data = le32_to_cpu(csrseq.data); + + /* Unlock IDT SMBus device */ +err_mutex_unlock: + mutex_unlock(&pdev->smb_mtx); + + return ret; +} + +/*=========================================================================== + * Sysfs/debugfs-nodes IO-operations + *=========================================================================== + */ + +/* + * eeprom_write() - EEPROM sysfs-node write callback + * @filep: Pointer to the file system node + * @kobj: Pointer to the kernel object related to the sysfs-node + * @attr: Attributes of the file + * @buf: Buffer to write data to + * @off: Offset at which data should be written to + * @count: Number of bytes to write + */ +static ssize_t eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Retrieve driver data */ + pdev = dev_get_drvdata(kobj_to_dev(kobj)); + + /* Perform EEPROM write operation */ + ret = idt_eeprom_write(pdev, (u16)off, (u16)count, (u8 *)buf); + return (ret != 0 ? ret : count); +} + +/* + * eeprom_read() - EEPROM sysfs-node read callback + * @filep: Pointer to the file system node + * @kobj: Pointer to the kernel object related to the sysfs-node + * @attr: Attributes of the file + * @buf: Buffer to write data to + * @off: Offset at which data should be written to + * @count: Number of bytes to write + */ +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Retrieve driver data */ + pdev = dev_get_drvdata(kobj_to_dev(kobj)); + + /* Perform EEPROM read operation */ + ret = idt_eeprom_read(pdev, (u16)off, (u16)count, (u8 *)buf); + return (ret != 0 ? ret : count); +} + +/* + * idt_dbgfs_csr_write() - CSR debugfs-node write callback + * @filep: Pointer to the file system file descriptor + * @buf: Buffer to read data from + * @count: Size of the buffer + * @offp: Offset within the file + * + * It accepts either "0x<reg addr>:0x<value>" for saving register address + * and writing value to specified DWORD register or "0x<reg addr>" for + * just saving register address in order to perform next read operation. + * + * WARNING No spaces are allowed. Incoming string must be strictly formated as: + * "<reg addr>:<value>". Register address must be aligned within 4 bytes + * (one DWORD). + */ +static ssize_t idt_dbgfs_csr_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *offp) +{ + struct idt_89hpesx_dev *pdev = filep->private_data; + char *colon_ch, *csraddr_str, *csrval_str; + int ret, csraddr_len, csrval_len; + u32 csraddr, csrval; + char *buf; + + /* Copy data from User-space */ + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, offp, ubuf, count); + if (ret < 0) + goto free_buf; + buf[count] = 0; + + /* Find position of colon in the buffer */ + colon_ch = strnchr(buf, count, ':'); + + /* + * If there is colon passed then new CSR value should be parsed as + * well, so allocate buffer for CSR address substring. + * If no colon is found, then string must have just one number with + * no new CSR value + */ + if (colon_ch != NULL) { + csraddr_len = colon_ch - buf; + csraddr_str = + kmalloc(sizeof(char)*(csraddr_len + 1), GFP_KERNEL); + if (csraddr_str == NULL) { + ret = -ENOMEM; + goto free_buf; + } + /* Copy the register address to the substring buffer */ + strncpy(csraddr_str, buf, csraddr_len); + csraddr_str[csraddr_len] = '\0'; + /* Register value must follow the colon */ + csrval_str = colon_ch + 1; + csrval_len = strnlen(csrval_str, count - csraddr_len); + } else /* if (str_colon == NULL) */ { + csraddr_str = (char *)buf; /* Just to shut warning up */ + csraddr_len = strnlen(csraddr_str, count); + csrval_str = NULL; + csrval_len = 0; + } + + /* Convert CSR address to u32 value */ + ret = kstrtou32(csraddr_str, 0, &csraddr); + if (ret != 0) + goto free_csraddr_str; + + /* Check whether passed register address is valid */ + if (csraddr > CSR_MAX || !IS_ALIGNED(csraddr, SZ_4)) { + ret = -EINVAL; + goto free_csraddr_str; + } + + /* Shift register address to the right so to have u16 address */ + pdev->csr = (csraddr >> 2); + + /* Parse new CSR value and send it to IDT, if colon has been found */ + if (colon_ch != NULL) { + ret = kstrtou32(csrval_str, 0, &csrval); + if (ret != 0) + goto free_csraddr_str; + + ret = idt_csr_write(pdev, pdev->csr, csrval); + if (ret != 0) + goto free_csraddr_str; + } + + /* Free memory only if colon has been found */ +free_csraddr_str: + if (colon_ch != NULL) + kfree(csraddr_str); + + /* Free buffer allocated for data retrieved from User-space */ +free_buf: + kfree(buf); + + return (ret != 0 ? ret : count); +} + +/* + * idt_dbgfs_csr_read() - CSR debugfs-node read callback + * @filep: Pointer to the file system file descriptor + * @buf: Buffer to write data to + * @count: Size of the buffer + * @offp: Offset within the file + * + * It just prints the pair "0x<reg addr>:0x<value>" to passed buffer. + */ +#define CSRBUF_SIZE ((size_t)32) +static ssize_t idt_dbgfs_csr_read(struct file *filep, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct idt_89hpesx_dev *pdev = filep->private_data; + u32 csraddr, csrval; + char buf[CSRBUF_SIZE]; + int ret, size; + + /* Perform CSR read operation */ + ret = idt_csr_read(pdev, pdev->csr, &csrval); + if (ret != 0) + return ret; + + /* Shift register address to the left so to have real address */ + csraddr = ((u32)pdev->csr << 2); + + /* Print the "0x<reg addr>:0x<value>" to buffer */ + size = snprintf(buf, CSRBUF_SIZE, "0x%05x:0x%08x\n", + (unsigned int)csraddr, (unsigned int)csrval); + + /* Copy data to User-space */ + return simple_read_from_buffer(ubuf, count, offp, buf, size); +} + +/* + * eeprom_attribute - EEPROM sysfs-node attributes + * + * NOTE Size will be changed in compliance with OF node. EEPROM attribute will + * be read-only as well if the corresponding flag is specified in OF node. + */ +static BIN_ATTR_RW(eeprom, EEPROM_DEF_SIZE); + +/* + * csr_dbgfs_ops - CSR debugfs-node read/write operations + */ +static const struct file_operations csr_dbgfs_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = idt_dbgfs_csr_write, + .read = idt_dbgfs_csr_read +}; + +/*=========================================================================== + * Driver init/deinit methods + *=========================================================================== + */ + +/* + * idt_set_defval() - disable EEPROM access by default + * @pdev: Pointer to the driver data + */ +static void idt_set_defval(struct idt_89hpesx_dev *pdev) +{ + /* If OF info is missing then use next values */ + pdev->eesize = 0; + pdev->eero = true; + pdev->inieecmd = 0; + pdev->eeaddr = 0; +} + +#ifdef CONFIG_OF +static const struct i2c_device_id ee_ids[]; +/* + * idt_ee_match_id() - check whether the node belongs to compatible EEPROMs + */ +static const struct i2c_device_id *idt_ee_match_id(struct device_node *node) +{ + const struct i2c_device_id *id = ee_ids; + char devname[I2C_NAME_SIZE]; + + /* Retrieve the device name without manufacturer name */ + if (of_modalias_node(node, devname, sizeof(devname))) + return NULL; + + /* Search through the device name */ + while (id->name[0]) { + if (strcmp(devname, id->name) == 0) + return id; + id++; + } + return NULL; +} + +/* + * idt_get_ofdata() - get IDT i2c-device parameters from device tree + * @pdev: Pointer to the driver data + */ +static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) +{ + const struct device_node *node = pdev->client->dev.of_node; + struct device *dev = &pdev->client->dev; + + /* Read dts node parameters */ + if (node) { + const struct i2c_device_id *ee_id = NULL; + struct device_node *child; + const __be32 *addr_be; + int len; + + /* Walk through all child nodes looking for compatible one */ + for_each_available_child_of_node(node, child) { + ee_id = idt_ee_match_id(child); + if (IS_ERR_OR_NULL(ee_id)) { + dev_warn(dev, "Skip unsupported child node %s", + child->full_name); + continue; + } else + break; + } + + /* If there is no child EEPROM device, then set zero size */ + if (!ee_id) { + idt_set_defval(pdev); + return; + } + + /* Retrieve EEPROM size */ + pdev->eesize = (u32)ee_id->driver_data; + + /* Get custom EEPROM address from 'reg' attribute */ + addr_be = of_get_property(child, "reg", &len); + if (!addr_be || (len < sizeof(*addr_be))) { + dev_warn(dev, "No reg on %s, use default address %d", + child->full_name, EEPROM_DEF_ADDR); + pdev->inieecmd = 0; + pdev->eeaddr = EEPROM_DEF_ADDR << 1; + } else { + pdev->inieecmd = EEPROM_USA; + pdev->eeaddr = be32_to_cpup(addr_be) << 1; + } + + /* Check EEPROM 'read-only' flag */ + if (of_get_property(child, "read-only", NULL)) + pdev->eero = true; + else /* if (!of_get_property(node, "read-only", NULL)) */ + pdev->eero = false; + + dev_dbg(dev, "EEPROM of %u bytes found by %hhu", + pdev->eesize, pdev->eeaddr); + } else { + dev_warn(dev, "No dts node, EEPROM access disabled"); + idt_set_defval(pdev); + } +} +#else +static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + + dev_warn(dev, "OF table is unsupported, EEPROM access disabled"); + + /* Nothing we can do, just set the default values */ + idt_set_defval(pdev); +} +#endif /* CONFIG_OF */ + +/* + * idt_create_pdev() - create and init data structure of the driver + * @client: i2c client of IDT PCIe-switch device + */ +static struct idt_89hpesx_dev *idt_create_pdev(struct i2c_client *client) +{ + struct idt_89hpesx_dev *pdev; + + /* Allocate memory for driver data */ + pdev = devm_kmalloc(&client->dev, sizeof(struct idt_89hpesx_dev), + GFP_KERNEL); + if (pdev == NULL) + return ERR_PTR(-ENOMEM); + + /* Initialize basic fields of the data */ + pdev->client = client; + i2c_set_clientdata(client, pdev); + + /* Read OF nodes information */ + idt_get_ofdata(pdev); + + /* Initialize basic CSR CMD field - use full DWORD-sized r/w ops */ + pdev->inicsrcmd = CSR_DWE; + pdev->csr = CSR_DEF; + + /* Enable Packet Error Checking if it's supported by adapter */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) { + pdev->iniccode = CCODE_PEC; + client->flags |= I2C_CLIENT_PEC; + } else /* PEC is unsupported */ { + pdev->iniccode = 0; + } + + return pdev; +} + +/* + * idt_free_pdev() - free data structure of the driver + * @pdev: Pointer to the driver data + */ +static void idt_free_pdev(struct idt_89hpesx_dev *pdev) +{ + /* Clear driver data from device private field */ + i2c_set_clientdata(pdev->client, NULL); +} + +/* + * idt_set_smbus_ops() - set supported SMBus operations + * @pdev: Pointer to the driver data + * Return status of smbus check operations + */ +static int idt_set_smbus_ops(struct idt_89hpesx_dev *pdev) +{ + struct i2c_adapter *adapter = pdev->client->adapter; + struct device *dev = &pdev->client->dev; + + /* Check i2c adapter read functionality */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) { + pdev->smb_read = idt_smb_read_block; + dev_dbg(dev, "SMBus block-read op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + pdev->smb_read = idt_smb_read_i2c_block; + dev_dbg(dev, "SMBus i2c-block-read op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA) && + i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + pdev->smb_read = idt_smb_read_word; + dev_warn(dev, "Use slow word/byte SMBus read ops"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + pdev->smb_read = idt_smb_read_byte; + dev_warn(dev, "Use slow byte SMBus read op"); + } else /* no supported smbus read operations */ { + dev_err(dev, "No supported SMBus read op"); + return -EPFNOSUPPORT; + } + + /* Check i2c adapter write functionality */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) { + pdev->smb_write = idt_smb_write_block; + dev_dbg(dev, "SMBus block-write op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { + pdev->smb_write = idt_smb_write_i2c_block; + dev_dbg(dev, "SMBus i2c-block-write op chosen"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_WORD_DATA) && + i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + pdev->smb_write = idt_smb_write_word; + dev_warn(dev, "Use slow word/byte SMBus write op"); + } else if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + pdev->smb_write = idt_smb_write_byte; + dev_warn(dev, "Use slow byte SMBus write op"); + } else /* no supported smbus write operations */ { + dev_err(dev, "No supported SMBus write op"); + return -EPFNOSUPPORT; + } + + /* Initialize IDT SMBus slave interface mutex */ + mutex_init(&pdev->smb_mtx); + + return 0; +} + +/* + * idt_check_dev() - check whether it's really IDT 89HPESx device + * @pdev: Pointer to the driver data + * Return status of i2c adapter check operation + */ +static int idt_check_dev(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + u32 viddid; + int ret; + + /* Read VID and DID directly from IDT memory space */ + ret = idt_csr_read(pdev, IDT_VIDDID_CSR, &viddid); + if (ret != 0) { + dev_err(dev, "Failed to read VID/DID"); + return ret; + } + + /* Check whether it's IDT device */ + if ((viddid & IDT_VID_MASK) != PCI_VENDOR_ID_IDT) { + dev_err(dev, "Got unsupported VID/DID: 0x%08x", viddid); + return -ENODEV; + } + + dev_info(dev, "Found IDT 89HPES device VID:0x%04x, DID:0x%04x", + (viddid & IDT_VID_MASK), (viddid >> 16)); + + return 0; +} + +/* + * idt_create_sysfs_files() - create sysfs attribute files + * @pdev: Pointer to the driver data + * Return status of operation + */ +static int idt_create_sysfs_files(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + int ret; + + /* Don't do anything if EEPROM isn't accessible */ + if (pdev->eesize == 0) { + dev_dbg(dev, "Skip creating sysfs-files"); + return 0; + } + + /* Allocate memory for attribute file */ + pdev->ee_file = devm_kmalloc(dev, sizeof(*pdev->ee_file), GFP_KERNEL); + if (!pdev->ee_file) + return -ENOMEM; + + /* Copy the declared EEPROM attr structure to change some of fields */ + memcpy(pdev->ee_file, &bin_attr_eeprom, sizeof(*pdev->ee_file)); + + /* In case of read-only EEPROM get rid of write ability */ + if (pdev->eero) { + pdev->ee_file->attr.mode &= ~0200; + pdev->ee_file->write = NULL; + } + /* Create EEPROM sysfs file */ + pdev->ee_file->size = pdev->eesize; + ret = sysfs_create_bin_file(&dev->kobj, pdev->ee_file); + if (ret != 0) { + dev_err(dev, "Failed to create EEPROM sysfs-node"); + return ret; + } + + return 0; +} + +/* + * idt_remove_sysfs_files() - remove sysfs attribute files + * @pdev: Pointer to the driver data + */ +static void idt_remove_sysfs_files(struct idt_89hpesx_dev *pdev) +{ + struct device *dev = &pdev->client->dev; + + /* Don't do anything if EEPROM wasn't accessible */ + if (pdev->eesize == 0) + return; + + /* Remove EEPROM sysfs file */ + sysfs_remove_bin_file(&dev->kobj, pdev->ee_file); +} + +/* + * idt_create_dbgfs_files() - create debugfs files + * @pdev: Pointer to the driver data + */ +#define CSRNAME_LEN ((size_t)32) +static void idt_create_dbgfs_files(struct idt_89hpesx_dev *pdev) +{ + struct i2c_client *cli = pdev->client; + char fname[CSRNAME_LEN]; + + /* Create Debugfs directory for CSR file */ + snprintf(fname, CSRNAME_LEN, "%d-%04hx", cli->adapter->nr, cli->addr); + pdev->csr_dir = debugfs_create_dir(fname, csr_dbgdir); + + /* Create Debugfs file for CSR read/write operations */ + pdev->csr_file = debugfs_create_file(cli->name, 0600, + pdev->csr_dir, pdev, &csr_dbgfs_ops); +} + +/* + * idt_remove_dbgfs_files() - remove debugfs files + * @pdev: Pointer to the driver data + */ +static void idt_remove_dbgfs_files(struct idt_89hpesx_dev *pdev) +{ + /* Remove CSR directory and it sysfs-node */ + debugfs_remove_recursive(pdev->csr_dir); +} + +/* + * idt_probe() - IDT 89HPESx driver probe() callback method + */ +static int idt_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct idt_89hpesx_dev *pdev; + int ret; + + /* Create driver data */ + pdev = idt_create_pdev(client); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + /* Set SMBus operations */ + ret = idt_set_smbus_ops(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Check whether it is truly IDT 89HPESx device */ + ret = idt_check_dev(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Create sysfs files */ + ret = idt_create_sysfs_files(pdev); + if (ret != 0) + goto err_free_pdev; + + /* Create debugfs files */ + idt_create_dbgfs_files(pdev); + + return 0; + +err_free_pdev: + idt_free_pdev(pdev); + + return ret; +} + +/* + * idt_remove() - IDT 89HPESx driver remove() callback method + */ +static int idt_remove(struct i2c_client *client) +{ + struct idt_89hpesx_dev *pdev = i2c_get_clientdata(client); + + /* Remove debugfs files first */ + idt_remove_dbgfs_files(pdev); + + /* Remove sysfs files */ + idt_remove_sysfs_files(pdev); + + /* Discard driver data structure */ + idt_free_pdev(pdev); + + return 0; +} + +/* + * ee_ids - array of supported EEPROMs + */ +static const struct i2c_device_id ee_ids[] = { + { "24c32", 4096}, + { "24c64", 8192}, + { "24c128", 16384}, + { "24c256", 32768}, + { "24c512", 65536}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ee_ids); + +/* + * idt_ids - supported IDT 89HPESx devices + */ +static const struct i2c_device_id idt_ids[] = { + { "89hpes8nt2", 0 }, + { "89hpes12nt3", 0 }, + + { "89hpes24nt6ag2", 0 }, + { "89hpes32nt8ag2", 0 }, + { "89hpes32nt8bg2", 0 }, + { "89hpes12nt12g2", 0 }, + { "89hpes16nt16g2", 0 }, + { "89hpes24nt24g2", 0 }, + { "89hpes32nt24ag2", 0 }, + { "89hpes32nt24bg2", 0 }, + + { "89hpes12n3", 0 }, + { "89hpes12n3a", 0 }, + { "89hpes24n3", 0 }, + { "89hpes24n3a", 0 }, + + { "89hpes32h8", 0 }, + { "89hpes32h8g2", 0 }, + { "89hpes48h12", 0 }, + { "89hpes48h12g2", 0 }, + { "89hpes48h12ag2", 0 }, + { "89hpes16h16", 0 }, + { "89hpes22h16", 0 }, + { "89hpes22h16g2", 0 }, + { "89hpes34h16", 0 }, + { "89hpes34h16g2", 0 }, + { "89hpes64h16", 0 }, + { "89hpes64h16g2", 0 }, + { "89hpes64h16ag2", 0 }, + + /* { "89hpes3t3", 0 }, // No SMBus-slave iface */ + { "89hpes12t3g2", 0 }, + { "89hpes24t3g2", 0 }, + /* { "89hpes4t4", 0 }, // No SMBus-slave iface */ + { "89hpes16t4", 0 }, + { "89hpes4t4g2", 0 }, + { "89hpes10t4g2", 0 }, + { "89hpes16t4g2", 0 }, + { "89hpes16t4ag2", 0 }, + { "89hpes5t5", 0 }, + { "89hpes6t5", 0 }, + { "89hpes8t5", 0 }, + { "89hpes8t5a", 0 }, + { "89hpes24t6", 0 }, + { "89hpes6t6g2", 0 }, + { "89hpes24t6g2", 0 }, + { "89hpes16t7", 0 }, + { "89hpes32t8", 0 }, + { "89hpes32t8g2", 0 }, + { "89hpes48t12", 0 }, + { "89hpes48t12g2", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, idt_ids); + +/* + * idt_driver - IDT 89HPESx driver structure + */ +static struct i2c_driver idt_driver = { + .driver = { + .name = IDT_NAME, + }, + .probe = idt_probe, + .remove = idt_remove, + .id_table = idt_ids, +}; + +/* + * idt_init() - IDT 89HPESx driver init() callback method + */ +static int __init idt_init(void) +{ + /* Create Debugfs directory first */ + if (debugfs_initialized()) + csr_dbgdir = debugfs_create_dir("idt_csr", NULL); + + /* Add new i2c-device driver */ + return i2c_add_driver(&idt_driver); +} +module_init(idt_init); + +/* + * idt_exit() - IDT 89HPESx driver exit() callback method + */ +static void __exit idt_exit(void) +{ + /* Discard debugfs directory and all files if any */ + debugfs_remove_recursive(csr_dbgdir); + + /* Unregister i2c-device driver */ + i2c_del_driver(&idt_driver); +} +module_exit(idt_exit); diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 6c1f49a85023..4fd21e86ad56 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -1336,7 +1336,6 @@ static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) static struct pci_error_handlers genwqe_err_handler = { .error_detected = genwqe_err_error_detected, .mmio_enabled = genwqe_err_result_none, - .link_reset = genwqe_err_result_none, .slot_reset = genwqe_err_slot_reset, .resume = genwqe_err_resume, }; diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h index cb851c14ca4b..5813b5f25006 100644 --- a/drivers/misc/genwqe/card_base.h +++ b/drivers/misc/genwqe/card_base.h @@ -41,7 +41,6 @@ #include "genwqe_driver.h" #define GENWQE_MSI_IRQS 4 /* Just one supported, no MSIx */ -#define GENWQE_FLAG_MSI_ENABLED (1 << 0) #define GENWQE_MAX_VFS 15 /* maximum 15 VFs are possible */ #define GENWQE_MAX_FUNCS 16 /* 1 PF and 15 VFs */ diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index 7f1b282d7d96..dd4617764f14 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -29,7 +29,7 @@ #include <linux/pci.h> #include <linux/string.h> #include <linux/fs.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/atomic.h> @@ -1396,7 +1396,7 @@ int genwqe_device_remove(struct genwqe_dev *cd) * application which will decrease this reference from * 1/unused to 0/illegal and not from 2/used 1/empty. */ - rc = atomic_read(&cd->cdev_genwqe.kobj.kref.refcount); + rc = kref_read(&cd->cdev_genwqe.kobj.kref); if (rc != 1) { dev_err(&pci_dev->dev, "[%s] err: cdev_genwqe...refcount=%d\n", __func__, rc); diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index fc2794b513fa..147b83011b58 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -740,13 +740,10 @@ int genwqe_read_softreset(struct genwqe_dev *cd) int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) { int rc; - struct pci_dev *pci_dev = cd->pci_dev; - rc = pci_enable_msi_range(pci_dev, 1, count); + rc = pci_alloc_irq_vectors(cd->pci_dev, 1, count, PCI_IRQ_MSI); if (rc < 0) return rc; - - cd->flags |= GENWQE_FLAG_MSI_ENABLED; return 0; } @@ -756,12 +753,7 @@ int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) */ void genwqe_reset_interrupt_capability(struct genwqe_dev *cd) { - struct pci_dev *pci_dev = cd->pci_dev; - - if (cd->flags & GENWQE_FLAG_MSI_ENABLED) { - pci_disable_msi(pci_dev); - cd->flags &= ~GENWQE_FLAG_MSI_ENABLED; - } + pci_free_irq_vectors(cd->pci_dev); } /** diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c index 520f58439080..e05c3245930a 100644 --- a/drivers/misc/ibmasm/ibmasmfs.c +++ b/drivers/misc/ibmasm/ibmasmfs.c @@ -76,7 +76,7 @@ #include <linux/fs.h> #include <linux/pagemap.h> #include <linux/slab.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/io.h> #include "ibmasm.h" #include "remote.h" diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 6b3bf9ab051d..c5a456b0a564 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -170,7 +170,7 @@ static void ibmasm_remove_one(struct pci_dev *pdev) ibmasm_unregister_uart(sp); dbg("Sending OS down message\n"); if (ibmasm_send_os_state(sp, SYSTEM_STATE_OS_DOWN)) - err("failed to get repsonse to 'Send OS State' command\n"); + err("failed to get response to 'Send OS State' command\n"); dbg("Disabling heartbeats\n"); ibmasm_heartbeat_exit(sp); dbg("Disabling interrupts\n"); diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c index 232034f5da48..5c7dd26db716 100644 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ b/drivers/misc/ibmasm/r_heartbeat.c @@ -20,7 +20,7 @@ * */ -#include <linux/sched.h> +#include <linux/sched/signal.h> #include "ibmasm.h" #include "dot_command.h" diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 99635dd9dbac..fc7efedbc4be 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -103,6 +103,8 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/module.h> +#include <linux/sched/task.h> + #include <asm/sections.h> #define v1printk(a...) do { \ diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index fb8705fc3aca..e389b0b5278d 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -23,6 +23,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> +#include <linux/sched/signal.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/types.h> diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index fdf954c2107f..67d27be60405 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h @@ -19,8 +19,14 @@ void lkdtm_SOFTLOCKUP(void); void lkdtm_HARDLOCKUP(void); void lkdtm_SPINLOCKUP(void); void lkdtm_HUNG_TASK(void); -void lkdtm_ATOMIC_UNDERFLOW(void); -void lkdtm_ATOMIC_OVERFLOW(void); +void lkdtm_REFCOUNT_SATURATE_INC(void); +void lkdtm_REFCOUNT_SATURATE_ADD(void); +void lkdtm_REFCOUNT_ZERO_DEC(void); +void lkdtm_REFCOUNT_ZERO_INC(void); +void lkdtm_REFCOUNT_ZERO_SUB(void); +void lkdtm_REFCOUNT_ZERO_ADD(void); +void lkdtm_CORRUPT_LIST_ADD(void); +void lkdtm_CORRUPT_LIST_DEL(void); /* lkdtm_heap.c */ void lkdtm_OVERWRITE_ALLOCATION(void); diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index 182ae1894b32..e3f4cd8876b5 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -5,8 +5,14 @@ * test source files. */ #include "lkdtm.h" +#include <linux/list.h> +#include <linux/refcount.h> #include <linux/sched.h> +struct lkdtm_list { + struct list_head node; +}; + /* * Make sure our attempts to over run the kernel stack doesn't trigger * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we @@ -75,12 +81,18 @@ void lkdtm_OVERFLOW(void) (void) recursive_loop(recur_count); } +static noinline void __lkdtm_CORRUPT_STACK(void *stack) +{ + memset(stack, 'a', 64); +} + noinline void lkdtm_CORRUPT_STACK(void) { /* Use default char array length that triggers stack protection. */ char data[8]; + __lkdtm_CORRUPT_STACK(&data); - memset((void *)data, 0, 64); + pr_info("Corrupted stack with '%16s'...\n", data); } void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) @@ -123,26 +135,147 @@ void lkdtm_HUNG_TASK(void) schedule(); } -void lkdtm_ATOMIC_UNDERFLOW(void) +void lkdtm_REFCOUNT_SATURATE_INC(void) { - atomic_t under = ATOMIC_INIT(INT_MIN); + refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); + + pr_info("attempting good refcount decrement\n"); + refcount_dec(&over); + refcount_inc(&over); + + pr_info("attempting bad refcount inc overflow\n"); + refcount_inc(&over); + refcount_inc(&over); + if (refcount_read(&over) == UINT_MAX) + pr_err("Correctly stayed saturated, but no BUG?!\n"); + else + pr_err("Fail: refcount wrapped\n"); +} + +void lkdtm_REFCOUNT_SATURATE_ADD(void) +{ + refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); + + pr_info("attempting good refcount decrement\n"); + refcount_dec(&over); + refcount_inc(&over); - pr_info("attempting good atomic increment\n"); - atomic_inc(&under); - atomic_dec(&under); + pr_info("attempting bad refcount add overflow\n"); + refcount_add(2, &over); + if (refcount_read(&over) == UINT_MAX) + pr_err("Correctly stayed saturated, but no BUG?!\n"); + else + pr_err("Fail: refcount wrapped\n"); +} + +void lkdtm_REFCOUNT_ZERO_DEC(void) +{ + refcount_t zero = REFCOUNT_INIT(1); - pr_info("attempting bad atomic underflow\n"); - atomic_dec(&under); + pr_info("attempting bad refcount decrement to zero\n"); + refcount_dec(&zero); + if (refcount_read(&zero) == 0) + pr_err("Stayed at zero, but no BUG?!\n"); + else + pr_err("Fail: refcount went crazy\n"); } -void lkdtm_ATOMIC_OVERFLOW(void) +void lkdtm_REFCOUNT_ZERO_SUB(void) { - atomic_t over = ATOMIC_INIT(INT_MAX); + refcount_t zero = REFCOUNT_INIT(1); - pr_info("attempting good atomic decrement\n"); - atomic_dec(&over); - atomic_inc(&over); + pr_info("attempting bad refcount subtract past zero\n"); + if (!refcount_sub_and_test(2, &zero)) + pr_info("wrap attempt was noticed\n"); + if (refcount_read(&zero) == 1) + pr_err("Correctly stayed above 0, but no BUG?!\n"); + else + pr_err("Fail: refcount wrapped\n"); +} + +void lkdtm_REFCOUNT_ZERO_INC(void) +{ + refcount_t zero = REFCOUNT_INIT(0); - pr_info("attempting bad atomic overflow\n"); - atomic_inc(&over); + pr_info("attempting bad refcount increment from zero\n"); + refcount_inc(&zero); + if (refcount_read(&zero) == 0) + pr_err("Stayed at zero, but no BUG?!\n"); + else + pr_err("Fail: refcount went past zero\n"); +} + +void lkdtm_REFCOUNT_ZERO_ADD(void) +{ + refcount_t zero = REFCOUNT_INIT(0); + + pr_info("attempting bad refcount addition from zero\n"); + refcount_add(2, &zero); + if (refcount_read(&zero) == 0) + pr_err("Stayed at zero, but no BUG?!\n"); + else + pr_err("Fail: refcount went past zero\n"); +} + +void lkdtm_CORRUPT_LIST_ADD(void) +{ + /* + * Initially, an empty list via LIST_HEAD: + * test_head.next = &test_head + * test_head.prev = &test_head + */ + LIST_HEAD(test_head); + struct lkdtm_list good, bad; + void *target[2] = { }; + void *redirection = ⌖ + + pr_info("attempting good list addition\n"); + + /* + * Adding to the list performs these actions: + * test_head.next->prev = &good.node + * good.node.next = test_head.next + * good.node.prev = test_head + * test_head.next = good.node + */ + list_add(&good.node, &test_head); + + pr_info("attempting corrupted list addition\n"); + /* + * In simulating this "write what where" primitive, the "what" is + * the address of &bad.node, and the "where" is the address held + * by "redirection". + */ + test_head.next = redirection; + list_add(&bad.node, &test_head); + + if (target[0] == NULL && target[1] == NULL) + pr_err("Overwrite did not happen, but no BUG?!\n"); + else + pr_err("list_add() corruption not detected!\n"); +} + +void lkdtm_CORRUPT_LIST_DEL(void) +{ + LIST_HEAD(test_head); + struct lkdtm_list item; + void *target[2] = { }; + void *redirection = ⌖ + + list_add(&item.node, &test_head); + + pr_info("attempting good list removal\n"); + list_del(&item.node); + + pr_info("attempting corrupted list removal\n"); + list_add(&item.node, &test_head); + + /* As with the list_add() test above, this corrupts "next". */ + item.node.next = redirection; + list_del(&item.node); + + if (target[0] == NULL && target[1] == NULL) + pr_err("Overwrite did not happen, but no BUG?!\n"); + else + pr_err("list_del() corruption not detected!\n"); } diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index f9154b8d67f6..b9a4cd4a9b68 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -197,6 +197,8 @@ struct crashtype crashtypes[] = { CRASHTYPE(EXCEPTION), CRASHTYPE(LOOP), CRASHTYPE(OVERFLOW), + CRASHTYPE(CORRUPT_LIST_ADD), + CRASHTYPE(CORRUPT_LIST_DEL), CRASHTYPE(CORRUPT_STACK), CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(OVERWRITE_ALLOCATION), @@ -218,8 +220,12 @@ struct crashtype crashtypes[] = { CRASHTYPE(WRITE_RO), CRASHTYPE(WRITE_RO_AFTER_INIT), CRASHTYPE(WRITE_KERN), - CRASHTYPE(ATOMIC_UNDERFLOW), - CRASHTYPE(ATOMIC_OVERFLOW), + CRASHTYPE(REFCOUNT_SATURATE_INC), + CRASHTYPE(REFCOUNT_SATURATE_ADD), + CRASHTYPE(REFCOUNT_ZERO_DEC), + CRASHTYPE(REFCOUNT_ZERO_INC), + CRASHTYPE(REFCOUNT_ZERO_SUB), + CRASHTYPE(REFCOUNT_ZERO_ADD), CRASHTYPE(USERCOPY_HEAP_SIZE_TO), CRASHTYPE(USERCOPY_HEAP_SIZE_FROM), CRASHTYPE(USERCOPY_HEAP_FLAG_TO), @@ -533,7 +539,9 @@ static void __exit lkdtm_module_exit(void) /* Handle test-specific clean-up. */ lkdtm_usercopy_exit(); - unregister_jprobe(lkdtm_jprobe); + if (lkdtm_jprobe != NULL) + unregister_jprobe(lkdtm_jprobe); + pr_info("Crash point unregistered\n"); } diff --git a/drivers/misc/lkdtm_heap.c b/drivers/misc/lkdtm_heap.c index 0f1581664c1c..ffb6aeac07b3 100644 --- a/drivers/misc/lkdtm_heap.c +++ b/drivers/misc/lkdtm_heap.c @@ -4,6 +4,7 @@ */ #include "lkdtm.h" #include <linux/slab.h> +#include <linux/sched.h> /* * This tries to stay within the next largest power-of-2 kmalloc cache diff --git a/drivers/misc/lkdtm_perms.c b/drivers/misc/lkdtm_perms.c index 45f1c0f96612..c7635a79341f 100644 --- a/drivers/misc/lkdtm_perms.c +++ b/drivers/misc/lkdtm_perms.c @@ -60,15 +60,18 @@ static noinline void execute_location(void *dst, bool write) static void execute_user_location(void *dst) { + int copied; + /* Intentionally crossing kernel/user memory boundary. */ void (*func)(void) = dst; pr_info("attempting ok execution at %p\n", do_nothing); do_nothing(); - if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE)) + copied = access_process_vm(current, (unsigned long)dst, do_nothing, + EXEC_SIZE, FOLL_WRITE); + if (copied < EXEC_SIZE) return; - flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE); pr_info("attempting bad execution at %p\n", func); func(); } diff --git a/drivers/misc/lkdtm_usercopy.c b/drivers/misc/lkdtm_usercopy.c index 1dd611423d8b..df6ac985fbb5 100644 --- a/drivers/misc/lkdtm_usercopy.c +++ b/drivers/misc/lkdtm_usercopy.c @@ -5,6 +5,7 @@ #include "lkdtm.h" #include <linux/slab.h> #include <linux/vmalloc.h> +#include <linux/sched/task_stack.h> #include <linux/mman.h> #include <linux/uaccess.h> #include <asm/cacheflush.h> diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 7ae89b4a21d5..0e7406ccb6dd 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -132,8 +132,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); - cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, - typeof(*cb), list); + cb = list_first_entry_or_null(&dev->amthif_cmd_list, typeof(*cb), list); if (!cb) { dev->iamthif_state = MEI_IAMTHIF_IDLE; cl->fp = NULL; @@ -144,7 +143,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_state = MEI_IAMTHIF_WRITING; cl->fp = cb->fp; - ret = mei_cl_write(cl, cb, false); + ret = mei_cl_write(cl, cb); if (ret < 0) return ret; @@ -167,7 +166,7 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) struct mei_device *dev = cl->dev; - list_add_tail(&cb->list, &dev->amthif_cmd_list.list); + list_add_tail(&cb->list, &dev->amthif_cmd_list); /* * The previous request is still in processing, queue this one. @@ -211,7 +210,7 @@ unsigned int mei_amthif_poll(struct file *file, poll_table *wait) * Return: 0, OK; otherwise, error. */ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { int ret; @@ -237,7 +236,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, */ int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev; int ret; @@ -312,50 +311,30 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) } /** - * mei_clear_list - removes all callbacks associated with file - * from mei_cb_list - * - * @file: file structure - * @mei_cb_list: callbacks list - * - * mei_clear_list is called to clear resources associated with file - * when application calls close function or Ctrl-C was pressed - */ -static void mei_clear_list(const struct file *file, - struct list_head *mei_cb_list) -{ - struct mei_cl_cb *cb, *next; - - list_for_each_entry_safe(cb, next, mei_cb_list, list) - if (file == cb->fp) - mei_io_cb_free(cb); -} - -/** * mei_amthif_release - the release function * * @dev: device structure -* @file: pointer to file structure +* @fp: pointer to file structure * * Return: 0 on success, <0 on error */ -int mei_amthif_release(struct mei_device *dev, struct file *file) +int mei_amthif_release(struct mei_device *dev, struct file *fp) { - struct mei_cl *cl = file->private_data; + struct mei_cl *cl = fp->private_data; if (dev->iamthif_open_count > 0) dev->iamthif_open_count--; - if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { + if (cl->fp == fp && dev->iamthif_state != MEI_IAMTHIF_IDLE) { dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", - dev->iamthif_state); + dev->iamthif_state); dev->iamthif_canceled = true; } - mei_clear_list(file, &dev->amthif_cmd_list.list); - mei_clear_list(file, &cl->rd_completed); - mei_clear_list(file, &dev->ctrl_rd_list.list); + /* Don't clean ctrl_rd_list here, the reads has to be completed */ + mei_io_list_free_fp(&dev->amthif_cmd_list, fp); + mei_io_list_free_fp(&cl->rd_completed, fp); return 0; } diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 75b9d4ac8b1e..29f2daed37e0 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -38,6 +38,9 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; #define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) +#define MEI_UUID_MKHIF_FIX UUID_LE(0x55213584, 0x9a29, 0x4916, \ + 0xba, 0xdf, 0xf, 0xb7, 0xed, 0x68, 0x2a, 0xeb) + #define MEI_UUID_ANY NULL_UUID_LE /** @@ -69,6 +72,90 @@ static void blacklist(struct mei_cl_device *cldev) cldev->do_match = 0; } +#define OSTYPE_LINUX 2 +struct mei_os_ver { + __le16 build; + __le16 reserved1; + u8 os_type; + u8 major; + u8 minor; + u8 reserved2; +} __packed; + +#define MKHI_FEATURE_PTT 0x10 + +struct mkhi_rule_id { + __le16 rule_type; + u8 feature_id; + u8 reserved; +} __packed; + +struct mkhi_fwcaps { + struct mkhi_rule_id id; + u8 len; + u8 data[0]; +} __packed; + +#define MKHI_FWCAPS_GROUP_ID 0x3 +#define MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD 6 +struct mkhi_msg_hdr { + u8 group_id; + u8 command; + u8 reserved; + u8 result; +} __packed; + +struct mkhi_msg { + struct mkhi_msg_hdr hdr; + u8 data[0]; +} __packed; + +static int mei_osver(struct mei_cl_device *cldev) +{ + const size_t size = sizeof(struct mkhi_msg_hdr) + + sizeof(struct mkhi_fwcaps) + + sizeof(struct mei_os_ver); + char buf[size]; + struct mkhi_msg *req; + struct mkhi_fwcaps *fwcaps; + struct mei_os_ver *os_ver; + unsigned int mode = MEI_CL_IO_TX_BLOCKING | MEI_CL_IO_TX_INTERNAL; + + memset(buf, 0, size); + + req = (struct mkhi_msg *)buf; + req->hdr.group_id = MKHI_FWCAPS_GROUP_ID; + req->hdr.command = MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD; + + fwcaps = (struct mkhi_fwcaps *)req->data; + + fwcaps->id.rule_type = 0x0; + fwcaps->id.feature_id = MKHI_FEATURE_PTT; + fwcaps->len = sizeof(*os_ver); + os_ver = (struct mei_os_ver *)fwcaps->data; + os_ver->os_type = OSTYPE_LINUX; + + return __mei_cl_send(cldev->cl, buf, size, mode); +} + +static void mei_mkhi_fix(struct mei_cl_device *cldev) +{ + int ret; + + if (!cldev->bus->hbm_f_os_supported) + return; + + ret = mei_cldev_enable(cldev); + if (ret) + return; + + ret = mei_osver(cldev); + if (ret < 0) + dev_err(&cldev->dev, "OS version command failed %d\n", ret); + + mei_cldev_disable(cldev); +} + /** * mei_wd - wd client on the bus, change protocol version * as the API has changed. @@ -162,7 +249,8 @@ static int mei_nfc_if_version(struct mei_cl *cl, WARN_ON(mutex_is_locked(&bus->device_lock)); - ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), 1); + ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), + MEI_CL_IO_TX_BLOCKING); if (ret < 0) { dev_err(bus->dev, "Could not send IF version cmd\n"); return ret; @@ -177,7 +265,7 @@ static int mei_nfc_if_version(struct mei_cl *cl, return -ENOMEM; ret = 0; - bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); + bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length, 0); if (bytes_recv < if_version_length) { dev_err(bus->dev, "Could not read IF version\n"); ret = -EIO; @@ -309,6 +397,7 @@ static struct mei_fixup { MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), MEI_FIXUP(MEI_UUID_WD, mei_wd), + MEI_FIXUP(MEI_UUID_MKHIF_FIX, mei_mkhi_fix), }; /** diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 8cac7ef9ad0d..df5f78ae3d25 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/kernel.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/slab.h> @@ -36,12 +36,12 @@ * @cl: host client * @buf: buffer to send * @length: buffer length - * @blocking: wait for write completion + * @mode: sending mode * * Return: written size bytes or < 0 on error */ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, - bool blocking) + unsigned int mode) { struct mei_device *bus; struct mei_cl_cb *cb; @@ -80,9 +80,11 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, goto out; } + cb->internal = !!(mode & MEI_CL_IO_TX_INTERNAL); + cb->blocking = !!(mode & MEI_CL_IO_TX_BLOCKING); memcpy(cb->buf.data, buf, length); - rets = mei_cl_write(cl, cb, blocking); + rets = mei_cl_write(cl, cb); out: mutex_unlock(&bus->device_lock); @@ -96,15 +98,18 @@ out: * @cl: host client * @buf: buffer to receive * @length: buffer length + * @mode: io mode * * Return: read size in bytes of < 0 on error */ -ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) +ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, + unsigned int mode) { struct mei_device *bus; struct mei_cl_cb *cb; size_t r_length; ssize_t rets; + bool nonblock = !!(mode & MEI_CL_IO_RX_NONBLOCK); if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -125,6 +130,11 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (rets && rets != -EBUSY) goto out; + if (nonblock) { + rets = -EAGAIN; + goto out; + } + /* wait on event only if there is no other waiter */ /* synchronized under device mutex */ if (!waitqueue_active(&cl->rx_wait)) { @@ -185,14 +195,30 @@ ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; - if (cl == NULL) - return -ENODEV; - - return __mei_cl_send(cl, buf, length, 1); + return __mei_cl_send(cl, buf, length, MEI_CL_IO_TX_BLOCKING); } EXPORT_SYMBOL_GPL(mei_cldev_send); /** + * mei_cldev_recv_nonblock - non block client receive (read) + * + * @cldev: me client device + * @buf: buffer to receive + * @length: buffer length + * + * Return: read size in bytes of < 0 on error + * -EAGAIN if function will block. + */ +ssize_t mei_cldev_recv_nonblock(struct mei_cl_device *cldev, u8 *buf, + size_t length) +{ + struct mei_cl *cl = cldev->cl; + + return __mei_cl_recv(cl, buf, length, MEI_CL_IO_RX_NONBLOCK); +} +EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock); + +/** * mei_cldev_recv - client receive (read) * * @cldev: me client device @@ -205,39 +231,45 @@ ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; - if (cl == NULL) - return -ENODEV; - - return __mei_cl_recv(cl, buf, length); + return __mei_cl_recv(cl, buf, length, 0); } EXPORT_SYMBOL_GPL(mei_cldev_recv); /** - * mei_cl_bus_event_work - dispatch rx event for a bus device - * and schedule new work + * mei_cl_bus_rx_work - dispatch rx event for a bus device * * @work: work */ -static void mei_cl_bus_event_work(struct work_struct *work) +static void mei_cl_bus_rx_work(struct work_struct *work) { struct mei_cl_device *cldev; struct mei_device *bus; - cldev = container_of(work, struct mei_cl_device, event_work); + cldev = container_of(work, struct mei_cl_device, rx_work); bus = cldev->bus; - if (cldev->event_cb) - cldev->event_cb(cldev, cldev->events, cldev->event_context); + if (cldev->rx_cb) + cldev->rx_cb(cldev); - cldev->events = 0; + mutex_lock(&bus->device_lock); + mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); + mutex_unlock(&bus->device_lock); +} - /* Prepare for the next read */ - if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { - mutex_lock(&bus->device_lock); - mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); - mutex_unlock(&bus->device_lock); - } +/** + * mei_cl_bus_notif_work - dispatch FW notif event for a bus device + * + * @work: work + */ +static void mei_cl_bus_notif_work(struct work_struct *work) +{ + struct mei_cl_device *cldev; + + cldev = container_of(work, struct mei_cl_device, notif_work); + + if (cldev->notif_cb) + cldev->notif_cb(cldev); } /** @@ -252,18 +284,13 @@ bool mei_cl_bus_notify_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; - if (!cldev || !cldev->event_cb) - return false; - - if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF))) + if (!cldev || !cldev->notif_cb) return false; if (!cl->notify_ev) return false; - set_bit(MEI_CL_EVENT_NOTIF, &cldev->events); - - schedule_work(&cldev->event_work); + schedule_work(&cldev->notif_work); cl->notify_ev = false; @@ -271,7 +298,7 @@ bool mei_cl_bus_notify_event(struct mei_cl *cl) } /** - * mei_cl_bus_rx_event - schedule rx event + * mei_cl_bus_rx_event - schedule rx event * * @cl: host client * @@ -282,66 +309,81 @@ bool mei_cl_bus_rx_event(struct mei_cl *cl) { struct mei_cl_device *cldev = cl->cldev; - if (!cldev || !cldev->event_cb) + if (!cldev || !cldev->rx_cb) return false; - if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX))) - return false; - - set_bit(MEI_CL_EVENT_RX, &cldev->events); - - schedule_work(&cldev->event_work); + schedule_work(&cldev->rx_work); return true; } /** - * mei_cldev_register_event_cb - register event callback + * mei_cldev_register_rx_cb - register Rx event callback * * @cldev: me client devices - * @event_cb: callback function - * @events_mask: requested events bitmask - * @context: driver context data + * @rx_cb: callback function * * Return: 0 on success * -EALREADY if an callback is already registered * <0 on other errors */ -int mei_cldev_register_event_cb(struct mei_cl_device *cldev, - unsigned long events_mask, - mei_cldev_event_cb_t event_cb, void *context) +int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb) { struct mei_device *bus = cldev->bus; int ret; - if (cldev->event_cb) + if (!rx_cb) + return -EINVAL; + if (cldev->rx_cb) return -EALREADY; - cldev->events = 0; - cldev->events_mask = events_mask; - cldev->event_cb = event_cb; - cldev->event_context = context; - INIT_WORK(&cldev->event_work, mei_cl_bus_event_work); + cldev->rx_cb = rx_cb; + INIT_WORK(&cldev->rx_work, mei_cl_bus_rx_work); - if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { - mutex_lock(&bus->device_lock); - ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); - mutex_unlock(&bus->device_lock); - if (ret && ret != -EBUSY) - return ret; - } + mutex_lock(&bus->device_lock); + ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); + mutex_unlock(&bus->device_lock); + if (ret && ret != -EBUSY) + return ret; - if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) { - mutex_lock(&bus->device_lock); - ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0); - mutex_unlock(&bus->device_lock); - if (ret) - return ret; - } + return 0; +} +EXPORT_SYMBOL_GPL(mei_cldev_register_rx_cb); + +/** + * mei_cldev_register_notif_cb - register FW notification event callback + * + * @cldev: me client devices + * @notif_cb: callback function + * + * Return: 0 on success + * -EALREADY if an callback is already registered + * <0 on other errors + */ +int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, + mei_cldev_cb_t notif_cb) +{ + struct mei_device *bus = cldev->bus; + int ret; + + if (!notif_cb) + return -EINVAL; + + if (cldev->notif_cb) + return -EALREADY; + + cldev->notif_cb = notif_cb; + INIT_WORK(&cldev->notif_work, mei_cl_bus_notif_work); + + mutex_lock(&bus->device_lock); + ret = mei_cl_notify_request(cldev->cl, NULL, 1); + mutex_unlock(&bus->device_lock); + if (ret) + return ret; return 0; } -EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb); +EXPORT_SYMBOL_GPL(mei_cldev_register_notif_cb); /** * mei_cldev_get_drvdata - driver data getter @@ -403,12 +445,12 @@ EXPORT_SYMBOL_GPL(mei_cldev_ver); */ bool mei_cldev_enabled(struct mei_cl_device *cldev) { - return cldev->cl && mei_cl_is_connected(cldev->cl); + return mei_cl_is_connected(cldev->cl); } EXPORT_SYMBOL_GPL(mei_cldev_enabled); /** - * mei_cldev_enable_device - enable me client device + * mei_cldev_enable - enable me client device * create connection with me client * * @cldev: me client device @@ -423,14 +465,13 @@ int mei_cldev_enable(struct mei_cl_device *cldev) cl = cldev->cl; - if (!cl) { + if (cl->state == MEI_FILE_UNINITIALIZED) { mutex_lock(&bus->device_lock); - cl = mei_cl_alloc_linked(bus); + ret = mei_cl_link(cl); mutex_unlock(&bus->device_lock); - if (IS_ERR(cl)) - return PTR_ERR(cl); + if (ret) + return ret; /* update pointers */ - cldev->cl = cl; cl->cldev = cldev; } @@ -458,6 +499,25 @@ out: EXPORT_SYMBOL_GPL(mei_cldev_enable); /** + * mei_cldev_unregister_callbacks - internal wrapper for unregistering + * callbacks. + * + * @cldev: client device + */ +static void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev) +{ + if (cldev->rx_cb) { + cancel_work_sync(&cldev->rx_work); + cldev->rx_cb = NULL; + } + + if (cldev->notif_cb) { + cancel_work_sync(&cldev->notif_work); + cldev->notif_cb = NULL; + } +} + +/** * mei_cldev_disable - disable me client device * disconnect form the me client * @@ -471,19 +531,19 @@ int mei_cldev_disable(struct mei_cl_device *cldev) struct mei_cl *cl; int err; - if (!cldev || !cldev->cl) + if (!cldev) return -ENODEV; cl = cldev->cl; bus = cldev->bus; - cldev->event_cb = NULL; + mei_cldev_unregister_callbacks(cldev); mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { - dev_err(bus->dev, "Already disconnected"); + dev_dbg(bus->dev, "Already disconnected"); err = 0; goto out; } @@ -497,15 +557,43 @@ out: mei_cl_flush_queues(cl, NULL); mei_cl_unlink(cl); - kfree(cl); - cldev->cl = NULL; - mutex_unlock(&bus->device_lock); return err; } 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 @@ -629,15 +717,12 @@ static int mei_cl_device_remove(struct device *dev) if (!cldev || !dev->driver) return 0; - if (cldev->event_cb) { - cldev->event_cb = NULL; - cancel_work_sync(&cldev->event_work); - } - cldrv = to_mei_cl_driver(dev->driver); if (cldrv->remove) ret = cldrv->remove(cldev); + mei_cldev_unregister_callbacks(cldev); + module_put(THIS_MODULE); dev->driver = NULL; return ret; @@ -754,6 +839,7 @@ static void mei_cl_bus_dev_release(struct device *dev) mei_me_cl_put(cldev->me_cl); mei_dev_bus_put(cldev->bus); + kfree(cldev->cl); kfree(cldev); } @@ -786,17 +872,25 @@ static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, struct mei_me_client *me_cl) { struct mei_cl_device *cldev; + struct mei_cl *cl; cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); if (!cldev) return NULL; + cl = mei_cl_allocate(bus); + if (!cl) { + kfree(cldev); + return NULL; + } + device_initialize(&cldev->dev); cldev->dev.parent = bus->dev; cldev->dev.bus = &mei_cl_bus_type; cldev->dev.type = &mei_cl_device_type; cldev->bus = mei_dev_bus_get(bus); cldev->me_cl = mei_me_cl_get(me_cl); + cldev->cl = cl; mei_cl_bus_set_name(cldev); cldev->is_added = 0; INIT_LIST_HEAD(&cldev->bus_list); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 6fe02350578d..d3e3372424d6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -14,7 +14,7 @@ * */ -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/slab.h> @@ -377,19 +377,19 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, } /** - * __mei_io_list_flush - removes and frees cbs belonging to cl. + * __mei_io_list_flush_cl - removes and frees cbs belonging to cl. * - * @list: an instance of our list structure + * @head: an instance of our list structure * @cl: host client, can be NULL for flushing the whole list * @free: whether to free the cbs */ -static void __mei_io_list_flush(struct mei_cl_cb *list, - struct mei_cl *cl, bool free) +static void __mei_io_list_flush_cl(struct list_head *head, + const struct mei_cl *cl, bool free) { struct mei_cl_cb *cb, *next; /* enable removing everything if no cl is specified */ - list_for_each_entry_safe(cb, next, &list->list, list) { + list_for_each_entry_safe(cb, next, head, list) { if (!cl || mei_cl_cmp_id(cl, cb->cl)) { list_del_init(&cb->list); if (free) @@ -399,25 +399,42 @@ static void __mei_io_list_flush(struct mei_cl_cb *list, } /** - * mei_io_list_flush - removes list entry belonging to cl. + * mei_io_list_flush_cl - removes list entry belonging to cl. * - * @list: An instance of our list structure + * @head: An instance of our list structure * @cl: host client */ -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +static inline void mei_io_list_flush_cl(struct list_head *head, + const struct mei_cl *cl) { - __mei_io_list_flush(list, cl, false); + __mei_io_list_flush_cl(head, cl, false); } /** - * mei_io_list_free - removes cb belonging to cl and free them + * mei_io_list_free_cl - removes cb belonging to cl and free them * - * @list: An instance of our list structure + * @head: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) +static inline void mei_io_list_free_cl(struct list_head *head, + const struct mei_cl *cl) { - __mei_io_list_flush(list, cl, true); + __mei_io_list_flush_cl(head, cl, true); +} + +/** + * mei_io_list_free_fp - free cb from a list that matches file pointer + * + * @head: io list + * @fp: file pointer (matching cb file object), may be NULL + */ +void mei_io_list_free_fp(struct list_head *head, const struct file *fp) +{ + struct mei_cl_cb *cb, *next; + + list_for_each_entry_safe(cb, next, head, list) + if (!fp || fp == cb->fp) + mei_io_cb_free(cb); } /** @@ -425,7 +442,7 @@ static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) * * @cl: host client * @length: size of the buffer - * @type: operation type + * @fop_type: operation type * @fp: associated file pointer (might be NULL) * * Return: cb on success and NULL on failure @@ -459,7 +476,7 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, * * @cl: host client * @length: size of the buffer - * @type: operation type + * @fop_type: operation type * @fp: associated file pointer (might be NULL) * * Return: cb on success and NULL on failure @@ -479,7 +496,7 @@ struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, if (!cb) return NULL; - list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list); + list_add_tail(&cb->list, &cl->dev->ctrl_wr_list); return cb; } @@ -504,27 +521,6 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp) } /** - * mei_cl_read_cb_flush - free client's read pending and completed cbs - * for a specific file - * - * @cl: host client - * @fp: file pointer (matching cb file object), may be NULL - */ -void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp) -{ - struct mei_cl_cb *cb, *next; - - list_for_each_entry_safe(cb, next, &cl->rd_completed, list) - if (!fp || fp == cb->fp) - mei_io_cb_free(cb); - - - list_for_each_entry_safe(cb, next, &cl->rd_pending, list) - if (!fp || fp == cb->fp) - mei_io_cb_free(cb); -} - -/** * mei_cl_flush_queues - flushes queue lists belonging to cl. * * @cl: host client @@ -542,18 +538,16 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) dev = cl->dev; cl_dbg(dev, cl, "remove list entry belonging to cl\n"); - mei_io_list_free(&cl->dev->write_list, cl); - mei_io_list_free(&cl->dev->write_waiting_list, cl); - mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); - mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); - mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); - - mei_cl_read_cb_flush(cl, fp); + mei_io_list_free_cl(&cl->dev->write_list, cl); + mei_io_list_free_cl(&cl->dev->write_waiting_list, cl); + mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl); + mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl); + mei_io_list_free_fp(&cl->rd_pending, fp); + mei_io_list_free_fp(&cl->rd_completed, fp); return 0; } - /** * mei_cl_init - initializes cl. * @@ -571,7 +565,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) INIT_LIST_HEAD(&cl->rd_pending); INIT_LIST_HEAD(&cl->link); cl->writing_state = MEI_IDLE; - cl->state = MEI_FILE_INITIALIZING; + cl->state = MEI_FILE_UNINITIALIZED; cl->dev = dev; } @@ -672,7 +666,12 @@ int mei_cl_unlink(struct mei_cl *cl) list_del_init(&cl->link); - cl->state = MEI_FILE_INITIALIZING; + cl->state = MEI_FILE_UNINITIALIZED; + cl->writing_state = MEI_IDLE; + + WARN_ON(!list_empty(&cl->rd_completed) || + !list_empty(&cl->rd_pending) || + !list_empty(&cl->link)); return 0; } @@ -686,7 +685,7 @@ void mei_host_client_init(struct mei_device *dev) pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); - pm_runtime_autosuspend(dev->dev); + pm_request_autosuspend(dev->dev); } /** @@ -751,24 +750,27 @@ static void mei_cl_wake_all(struct mei_cl *cl) * * @cl: host client */ -void mei_cl_set_disconnected(struct mei_cl *cl) +static void mei_cl_set_disconnected(struct mei_cl *cl) { struct mei_device *dev = cl->dev; if (cl->state == MEI_FILE_DISCONNECTED || - cl->state == MEI_FILE_INITIALIZING) + cl->state <= MEI_FILE_INITIALIZING) return; cl->state = MEI_FILE_DISCONNECTED; - mei_io_list_free(&dev->write_list, cl); - mei_io_list_free(&dev->write_waiting_list, cl); - mei_io_list_flush(&dev->ctrl_rd_list, cl); - mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_io_list_free_cl(&dev->write_list, cl); + mei_io_list_free_cl(&dev->write_waiting_list, cl); + mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); + mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); + mei_io_list_free_cl(&dev->amthif_cmd_list, cl); mei_cl_wake_all(cl); cl->rx_flow_ctrl_creds = 0; cl->tx_flow_ctrl_creds = 0; cl->timer_count = 0; + mei_cl_bus_module_put(cl); + if (!cl->me_cl) return; @@ -824,7 +826,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); cl->timer_count = MEI_CONNECT_TIMEOUT; mei_schedule_stall_timer(dev); @@ -842,7 +844,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) * Return: 0, OK; otherwise, error. */ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -857,7 +859,7 @@ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, ret = mei_cl_send_disconnect(cl, cb); if (ret) - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -979,7 +981,7 @@ static bool mei_cl_is_other_connecting(struct mei_cl *cl) dev = cl->dev; - list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) { + list_for_each_entry(cb, &dev->ctrl_rd_list, list) { if (cb->fop_type == MEI_FOP_CONNECT && mei_cl_me_id(cl) == mei_cl_me_id(cb->cl)) return true; @@ -1010,7 +1012,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); cl->timer_count = MEI_CONNECT_TIMEOUT; mei_schedule_stall_timer(dev); return 0; @@ -1026,7 +1028,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) * Return: 0, OK; otherwise, error. */ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -1044,7 +1046,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, rets = mei_cl_send_connect(cl, cb); if (rets) - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return rets; } @@ -1072,13 +1074,17 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, dev = cl->dev; + if (!mei_cl_bus_module_get(cl)) + return -ENODEV; + rets = mei_cl_set_connecting(cl, me_cl); if (rets) - return rets; + goto nortpm; if (mei_cl_is_fixed_address(cl)) { cl->state = MEI_FILE_CONNECTED; - return 0; + rets = 0; + goto nortpm; } rets = pm_runtime_get(dev->dev); @@ -1112,8 +1118,8 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, if (!mei_cl_is_connected(cl)) { if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { - mei_io_list_flush(&dev->ctrl_rd_list, cl); - mei_io_list_flush(&dev->ctrl_wr_list, cl); + mei_io_list_flush_cl(&dev->ctrl_rd_list, cl); + mei_io_list_flush_cl(&dev->ctrl_wr_list, cl); /* ignore disconnect return valuue; * in case of failure reset will be invoked */ @@ -1265,7 +1271,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) * Return: 0 on such and error otherwise. */ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -1283,11 +1289,11 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, ret = mei_hbm_cl_notify_req(dev, cl, request); if (ret) { cl->status = ret; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); return 0; } @@ -1320,6 +1326,9 @@ int mei_cl_notify_request(struct mei_cl *cl, return -EOPNOTSUPP; } + if (!mei_cl_is_connected(cl)) + return -ENODEV; + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { pm_runtime_put_noidle(dev->dev); @@ -1339,7 +1348,7 @@ int mei_cl_notify_request(struct mei_cl *cl, rets = -ENODEV; goto out; } - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + list_move_tail(&cb->list, &dev->ctrl_rd_list); } mutex_unlock(&dev->device_lock); @@ -1414,6 +1423,11 @@ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) dev = cl->dev; + if (!dev->hbm_f_ev_supported) { + cl_dbg(dev, cl, "notifications not supported\n"); + return -EOPNOTSUPP; + } + if (!mei_cl_is_connected(cl)) return -ENODEV; @@ -1514,7 +1528,7 @@ nortpm: * Return: 0, OK; otherwise error. */ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev; struct mei_msg_data *buf; @@ -1536,7 +1550,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1; if (rets < 0) - return rets; + goto err; if (rets == 0) { cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); @@ -1570,11 +1584,8 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, cb->buf.size, cb->buf_idx); rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); - if (rets) { - cl->status = rets; - list_move_tail(&cb->list, &cmpl_list->list); - return rets; - } + if (rets) + goto err; cl->status = 0; cl->writing_state = MEI_WRITING; @@ -1582,14 +1593,21 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, cb->completed = mei_hdr.msg_complete == 1; if (first_chunk) { - if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) - return -EIO; + if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) { + rets = -EIO; + goto err; + } } if (mei_hdr.msg_complete) - list_move_tail(&cb->list, &dev->write_waiting_list.list); + list_move_tail(&cb->list, &dev->write_waiting_list); return 0; + +err: + cl->status = rets; + list_move_tail(&cb->list, cmpl_list); + return rets; } /** @@ -1598,18 +1616,17 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, * * @cl: host client * @cb: write callback with filled data - * @blocking: block until completed * * Return: number of bytes sent on success, <0 on failure. */ -int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb) { struct mei_device *dev; struct mei_msg_data *buf; struct mei_msg_hdr mei_hdr; int size; int rets; - + bool blocking; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -1621,6 +1638,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) buf = &cb->buf; size = buf->size; + blocking = cb->blocking; cl_dbg(dev, cl, "size=%d\n", size); @@ -1678,9 +1696,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) out: if (mei_hdr.msg_complete) - list_add_tail(&cb->list, &dev->write_waiting_list.list); + list_add_tail(&cb->list, &dev->write_waiting_list); else - list_add_tail(&cb->list, &dev->write_list.list); + list_add_tail(&cb->list, &dev->write_list); cb = NULL; if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index d2bfabecd882..545ae319ba90 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -83,17 +83,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl) * MEI IO Functions */ void mei_io_cb_free(struct mei_cl_cb *priv_cb); - -/** - * mei_io_list_init - Sets up a queue list. - * - * @list: An instance cl callback structure - */ -static inline void mei_io_list_init(struct mei_cl_cb *list) -{ - INIT_LIST_HEAD(&list->list); -} -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl); +void mei_io_list_free_fp(struct list_head *head, const struct file *fp); /* * MEI Host Client Functions @@ -110,7 +100,6 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev); struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp); -void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp); struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops type, const struct file *fp); @@ -209,19 +198,18 @@ static inline u8 mei_cl_host_addr(const struct mei_cl *cl) } int mei_cl_disconnect(struct mei_cl *cl); -void mei_cl_set_disconnected(struct mei_cl *cl); int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, const struct file *file); int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp); int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr, - struct mei_cl_cb *cmpl_list); -int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); + struct list_head *cmpl_list); +int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); @@ -232,7 +220,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); int mei_cl_notify_request(struct mei_cl *cl, const struct file *file, u8 request); int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); void mei_cl_notify(struct mei_cl *cl); diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index c6c051b52f55..a617aa5a3ad8 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -67,7 +67,7 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, me_cl->props.max_number_of_connections, me_cl->props.max_msg_length, me_cl->props.single_recv_buf, - atomic_read(&me_cl->refcnt.refcount)); + kref_read(&me_cl->refcnt)); mei_me_cl_put(me_cl); } @@ -180,6 +180,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, dev->hbm_f_ev_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tFA: %01d\n", dev->hbm_f_fa_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tOS: %01d\n", + dev->hbm_f_os_supported); } pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index dd7f15a65eed..ba3a774c8d71 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -815,7 +815,7 @@ static void mei_hbm_cl_res(struct mei_device *dev, struct mei_cl_cb *cb, *next; cl = NULL; - list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) { cl = cb->cl; @@ -989,6 +989,10 @@ static void mei_hbm_config_features(struct mei_device *dev) /* Fixed Address Client Support */ if (dev->version.major_version >= HBM_MAJOR_VERSION_FA) dev->hbm_f_fa_supported = 1; + + /* OS ver message Support */ + if (dev->version.major_version >= HBM_MAJOR_VERSION_OS) + dev->hbm_f_os_supported = 1; } /** diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 7ad15d678878..c8307e8b4c16 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -122,6 +122,8 @@ #define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ #define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ +#define MEI_DEV_ID_LBG 0xA1BA /* Lewisburg (SPT) */ + #define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */ #define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */ diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 56c2101e80ad..71216affcab1 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -140,6 +140,19 @@ static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) } /** + * mei_hcsr_set_hig - set host interrupt (set H_IG) + * + * @dev: the device structure + */ +static inline void mei_hcsr_set_hig(struct mei_device *dev) +{ + u32 hcsr; + + hcsr = mei_hcsr_read(dev) | H_IG; + mei_hcsr_set(dev, hcsr); +} + +/** * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register * * @dev: the device structure @@ -246,6 +259,36 @@ static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev) return hw->pg_state; } +static inline u32 me_intr_src(u32 hcsr) +{ + return hcsr & H_CSR_IS_MASK; +} + +/** + * me_intr_disable - disables mei device interrupts + * using supplied hcsr register value. + * + * @dev: the device structure + * @hcsr: supplied hcsr register value + */ +static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) +{ + hcsr &= ~H_CSR_IE_MASK; + mei_hcsr_set(dev, hcsr); +} + +/** + * mei_me_intr_clear - clear and stop interrupts + * + * @dev: the device structure + * @hcsr: supplied hcsr register value + */ +static inline void me_intr_clear(struct mei_device *dev, u32 hcsr) +{ + if (me_intr_src(hcsr)) + mei_hcsr_write(dev, hcsr); +} + /** * mei_me_intr_clear - clear and stop interrupts * @@ -255,8 +298,7 @@ static void mei_me_intr_clear(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - if (hcsr & H_CSR_IS_MASK) - mei_hcsr_write(dev, hcsr); + me_intr_clear(dev, hcsr); } /** * mei_me_intr_enable - enables mei device interrupts @@ -280,8 +322,19 @@ static void mei_me_intr_disable(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - hcsr &= ~H_CSR_IE_MASK; - mei_hcsr_set(dev, hcsr); + me_intr_disable(dev, hcsr); +} + +/** + * mei_me_synchronize_irq - wait for pending IRQ handlers + * + * @dev: the device structure + */ +static void mei_me_synchronize_irq(struct mei_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + + synchronize_irq(pdev->irq); } /** @@ -341,6 +394,19 @@ static bool mei_me_hw_is_ready(struct mei_device *dev) } /** + * mei_me_hw_is_resetting - check whether the me(hw) is in reset + * + * @dev: mei device + * Return: bool + */ +static bool mei_me_hw_is_resetting(struct mei_device *dev) +{ + u32 mecsr = mei_me_mecsr_read(dev); + + return (mecsr & ME_RST_HRA) == ME_RST_HRA; +} + +/** * mei_me_hw_ready_wait - wait until the me(hw) has turned ready * or timeout is reached * @@ -450,7 +516,7 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) /** - * mei_me_write_message - writes a message to mei device. + * mei_me_hbuf_write - writes a message to host hw buffer. * * @dev: the device structure * @header: mei HECI header of message @@ -458,14 +524,13 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) * * Return: -EIO if write has failed */ -static int mei_me_write_message(struct mei_device *dev, - struct mei_msg_hdr *header, - unsigned char *buf) +static int mei_me_hbuf_write(struct mei_device *dev, + struct mei_msg_hdr *header, + const unsigned char *buf) { unsigned long rem; unsigned long length = header->length; u32 *reg_buf = (u32 *)buf; - u32 hcsr; u32 dw_cnt; int i; int empty_slots; @@ -492,8 +557,7 @@ static int mei_me_write_message(struct mei_device *dev, mei_me_hcbww_write(dev, reg); } - hcsr = mei_hcsr_read(dev) | H_IG; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set_hig(dev); if (!mei_me_hw_is_ready(dev)) return -EIO; @@ -540,7 +604,6 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, unsigned long buffer_length) { u32 *reg_buf = (u32 *)buffer; - u32 hcsr; for (; buffer_length >= sizeof(u32); buffer_length -= sizeof(u32)) *reg_buf++ = mei_me_mecbrw_read(dev); @@ -551,8 +614,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, memcpy(reg_buf, ®, buffer_length); } - hcsr = mei_hcsr_read(dev) | H_IG; - mei_hcsr_set(dev, hcsr); + mei_hcsr_set_hig(dev); return 0; } @@ -956,13 +1018,14 @@ static void mei_me_pg_legacy_intr(struct mei_device *dev) * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler * * @dev: the device structure + * @intr_source: interrupt source */ -static void mei_me_d0i3_intr(struct mei_device *dev) +static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source) { struct mei_me_hw *hw = to_me_hw(dev); if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && - (hw->intr_source & H_D0I3C_IS)) { + (intr_source & H_D0I3C_IS)) { dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; if (hw->pg_state == MEI_PG_ON) { hw->pg_state = MEI_PG_OFF; @@ -981,7 +1044,7 @@ static void mei_me_d0i3_intr(struct mei_device *dev) wake_up(&dev->wait_pg); } - if (hw->pg_state == MEI_PG_ON && (hw->intr_source & H_IS)) { + if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) { /* * HW sent some data and we are in D0i3, so * we got here because of HW initiated exit from D0i3. @@ -996,13 +1059,14 @@ static void mei_me_d0i3_intr(struct mei_device *dev) * mei_me_pg_intr - perform pg processing in interrupt thread handler * * @dev: the device structure + * @intr_source: interrupt source */ -static void mei_me_pg_intr(struct mei_device *dev) +static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source) { struct mei_me_hw *hw = to_me_hw(dev); if (hw->d0i3_supported) - mei_me_d0i3_intr(dev); + mei_me_d0i3_intr(dev, intr_source); else mei_me_pg_legacy_intr(dev); } @@ -1121,19 +1185,16 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *)dev_id; - struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr; hcsr = mei_hcsr_read(dev); - if (!(hcsr & H_CSR_IS_MASK)) + if (!me_intr_src(hcsr)) return IRQ_NONE; - hw->intr_source = hcsr & H_CSR_IS_MASK; - dev_dbg(dev->dev, "interrupt source 0x%08X.\n", hw->intr_source); - - /* clear H_IS and H_D0I3C_IS bits in H_CSR to clear the interrupts */ - mei_hcsr_write(dev, hcsr); + dev_dbg(dev->dev, "interrupt source 0x%08X\n", me_intr_src(hcsr)); + /* disable interrupts on device */ + me_intr_disable(dev, hcsr); return IRQ_WAKE_THREAD; } @@ -1150,14 +1211,19 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; - struct mei_cl_cb complete_list; + struct list_head cmpl_list; s32 slots; + u32 hcsr; int rets = 0; dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n"); /* initialize our complete list */ mutex_lock(&dev->device_lock); - mei_io_list_init(&complete_list); + + hcsr = mei_hcsr_read(dev); + me_intr_clear(dev, hcsr); + + INIT_LIST_HEAD(&cmpl_list); /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { @@ -1166,7 +1232,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) goto end; } - mei_me_pg_intr(dev); + if (mei_me_hw_is_resetting(dev)) + mei_hcsr_set_hig(dev); + + mei_me_pg_intr(dev, me_intr_src(hcsr)); /* check if we need to start the dev */ if (!mei_host_is_ready(dev)) { @@ -1183,7 +1252,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) slots = mei_count_full_read_slots(dev); while (slots > 0) { dev_dbg(dev->dev, "slots to read = %08x\n", slots); - rets = mei_irq_read_handler(dev, &complete_list, &slots); + rets = mei_irq_read_handler(dev, &cmpl_list, &slots); /* There is a race between ME write and interrupt delivery: * Not all data is always available immediately after the * interrupt, so try to read again on the next interrupt. @@ -1208,14 +1277,15 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) */ if (dev->pg_event != MEI_PG_EVENT_WAIT && dev->pg_event != MEI_PG_EVENT_RECEIVED) { - rets = mei_irq_write_handler(dev, &complete_list); + rets = mei_irq_write_handler(dev, &cmpl_list); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } - mei_irq_compl_handler(dev, &complete_list); + mei_irq_compl_handler(dev, &cmpl_list); end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); + mei_me_intr_enable(dev); mutex_unlock(&dev->device_lock); return IRQ_HANDLED; } @@ -1238,12 +1308,13 @@ static const struct mei_hw_ops mei_me_hw_ops = { .intr_clear = mei_me_intr_clear, .intr_enable = mei_me_intr_enable, .intr_disable = mei_me_intr_disable, + .synchronize_irq = mei_me_synchronize_irq, .hbuf_free_slots = mei_me_hbuf_empty_slots, .hbuf_is_ready = mei_me_hbuf_is_empty, .hbuf_max_len = mei_me_hbuf_max_len, - .write = mei_me_write_message, + .write = mei_me_hbuf_write, .rdbuf_full_slots = mei_me_count_full_read_slots, .read_hdr = mei_me_mecbrw_read, @@ -1343,7 +1414,7 @@ const struct mei_cfg mei_me_pch8_sps_cfg = { * @pdev: The pci device structure * @cfg: per device generation config * - * Return: The mei_device_device pointer on success, NULL on failure. + * Return: The mei_device pointer on success, NULL on failure. */ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, const struct mei_cfg *cfg) @@ -1351,8 +1422,8 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, struct mei_device *dev; struct mei_me_hw *hw; - dev = kzalloc(sizeof(struct mei_device) + - sizeof(struct mei_me_hw), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + + sizeof(struct mei_me_hw), GFP_KERNEL); if (!dev) return NULL; hw = to_me_hw(dev); diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 2ee14dc1b2ea..cf64847a35b9 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -51,14 +51,12 @@ struct mei_cfg { * * @cfg: per device generation config and ops * @mem_addr: io memory address - * @intr_source: interrupt source * @pg_state: power gating state * @d0i3_supported: di03 support */ struct mei_me_hw { const struct mei_cfg *cfg; void __iomem *mem_addr; - u32 intr_source; enum mei_pg_state pg_state; bool d0i3_supported; }; diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 60415a2bfcbd..24e4a4c96606 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -19,7 +19,7 @@ #include <linux/ktime.h> #include <linux/delay.h> #include <linux/kthread.h> -#include <linux/irqreturn.h> +#include <linux/interrupt.h> #include <linux/pm_runtime.h> #include <linux/mei.h> @@ -441,6 +441,18 @@ static void mei_txe_intr_enable(struct mei_device *dev) } /** + * mei_txe_synchronize_irq - wait for pending IRQ handlers + * + * @dev: the device structure + */ +static void mei_txe_synchronize_irq(struct mei_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + + synchronize_irq(pdev->irq); +} + +/** * mei_txe_pending_interrupts - check if there are pending interrupts * only Aliveness, Input ready, and output doorbell are of relevance * @@ -691,7 +703,8 @@ static void mei_txe_hw_config(struct mei_device *dev) */ static int mei_txe_write(struct mei_device *dev, - struct mei_msg_hdr *header, unsigned char *buf) + struct mei_msg_hdr *header, + const unsigned char *buf) { struct mei_txe_hw *hw = to_txe_hw(dev); unsigned long rem; @@ -1044,7 +1057,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) { struct mei_device *dev = (struct mei_device *) dev_id; struct mei_txe_hw *hw = to_txe_hw(dev); - struct mei_cl_cb complete_list; + struct list_head cmpl_list; s32 slots; int rets = 0; @@ -1056,7 +1069,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) /* initialize our complete list */ mutex_lock(&dev->device_lock); - mei_io_list_init(&complete_list); + INIT_LIST_HEAD(&cmpl_list); if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) mei_txe_check_and_ack_intrs(dev, true); @@ -1113,7 +1126,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) slots = mei_count_full_read_slots(dev); if (test_and_clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause)) { /* Read from TXE */ - rets = mei_irq_read_handler(dev, &complete_list, &slots); + rets = mei_irq_read_handler(dev, &cmpl_list, &slots); if (rets && dev->dev_state != MEI_DEV_RESETTING) { dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", rets); @@ -1131,14 +1144,14 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) if (hw->aliveness && dev->hbuf_is_ready) { /* get the real register value */ dev->hbuf_is_ready = mei_hbuf_is_ready(dev); - rets = mei_irq_write_handler(dev, &complete_list); + rets = mei_irq_write_handler(dev, &cmpl_list); if (rets && rets != -EMSGSIZE) dev_err(dev->dev, "mei_irq_write_handler ret = %d.\n", rets); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } - mei_irq_compl_handler(dev, &complete_list); + mei_irq_compl_handler(dev, &cmpl_list); end: dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); @@ -1167,6 +1180,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = { .intr_clear = mei_txe_intr_clear, .intr_enable = mei_txe_intr_enable, .intr_disable = mei_txe_intr_disable, + .synchronize_irq = mei_txe_synchronize_irq, .hbuf_free_slots = mei_txe_hbuf_empty_slots, .hbuf_is_ready = mei_txe_is_input_ready, @@ -1193,8 +1207,8 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) struct mei_device *dev; struct mei_txe_hw *hw; - dev = kzalloc(sizeof(struct mei_device) + - sizeof(struct mei_txe_hw), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + + sizeof(struct mei_txe_hw), GFP_KERNEL); if (!dev) return NULL; diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h index ce3ed0b88b0c..e1e8b66d7648 100644 --- a/drivers/misc/mei/hw-txe.h +++ b/drivers/misc/mei/hw-txe.h @@ -45,7 +45,7 @@ * @intr_cause: translated interrupt cause */ struct mei_txe_hw { - void __iomem *mem_addr[NUM_OF_MEM_BARS]; + void __iomem * const *mem_addr; u32 aliveness; u32 readiness; u32 slots; diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 9daf3f9aed25..e1e4d47d4d7d 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -76,6 +76,12 @@ #define HBM_MINOR_VERSION_FA 0 #define HBM_MAJOR_VERSION_FA 2 +/* + * MEI version with OS ver message support + */ +#define HBM_MINOR_VERSION_OS 0 +#define HBM_MAJOR_VERSION_OS 2 + /* Host bus message command opcode */ #define MEI_HBM_CMD_OP_MSK 0x7f /* Host bus message command RESPONSE */ diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 9a9c2484d107..13c55b8f9261 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -122,6 +122,8 @@ int mei_reset(struct mei_device *dev) mei_dev_state_str(state), fw_sts_str); } + mei_clear_interrupts(dev); + /* we're already in reset, cancel the init timer * if the reset was called due the hbm protocol error * we need to call it before hw start @@ -273,8 +275,6 @@ int mei_restart(struct mei_device *dev) mutex_lock(&dev->device_lock); - mei_clear_interrupts(dev); - dev->dev_state = MEI_DEV_POWER_UP; dev->reset_count = 0; @@ -302,6 +302,9 @@ static void mei_reset_work(struct work_struct *work) container_of(work, struct mei_device, reset_work); int ret; + mei_clear_interrupts(dev); + mei_synchronize_irq(dev); + mutex_lock(&dev->device_lock); ret = mei_reset(dev); @@ -326,6 +329,9 @@ void mei_stop(struct mei_device *dev) mei_cancel_work(dev); + mei_clear_interrupts(dev); + mei_synchronize_irq(dev); + mutex_lock(&dev->device_lock); dev->dev_state = MEI_DEV_POWER_DOWN; @@ -347,16 +353,16 @@ EXPORT_SYMBOL_GPL(mei_stop); bool mei_write_is_idle(struct mei_device *dev) { bool idle = (dev->dev_state == MEI_DEV_ENABLED && - list_empty(&dev->ctrl_wr_list.list) && - list_empty(&dev->write_list.list) && - list_empty(&dev->write_waiting_list.list)); + list_empty(&dev->ctrl_wr_list) && + list_empty(&dev->write_list) && + list_empty(&dev->write_waiting_list)); dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n", idle, mei_dev_state_str(dev->dev_state), - list_empty(&dev->ctrl_wr_list.list), - list_empty(&dev->write_list.list), - list_empty(&dev->write_waiting_list.list)); + list_empty(&dev->ctrl_wr_list), + list_empty(&dev->write_list), + list_empty(&dev->write_waiting_list)); return idle; } @@ -386,17 +392,17 @@ void mei_device_init(struct mei_device *dev, dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; - mei_io_list_init(&dev->write_list); - mei_io_list_init(&dev->write_waiting_list); - mei_io_list_init(&dev->ctrl_wr_list); - mei_io_list_init(&dev->ctrl_rd_list); + INIT_LIST_HEAD(&dev->write_list); + INIT_LIST_HEAD(&dev->write_waiting_list); + INIT_LIST_HEAD(&dev->ctrl_wr_list); + INIT_LIST_HEAD(&dev->ctrl_rd_list); INIT_DELAYED_WORK(&dev->timer_work, mei_timer); INIT_WORK(&dev->reset_work, mei_reset_work); INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work); INIT_LIST_HEAD(&dev->iamthif_cl.link); - mei_io_list_init(&dev->amthif_cmd_list); + INIT_LIST_HEAD(&dev->amthif_cmd_list); bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); dev->open_handle_count = 0; diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 5a4893ce9c24..406e9e2b2fff 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -35,14 +35,14 @@ * for the completed callbacks * * @dev: mei device - * @compl_list: list of completed cbs + * @cmpl_list: list of completed cbs */ -void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) +void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list) { struct mei_cl_cb *cb, *next; struct mei_cl *cl; - list_for_each_entry_safe(cb, next, &compl_list->list, list) { + list_for_each_entry_safe(cb, next, cmpl_list, list) { cl = cb->cl; list_del_init(&cb->list); @@ -92,13 +92,13 @@ void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) * * @cl: reading client * @mei_hdr: header of mei client message - * @complete_list: completion list + * @cmpl_list: completion list * * Return: always 0 */ int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *complete_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; struct mei_cl_cb *cb; @@ -118,7 +118,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (!mei_cl_is_connected(cl)) { cl_dbg(dev, cl, "not connected\n"); - list_move_tail(&cb->list, &complete_list->list); cb->status = -ENODEV; goto discard; } @@ -128,8 +127,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (buf_sz < cb->buf_idx) { cl_err(dev, cl, "message is too big len %d idx %zu\n", mei_hdr->length, cb->buf_idx); - - list_move_tail(&cb->list, &complete_list->list); cb->status = -EMSGSIZE; goto discard; } @@ -137,8 +134,6 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (cb->buf.size < buf_sz) { cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n", cb->buf.size, mei_hdr->length, cb->buf_idx); - - list_move_tail(&cb->list, &complete_list->list); cb->status = -EMSGSIZE; goto discard; } @@ -149,7 +144,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, if (mei_hdr->msg_complete) { cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx); - list_move_tail(&cb->list, &complete_list->list); + list_move_tail(&cb->list, cmpl_list); } else { pm_runtime_mark_last_busy(dev->dev); pm_request_autosuspend(dev->dev); @@ -158,6 +153,8 @@ int mei_cl_irq_read_msg(struct mei_cl *cl, return 0; discard: + if (cb) + list_move_tail(&cb->list, cmpl_list); mei_irq_discard_msg(dev, mei_hdr); return 0; } @@ -172,7 +169,7 @@ discard: * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -186,7 +183,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, return -EMSGSIZE; ret = mei_hbm_cl_disconnect_rsp(dev, cl); - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -202,7 +199,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list) + struct list_head *cmpl_list) { struct mei_device *dev = cl->dev; u32 msg_slots; @@ -222,7 +219,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, if (ret) { cl->status = ret; cb->buf_idx = 0; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); return ret; } @@ -252,7 +249,7 @@ static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr) * Return: 0 on success, <0 on failure. */ int mei_irq_read_handler(struct mei_device *dev, - struct mei_cl_cb *cmpl_list, s32 *slots) + struct list_head *cmpl_list, s32 *slots) { struct mei_msg_hdr *mei_hdr; struct mei_cl *cl; @@ -350,12 +347,11 @@ EXPORT_SYMBOL_GPL(mei_irq_read_handler); * * Return: 0 on success, <0 on failure. */ -int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) +int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list) { struct mei_cl *cl; struct mei_cl_cb *cb, *next; - struct mei_cl_cb *list; s32 slots; int ret; @@ -370,19 +366,18 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) /* complete all waiting for write CB */ dev_dbg(dev->dev, "complete all waiting for write cb.\n"); - list = &dev->write_waiting_list; - list_for_each_entry_safe(cb, next, &list->list, list) { + list_for_each_entry_safe(cb, next, &dev->write_waiting_list, list) { cl = cb->cl; cl->status = 0; cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); cl->writing_state = MEI_WRITE_COMPLETE; - list_move_tail(&cb->list, &cmpl_list->list); + list_move_tail(&cb->list, cmpl_list); } /* complete control write list CB */ dev_dbg(dev->dev, "complete control write list cb.\n"); - list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list, list) { cl = cb->cl; switch (cb->fop_type) { case MEI_FOP_DISCONNECT: @@ -426,7 +421,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete write list CB */ dev_dbg(dev->dev, "complete write list cb.\n"); - list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { + list_for_each_entry_safe(cb, next, &dev->write_list, list) { cl = cb->cl; if (cl == &dev->iamthif_cl) ret = mei_amthif_irq_write(cl, cb, cmpl_list); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index fa50635512e8..bf816449cd40 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -26,7 +26,7 @@ #include <linux/init.h> #include <linux/ioctl.h> #include <linux/cdev.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/uuid.h> #include <linux/compat.h> #include <linux/jiffies.h> @@ -182,32 +182,36 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, goto out; } - if (rets == -EBUSY && - !mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, file)) { - rets = -ENOMEM; - goto out; - } - do { - mutex_unlock(&dev->device_lock); - - if (wait_event_interruptible(cl->rx_wait, - (!list_empty(&cl->rd_completed)) || - (!mei_cl_is_connected(cl)))) { +again: + mutex_unlock(&dev->device_lock); + if (wait_event_interruptible(cl->rx_wait, + !list_empty(&cl->rd_completed) || + !mei_cl_is_connected(cl))) { + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + mutex_lock(&dev->device_lock); - if (signal_pending(current)) - return -EINTR; - return -ERESTARTSYS; - } + if (!mei_cl_is_connected(cl)) { + rets = -ENODEV; + goto out; + } - mutex_lock(&dev->device_lock); - if (!mei_cl_is_connected(cl)) { - rets = -ENODEV; - goto out; - } + cb = mei_cl_read_cb(cl, file); + if (!cb) { + /* + * For amthif all the waiters are woken up, + * but only fp with matching cb->fp get the cb, + * the others have to return to wait on read. + */ + if (cl == &dev->iamthif_cl) + goto again; - cb = mei_cl_read_cb(cl, file); - } while (!cb); + rets = 0; + goto out; + } copy_buffer: /* now copy the data to user space */ @@ -322,7 +326,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - rets = mei_cl_write(cl, cb, false); + rets = mei_cl_write(cl, cb); out: mutex_unlock(&dev->device_lock); return rets; @@ -653,7 +657,7 @@ static int mei_fasync(int fd, struct file *file, int band) } /** - * fw_status_show - mei device attribute show method + * fw_status_show - mei device fw_status attribute show method * * @device: device pointer * @attr: attribute pointer @@ -684,8 +688,49 @@ static ssize_t fw_status_show(struct device *device, } static DEVICE_ATTR_RO(fw_status); +/** + * hbm_ver_show - display HBM protocol version negotiated with FW + * + * @device: device pointer + * @attr: attribute pointer + * @buf: char out buffer + * + * Return: number of the bytes printed into buf or error + */ +static ssize_t hbm_ver_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct mei_device *dev = dev_get_drvdata(device); + struct hbm_version ver; + + mutex_lock(&dev->device_lock); + ver = dev->version; + mutex_unlock(&dev->device_lock); + + return sprintf(buf, "%u.%u\n", ver.major_version, ver.minor_version); +} +static DEVICE_ATTR_RO(hbm_ver); + +/** + * hbm_ver_drv_show - display HBM protocol version advertised by driver + * + * @device: device pointer + * @attr: attribute pointer + * @buf: char out buffer + * + * Return: number of the bytes printed into buf or error + */ +static ssize_t hbm_ver_drv_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u.%u\n", HBM_MAJOR_VERSION, HBM_MINOR_VERSION); +} +static DEVICE_ATTR_RO(hbm_ver_drv); + static struct attribute *mei_attrs[] = { &dev_attr_fw_status.attr, + &dev_attr_hbm_ver.attr, + &dev_attr_hbm_ver_drv.attr, NULL }; ATTRIBUTE_GROUPS(mei); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 1169fd9e7d02..d41aac53a2ac 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -55,7 +55,8 @@ extern const uuid_le mei_amthif_guid; /* File state */ enum file_state { - MEI_FILE_INITIALIZING = 0, + MEI_FILE_UNINITIALIZED = 0, + MEI_FILE_INITIALIZING, MEI_FILE_CONNECTING, MEI_FILE_CONNECTED, MEI_FILE_DISCONNECTING, @@ -109,6 +110,21 @@ enum mei_cb_file_ops { MEI_FOP_NOTIFY_STOP, }; +/** + * enum mei_cl_io_mode - io mode between driver and fw + * + * @MEI_CL_IO_TX_BLOCKING: send is blocking + * @MEI_CL_IO_TX_INTERNAL: internal communication between driver and FW + * + * @MEI_CL_IO_RX_NONBLOCK: recv is non-blocking + */ +enum mei_cl_io_mode { + MEI_CL_IO_TX_BLOCKING = BIT(0), + MEI_CL_IO_TX_INTERNAL = BIT(1), + + MEI_CL_IO_RX_NONBLOCK = BIT(2), +}; + /* * Intel MEI message data struct */ @@ -169,6 +185,7 @@ struct mei_cl; * @fp: pointer to file structure * @status: io status of the cb * @internal: communication between driver and FW flag + * @blocking: transmission blocking mode * @completed: the transfer or reception has completed */ struct mei_cl_cb { @@ -180,6 +197,7 @@ struct mei_cl_cb { const struct file *fp; int status; u32 internal:1; + u32 blocking:1; u32 completed:1; }; @@ -253,6 +271,7 @@ struct mei_cl { * @intr_clear : clear pending interrupts * @intr_enable : enable interrupts * @intr_disable : disable interrupts + * @synchronize_irq : synchronize irqs * * @hbuf_free_slots : query for write buffer empty slots * @hbuf_is_ready : query if write buffer is empty @@ -274,7 +293,6 @@ struct mei_hw_ops { int (*hw_start)(struct mei_device *dev); void (*hw_config)(struct mei_device *dev); - int (*fw_status)(struct mei_device *dev, struct mei_fw_status *fw_sts); enum mei_pg_state (*pg_state)(struct mei_device *dev); bool (*pg_in_transition)(struct mei_device *dev); @@ -283,14 +301,14 @@ struct mei_hw_ops { void (*intr_clear)(struct mei_device *dev); void (*intr_enable)(struct mei_device *dev); void (*intr_disable)(struct mei_device *dev); + void (*synchronize_irq)(struct mei_device *dev); int (*hbuf_free_slots)(struct mei_device *dev); bool (*hbuf_is_ready)(struct mei_device *dev); size_t (*hbuf_max_len)(const struct mei_device *dev); - int (*write)(struct mei_device *dev, struct mei_msg_hdr *hdr, - unsigned char *buf); + const unsigned char *buf); int (*rdbuf_full_slots)(struct mei_device *dev); @@ -304,11 +322,14 @@ void mei_cl_bus_rescan(struct mei_device *bus); void mei_cl_bus_rescan_work(struct work_struct *work); void mei_cl_bus_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, - bool blocking); -ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); + unsigned int mode); +ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, + unsigned int mode); 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); @@ -387,6 +408,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @hbm_f_ev_supported : hbm feature event notification * @hbm_f_fa_supported : hbm feature fixed address client * @hbm_f_ie_supported : hbm feature immediate reply to enum request + * @hbm_f_os_supported : hbm feature support OS ver message * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients @@ -419,10 +441,10 @@ struct mei_device { struct cdev cdev; int minor; - struct mei_cl_cb write_list; - struct mei_cl_cb write_waiting_list; - struct mei_cl_cb ctrl_wr_list; - struct mei_cl_cb ctrl_rd_list; + struct list_head write_list; + struct list_head write_waiting_list; + struct list_head ctrl_wr_list; + struct list_head ctrl_rd_list; struct list_head file_list; long open_handle_count; @@ -468,6 +490,7 @@ struct mei_device { unsigned int hbm_f_ev_supported:1; unsigned int hbm_f_fa_supported:1; unsigned int hbm_f_ie_supported:1; + unsigned int hbm_f_os_supported:1; struct rw_semaphore me_clients_rwsem; struct list_head me_clients; @@ -478,7 +501,7 @@ struct mei_device { bool override_fixed_address; /* amthif list for cmd waiting */ - struct mei_cl_cb amthif_cmd_list; + struct list_head amthif_cmd_list; struct mei_cl iamthif_cl; long iamthif_open_count; u32 iamthif_stall_timer; @@ -550,10 +573,10 @@ void mei_cancel_work(struct mei_device *dev); void mei_timer(struct work_struct *work); void mei_schedule_stall_timer(struct mei_device *dev); int mei_irq_read_handler(struct mei_device *dev, - struct mei_cl_cb *cmpl_list, s32 *slots); + struct list_head *cmpl_list, s32 *slots); -int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); -void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list); +int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list); +void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list); /* * AMTHIF - AMT Host Interface Functions @@ -569,12 +592,12 @@ int mei_amthif_release(struct mei_device *dev, struct file *file); int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_run_next_cmd(struct mei_device *dev); int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, - struct mei_cl_cb *cmpl_list); + struct list_head *cmpl_list); void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb); int mei_amthif_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr, - struct mei_cl_cb *complete_list); + struct list_head *cmpl_list); int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); /* @@ -627,6 +650,11 @@ static inline void mei_disable_interrupts(struct mei_device *dev) dev->ops->intr_disable(dev); } +static inline void mei_synchronize_irq(struct mei_device *dev) +{ + dev->ops->synchronize_irq(dev); +} + static inline bool mei_host_is_ready(struct mei_device *dev) { return dev->ops->host_is_ready(dev); @@ -652,7 +680,7 @@ static inline size_t mei_hbuf_max_len(const struct mei_device *dev) } static inline int mei_write_message(struct mei_device *dev, - struct mei_msg_hdr *hdr, void *buf) + struct mei_msg_hdr *hdr, const void *buf) { return dev->ops->write(dev, hdr, buf); } diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index f3ffd883b232..0a668fdfbbe9 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -87,6 +87,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)}, @@ -148,18 +149,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; /* enable pci dev */ - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(&pdev->dev, "failed to enable pci device.\n"); goto end; } /* set PCI host mastering */ pci_set_master(pdev); - /* pci request regions for mei driver */ - err = pci_request_regions(pdev, KBUILD_MODNAME); + /* pci request regions and mapping IO device memory for mei driver */ + err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); if (err) { dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; + goto end; } if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) || @@ -172,24 +173,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } if (err) { dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); - goto release_regions; + goto end; } - /* allocates and initializes the mei dev structure */ dev = mei_me_dev_init(pdev, cfg); if (!dev) { err = -ENOMEM; - goto release_regions; + goto end; } hw = to_me_hw(dev); - /* mapping IO device memory */ - hw->mem_addr = pci_iomap(pdev, 0, 0); - if (!hw->mem_addr) { - dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); - err = -ENOMEM; - goto free_device; - } + hw->mem_addr = pcim_iomap_table(pdev)[0]; + pci_enable_msi(pdev); /* request and enable interrupt */ @@ -202,7 +197,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) { dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", pdev->irq); - goto disable_msi; + goto end; } if (mei_start(dev)) { @@ -241,15 +236,6 @@ release_irq: mei_cancel_work(dev); mei_disable_interrupts(dev); free_irq(pdev->irq, dev); -disable_msi: - pci_disable_msi(pdev); - pci_iounmap(pdev, hw->mem_addr); -free_device: - kfree(dev); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); end: dev_err(&pdev->dev, "initialization failed.\n"); return err; @@ -266,7 +252,6 @@ end: static void mei_me_remove(struct pci_dev *pdev) { struct mei_device *dev; - struct mei_me_hw *hw; dev = pci_get_drvdata(pdev); if (!dev) @@ -275,33 +260,19 @@ static void mei_me_remove(struct pci_dev *pdev) if (mei_pg_is_enabled(dev)) pm_runtime_get_noresume(&pdev->dev); - hw = to_me_hw(dev); - - dev_dbg(&pdev->dev, "stop\n"); mei_stop(dev); if (!pci_dev_run_wake(pdev)) mei_me_unset_pm_domain(dev); - /* disable interrupts */ mei_disable_interrupts(dev); free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - - if (hw->mem_addr) - pci_iounmap(pdev, hw->mem_addr); mei_deregister(dev); - - kfree(dev); - - pci_release_regions(pdev); - pci_disable_device(pdev); - - } + #ifdef CONFIG_PM_SLEEP static int mei_me_pci_suspend(struct device *device) { diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 58ffd30dcc91..fe088b40daf9 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -52,17 +52,6 @@ static inline void mei_txe_set_pm_domain(struct mei_device *dev) {} static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM */ -static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) -{ - int i; - - for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { - if (hw->mem_addr[i]) { - pci_iounmap(pdev, hw->mem_addr[i]); - hw->mem_addr[i] = NULL; - } - } -} /** * mei_txe_probe - Device Initialization Routine * @@ -75,22 +64,22 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct mei_device *dev; struct mei_txe_hw *hw; + const int mask = BIT(SEC_BAR) | BIT(BRIDGE_BAR); int err; - int i; /* enable pci dev */ - err = pci_enable_device(pdev); + err = pcim_enable_device(pdev); if (err) { dev_err(&pdev->dev, "failed to enable pci device.\n"); goto end; } /* set PCI host mastering */ pci_set_master(pdev); - /* pci request regions for mei driver */ - err = pci_request_regions(pdev, KBUILD_MODNAME); + /* pci request regions and mapping IO device memory for mei driver */ + err = pcim_iomap_regions(pdev, mask, KBUILD_MODNAME); if (err) { dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; + goto end; } err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); @@ -98,7 +87,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (err) { dev_err(&pdev->dev, "No suitable DMA available.\n"); - goto release_regions; + goto end; } } @@ -106,20 +95,10 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev = mei_txe_dev_init(pdev); if (!dev) { err = -ENOMEM; - goto release_regions; + goto end; } hw = to_txe_hw(dev); - - /* mapping IO device memory */ - for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { - hw->mem_addr[i] = pci_iomap(pdev, i, 0); - if (!hw->mem_addr[i]) { - dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); - err = -ENOMEM; - goto free_device; - } - } - + hw->mem_addr = pcim_iomap_table(pdev); pci_enable_msi(pdev); @@ -140,7 +119,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) { dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n", pdev->irq); - goto free_device; + goto end; } if (mei_start(dev)) { @@ -173,23 +152,9 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) stop: mei_stop(dev); release_irq: - mei_cancel_work(dev); - - /* disable interrupts */ mei_disable_interrupts(dev); - free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - -free_device: - mei_txe_pci_iounmap(pdev, hw); - - kfree(dev); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); end: dev_err(&pdev->dev, "initialization failed.\n"); return err; @@ -206,38 +171,24 @@ end: static void mei_txe_remove(struct pci_dev *pdev) { struct mei_device *dev; - struct mei_txe_hw *hw; dev = pci_get_drvdata(pdev); if (!dev) { - dev_err(&pdev->dev, "mei: dev =NULL\n"); + dev_err(&pdev->dev, "mei: dev == NULL\n"); return; } pm_runtime_get_noresume(&pdev->dev); - hw = to_txe_hw(dev); - mei_stop(dev); if (!pci_dev_run_wake(pdev)) mei_txe_unset_pm_domain(dev); - /* disable interrupts */ mei_disable_interrupts(dev); free_irq(pdev->irq, dev); - pci_disable_msi(pdev); - - pci_set_drvdata(pdev, NULL); - - mei_txe_pci_iounmap(pdev, hw); mei_deregister(dev); - - kfree(dev); - - pci_release_regions(pdev); - pci_disable_device(pdev); } diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c index be37890abb93..77b16ca66846 100644 --- a/drivers/misc/mic/bus/mic_bus.c +++ b/drivers/misc/mic/bus/mic_bus.c @@ -143,7 +143,7 @@ static void mbus_release_dev(struct device *d) } struct mbus_device * -mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, +mbus_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, struct mbus_hw_ops *hw_ops, int index, void __iomem *mmio_va) { @@ -158,7 +158,7 @@ mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, mbdev->dev.parent = pdev; mbdev->id.device = id; mbdev->id.vendor = MBUS_DEV_ANY_ID; - mbdev->dev.archdata.dma_ops = dma_ops; + mbdev->dev.dma_ops = dma_ops; mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask; dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64)); mbdev->dev.release = mbus_release_dev; diff --git a/drivers/misc/mic/bus/scif_bus.c b/drivers/misc/mic/bus/scif_bus.c index ff6e01c25810..a444db5f61fe 100644 --- a/drivers/misc/mic/bus/scif_bus.c +++ b/drivers/misc/mic/bus/scif_bus.c @@ -138,7 +138,7 @@ static void scif_release_dev(struct device *d) } struct scif_hw_dev * -scif_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, +scif_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, struct mic_mw *mmio, struct mic_mw *aper, void *dp, void __iomem *rdp, struct dma_chan **chan, int num_chan, @@ -154,7 +154,7 @@ scif_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, sdev->dev.parent = pdev; sdev->id.device = id; sdev->id.vendor = SCIF_DEV_ANY_ID; - sdev->dev.archdata.dma_ops = dma_ops; + sdev->dev.dma_ops = dma_ops; sdev->dev.release = scif_release_dev; sdev->hw_ops = hw_ops; sdev->dnode = dnode; diff --git a/drivers/misc/mic/bus/scif_bus.h b/drivers/misc/mic/bus/scif_bus.h index 94f29ac608b6..ff59568219ad 100644 --- a/drivers/misc/mic/bus/scif_bus.h +++ b/drivers/misc/mic/bus/scif_bus.h @@ -113,7 +113,7 @@ int scif_register_driver(struct scif_driver *driver); void scif_unregister_driver(struct scif_driver *driver); struct scif_hw_dev * scif_register_device(struct device *pdev, int id, - struct dma_map_ops *dma_ops, + const struct dma_map_ops *dma_ops, struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, struct mic_mw *mmio, struct mic_mw *aper, void *dp, void __iomem *rdp, diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c index 303da222f5b6..fd7f2a6049f8 100644 --- a/drivers/misc/mic/bus/vop_bus.c +++ b/drivers/misc/mic/bus/vop_bus.c @@ -154,7 +154,7 @@ vop_register_device(struct device *pdev, int id, vdev->dev.parent = pdev; vdev->id.device = id; vdev->id.vendor = VOP_DEV_ANY_ID; - vdev->dev.archdata.dma_ops = (struct dma_map_ops *)dma_ops; + vdev->dev.dma_ops = dma_ops; vdev->dev.dma_mask = &vdev->dev.coherent_dma_mask; dma_set_mask(&vdev->dev, DMA_BIT_MASK(64)); vdev->dev.release = vop_release_dev; diff --git a/drivers/misc/mic/cosm/cosm_scif_server.c b/drivers/misc/mic/cosm/cosm_scif_server.c index 5696df4326b5..85f7d09cc65f 100644 --- a/drivers/misc/mic/cosm/cosm_scif_server.c +++ b/drivers/misc/mic/cosm/cosm_scif_server.c @@ -19,6 +19,8 @@ * */ #include <linux/kthread.h> +#include <linux/sched/signal.h> + #include "cosm_main.h" /* diff --git a/drivers/misc/mic/cosm_client/cosm_scif_client.c b/drivers/misc/mic/cosm_client/cosm_scif_client.c index 03e98bf1ac15..aa530fcceaa9 100644 --- a/drivers/misc/mic/cosm_client/cosm_scif_client.c +++ b/drivers/misc/mic/cosm_client/cosm_scif_client.c @@ -22,6 +22,8 @@ #include <linux/delay.h> #include <linux/reboot.h> #include <linux/kthread.h> +#include <linux/sched/signal.h> + #include "../cosm/cosm_main.h" #define COSM_SCIF_MAX_RETRIES 10 diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 9599d732aff3..c327985c9523 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -245,7 +245,7 @@ static void __mic_dma_unmap_sg(struct device *dev, dma_unmap_sg(&mdev->pdev->dev, sg, nents, dir); } -static struct dma_map_ops __mic_dma_ops = { +static const struct dma_map_ops __mic_dma_ops = { .alloc = __mic_dma_alloc, .free = __mic_dma_free, .map_page = __mic_dma_map_page, @@ -344,7 +344,7 @@ mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, mic_unmap_single(mdev, dma_addr, size); } -static struct dma_map_ops mic_dma_ops = { +static const struct dma_map_ops mic_dma_ops = { .map_page = mic_dma_map_page, .unmap_page = mic_dma_unmap_page, }; diff --git a/drivers/misc/mic/scif/scif_main.h b/drivers/misc/mic/scif/scif_main.h index a08f0b600a9e..0e5eff9ad080 100644 --- a/drivers/misc/mic/scif/scif_main.h +++ b/drivers/misc/mic/scif/scif_main.h @@ -18,7 +18,7 @@ #ifndef SCIF_MAIN_H #define SCIF_MAIN_H -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/pci.h> #include <linux/miscdevice.h> #include <linux/dmaengine.h> diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index f806a4471eb9..329727e00e97 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -17,6 +17,9 @@ */ #include <linux/dma_remapping.h> #include <linux/pagemap.h> +#include <linux/sched/mm.h> +#include <linux/sched/signal.h> + #include "scif_main.h" #include "scif_map.h" diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c index 1a2b67f3183d..c2e29d7f0de8 100644 --- a/drivers/misc/mic/vop/vop_main.c +++ b/drivers/misc/mic/vop/vop_main.c @@ -374,7 +374,7 @@ unmap: static int vop_find_vqs(struct virtio_device *dev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], struct irq_affinity *desc) { struct _vop_vdev *vdev = to_vopvdev(dev); struct vop_device *vpdev = vdev->vpdev; diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index 88e45234d527..fed992e2c258 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -292,7 +292,6 @@ static int vop_virtio_add_device(struct vop_vdev *vdev, if (ret) { dev_err(vop_dev(vdev), "%s %d err %d\n", __func__, __LINE__, ret); - kfree(vdev); return ret; } diff --git a/drivers/misc/panel.c b/drivers/misc/panel.c index 6030ac5b8c63..ef2ece0f26af 100644 --- a/drivers/misc/panel.c +++ b/drivers/misc/panel.c @@ -56,6 +56,7 @@ #include <linux/list.h> #include <linux/notifier.h> #include <linux/reboot.h> +#include <linux/workqueue.h> #include <generated/utsrelease.h> #include <linux/io.h> @@ -64,8 +65,6 @@ #define LCD_MINOR 156 #define KEYPAD_MINOR 185 -#define PANEL_VERSION "0.9.5" - #define LCD_MAXBYTES 256 /* max burst write */ #define KEYPAD_BUFFER 64 @@ -77,8 +76,8 @@ /* a key repeats this times INPUT_POLL_TIME */ #define KEYPAD_REP_DELAY (2) -/* keep the light on this times INPUT_POLL_TIME for each flash */ -#define FLASH_LIGHT_TEMPO (200) +/* keep the light on this many seconds for each flash */ +#define FLASH_LIGHT_TEMPO (4) /* converts an r_str() input to an active high, bits string : 000BAOSE */ #define PNL_PINPUT(a) ((((unsigned char)(a)) ^ 0x7F) >> 3) @@ -121,8 +120,6 @@ #define PIN_SELECP 17 #define PIN_NOT_SET 127 -#define LCD_FLAG_S 0x0001 -#define LCD_FLAG_ID 0x0002 #define LCD_FLAG_B 0x0004 /* blink on */ #define LCD_FLAG_C 0x0008 /* cursor on */ #define LCD_FLAG_D 0x0010 /* display on */ @@ -256,7 +253,10 @@ static struct { int hwidth; int charset; int proto; - int light_tempo; + + struct delayed_work bl_work; + struct mutex bl_tempo_lock; /* Protects access to bl_tempo */ + bool bl_tempo; /* TODO: use union here? */ struct { @@ -661,8 +661,6 @@ static void lcd_get_bits(unsigned int port, int *val) } } -static void init_scan_timer(void); - /* sets data port bits according to current signals values */ static int set_data_bits(void) { @@ -794,11 +792,8 @@ static void lcd_send_serial(int byte) } /* turn the backlight on or off */ -static void lcd_backlight(int on) +static void __lcd_backlight(int on) { - if (lcd.pins.bl == PIN_NONE) - return; - /* The backlight is activated by setting the AUTOFEED line to +5V */ spin_lock_irq(&pprt_lock); if (on) @@ -809,6 +804,44 @@ static void lcd_backlight(int on) spin_unlock_irq(&pprt_lock); } +static void lcd_backlight(int on) +{ + if (lcd.pins.bl == PIN_NONE) + return; + + mutex_lock(&lcd.bl_tempo_lock); + if (!lcd.bl_tempo) + __lcd_backlight(on); + mutex_unlock(&lcd.bl_tempo_lock); +} + +static void lcd_bl_off(struct work_struct *work) +{ + mutex_lock(&lcd.bl_tempo_lock); + if (lcd.bl_tempo) { + lcd.bl_tempo = false; + if (!(lcd.flags & LCD_FLAG_L)) + __lcd_backlight(0); + } + mutex_unlock(&lcd.bl_tempo_lock); +} + +/* turn the backlight on for a little while */ +static void lcd_poke(void) +{ + if (lcd.pins.bl == PIN_NONE) + return; + + cancel_delayed_work_sync(&lcd.bl_work); + + mutex_lock(&lcd.bl_tempo_lock); + if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L)) + __lcd_backlight(1); + lcd.bl_tempo = true; + schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ); + mutex_unlock(&lcd.bl_tempo_lock); +} + /* send a command to the LCD panel in serial mode */ static void lcd_write_cmd_s(int cmd) { @@ -907,6 +940,13 @@ static void lcd_gotoxy(void) (lcd.hwidth - 1) : lcd.bwidth - 1)); } +static void lcd_home(void) +{ + lcd.addr.x = 0; + lcd.addr.y = 0; + lcd_gotoxy(); +} + static void lcd_print(char c) { if (lcd.addr.x < lcd.bwidth) { @@ -925,9 +965,7 @@ static void lcd_clear_fast_s(void) { int pos; - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { @@ -939,9 +977,7 @@ static void lcd_clear_fast_s(void) } spin_unlock_irq(&pprt_lock); - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); } /* fills the display with spaces and resets X/Y */ @@ -949,9 +985,7 @@ static void lcd_clear_fast_p8(void) { int pos; - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { @@ -977,9 +1011,7 @@ static void lcd_clear_fast_p8(void) } spin_unlock_irq(&pprt_lock); - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); } /* fills the display with spaces and resets X/Y */ @@ -987,9 +1019,7 @@ static void lcd_clear_fast_tilcd(void) { int pos; - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); spin_lock_irq(&pprt_lock); for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) { @@ -1000,9 +1030,7 @@ static void lcd_clear_fast_tilcd(void) spin_unlock_irq(&pprt_lock); - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); } /* clears the display and resets X/Y */ @@ -1108,13 +1136,8 @@ static inline int handle_lcd_special_code(void) processed = 1; break; case '*': - /* flash back light using the keypad timer */ - if (scan_timer.function) { - if (lcd.light_tempo == 0 && - ((lcd.flags & LCD_FLAG_L) == 0)) - lcd_backlight(1); - lcd.light_tempo = FLASH_LIGHT_TEMPO; - } + /* flash back light */ + lcd_poke(); processed = 1; break; case 'f': /* Small Font */ @@ -1278,21 +1301,14 @@ static inline int handle_lcd_special_code(void) lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS | ((lcd.flags & LCD_FLAG_F) - ? LCD_CMD_TWO_LINES : 0) - | ((lcd.flags & LCD_FLAG_N) ? LCD_CMD_FONT_5X10_DOTS + : 0) + | ((lcd.flags & LCD_FLAG_N) + ? LCD_CMD_TWO_LINES : 0)); /* check whether L flag was changed */ - else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) { - if (lcd.flags & (LCD_FLAG_L)) - lcd_backlight(1); - else if (lcd.light_tempo == 0) - /* - * switch off the light only when the tempo - * lighting is gone - */ - lcd_backlight(0); - } + else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) + lcd_backlight(!!(lcd.flags & LCD_FLAG_L)); } return processed; @@ -1376,9 +1392,7 @@ static void lcd_write_char(char c) processed = 1; } else if (!strcmp(lcd.esc_seq.buf, "[H")) { /* cursor to home */ - lcd.addr.x = 0; - lcd.addr.y = 0; - lcd_gotoxy(); + lcd_home(); processed = 1; } /* codes starting with ^[[L */ @@ -1625,8 +1639,10 @@ static void lcd_init(void) else lcd_char_conv = NULL; - if (lcd.pins.bl != PIN_NONE) - init_scan_timer(); + if (lcd.pins.bl != PIN_NONE) { + mutex_init(&lcd.bl_tempo_lock); + INIT_DELAYED_WORK(&lcd.bl_work, lcd_bl_off); + } pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E], lcd_bits[LCD_PORT_C][LCD_BIT_E]); @@ -1655,14 +1671,11 @@ static void lcd_init(void) panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE); #endif #else - panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\nPanel-" - PANEL_VERSION); + panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE); #endif - lcd.addr.x = 0; - lcd.addr.y = 0; /* clear the display on the next device opening */ lcd.must_clear = true; - lcd_gotoxy(); + lcd_home(); } /* @@ -1997,19 +2010,8 @@ static void panel_scan_timer(void) panel_process_inputs(); } - if (lcd.enabled && lcd.initialized) { - if (keypressed) { - if (lcd.light_tempo == 0 && - ((lcd.flags & LCD_FLAG_L) == 0)) - lcd_backlight(1); - lcd.light_tempo = FLASH_LIGHT_TEMPO; - } else if (lcd.light_tempo > 0) { - lcd.light_tempo--; - if (lcd.light_tempo == 0 && - ((lcd.flags & LCD_FLAG_L) == 0)) - lcd_backlight(0); - } - } + if (keypressed && lcd.enabled && lcd.initialized) + lcd_poke(); mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME); } @@ -2270,25 +2272,26 @@ static void panel_detach(struct parport *port) if (scan_timer.function) del_timer_sync(&scan_timer); - if (pprt) { - if (keypad.enabled) { - misc_deregister(&keypad_dev); - keypad_initialized = 0; - } + if (keypad.enabled) { + misc_deregister(&keypad_dev); + keypad_initialized = 0; + } - if (lcd.enabled) { - panel_lcd_print("\x0cLCD driver " PANEL_VERSION - "\nunloaded.\x1b[Lc\x1b[Lb\x1b[L-"); - misc_deregister(&lcd_dev); - lcd.initialized = false; + if (lcd.enabled) { + panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); + misc_deregister(&lcd_dev); + if (lcd.pins.bl != PIN_NONE) { + cancel_delayed_work_sync(&lcd.bl_work); + __lcd_backlight(0); } - - /* TODO: free all input signals */ - parport_release(pprt); - parport_unregister_device(pprt); - pprt = NULL; - unregister_reboot_notifier(&panel_notifier); + lcd.initialized = false; } + + /* TODO: free all input signals */ + parport_release(pprt); + parport_unregister_device(pprt); + pprt = NULL; + unregister_reboot_notifier(&panel_notifier); } static struct parport_driver panel_driver = { @@ -2400,7 +2403,7 @@ static int __init panel_init_module(void) if (!lcd.enabled && !keypad.enabled) { /* no device enabled, let's exit */ - pr_err("driver version " PANEL_VERSION " disabled.\n"); + pr_err("panel driver disabled.\n"); return -ENODEV; } @@ -2411,12 +2414,10 @@ static int __init panel_init_module(void) } if (pprt) - pr_info("driver version " PANEL_VERSION - " registered on parport%d (io=0x%lx).\n", parport, - pprt->port->base); + pr_info("panel driver registered on parport%d (io=0x%lx).\n", + parport, pprt->port->base); else - pr_info("driver version " PANEL_VERSION - " not yet registered\n"); + pr_info("panel driver not yet registered\n"); return 0; } diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index 6fb773dbcd0c..93be82fc338a 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c @@ -219,15 +219,20 @@ static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, int write, unsigned long *paddr, int *pageshift) { pgd_t *pgdp; - pmd_t *pmdp; + p4d_t *p4dp; pud_t *pudp; + pmd_t *pmdp; pte_t pte; pgdp = pgd_offset(vma->vm_mm, vaddr); if (unlikely(pgd_none(*pgdp))) goto err; - pudp = pud_offset(pgdp, vaddr); + p4dp = p4d_offset(pgdp, vaddr); + if (unlikely(p4d_none(*p4dp))) + goto err; + + pudp = pud_offset(p4dp, vaddr); if (unlikely(pud_none(*pudp))) goto err; diff --git a/drivers/misc/sgi-gru/grumain.c b/drivers/misc/sgi-gru/grumain.c index 33741ad4a74a..3641f1334cf0 100644 --- a/drivers/misc/sgi-gru/grumain.c +++ b/drivers/misc/sgi-gru/grumain.c @@ -926,13 +926,14 @@ again: * * Note: gru segments alway mmaped on GRU_GSEG_PAGESIZE boundaries. */ -int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +int gru_fault(struct vm_fault *vmf) { + struct vm_area_struct *vma = vmf->vma; struct gru_thread_state *gts; unsigned long paddr, vaddr; unsigned long expires; - vaddr = (unsigned long)vmf->virtual_address; + vaddr = vmf->address; gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n", vma, vaddr, GSEG_BASE(vaddr)); STAT(nopfn); diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 5c3ce2459675..b5e308b50ed1 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h @@ -665,7 +665,7 @@ extern unsigned long gru_reserve_cb_resources(struct gru_state *gru, int cbr_au_count, char *cbmap); extern unsigned long gru_reserve_ds_resources(struct gru_state *gru, int dsr_au_count, char *dsmap); -extern int gru_fault(struct vm_area_struct *, struct vm_fault *vmf); +extern int gru_fault(struct vm_fault *vmf); extern struct gru_mm_struct *gru_register_mmu_notifier(void); extern void gru_drop_mmu_notifier(struct gru_mm_struct *gms); diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 557f9782c53c..0c26eaf5f62b 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -118,6 +118,8 @@ static DEFINE_SPINLOCK(xpnet_broadcast_lock); * now, the default is 64KB. */ #define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES) +/* 68 comes from min TCP+IP+MAC header */ +#define XPNET_MIN_MTU 68 /* 32KB has been determined to be the ideal */ #define XPNET_DEF_MTU (0x8000UL) @@ -330,22 +332,6 @@ xpnet_dev_stop(struct net_device *dev) return 0; } -static int -xpnet_dev_change_mtu(struct net_device *dev, int new_mtu) -{ - /* 68 comes from min TCP+IP+MAC header */ - if ((new_mtu < 68) || (new_mtu > XPNET_MAX_MTU)) { - dev_err(xpnet, "ifconfig %s mtu %d failed; value must be " - "between 68 and %ld\n", dev->name, new_mtu, - XPNET_MAX_MTU); - return -EINVAL; - } - - dev->mtu = new_mtu; - dev_dbg(xpnet, "ifconfig %s mtu set to %d\n", dev->name, new_mtu); - return 0; -} - /* * Notification that the other end has received the message and * DMA'd the skb information. At this point, they are done with @@ -519,7 +505,6 @@ static const struct net_device_ops xpnet_netdev_ops = { .ndo_open = xpnet_dev_open, .ndo_stop = xpnet_dev_stop, .ndo_start_xmit = xpnet_dev_hard_start_xmit, - .ndo_change_mtu = xpnet_dev_change_mtu, .ndo_tx_timeout = xpnet_dev_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, @@ -555,6 +540,8 @@ xpnet_init(void) xpnet_device->netdev_ops = &xpnet_netdev_ops; xpnet_device->mtu = XPNET_DEF_MTU; + xpnet_device->min_mtu = XPNET_MIN_MTU; + xpnet_device->max_mtu = XPNET_MAX_MTU; /* * Multicast assumes the LSB of the first octet is set for multicast diff --git a/drivers/misc/sram-exec.c b/drivers/misc/sram-exec.c new file mode 100644 index 000000000000..ac522417c462 --- /dev/null +++ b/drivers/misc/sram-exec.c @@ -0,0 +1,105 @@ +/* + * SRAM protect-exec region helper functions + * + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Dave Gerlach + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/genalloc.h> +#include <linux/sram.h> + +#include <asm/cacheflush.h> + +#include "sram.h" + +static DEFINE_MUTEX(exec_pool_list_mutex); +static LIST_HEAD(exec_pool_list); + +int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block, + struct sram_partition *part) +{ + unsigned long base = (unsigned long)part->base; + unsigned long end = base + block->size; + + if (!PAGE_ALIGNED(base) || !PAGE_ALIGNED(end)) { + dev_err(sram->dev, + "SRAM pool marked with 'protect-exec' is not page aligned and will not be created.\n"); + return -ENOMEM; + } + + return 0; +} + +int sram_add_protect_exec(struct sram_partition *part) +{ + mutex_lock(&exec_pool_list_mutex); + list_add_tail(&part->list, &exec_pool_list); + mutex_unlock(&exec_pool_list_mutex); + + return 0; +} + +/** + * sram_exec_copy - copy data to a protected executable region of sram + * + * @pool: struct gen_pool retrieved that is part of this sram + * @dst: Destination address for the copy, that must be inside pool + * @src: Source address for the data to copy + * @size: Size of copy to perform, which starting from dst, must reside in pool + * + * This helper function allows sram driver to act as central control location + * of 'protect-exec' pools which are normal sram pools but are always set + * read-only and executable except when copying data to them, at which point + * they are set to read-write non-executable, to make sure no memory is + * writeable and executable at the same time. This region must be page-aligned + * and is checked during probe, otherwise page attribute manipulation would + * not be possible. + */ +int sram_exec_copy(struct gen_pool *pool, void *dst, void *src, + size_t size) +{ + struct sram_partition *part = NULL, *p; + unsigned long base; + int pages; + + mutex_lock(&exec_pool_list_mutex); + list_for_each_entry(p, &exec_pool_list, list) { + if (p->pool == pool) + part = p; + } + mutex_unlock(&exec_pool_list_mutex); + + if (!part) + return -EINVAL; + + if (!addr_in_gen_pool(pool, (unsigned long)dst, size)) + return -EINVAL; + + base = (unsigned long)part->base; + pages = PAGE_ALIGN(size) / PAGE_SIZE; + + mutex_lock(&part->lock); + + set_memory_nx((unsigned long)base, pages); + set_memory_rw((unsigned long)base, pages); + + memcpy(dst, src, size); + + set_memory_ro((unsigned long)base, pages); + set_memory_x((unsigned long)base, pages); + + mutex_unlock(&part->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(sram_exec_copy); diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index f84b53d6ce50..d1185b78cf9a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -19,42 +19,21 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/genalloc.h> #include <linux/io.h> #include <linux/list_sort.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/slab.h> +#include <linux/mfd/syscon.h> +#include <soc/at91/atmel-secumod.h> -#define SRAM_GRANULARITY 32 - -struct sram_partition { - void __iomem *base; - - struct gen_pool *pool; - struct bin_attribute battr; - struct mutex lock; -}; - -struct sram_dev { - struct device *dev; - void __iomem *virt_base; - - struct gen_pool *pool; - struct clk *clk; +#include "sram.h" - struct sram_partition *partition; - u32 partitions; -}; - -struct sram_reserve { - struct list_head list; - u32 start; - u32 size; - bool export; - bool pool; - const char *label; -}; +#define SRAM_GRANULARITY 32 static ssize_t sram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, @@ -143,6 +122,18 @@ static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block, if (ret) return ret; } + if (block->protect_exec) { + ret = sram_check_protect_exec(sram, block, part); + if (ret) + return ret; + + ret = sram_add_pool(sram, block, start, part); + if (ret) + return ret; + + sram_add_protect_exec(part); + } + sram->partitions++; return 0; @@ -228,7 +219,11 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) if (of_find_property(child, "pool", NULL)) block->pool = true; - if ((block->export || block->pool) && block->size) { + if (of_find_property(child, "protect-exec", NULL)) + block->protect_exec = true; + + if ((block->export || block->pool || block->protect_exec) && + block->size) { exports++; label = NULL; @@ -244,8 +239,10 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) block->label = devm_kstrdup(sram->dev, label, GFP_KERNEL); - if (!block->label) + if (!block->label) { + ret = -ENOMEM; goto err_chunks; + } dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x\n", block->export ? "exported " : "", block->label, @@ -288,7 +285,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) goto err_chunks; } - if ((block->export || block->pool) && block->size) { + if ((block->export || block->pool || block->protect_exec) && + block->size) { ret = sram_add_partition(sram, block, res->start + block->start); if (ret) { @@ -334,12 +332,33 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) return ret; } +static int atmel_securam_wait(void) +{ + struct regmap *regmap; + u32 val; + + regmap = syscon_regmap_lookup_by_compatible("atmel,sama5d2-secumod"); + if (IS_ERR(regmap)) + return -ENODEV; + + return regmap_read_poll_timeout(regmap, AT91_SECUMOD_RAMRDY, val, + val & AT91_SECUMOD_RAMRDY_READY, + 10000, 500000); +} + +static const struct of_device_id sram_dt_ids[] = { + { .compatible = "mmio-sram" }, + { .compatible = "atmel,sama5d2-securam", .data = atmel_securam_wait }, + {} +}; + static int sram_probe(struct platform_device *pdev) { struct sram_dev *sram; struct resource *res; size_t size; int ret; + int (*init_func)(void); sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL); if (!sram) @@ -384,6 +403,13 @@ static int sram_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sram); + init_func = of_device_get_match_data(&pdev->dev); + if (init_func) { + ret = init_func(); + if (ret) + return ret; + } + dev_dbg(sram->dev, "SRAM pool: %zu KiB @ 0x%p\n", gen_pool_size(sram->pool) / 1024, sram->virt_base); @@ -405,17 +431,10 @@ static int sram_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF -static const struct of_device_id sram_dt_ids[] = { - { .compatible = "mmio-sram" }, - {} -}; -#endif - static struct platform_driver sram_driver = { .driver = { .name = "sram", - .of_match_table = of_match_ptr(sram_dt_ids), + .of_match_table = sram_dt_ids, }, .probe = sram_probe, .remove = sram_remove, diff --git a/drivers/misc/sram.h b/drivers/misc/sram.h new file mode 100644 index 000000000000..c181ce4c8fca --- /dev/null +++ b/drivers/misc/sram.h @@ -0,0 +1,58 @@ +/* + * Defines for the SRAM driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __SRAM_H +#define __SRAM_H + +struct sram_partition { + void __iomem *base; + + struct gen_pool *pool; + struct bin_attribute battr; + struct mutex lock; + struct list_head list; +}; + +struct sram_dev { + struct device *dev; + void __iomem *virt_base; + + struct gen_pool *pool; + struct clk *clk; + + struct sram_partition *partition; + u32 partitions; +}; + +struct sram_reserve { + struct list_head list; + u32 start; + u32 size; + bool export; + bool pool; + bool protect_exec; + const char *label; +}; + +#ifdef CONFIG_SRAM_EXEC +int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block, + struct sram_partition *part); +int sram_add_protect_exec(struct sram_partition *part); +#else +static inline int sram_check_protect_exec(struct sram_dev *sram, + struct sram_reserve *block, + struct sram_partition *part) +{ + return -ENODEV; +} + +static inline int sram_add_protect_exec(struct sram_partition *part) +{ + return -ENODEV; +} +#endif /* CONFIG_SRAM_EXEC */ +#endif /* __SRAM_H */ diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c index c344483fa7d6..2cde80c7bb93 100644 --- a/drivers/misc/vexpress-syscfg.c +++ b/drivers/misc/vexpress-syscfg.c @@ -16,7 +16,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/syscore_ops.h> #include <linux/vexpress.h> diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c index f866a4baecb5..21d0fa592145 100644 --- a/drivers/misc/vmw_vmci/vmci_context.c +++ b/drivers/misc/vmw_vmci/vmci_context.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/sched.h> +#include <linux/cred.h> #include <linux/slab.h> #include "vmci_queue_pair.h" @@ -303,7 +304,7 @@ int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg) vmci_dg_size = VMCI_DG_SIZE(dg); if (vmci_dg_size > VMCI_MAX_DG_SIZE) { - pr_devel("Datagram too large (bytes=%Zu)\n", vmci_dg_size); + pr_devel("Datagram too large (bytes=%zu)\n", vmci_dg_size); return VMCI_ERROR_INVALID_ARGS; } diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c index 8449516d6ac6..84258a48029d 100644 --- a/drivers/misc/vmw_vmci/vmci_event.c +++ b/drivers/misc/vmw_vmci/vmci_event.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/rculist.h> #include "vmci_driver.h" #include "vmci_event.h" diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 189b32519748..dad5abee656e 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -54,10 +54,7 @@ struct vmci_guest_device { struct device *dev; /* PCI device we are attached to */ void __iomem *iobase; - unsigned int irq; - unsigned int intr_type; bool exclusive_vectors; - struct msix_entry msix_entries[VMCI_MAX_INTRS]; struct tasklet_struct datagram_tasklet; struct tasklet_struct bm_tasklet; @@ -369,30 +366,6 @@ static void vmci_process_bitmap(unsigned long data) } /* - * Enable MSI-X. Try exclusive vectors first, then shared vectors. - */ -static int vmci_enable_msix(struct pci_dev *pdev, - struct vmci_guest_device *vmci_dev) -{ - int i; - int result; - - for (i = 0; i < VMCI_MAX_INTRS; ++i) { - vmci_dev->msix_entries[i].entry = i; - vmci_dev->msix_entries[i].vector = i; - } - - result = pci_enable_msix_exact(pdev, - vmci_dev->msix_entries, VMCI_MAX_INTRS); - if (result == 0) - vmci_dev->exclusive_vectors = true; - else if (result == -ENOSPC) - result = pci_enable_msix_exact(pdev, vmci_dev->msix_entries, 1); - - return result; -} - -/* * Interrupt handler for legacy or MSI interrupt, or for first MSI-X * interrupt (vector VMCI_INTR_DATAGRAM). */ @@ -406,7 +379,7 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev) * Otherwise we must read the ICR to determine what to do. */ - if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) { + if (dev->exclusive_vectors) { tasklet_schedule(&dev->datagram_tasklet); } else { unsigned int icr; @@ -491,7 +464,6 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, } vmci_dev->dev = &pdev->dev; - vmci_dev->intr_type = VMCI_INTR_TYPE_INTX; vmci_dev->exclusive_vectors = false; vmci_dev->iobase = iobase; @@ -592,26 +564,26 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * Enable interrupts. Try MSI-X first, then MSI, and then fallback on * legacy interrupts. */ - if (!vmci_disable_msix && !vmci_enable_msix(pdev, vmci_dev)) { - vmci_dev->intr_type = VMCI_INTR_TYPE_MSIX; - vmci_dev->irq = vmci_dev->msix_entries[0].vector; - } else if (!vmci_disable_msi && !pci_enable_msi(pdev)) { - vmci_dev->intr_type = VMCI_INTR_TYPE_MSI; - vmci_dev->irq = pdev->irq; + error = pci_alloc_irq_vectors(pdev, VMCI_MAX_INTRS, VMCI_MAX_INTRS, + PCI_IRQ_MSIX); + if (error < 0) { + error = pci_alloc_irq_vectors(pdev, 1, 1, + PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY); + if (error < 0) + goto err_remove_bitmap; } else { - vmci_dev->intr_type = VMCI_INTR_TYPE_INTX; - vmci_dev->irq = pdev->irq; + vmci_dev->exclusive_vectors = true; } /* * Request IRQ for legacy or MSI interrupts, or for first * MSI-X vector. */ - error = request_irq(vmci_dev->irq, vmci_interrupt, IRQF_SHARED, - KBUILD_MODNAME, vmci_dev); + error = request_irq(pci_irq_vector(pdev, 0), vmci_interrupt, + IRQF_SHARED, KBUILD_MODNAME, vmci_dev); if (error) { dev_err(&pdev->dev, "Irq %u in use: %d\n", - vmci_dev->irq, error); + pci_irq_vector(pdev, 0), error); goto err_disable_msi; } @@ -622,13 +594,13 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, * between the vectors. */ if (vmci_dev->exclusive_vectors) { - error = request_irq(vmci_dev->msix_entries[1].vector, + error = request_irq(pci_irq_vector(pdev, 1), vmci_interrupt_bm, 0, KBUILD_MODNAME, vmci_dev); if (error) { dev_err(&pdev->dev, "Failed to allocate irq %u: %d\n", - vmci_dev->msix_entries[1].vector, error); + pci_irq_vector(pdev, 1), error); goto err_free_irq; } } @@ -651,15 +623,12 @@ static int vmci_guest_probe_device(struct pci_dev *pdev, return 0; err_free_irq: - free_irq(vmci_dev->irq, vmci_dev); + free_irq(pci_irq_vector(pdev, 0), vmci_dev); tasklet_kill(&vmci_dev->datagram_tasklet); tasklet_kill(&vmci_dev->bm_tasklet); err_disable_msi: - if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) - pci_disable_msix(pdev); - else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) - pci_disable_msi(pdev); + pci_free_irq_vectors(pdev); vmci_err = vmci_event_unsubscribe(ctx_update_sub_id); if (vmci_err < VMCI_SUCCESS) @@ -719,14 +688,10 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) * MSI-X, we might have multiple vectors, each with their own * IRQ, which we must free too. */ - free_irq(vmci_dev->irq, vmci_dev); - if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) { - if (vmci_dev->exclusive_vectors) - free_irq(vmci_dev->msix_entries[1].vector, vmci_dev); - pci_disable_msix(pdev); - } else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) { - pci_disable_msi(pdev); - } + if (vmci_dev->exclusive_vectors) + free_irq(pci_irq_vector(pdev, 1), vmci_dev); + free_irq(pci_irq_vector(pdev, 0), vmci_dev); + pci_free_irq_vectors(pdev); tasklet_kill(&vmci_dev->datagram_tasklet); tasklet_kill(&vmci_dev->bm_tasklet); diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index ec090105eb4b..8a16a26e9658 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/sched.h> +#include <linux/cred.h> #include <linux/slab.h> #include <linux/file.h> #include <linux/init.h> diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index f84a4275ca29..498c0854305f 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -2928,7 +2928,7 @@ int vmci_qpair_get_produce_indexes(const struct vmci_qp *qpair, EXPORT_SYMBOL_GPL(vmci_qpair_get_produce_indexes); /* - * vmci_qpair_get_consume_indexes() - Retrieves the indexes of the comsumer. + * vmci_qpair_get_consume_indexes() - Retrieves the indexes of the consumer. * @qpair: Pointer to the queue pair struct. * @consumer_tail: Reference used for storing consumer tail index. * @producer_head: Reference used for storing the producer head index. diff --git a/drivers/misc/vmw_vmci/vmci_resource.c b/drivers/misc/vmw_vmci/vmci_resource.c index 9a53a30de445..1ab6e8737a5f 100644 --- a/drivers/misc/vmw_vmci/vmci_resource.c +++ b/drivers/misc/vmw_vmci/vmci_resource.c @@ -17,6 +17,7 @@ #include <linux/hash.h> #include <linux/types.h> #include <linux/rculist.h> +#include <linux/completion.h> #include "vmci_resource.h" #include "vmci_driver.h" |