diff options
Diffstat (limited to 'drivers/misc')
56 files changed, 6209 insertions, 1732 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ee9402324a23..bbeb4516facf 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -51,16 +51,6 @@ config AD525X_DPOT_SPI To compile this driver as a module, choose M here: the module will be called ad525x_dpot-spi. -config ATMEL_PWM - tristate "Atmel AT32/AT91 PWM support" - depends on HAVE_CLK - depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 - help - This option enables device driver support for the PWM channels - on certain Atmel processors. Pulse Width Modulation is used for - purposes including software controlled power-efficient backlights - on LCD displays, motor control, and waveform generation. - config ATMEL_TCLIB bool "Atmel AT32/AT91 Timer/Counter Library" depends on (AVR32 || ARCH_AT91) @@ -537,4 +527,5 @@ source "drivers/misc/vmw_vmci/Kconfig" source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" +source "drivers/misc/cxl/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d59ce1261b38..7d5c4cd118c4 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o obj-$(CONFIG_INTEL_MID_PTI) += pti.o -obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_BMP085) += bmp085.o @@ -56,3 +55,4 @@ obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o +obj-$(CONFIG_CXL_BASE) += cxl/ diff --git a/drivers/misc/altera-stapl/altera.c b/drivers/misc/altera-stapl/altera.c index 24272e022bec..bca2630d006f 100644 --- a/drivers/misc/altera-stapl/altera.c +++ b/drivers/misc/altera-stapl/altera.c @@ -454,7 +454,7 @@ exit_done: name = &p[str_table + name_id]; - if (strnicmp(aconf->action, name, strlen(name)) == 0) { + if (strncasecmp(aconf->action, name, strlen(name)) == 0) { action_found = 1; current_proc = get_unaligned_be32(&p[action_table + @@ -2176,7 +2176,7 @@ static int altera_get_note(u8 *p, s32 program_size, key_ptr = &p[note_strings + get_unaligned_be32( &p[note_table + (8 * i)])]; - if ((strnicmp(key, key_ptr, strlen(key_ptr)) == 0) && + if ((strncasecmp(key, key_ptr, strlen(key_ptr)) == 0) && (key != NULL)) { status = 0; diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c deleted file mode 100644 index a6dc56e1bc58..000000000000 --- a/drivers/misc/atmel_pwm.c +++ /dev/null @@ -1,402 +0,0 @@ -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/atmel_pwm.h> - - -/* - * This is a simple driver for the PWM controller found in various newer - * Atmel SOCs, including the AVR32 series and the AT91sam9263. - * - * Chips with current Linux ports have only 4 PWM channels, out of max 32. - * AT32UC3A and AT32UC3B chips have 7 channels (but currently no Linux). - * Docs are inconsistent about the width of the channel counter registers; - * it's at least 16 bits, but several places say 20 bits. - */ -#define PWM_NCHAN 4 /* max 32 */ - -struct pwm { - spinlock_t lock; - struct platform_device *pdev; - u32 mask; - int irq; - void __iomem *base; - struct clk *clk; - struct pwm_channel *channel[PWM_NCHAN]; - void (*handler[PWM_NCHAN])(struct pwm_channel *); -}; - - -/* global PWM controller registers */ -#define PWM_MR 0x00 -#define PWM_ENA 0x04 -#define PWM_DIS 0x08 -#define PWM_SR 0x0c -#define PWM_IER 0x10 -#define PWM_IDR 0x14 -#define PWM_IMR 0x18 -#define PWM_ISR 0x1c - -static inline void pwm_writel(const struct pwm *p, unsigned offset, u32 val) -{ - __raw_writel(val, p->base + offset); -} - -static inline u32 pwm_readl(const struct pwm *p, unsigned offset) -{ - return __raw_readl(p->base + offset); -} - -static inline void __iomem *pwmc_regs(const struct pwm *p, int index) -{ - return p->base + 0x200 + index * 0x20; -} - -static struct pwm *pwm; - -static void pwm_dumpregs(struct pwm_channel *ch, char *tag) -{ - struct device *dev = &pwm->pdev->dev; - - dev_dbg(dev, "%s: mr %08x, sr %08x, imr %08x\n", - tag, - pwm_readl(pwm, PWM_MR), - pwm_readl(pwm, PWM_SR), - pwm_readl(pwm, PWM_IMR)); - dev_dbg(dev, - "pwm ch%d - mr %08x, dty %u, prd %u, cnt %u\n", - ch->index, - pwm_channel_readl(ch, PWM_CMR), - pwm_channel_readl(ch, PWM_CDTY), - pwm_channel_readl(ch, PWM_CPRD), - pwm_channel_readl(ch, PWM_CCNT)); -} - - -/** - * pwm_channel_alloc - allocate an unused PWM channel - * @index: identifies the channel - * @ch: structure to be initialized - * - * Drivers allocate PWM channels according to the board's wiring, and - * matching board-specific setup code. Returns zero or negative errno. - */ -int pwm_channel_alloc(int index, struct pwm_channel *ch) -{ - unsigned long flags; - int status = 0; - - if (!pwm) - return -EPROBE_DEFER; - - if (!(pwm->mask & 1 << index)) - return -ENODEV; - - if (index < 0 || index >= PWM_NCHAN || !ch) - return -EINVAL; - memset(ch, 0, sizeof *ch); - - spin_lock_irqsave(&pwm->lock, flags); - if (pwm->channel[index]) - status = -EBUSY; - else { - clk_enable(pwm->clk); - - ch->regs = pwmc_regs(pwm, index); - ch->index = index; - - /* REVISIT: ap7000 seems to go 2x as fast as we expect!! */ - ch->mck = clk_get_rate(pwm->clk); - - pwm->channel[index] = ch; - pwm->handler[index] = NULL; - - /* channel and irq are always disabled when we return */ - pwm_writel(pwm, PWM_DIS, 1 << index); - pwm_writel(pwm, PWM_IDR, 1 << index); - } - spin_unlock_irqrestore(&pwm->lock, flags); - return status; -} -EXPORT_SYMBOL(pwm_channel_alloc); - -static int pwmcheck(struct pwm_channel *ch) -{ - int index; - - if (!pwm) - return -ENODEV; - if (!ch) - return -EINVAL; - index = ch->index; - if (index < 0 || index >= PWM_NCHAN || pwm->channel[index] != ch) - return -EINVAL; - - return index; -} - -/** - * pwm_channel_free - release a previously allocated channel - * @ch: the channel being released - * - * The channel is completely shut down (counter and IRQ disabled), - * and made available for re-use. Returns zero, or negative errno. - */ -int pwm_channel_free(struct pwm_channel *ch) -{ - unsigned long flags; - int t; - - spin_lock_irqsave(&pwm->lock, flags); - t = pwmcheck(ch); - if (t >= 0) { - pwm->channel[t] = NULL; - pwm->handler[t] = NULL; - - /* channel and irq are always disabled when we return */ - pwm_writel(pwm, PWM_DIS, 1 << t); - pwm_writel(pwm, PWM_IDR, 1 << t); - - clk_disable(pwm->clk); - t = 0; - } - spin_unlock_irqrestore(&pwm->lock, flags); - return t; -} -EXPORT_SYMBOL(pwm_channel_free); - -int __pwm_channel_onoff(struct pwm_channel *ch, int enabled) -{ - unsigned long flags; - int t; - - /* OMITTED FUNCTIONALITY: starting several channels in synch */ - - spin_lock_irqsave(&pwm->lock, flags); - t = pwmcheck(ch); - if (t >= 0) { - pwm_writel(pwm, enabled ? PWM_ENA : PWM_DIS, 1 << t); - t = 0; - pwm_dumpregs(ch, enabled ? "enable" : "disable"); - } - spin_unlock_irqrestore(&pwm->lock, flags); - - return t; -} -EXPORT_SYMBOL(__pwm_channel_onoff); - -/** - * pwm_clk_alloc - allocate and configure CLKA or CLKB - * @prescale: from 0..10, the power of two used to divide MCK - * @div: from 1..255, the linear divisor to use - * - * Returns PWM_CPR_CLKA, PWM_CPR_CLKB, or negative errno. The allocated - * clock will run with a period of (2^prescale * div) / MCK, or twice as - * long if center aligned PWM output is used. The clock must later be - * deconfigured using pwm_clk_free(). - */ -int pwm_clk_alloc(unsigned prescale, unsigned div) -{ - unsigned long flags; - u32 mr; - u32 val = (prescale << 8) | div; - int ret = -EBUSY; - - if (prescale >= 10 || div == 0 || div > 255) - return -EINVAL; - - spin_lock_irqsave(&pwm->lock, flags); - mr = pwm_readl(pwm, PWM_MR); - if ((mr & 0xffff) == 0) { - mr |= val; - ret = PWM_CPR_CLKA; - } else if ((mr & (0xffff << 16)) == 0) { - mr |= val << 16; - ret = PWM_CPR_CLKB; - } - if (ret > 0) - pwm_writel(pwm, PWM_MR, mr); - spin_unlock_irqrestore(&pwm->lock, flags); - return ret; -} -EXPORT_SYMBOL(pwm_clk_alloc); - -/** - * pwm_clk_free - deconfigure and release CLKA or CLKB - * - * Reverses the effect of pwm_clk_alloc(). - */ -void pwm_clk_free(unsigned clk) -{ - unsigned long flags; - u32 mr; - - spin_lock_irqsave(&pwm->lock, flags); - mr = pwm_readl(pwm, PWM_MR); - if (clk == PWM_CPR_CLKA) - pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 0)); - if (clk == PWM_CPR_CLKB) - pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 16)); - spin_unlock_irqrestore(&pwm->lock, flags); -} -EXPORT_SYMBOL(pwm_clk_free); - -/** - * pwm_channel_handler - manage channel's IRQ handler - * @ch: the channel - * @handler: the handler to use, possibly NULL - * - * If the handler is non-null, the handler will be called after every - * period of this PWM channel. If the handler is null, this channel - * won't generate an IRQ. - */ -int pwm_channel_handler(struct pwm_channel *ch, - void (*handler)(struct pwm_channel *ch)) -{ - unsigned long flags; - int t; - - spin_lock_irqsave(&pwm->lock, flags); - t = pwmcheck(ch); - if (t >= 0) { - pwm->handler[t] = handler; - pwm_writel(pwm, handler ? PWM_IER : PWM_IDR, 1 << t); - t = 0; - } - spin_unlock_irqrestore(&pwm->lock, flags); - - return t; -} -EXPORT_SYMBOL(pwm_channel_handler); - -static irqreturn_t pwm_irq(int id, void *_pwm) -{ - struct pwm *p = _pwm; - irqreturn_t handled = IRQ_NONE; - u32 irqstat; - int index; - - spin_lock(&p->lock); - - /* ack irqs, then handle them */ - irqstat = pwm_readl(pwm, PWM_ISR); - - while (irqstat) { - struct pwm_channel *ch; - void (*handler)(struct pwm_channel *ch); - - index = ffs(irqstat) - 1; - irqstat &= ~(1 << index); - ch = pwm->channel[index]; - handler = pwm->handler[index]; - if (handler && ch) { - spin_unlock(&p->lock); - handler(ch); - spin_lock(&p->lock); - handled = IRQ_HANDLED; - } - } - - spin_unlock(&p->lock); - return handled; -} - -static int __init pwm_probe(struct platform_device *pdev) -{ - struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int irq = platform_get_irq(pdev, 0); - u32 *mp = pdev->dev.platform_data; - struct pwm *p; - int status = -EIO; - - if (pwm) - return -EBUSY; - if (!r || irq < 0 || !mp || !*mp) - return -ENODEV; - if (*mp & ~((1<<PWM_NCHAN)-1)) { - dev_warn(&pdev->dev, "mask 0x%x ... more than %d channels\n", - *mp, PWM_NCHAN); - return -EINVAL; - } - - p = kzalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return -ENOMEM; - - spin_lock_init(&p->lock); - p->pdev = pdev; - p->mask = *mp; - p->irq = irq; - p->base = ioremap(r->start, resource_size(r)); - if (!p->base) - goto fail; - p->clk = clk_get(&pdev->dev, "pwm_clk"); - if (IS_ERR(p->clk)) { - status = PTR_ERR(p->clk); - p->clk = NULL; - goto fail; - } - - status = request_irq(irq, pwm_irq, 0, pdev->name, p); - if (status < 0) - goto fail; - - pwm = p; - platform_set_drvdata(pdev, p); - - return 0; - -fail: - if (p->clk) - clk_put(p->clk); - if (p->base) - iounmap(p->base); - - kfree(p); - return status; -} - -static int __exit pwm_remove(struct platform_device *pdev) -{ - struct pwm *p = platform_get_drvdata(pdev); - - if (p != pwm) - return -EINVAL; - - clk_enable(pwm->clk); - pwm_writel(pwm, PWM_DIS, (1 << PWM_NCHAN) - 1); - pwm_writel(pwm, PWM_IDR, (1 << PWM_NCHAN) - 1); - clk_disable(pwm->clk); - - pwm = NULL; - - free_irq(p->irq, p); - clk_put(p->clk); - iounmap(p->base); - kfree(p); - - return 0; -} - -static struct platform_driver atmel_pwm_driver = { - .driver = { - .name = "atmel_pwm", - .owner = THIS_MODULE, - }, - .remove = __exit_p(pwm_remove), - - /* NOTE: PWM can keep running in AVR32 "idle" and "frozen" states; - * and all AT91sam9263 states, albeit at reduced clock rate if - * MCK becomes the slow clock (i.e. what Linux labels STR). - */ -}; - -module_platform_driver_probe(atmel_pwm_driver, pwm_probe); - -MODULE_DESCRIPTION("Driver for AT32/AT91 PWM module"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:atmel_pwm"); diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index c8d8e38d0d8a..0ca05c3ec8d6 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -35,60 +35,31 @@ static LIST_HEAD(tc_list); /** * atmel_tc_alloc - allocate a specified TC block * @block: which block to allocate - * @name: name to be associated with the iomem resource * * Caller allocates a block. If it is available, a pointer to a * pre-initialized struct atmel_tc is returned. The caller can access * the registers directly through the "regs" field. */ -struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) +struct atmel_tc *atmel_tc_alloc(unsigned block) { struct atmel_tc *tc; struct platform_device *pdev = NULL; - struct resource *r; - size_t size; spin_lock(&tc_list_lock); list_for_each_entry(tc, &tc_list, node) { - if (tc->pdev->dev.of_node) { - if (of_alias_get_id(tc->pdev->dev.of_node, "tcb") - == block) { - pdev = tc->pdev; - break; - } - } else if (tc->pdev->id == block) { + if (tc->allocated) + continue; + + if ((tc->pdev->dev.of_node && tc->id == block) || + (tc->pdev->id == block)) { pdev = tc->pdev; + tc->allocated = true; break; } } - - if (!pdev || tc->iomem) - goto fail; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - goto fail; - - size = resource_size(r); - r = request_mem_region(r->start, size, name); - if (!r) - goto fail; - - tc->regs = ioremap(r->start, size); - if (!tc->regs) - goto fail_ioremap; - - tc->iomem = r; - -out: spin_unlock(&tc_list_lock); - return tc; -fail_ioremap: - release_mem_region(r->start, size); -fail: - tc = NULL; - goto out; + return pdev ? tc : NULL; } EXPORT_SYMBOL_GPL(atmel_tc_alloc); @@ -96,19 +67,14 @@ EXPORT_SYMBOL_GPL(atmel_tc_alloc); * atmel_tc_free - release a specified TC block * @tc: Timer/counter block that was returned by atmel_tc_alloc() * - * This reverses the effect of atmel_tc_alloc(), unmapping the I/O - * registers, invalidating the resource returned by that routine and - * making the TC available to other drivers. + * This reverses the effect of atmel_tc_alloc(), invalidating the resource + * returned by that routine and making the TC available to other drivers. */ void atmel_tc_free(struct atmel_tc *tc) { spin_lock(&tc_list_lock); - if (tc->regs) { - iounmap(tc->regs); - release_mem_region(tc->iomem->start, resource_size(tc->iomem)); - tc->regs = NULL; - tc->iomem = NULL; - } + if (tc->allocated) + tc->allocated = false; spin_unlock(&tc_list_lock); } EXPORT_SYMBOL_GPL(atmel_tc_free); @@ -142,25 +108,27 @@ static int __init tc_probe(struct platform_device *pdev) struct atmel_tc *tc; struct clk *clk; int irq; - - if (!platform_get_resource(pdev, IORESOURCE_MEM, 0)) - return -EINVAL; + struct resource *r; + unsigned int i; irq = platform_get_irq(pdev, 0); if (irq < 0) return -EINVAL; - tc = kzalloc(sizeof(struct atmel_tc), GFP_KERNEL); + tc = devm_kzalloc(&pdev->dev, sizeof(struct atmel_tc), GFP_KERNEL); if (!tc) return -ENOMEM; tc->pdev = pdev; - clk = clk_get(&pdev->dev, "t0_clk"); - if (IS_ERR(clk)) { - kfree(tc); - return -EINVAL; - } + clk = devm_clk_get(&pdev->dev, "t0_clk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tc->regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(tc->regs)) + return PTR_ERR(tc->regs); /* Now take SoC information if available */ if (pdev->dev.of_node) { @@ -168,13 +136,17 @@ static int __init tc_probe(struct platform_device *pdev) match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); if (match) tc->tcb_config = match->data; + + tc->id = of_alias_get_id(tc->pdev->dev.of_node, "tcb"); + } else { + tc->id = pdev->id; } tc->clk[0] = clk; - tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); + tc->clk[1] = devm_clk_get(&pdev->dev, "t1_clk"); if (IS_ERR(tc->clk[1])) tc->clk[1] = clk; - tc->clk[2] = clk_get(&pdev->dev, "t2_clk"); + tc->clk[2] = devm_clk_get(&pdev->dev, "t2_clk"); if (IS_ERR(tc->clk[2])) tc->clk[2] = clk; @@ -186,18 +158,33 @@ static int __init tc_probe(struct platform_device *pdev) if (tc->irq[2] < 0) tc->irq[2] = irq; + for (i = 0; i < 3; i++) + writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); + spin_lock(&tc_list_lock); list_add_tail(&tc->node, &tc_list); spin_unlock(&tc_list_lock); + platform_set_drvdata(pdev, tc); + return 0; } +static void tc_shutdown(struct platform_device *pdev) +{ + int i; + struct atmel_tc *tc = platform_get_drvdata(pdev); + + for (i = 0; i < 3; i++) + writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); +} + static struct platform_driver tc_driver = { .driver = { .name = "atmel_tcb", .of_match_table = of_match_ptr(atmel_tcb_dt_ids), }, + .shutdown = tc_shutdown, }; static int __init tc_init(void) diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index 7be89832db19..339b252fcedd 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -16,6 +16,7 @@ #include <linux/completion.h> #include <linux/miscdevice.h> #include <linux/dmaengine.h> +#include <linux/fsldma.h> #include <linux/interrupt.h> #include <linux/highmem.h> #include <linux/kernel.h> @@ -518,23 +519,22 @@ static noinline int fpga_program_dma(struct fpga_dev *priv) config.direction = DMA_MEM_TO_DEV; config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; config.dst_maxburst = fpga_fifo_size(priv->regs) / 2 / 4; - ret = chan->device->device_control(chan, DMA_SLAVE_CONFIG, - (unsigned long)&config); + ret = dmaengine_slave_config(chan, &config); if (ret) { dev_err(priv->dev, "DMA slave configuration failed\n"); goto out_dma_unmap; } - ret = chan->device->device_control(chan, FSLDMA_EXTERNAL_START, 1); + ret = fsl_dma_external_start(chan, 1) if (ret) { dev_err(priv->dev, "DMA external control setup failed\n"); goto out_dma_unmap; } /* setup and submit the DMA transaction */ - tx = chan->device->device_prep_dma_sg(chan, - table.sgl, num_pages, - vb->sglist, vb->sglen, 0); + + tx = dmaengine_prep_dma_sg(chan, table.sgl, num_pages, + vb->sglist, vb->sglen, 0); if (!tx) { dev_err(priv->dev, "Unable to prep DMA transaction\n"); ret = -ENOMEM; @@ -749,13 +749,8 @@ static ssize_t fpga_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct fpga_dev *priv = filp->private_data; - - count = min_t(size_t, priv->bytes - *f_pos, count); - if (copy_to_user(buf, priv->vb.vaddr + *f_pos, count)) - return -EFAULT; - - *f_pos += count; - return count; + return simple_read_from_buffer(buf, count, ppos, + priv->vb.vaddr, priv->bytes); } static loff_t fpga_llseek(struct file *filp, loff_t offset, int origin) @@ -767,26 +762,7 @@ static loff_t fpga_llseek(struct file *filp, loff_t offset, int origin) if ((filp->f_flags & O_ACCMODE) != O_RDONLY) return -EINVAL; - switch (origin) { - case SEEK_SET: /* seek relative to the beginning of the file */ - newpos = offset; - break; - case SEEK_CUR: /* seek relative to current position in the file */ - newpos = filp->f_pos + offset; - break; - case SEEK_END: /* seek relative to the end of the file */ - newpos = priv->fw_size - offset; - break; - default: - return -EINVAL; - } - - /* check for sanity */ - if (newpos > priv->fw_size) - return -EINVAL; - - filp->f_pos = newpos; - return newpos; + return fixed_size_llseek(file, offset, origin, priv->fw_size); } static const struct file_operations fpga_fops = { diff --git a/drivers/misc/cxl/Kconfig b/drivers/misc/cxl/Kconfig new file mode 100644 index 000000000000..a990b39b4dfb --- /dev/null +++ b/drivers/misc/cxl/Kconfig @@ -0,0 +1,25 @@ +# +# IBM Coherent Accelerator (CXL) compatible devices +# + +config CXL_BASE + bool + default n + select PPC_COPRO_BASE + +config CXL + tristate "Support for IBM Coherent Accelerators (CXL)" + depends on PPC_POWERNV && PCI_MSI + select CXL_BASE + default m + help + Select this option to enable driver support for IBM Coherent + Accelerators (CXL). CXL is otherwise known as Coherent Accelerator + Processor Interface (CAPI). CAPI allows accelerators in FPGAs to be + coherently attached to a CPU via an MMU. This driver enables + userspace programs to access these accelerators via /dev/cxl/afuM.N + devices. + + CAPI adapters are found in POWER8 based systems. + + If unsure, say N. diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile new file mode 100644 index 000000000000..165e98fef2c2 --- /dev/null +++ b/drivers/misc/cxl/Makefile @@ -0,0 +1,3 @@ +cxl-y += main.o file.o irq.o fault.o native.o context.o sysfs.o debugfs.o pci.o +obj-$(CONFIG_CXL) += cxl.o +obj-$(CONFIG_CXL_BASE) += base.o diff --git a/drivers/misc/cxl/base.c b/drivers/misc/cxl/base.c new file mode 100644 index 000000000000..0654ad83675e --- /dev/null +++ b/drivers/misc/cxl/base.c @@ -0,0 +1,86 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/rcupdate.h> +#include <asm/errno.h> +#include <misc/cxl.h> +#include "cxl.h" + +/* protected by rcu */ +static struct cxl_calls *cxl_calls; + +atomic_t cxl_use_count = ATOMIC_INIT(0); +EXPORT_SYMBOL(cxl_use_count); + +#ifdef CONFIG_CXL_MODULE + +static inline struct cxl_calls *cxl_calls_get(void) +{ + struct cxl_calls *calls = NULL; + + rcu_read_lock(); + calls = rcu_dereference(cxl_calls); + if (calls && !try_module_get(calls->owner)) + calls = NULL; + rcu_read_unlock(); + + return calls; +} + +static inline void cxl_calls_put(struct cxl_calls *calls) +{ + BUG_ON(calls != cxl_calls); + + /* we don't need to rcu this, as we hold a reference to the module */ + module_put(cxl_calls->owner); +} + +#else /* !defined CONFIG_CXL_MODULE */ + +static inline struct cxl_calls *cxl_calls_get(void) +{ + return cxl_calls; +} + +static inline void cxl_calls_put(struct cxl_calls *calls) { } + +#endif /* CONFIG_CXL_MODULE */ + +void cxl_slbia(struct mm_struct *mm) +{ + struct cxl_calls *calls; + + calls = cxl_calls_get(); + if (!calls) + return; + + if (cxl_ctx_in_use()) + calls->cxl_slbia(mm); + + cxl_calls_put(calls); +} + +int register_cxl_calls(struct cxl_calls *calls) +{ + if (cxl_calls) + return -EBUSY; + + rcu_assign_pointer(cxl_calls, calls); + return 0; +} +EXPORT_SYMBOL_GPL(register_cxl_calls); + +void unregister_cxl_calls(struct cxl_calls *calls) +{ + BUG_ON(cxl_calls->owner != calls->owner); + RCU_INIT_POINTER(cxl_calls, NULL); + synchronize_rcu(); +} +EXPORT_SYMBOL_GPL(unregister_cxl_calls); diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c new file mode 100644 index 000000000000..cca472109135 --- /dev/null +++ b/drivers/misc/cxl/context.c @@ -0,0 +1,193 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/sched.h> +#include <linux/pid.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/debugfs.h> +#include <linux/slab.h> +#include <linux/idr.h> +#include <asm/cputable.h> +#include <asm/current.h> +#include <asm/copro.h> + +#include "cxl.h" + +/* + * Allocates space for a CXL context. + */ +struct cxl_context *cxl_context_alloc(void) +{ + return kzalloc(sizeof(struct cxl_context), GFP_KERNEL); +} + +/* + * Initialises a CXL context. + */ +int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master) +{ + int i; + + spin_lock_init(&ctx->sste_lock); + ctx->afu = afu; + ctx->master = master; + ctx->pid = NULL; /* Set in start work ioctl */ + + /* + * Allocate the segment table before we put it in the IDR so that we + * can always access it when dereferenced from IDR. For the same + * reason, the segment table is only destroyed after the context is + * removed from the IDR. Access to this in the IOCTL is protected by + * Linux filesytem symantics (can't IOCTL until open is complete). + */ + i = cxl_alloc_sst(ctx); + if (i) + return i; + + INIT_WORK(&ctx->fault_work, cxl_handle_fault); + + init_waitqueue_head(&ctx->wq); + spin_lock_init(&ctx->lock); + + ctx->irq_bitmap = NULL; + ctx->pending_irq = false; + ctx->pending_fault = false; + ctx->pending_afu_err = false; + + /* + * When we have to destroy all contexts in cxl_context_detach_all() we + * end up with afu_release_irqs() called from inside a + * idr_for_each_entry(). Hence we need to make sure that anything + * dereferenced from this IDR is ok before we allocate the IDR here. + * This clears out the IRQ ranges to ensure this. + */ + for (i = 0; i < CXL_IRQ_RANGES; i++) + ctx->irqs.range[i] = 0; + + mutex_init(&ctx->status_mutex); + + ctx->status = OPENED; + + /* + * Allocating IDR! We better make sure everything's setup that + * dereferences from it. + */ + idr_preload(GFP_KERNEL); + spin_lock(&afu->contexts_lock); + i = idr_alloc(&ctx->afu->contexts_idr, ctx, 0, + ctx->afu->num_procs, GFP_NOWAIT); + spin_unlock(&afu->contexts_lock); + idr_preload_end(); + if (i < 0) + return i; + + ctx->pe = i; + ctx->elem = &ctx->afu->spa[i]; + ctx->pe_inserted = false; + return 0; +} + +/* + * Map a per-context mmio space into the given vma. + */ +int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma) +{ + u64 len = vma->vm_end - vma->vm_start; + len = min(len, ctx->psn_size); + + if (ctx->afu->current_mode == CXL_MODE_DEDICATED) { + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + return vm_iomap_memory(vma, ctx->afu->psn_phys, ctx->afu->adapter->ps_size); + } + + /* make sure there is a valid per process space for this AFU */ + if ((ctx->master && !ctx->afu->psa) || (!ctx->afu->pp_psa)) { + pr_devel("AFU doesn't support mmio space\n"); + return -EINVAL; + } + + /* Can't mmap until the AFU is enabled */ + if (!ctx->afu->enabled) + return -EBUSY; + + pr_devel("%s: mmio physical: %llx pe: %i master:%i\n", __func__, + ctx->psn_phys, ctx->pe , ctx->master); + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + return vm_iomap_memory(vma, ctx->psn_phys, len); +} + +/* + * Detach a context from the hardware. This disables interrupts and doesn't + * return until all outstanding interrupts for this context have completed. The + * hardware should no longer access *ctx after this has returned. + */ +static void __detach_context(struct cxl_context *ctx) +{ + enum cxl_context_status status; + + mutex_lock(&ctx->status_mutex); + status = ctx->status; + ctx->status = CLOSED; + mutex_unlock(&ctx->status_mutex); + if (status != STARTED) + return; + + WARN_ON(cxl_detach_process(ctx)); + afu_release_irqs(ctx); + flush_work(&ctx->fault_work); /* Only needed for dedicated process */ + wake_up_all(&ctx->wq); +} + +/* + * Detach the given context from the AFU. This doesn't actually + * free the context but it should stop the context running in hardware + * (ie. prevent this context from generating any further interrupts + * so that it can be freed). + */ +void cxl_context_detach(struct cxl_context *ctx) +{ + __detach_context(ctx); +} + +/* + * Detach all contexts on the given AFU. + */ +void cxl_context_detach_all(struct cxl_afu *afu) +{ + struct cxl_context *ctx; + int tmp; + + rcu_read_lock(); + idr_for_each_entry(&afu->contexts_idr, ctx, tmp) + /* + * Anything done in here needs to be setup before the IDR is + * created and torn down after the IDR removed + */ + __detach_context(ctx); + rcu_read_unlock(); +} + +void cxl_context_free(struct cxl_context *ctx) +{ + spin_lock(&ctx->afu->contexts_lock); + idr_remove(&ctx->afu->contexts_idr, ctx->pe); + spin_unlock(&ctx->afu->contexts_lock); + synchronize_rcu(); + + free_page((u64)ctx->sstp); + ctx->sstp = NULL; + + put_pid(ctx->pid); + kfree(ctx); +} diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h new file mode 100644 index 000000000000..3d2b8677ec8a --- /dev/null +++ b/drivers/misc/cxl/cxl.h @@ -0,0 +1,629 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _CXL_H_ +#define _CXL_H_ + +#include <linux/interrupt.h> +#include <linux/semaphore.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/pid.h> +#include <linux/io.h> +#include <linux/pci.h> +#include <asm/cputable.h> +#include <asm/mmu.h> +#include <asm/reg.h> +#include <misc/cxl.h> + +#include <uapi/misc/cxl.h> + +extern uint cxl_verbose; + +#define CXL_TIMEOUT 5 + +/* + * Bump version each time a user API change is made, whether it is + * backwards compatible ot not. + */ +#define CXL_API_VERSION 1 +#define CXL_API_VERSION_COMPATIBLE 1 + +/* + * Opaque types to avoid accidentally passing registers for the wrong MMIO + * + * At the end of the day, I'm not married to using typedef here, but it might + * (and has!) help avoid bugs like mixing up CXL_PSL_CtxTime and + * CXL_PSL_CtxTime_An, or calling cxl_p1n_write instead of cxl_p1_write. + * + * I'm quite happy if these are changed back to #defines before upstreaming, it + * should be little more than a regexp search+replace operation in this file. + */ +typedef struct { + const int x; +} cxl_p1_reg_t; +typedef struct { + const int x; +} cxl_p1n_reg_t; +typedef struct { + const int x; +} cxl_p2n_reg_t; +#define cxl_reg_off(reg) \ + (reg.x) + +/* Memory maps. Ref CXL Appendix A */ + +/* PSL Privilege 1 Memory Map */ +/* Configuration and Control area */ +static const cxl_p1_reg_t CXL_PSL_CtxTime = {0x0000}; +static const cxl_p1_reg_t CXL_PSL_ErrIVTE = {0x0008}; +static const cxl_p1_reg_t CXL_PSL_KEY1 = {0x0010}; +static const cxl_p1_reg_t CXL_PSL_KEY2 = {0x0018}; +static const cxl_p1_reg_t CXL_PSL_Control = {0x0020}; +/* Downloading */ +static const cxl_p1_reg_t CXL_PSL_DLCNTL = {0x0060}; +static const cxl_p1_reg_t CXL_PSL_DLADDR = {0x0068}; + +/* PSL Lookaside Buffer Management Area */ +static const cxl_p1_reg_t CXL_PSL_LBISEL = {0x0080}; +static const cxl_p1_reg_t CXL_PSL_SLBIE = {0x0088}; +static const cxl_p1_reg_t CXL_PSL_SLBIA = {0x0090}; +static const cxl_p1_reg_t CXL_PSL_TLBIE = {0x00A0}; +static const cxl_p1_reg_t CXL_PSL_TLBIA = {0x00A8}; +static const cxl_p1_reg_t CXL_PSL_AFUSEL = {0x00B0}; + +/* 0x00C0:7EFF Implementation dependent area */ +static const cxl_p1_reg_t CXL_PSL_FIR1 = {0x0100}; +static const cxl_p1_reg_t CXL_PSL_FIR2 = {0x0108}; +static const cxl_p1_reg_t CXL_PSL_VERSION = {0x0118}; +static const cxl_p1_reg_t CXL_PSL_RESLCKTO = {0x0128}; +static const cxl_p1_reg_t CXL_PSL_FIR_CNTL = {0x0148}; +static const cxl_p1_reg_t CXL_PSL_DSNDCTL = {0x0150}; +static const cxl_p1_reg_t CXL_PSL_SNWRALLOC = {0x0158}; +static const cxl_p1_reg_t CXL_PSL_TRACE = {0x0170}; +/* 0x7F00:7FFF Reserved PCIe MSI-X Pending Bit Array area */ +/* 0x8000:FFFF Reserved PCIe MSI-X Table Area */ + +/* PSL Slice Privilege 1 Memory Map */ +/* Configuration Area */ +static const cxl_p1n_reg_t CXL_PSL_SR_An = {0x00}; +static const cxl_p1n_reg_t CXL_PSL_LPID_An = {0x08}; +static const cxl_p1n_reg_t CXL_PSL_AMBAR_An = {0x10}; +static const cxl_p1n_reg_t CXL_PSL_SPOffset_An = {0x18}; +static const cxl_p1n_reg_t CXL_PSL_ID_An = {0x20}; +static const cxl_p1n_reg_t CXL_PSL_SERR_An = {0x28}; +/* Memory Management and Lookaside Buffer Management */ +static const cxl_p1n_reg_t CXL_PSL_SDR_An = {0x30}; +static const cxl_p1n_reg_t CXL_PSL_AMOR_An = {0x38}; +/* Pointer Area */ +static const cxl_p1n_reg_t CXL_HAURP_An = {0x80}; +static const cxl_p1n_reg_t CXL_PSL_SPAP_An = {0x88}; +static const cxl_p1n_reg_t CXL_PSL_LLCMD_An = {0x90}; +/* Control Area */ +static const cxl_p1n_reg_t CXL_PSL_SCNTL_An = {0xA0}; +static const cxl_p1n_reg_t CXL_PSL_CtxTime_An = {0xA8}; +static const cxl_p1n_reg_t CXL_PSL_IVTE_Offset_An = {0xB0}; +static const cxl_p1n_reg_t CXL_PSL_IVTE_Limit_An = {0xB8}; +/* 0xC0:FF Implementation Dependent Area */ +static const cxl_p1n_reg_t CXL_PSL_FIR_SLICE_An = {0xC0}; +static const cxl_p1n_reg_t CXL_AFU_DEBUG_An = {0xC8}; +static const cxl_p1n_reg_t CXL_PSL_APCALLOC_A = {0xD0}; +static const cxl_p1n_reg_t CXL_PSL_COALLOC_A = {0xD8}; +static const cxl_p1n_reg_t CXL_PSL_RXCTL_A = {0xE0}; +static const cxl_p1n_reg_t CXL_PSL_SLICE_TRACE = {0xE8}; + +/* PSL Slice Privilege 2 Memory Map */ +/* Configuration and Control Area */ +static const cxl_p2n_reg_t CXL_PSL_PID_TID_An = {0x000}; +static const cxl_p2n_reg_t CXL_CSRP_An = {0x008}; +static const cxl_p2n_reg_t CXL_AURP0_An = {0x010}; +static const cxl_p2n_reg_t CXL_AURP1_An = {0x018}; +static const cxl_p2n_reg_t CXL_SSTP0_An = {0x020}; +static const cxl_p2n_reg_t CXL_SSTP1_An = {0x028}; +static const cxl_p2n_reg_t CXL_PSL_AMR_An = {0x030}; +/* Segment Lookaside Buffer Management */ +static const cxl_p2n_reg_t CXL_SLBIE_An = {0x040}; +static const cxl_p2n_reg_t CXL_SLBIA_An = {0x048}; +static const cxl_p2n_reg_t CXL_SLBI_Select_An = {0x050}; +/* Interrupt Registers */ +static const cxl_p2n_reg_t CXL_PSL_DSISR_An = {0x060}; +static const cxl_p2n_reg_t CXL_PSL_DAR_An = {0x068}; +static const cxl_p2n_reg_t CXL_PSL_DSR_An = {0x070}; +static const cxl_p2n_reg_t CXL_PSL_TFC_An = {0x078}; +static const cxl_p2n_reg_t CXL_PSL_PEHandle_An = {0x080}; +static const cxl_p2n_reg_t CXL_PSL_ErrStat_An = {0x088}; +/* AFU Registers */ +static const cxl_p2n_reg_t CXL_AFU_Cntl_An = {0x090}; +static const cxl_p2n_reg_t CXL_AFU_ERR_An = {0x098}; +/* Work Element Descriptor */ +static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; +/* 0x0C0:FFF Implementation Dependent Area */ + +#define CXL_PSL_SPAP_Addr 0x0ffffffffffff000ULL +#define CXL_PSL_SPAP_Size 0x0000000000000ff0ULL +#define CXL_PSL_SPAP_Size_Shift 4 +#define CXL_PSL_SPAP_V 0x0000000000000001ULL + +/****** CXL_PSL_DLCNTL *****************************************************/ +#define CXL_PSL_DLCNTL_D (0x1ull << (63-28)) +#define CXL_PSL_DLCNTL_C (0x1ull << (63-29)) +#define CXL_PSL_DLCNTL_E (0x1ull << (63-30)) +#define CXL_PSL_DLCNTL_S (0x1ull << (63-31)) +#define CXL_PSL_DLCNTL_CE (CXL_PSL_DLCNTL_C | CXL_PSL_DLCNTL_E) +#define CXL_PSL_DLCNTL_DCES (CXL_PSL_DLCNTL_D | CXL_PSL_DLCNTL_CE | CXL_PSL_DLCNTL_S) + +/****** CXL_PSL_SR_An ******************************************************/ +#define CXL_PSL_SR_An_SF MSR_SF /* 64bit */ +#define CXL_PSL_SR_An_TA (1ull << (63-1)) /* Tags active, GA1: 0 */ +#define CXL_PSL_SR_An_HV MSR_HV /* Hypervisor, GA1: 0 */ +#define CXL_PSL_SR_An_PR MSR_PR /* Problem state, GA1: 1 */ +#define CXL_PSL_SR_An_ISL (1ull << (63-53)) /* Ignore Segment Large Page */ +#define CXL_PSL_SR_An_TC (1ull << (63-54)) /* Page Table secondary hash */ +#define CXL_PSL_SR_An_US (1ull << (63-56)) /* User state, GA1: X */ +#define CXL_PSL_SR_An_SC (1ull << (63-58)) /* Segment Table secondary hash */ +#define CXL_PSL_SR_An_R MSR_DR /* Relocate, GA1: 1 */ +#define CXL_PSL_SR_An_MP (1ull << (63-62)) /* Master Process */ +#define CXL_PSL_SR_An_LE (1ull << (63-63)) /* Little Endian */ + +/****** CXL_PSL_LLCMD_An ****************************************************/ +#define CXL_LLCMD_TERMINATE 0x0001000000000000ULL +#define CXL_LLCMD_REMOVE 0x0002000000000000ULL +#define CXL_LLCMD_SUSPEND 0x0003000000000000ULL +#define CXL_LLCMD_RESUME 0x0004000000000000ULL +#define CXL_LLCMD_ADD 0x0005000000000000ULL +#define CXL_LLCMD_UPDATE 0x0006000000000000ULL +#define CXL_LLCMD_HANDLE_MASK 0x000000000000ffffULL + +/****** CXL_PSL_ID_An ****************************************************/ +#define CXL_PSL_ID_An_F (1ull << (63-31)) +#define CXL_PSL_ID_An_L (1ull << (63-30)) + +/****** CXL_PSL_SCNTL_An ****************************************************/ +#define CXL_PSL_SCNTL_An_CR (0x1ull << (63-15)) +/* Programming Modes: */ +#define CXL_PSL_SCNTL_An_PM_MASK (0xffffull << (63-31)) +#define CXL_PSL_SCNTL_An_PM_Shared (0x0000ull << (63-31)) +#define CXL_PSL_SCNTL_An_PM_OS (0x0001ull << (63-31)) +#define CXL_PSL_SCNTL_An_PM_Process (0x0002ull << (63-31)) +#define CXL_PSL_SCNTL_An_PM_AFU (0x0004ull << (63-31)) +#define CXL_PSL_SCNTL_An_PM_AFU_PBT (0x0104ull << (63-31)) +/* Purge Status (ro) */ +#define CXL_PSL_SCNTL_An_Ps_MASK (0x3ull << (63-39)) +#define CXL_PSL_SCNTL_An_Ps_Pending (0x1ull << (63-39)) +#define CXL_PSL_SCNTL_An_Ps_Complete (0x3ull << (63-39)) +/* Purge */ +#define CXL_PSL_SCNTL_An_Pc (0x1ull << (63-48)) +/* Suspend Status (ro) */ +#define CXL_PSL_SCNTL_An_Ss_MASK (0x3ull << (63-55)) +#define CXL_PSL_SCNTL_An_Ss_Pending (0x1ull << (63-55)) +#define CXL_PSL_SCNTL_An_Ss_Complete (0x3ull << (63-55)) +/* Suspend Control */ +#define CXL_PSL_SCNTL_An_Sc (0x1ull << (63-63)) + +/* AFU Slice Enable Status (ro) */ +#define CXL_AFU_Cntl_An_ES_MASK (0x7ull << (63-2)) +#define CXL_AFU_Cntl_An_ES_Disabled (0x0ull << (63-2)) +#define CXL_AFU_Cntl_An_ES_Enabled (0x4ull << (63-2)) +/* AFU Slice Enable */ +#define CXL_AFU_Cntl_An_E (0x1ull << (63-3)) +/* AFU Slice Reset status (ro) */ +#define CXL_AFU_Cntl_An_RS_MASK (0x3ull << (63-5)) +#define CXL_AFU_Cntl_An_RS_Pending (0x1ull << (63-5)) +#define CXL_AFU_Cntl_An_RS_Complete (0x2ull << (63-5)) +/* AFU Slice Reset */ +#define CXL_AFU_Cntl_An_RA (0x1ull << (63-7)) + +/****** CXL_SSTP0/1_An ******************************************************/ +/* These top bits are for the segment that CONTAINS the segment table */ +#define CXL_SSTP0_An_B_SHIFT SLB_VSID_SSIZE_SHIFT +#define CXL_SSTP0_An_KS (1ull << (63-2)) +#define CXL_SSTP0_An_KP (1ull << (63-3)) +#define CXL_SSTP0_An_N (1ull << (63-4)) +#define CXL_SSTP0_An_L (1ull << (63-5)) +#define CXL_SSTP0_An_C (1ull << (63-6)) +#define CXL_SSTP0_An_TA (1ull << (63-7)) +#define CXL_SSTP0_An_LP_SHIFT (63-9) /* 2 Bits */ +/* And finally, the virtual address & size of the segment table: */ +#define CXL_SSTP0_An_SegTableSize_SHIFT (63-31) /* 12 Bits */ +#define CXL_SSTP0_An_SegTableSize_MASK \ + (((1ull << 12) - 1) << CXL_SSTP0_An_SegTableSize_SHIFT) +#define CXL_SSTP0_An_STVA_U_MASK ((1ull << (63-49))-1) +#define CXL_SSTP1_An_STVA_L_MASK (~((1ull << (63-55))-1)) +#define CXL_SSTP1_An_V (1ull << (63-63)) + +/****** CXL_PSL_SLBIE_[An] **************************************************/ +/* write: */ +#define CXL_SLBIE_C PPC_BIT(36) /* Class */ +#define CXL_SLBIE_SS PPC_BITMASK(37, 38) /* Segment Size */ +#define CXL_SLBIE_SS_SHIFT PPC_BITLSHIFT(38) +#define CXL_SLBIE_TA PPC_BIT(38) /* Tags Active */ +/* read: */ +#define CXL_SLBIE_MAX PPC_BITMASK(24, 31) +#define CXL_SLBIE_PENDING PPC_BITMASK(56, 63) + +/****** Common to all CXL_TLBIA/SLBIA_[An] **********************************/ +#define CXL_TLB_SLB_P (1ull) /* Pending (read) */ + +/****** Common to all CXL_TLB/SLB_IA/IE_[An] registers **********************/ +#define CXL_TLB_SLB_IQ_ALL (0ull) /* Inv qualifier */ +#define CXL_TLB_SLB_IQ_LPID (1ull) /* Inv qualifier */ +#define CXL_TLB_SLB_IQ_LPIDPID (3ull) /* Inv qualifier */ + +/****** CXL_PSL_AFUSEL ******************************************************/ +#define CXL_PSL_AFUSEL_A (1ull << (63-55)) /* Adapter wide invalidates affect all AFUs */ + +/****** CXL_PSL_DSISR_An ****************************************************/ +#define CXL_PSL_DSISR_An_DS (1ull << (63-0)) /* Segment not found */ +#define CXL_PSL_DSISR_An_DM (1ull << (63-1)) /* PTE not found (See also: M) or protection fault */ +#define CXL_PSL_DSISR_An_ST (1ull << (63-2)) /* Segment Table PTE not found */ +#define CXL_PSL_DSISR_An_UR (1ull << (63-3)) /* AURP PTE not found */ +#define CXL_PSL_DSISR_TRANS (CXL_PSL_DSISR_An_DS | CXL_PSL_DSISR_An_DM | CXL_PSL_DSISR_An_ST | CXL_PSL_DSISR_An_UR) +#define CXL_PSL_DSISR_An_PE (1ull << (63-4)) /* PSL Error (implementation specific) */ +#define CXL_PSL_DSISR_An_AE (1ull << (63-5)) /* AFU Error */ +#define CXL_PSL_DSISR_An_OC (1ull << (63-6)) /* OS Context Warning */ +/* NOTE: Bits 32:63 are undefined if DSISR[DS] = 1 */ +#define CXL_PSL_DSISR_An_M DSISR_NOHPTE /* PTE not found */ +#define CXL_PSL_DSISR_An_P DSISR_PROTFAULT /* Storage protection violation */ +#define CXL_PSL_DSISR_An_A (1ull << (63-37)) /* AFU lock access to write through or cache inhibited storage */ +#define CXL_PSL_DSISR_An_S DSISR_ISSTORE /* Access was afu_wr or afu_zero */ +#define CXL_PSL_DSISR_An_K DSISR_KEYFAULT /* Access not permitted by virtual page class key protection */ + +/****** CXL_PSL_TFC_An ******************************************************/ +#define CXL_PSL_TFC_An_A (1ull << (63-28)) /* Acknowledge non-translation fault */ +#define CXL_PSL_TFC_An_C (1ull << (63-29)) /* Continue (abort transaction) */ +#define CXL_PSL_TFC_An_AE (1ull << (63-30)) /* Restart PSL with address error */ +#define CXL_PSL_TFC_An_R (1ull << (63-31)) /* Restart PSL transaction */ + +/* cxl_process_element->software_status */ +#define CXL_PE_SOFTWARE_STATE_V (1ul << (31 - 0)) /* Valid */ +#define CXL_PE_SOFTWARE_STATE_C (1ul << (31 - 29)) /* Complete */ +#define CXL_PE_SOFTWARE_STATE_S (1ul << (31 - 30)) /* Suspend */ +#define CXL_PE_SOFTWARE_STATE_T (1ul << (31 - 31)) /* Terminate */ + +/* SPA->sw_command_status */ +#define CXL_SPA_SW_CMD_MASK 0xffff000000000000ULL +#define CXL_SPA_SW_CMD_TERMINATE 0x0001000000000000ULL +#define CXL_SPA_SW_CMD_REMOVE 0x0002000000000000ULL +#define CXL_SPA_SW_CMD_SUSPEND 0x0003000000000000ULL +#define CXL_SPA_SW_CMD_RESUME 0x0004000000000000ULL +#define CXL_SPA_SW_CMD_ADD 0x0005000000000000ULL +#define CXL_SPA_SW_CMD_UPDATE 0x0006000000000000ULL +#define CXL_SPA_SW_STATE_MASK 0x0000ffff00000000ULL +#define CXL_SPA_SW_STATE_TERMINATED 0x0000000100000000ULL +#define CXL_SPA_SW_STATE_REMOVED 0x0000000200000000ULL +#define CXL_SPA_SW_STATE_SUSPENDED 0x0000000300000000ULL +#define CXL_SPA_SW_STATE_RESUMED 0x0000000400000000ULL +#define CXL_SPA_SW_STATE_ADDED 0x0000000500000000ULL +#define CXL_SPA_SW_STATE_UPDATED 0x0000000600000000ULL +#define CXL_SPA_SW_PSL_ID_MASK 0x00000000ffff0000ULL +#define CXL_SPA_SW_LINK_MASK 0x000000000000ffffULL + +#define CXL_MAX_SLICES 4 +#define MAX_AFU_MMIO_REGS 3 + +#define CXL_MODE_DEDICATED 0x1 +#define CXL_MODE_DIRECTED 0x2 +#define CXL_MODE_TIME_SLICED 0x4 +#define CXL_SUPPORTED_MODES (CXL_MODE_DEDICATED | CXL_MODE_DIRECTED) + +enum cxl_context_status { + CLOSED, + OPENED, + STARTED +}; + +enum prefault_modes { + CXL_PREFAULT_NONE, + CXL_PREFAULT_WED, + CXL_PREFAULT_ALL, +}; + +struct cxl_sste { + __be64 esid_data; + __be64 vsid_data; +}; + +#define to_cxl_adapter(d) container_of(d, struct cxl, dev) +#define to_cxl_afu(d) container_of(d, struct cxl_afu, dev) + +struct cxl_afu { + irq_hw_number_t psl_hwirq; + irq_hw_number_t serr_hwirq; + unsigned int serr_virq; + void __iomem *p1n_mmio; + void __iomem *p2n_mmio; + phys_addr_t psn_phys; + u64 pp_offset; + u64 pp_size; + void __iomem *afu_desc_mmio; + struct cxl *adapter; + struct device dev; + struct cdev afu_cdev_s, afu_cdev_m, afu_cdev_d; + struct device *chardev_s, *chardev_m, *chardev_d; + struct idr contexts_idr; + struct dentry *debugfs; + spinlock_t contexts_lock; + struct mutex spa_mutex; + spinlock_t afu_cntl_lock; + + /* + * Only the first part of the SPA is used for the process element + * linked list. The only other part that software needs to worry about + * is sw_command_status, which we store a separate pointer to. + * Everything else in the SPA is only used by hardware + */ + struct cxl_process_element *spa; + __be64 *sw_command_status; + unsigned int spa_size; + int spa_order; + int spa_max_procs; + unsigned int psl_virq; + + int pp_irqs; + int irqs_max; + int num_procs; + int max_procs_virtualised; + int slice; + int modes_supported; + int current_mode; + enum prefault_modes prefault_mode; + bool psa; + bool pp_psa; + bool enabled; +}; + +/* + * This is a cxl context. If the PSL is in dedicated mode, there will be one + * of these per AFU. If in AFU directed there can be lots of these. + */ +struct cxl_context { + struct cxl_afu *afu; + + /* Problem state MMIO */ + phys_addr_t psn_phys; + u64 psn_size; + + spinlock_t sste_lock; /* Protects segment table entries */ + struct cxl_sste *sstp; + u64 sstp0, sstp1; + unsigned int sst_size, sst_lru; + + wait_queue_head_t wq; + struct pid *pid; + spinlock_t lock; /* Protects pending_irq_mask, pending_fault and fault_addr */ + /* Only used in PR mode */ + u64 process_token; + + unsigned long *irq_bitmap; /* Accessed from IRQ context */ + struct cxl_irq_ranges irqs; + u64 fault_addr; + u64 fault_dsisr; + u64 afu_err; + + /* + * This status and it's lock pretects start and detach context + * from racing. It also prevents detach from racing with + * itself + */ + enum cxl_context_status status; + struct mutex status_mutex; + + + /* XXX: Is it possible to need multiple work items at once? */ + struct work_struct fault_work; + u64 dsisr; + u64 dar; + + struct cxl_process_element *elem; + + int pe; /* process element handle */ + u32 irq_count; + bool pe_inserted; + bool master; + bool kernel; + bool pending_irq; + bool pending_fault; + bool pending_afu_err; +}; + +struct cxl { + void __iomem *p1_mmio; + void __iomem *p2_mmio; + irq_hw_number_t err_hwirq; + unsigned int err_virq; + spinlock_t afu_list_lock; + struct cxl_afu *afu[CXL_MAX_SLICES]; + struct device dev; + struct dentry *trace; + struct dentry *psl_err_chk; + struct dentry *debugfs; + struct bin_attribute cxl_attr; + int adapter_num; + int user_irqs; + u64 afu_desc_off; + u64 afu_desc_size; + u64 ps_off; + u64 ps_size; + u16 psl_rev; + u16 base_image; + u8 vsec_status; + u8 caia_major; + u8 caia_minor; + u8 slices; + bool user_image_loaded; + bool perst_loads_image; + bool perst_select_user; +}; + +int cxl_alloc_one_irq(struct cxl *adapter); +void cxl_release_one_irq(struct cxl *adapter, int hwirq); +int cxl_alloc_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter, unsigned int num); +void cxl_release_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter); +int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq, unsigned int virq); + +/* common == phyp + powernv */ +struct cxl_process_element_common { + __be32 tid; + __be32 pid; + __be64 csrp; + __be64 aurp0; + __be64 aurp1; + __be64 sstp0; + __be64 sstp1; + __be64 amr; + u8 reserved3[4]; + __be64 wed; +} __packed; + +/* just powernv */ +struct cxl_process_element { + __be64 sr; + __be64 SPOffset; + __be64 sdr; + __be64 haurp; + __be32 ctxtime; + __be16 ivte_offsets[4]; + __be16 ivte_ranges[4]; + __be32 lpid; + struct cxl_process_element_common common; + __be32 software_state; +} __packed; + +static inline void __iomem *_cxl_p1_addr(struct cxl *cxl, cxl_p1_reg_t reg) +{ + WARN_ON(!cpu_has_feature(CPU_FTR_HVMODE)); + return cxl->p1_mmio + cxl_reg_off(reg); +} + +#define cxl_p1_write(cxl, reg, val) \ + out_be64(_cxl_p1_addr(cxl, reg), val) +#define cxl_p1_read(cxl, reg) \ + in_be64(_cxl_p1_addr(cxl, reg)) + +static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg) +{ + WARN_ON(!cpu_has_feature(CPU_FTR_HVMODE)); + return afu->p1n_mmio + cxl_reg_off(reg); +} + +#define cxl_p1n_write(afu, reg, val) \ + out_be64(_cxl_p1n_addr(afu, reg), val) +#define cxl_p1n_read(afu, reg) \ + in_be64(_cxl_p1n_addr(afu, reg)) + +static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg) +{ + return afu->p2n_mmio + cxl_reg_off(reg); +} + +#define cxl_p2n_write(afu, reg, val) \ + out_be64(_cxl_p2n_addr(afu, reg), val) +#define cxl_p2n_read(afu, reg) \ + in_be64(_cxl_p2n_addr(afu, reg)) + +struct cxl_calls { + void (*cxl_slbia)(struct mm_struct *mm); + struct module *owner; +}; +int register_cxl_calls(struct cxl_calls *calls); +void unregister_cxl_calls(struct cxl_calls *calls); + +int cxl_alloc_adapter_nr(struct cxl *adapter); +void cxl_remove_adapter_nr(struct cxl *adapter); + +int cxl_file_init(void); +void cxl_file_exit(void); +int cxl_register_adapter(struct cxl *adapter); +int cxl_register_afu(struct cxl_afu *afu); +int cxl_chardev_d_afu_add(struct cxl_afu *afu); +int cxl_chardev_m_afu_add(struct cxl_afu *afu); +int cxl_chardev_s_afu_add(struct cxl_afu *afu); +void cxl_chardev_afu_remove(struct cxl_afu *afu); + +void cxl_context_detach_all(struct cxl_afu *afu); +void cxl_context_free(struct cxl_context *ctx); +void cxl_context_detach(struct cxl_context *ctx); + +int cxl_sysfs_adapter_add(struct cxl *adapter); +void cxl_sysfs_adapter_remove(struct cxl *adapter); +int cxl_sysfs_afu_add(struct cxl_afu *afu); +void cxl_sysfs_afu_remove(struct cxl_afu *afu); +int cxl_sysfs_afu_m_add(struct cxl_afu *afu); +void cxl_sysfs_afu_m_remove(struct cxl_afu *afu); + +int cxl_afu_activate_mode(struct cxl_afu *afu, int mode); +int _cxl_afu_deactivate_mode(struct cxl_afu *afu, int mode); +int cxl_afu_deactivate_mode(struct cxl_afu *afu); +int cxl_afu_select_best_mode(struct cxl_afu *afu); + +unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq, + irq_handler_t handler, void *cookie); +void cxl_unmap_irq(unsigned int virq, void *cookie); +int cxl_register_psl_irq(struct cxl_afu *afu); +void cxl_release_psl_irq(struct cxl_afu *afu); +int cxl_register_psl_err_irq(struct cxl *adapter); +void cxl_release_psl_err_irq(struct cxl *adapter); +int cxl_register_serr_irq(struct cxl_afu *afu); +void cxl_release_serr_irq(struct cxl_afu *afu); +int afu_register_irqs(struct cxl_context *ctx, u32 count); +void afu_release_irqs(struct cxl_context *ctx); +irqreturn_t cxl_slice_irq_err(int irq, void *data); + +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_handle_fault(struct work_struct *work); +void cxl_prefault(struct cxl_context *ctx, u64 wed); + +struct cxl *get_cxl_adapter(int num); +int cxl_alloc_sst(struct cxl_context *ctx); + +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); +void cxl_context_free(struct cxl_context *ctx); +int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma); + +/* This matches the layout of the H_COLLECT_CA_INT_INFO retbuf */ +struct cxl_irq_info { + u64 dsisr; + u64 dar; + u64 dsr; + u32 pid; + u32 tid; + u64 afu_err; + u64 errstat; + u64 padding[3]; /* to match the expected retbuf size for plpar_hcall9 */ +}; + +int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, + u64 amr); +int cxl_detach_process(struct cxl_context *ctx); + +int cxl_get_irq(struct cxl_context *ctx, struct cxl_irq_info *info); +int cxl_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask); + +int cxl_check_error(struct cxl_afu *afu); +int cxl_afu_slbia(struct cxl_afu *afu); +int cxl_tlb_slb_invalidate(struct cxl *adapter); +int cxl_afu_disable(struct cxl_afu *afu); +int cxl_afu_reset(struct cxl_afu *afu); +int cxl_psl_purge(struct cxl_afu *afu); + +void cxl_stop_trace(struct cxl *cxl); + +extern struct pci_driver cxl_pci_driver; + +#endif diff --git a/drivers/misc/cxl/debugfs.c b/drivers/misc/cxl/debugfs.c new file mode 100644 index 000000000000..825c412580bc --- /dev/null +++ b/drivers/misc/cxl/debugfs.c @@ -0,0 +1,132 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/debugfs.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "cxl.h" + +static struct dentry *cxl_debugfs; + +void cxl_stop_trace(struct cxl *adapter) +{ + int slice; + + /* Stop the trace */ + cxl_p1_write(adapter, CXL_PSL_TRACE, 0x8000000000000017LL); + + /* Stop the slice traces */ + spin_lock(&adapter->afu_list_lock); + for (slice = 0; slice < adapter->slices; slice++) { + if (adapter->afu[slice]) + cxl_p1n_write(adapter->afu[slice], CXL_PSL_SLICE_TRACE, 0x8000000000000000LL); + } + spin_unlock(&adapter->afu_list_lock); +} + +/* Helpers to export CXL mmaped IO registers via debugfs */ +static int debugfs_io_u64_get(void *data, u64 *val) +{ + *val = in_be64((u64 __iomem *)data); + return 0; +} + +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"); + +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 *)value, &fops_io_x64); +} + +int cxl_debugfs_adapter_add(struct cxl *adapter) +{ + struct dentry *dir; + char buf[32]; + + if (!cxl_debugfs) + return -ENODEV; + + snprintf(buf, 32, "card%i", adapter->adapter_num); + dir = debugfs_create_dir(buf, cxl_debugfs); + if (IS_ERR(dir)) + return PTR_ERR(dir); + adapter->debugfs = dir; + + debugfs_create_io_x64("fir1", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR1)); + debugfs_create_io_x64("fir2", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR2)); + debugfs_create_io_x64("fir_cntl", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_FIR_CNTL)); + debugfs_create_io_x64("err_ivte", S_IRUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_ErrIVTE)); + + debugfs_create_io_x64("trace", S_IRUSR | S_IWUSR, dir, _cxl_p1_addr(adapter, CXL_PSL_TRACE)); + + return 0; +} + +void cxl_debugfs_adapter_remove(struct cxl *adapter) +{ + debugfs_remove_recursive(adapter->debugfs); +} + +int cxl_debugfs_afu_add(struct cxl_afu *afu) +{ + struct dentry *dir; + char buf[32]; + + if (!afu->adapter->debugfs) + return -ENODEV; + + snprintf(buf, 32, "psl%i.%i", afu->adapter->adapter_num, afu->slice); + dir = debugfs_create_dir(buf, afu->adapter->debugfs); + if (IS_ERR(dir)) + return PTR_ERR(dir); + afu->debugfs = dir; + + debugfs_create_io_x64("fir", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_FIR_SLICE_An)); + debugfs_create_io_x64("serr", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SERR_An)); + debugfs_create_io_x64("afu_debug", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_AFU_DEBUG_An)); + debugfs_create_io_x64("sr", S_IRUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SR_An)); + + debugfs_create_io_x64("dsisr", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_DSISR_An)); + debugfs_create_io_x64("dar", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_DAR_An)); + debugfs_create_io_x64("sstp0", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP0_An)); + debugfs_create_io_x64("sstp1", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_SSTP1_An)); + debugfs_create_io_x64("err_status", S_IRUSR, dir, _cxl_p2n_addr(afu, CXL_PSL_ErrStat_An)); + + debugfs_create_io_x64("trace", S_IRUSR | S_IWUSR, dir, _cxl_p1n_addr(afu, CXL_PSL_SLICE_TRACE)); + + return 0; +} + +void cxl_debugfs_afu_remove(struct cxl_afu *afu) +{ + debugfs_remove_recursive(afu->debugfs); +} + +int __init cxl_debugfs_init(void) +{ + struct dentry *ent; + ent = debugfs_create_dir("cxl", NULL); + if (IS_ERR(ent)) + return PTR_ERR(ent); + cxl_debugfs = ent; + + return 0; +} + +void cxl_debugfs_exit(void) +{ + debugfs_remove_recursive(cxl_debugfs); +} diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c new file mode 100644 index 000000000000..c99e896604ee --- /dev/null +++ b/drivers/misc/cxl/fault.c @@ -0,0 +1,295 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/pid.h> +#include <linux/mm.h> +#include <linux/moduleparam.h> + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "cxl" "." +#include <asm/current.h> +#include <asm/copro.h> +#include <asm/mmu.h> + +#include "cxl.h" + +static bool sste_matches(struct cxl_sste *sste, struct copro_slb *slb) +{ + return ((sste->vsid_data == cpu_to_be64(slb->vsid)) && + (sste->esid_data == cpu_to_be64(slb->esid))); +} + +/* + * This finds a free SSTE for the given SLB, or returns NULL if it's already in + * the segment table. + */ +static struct cxl_sste* find_free_sste(struct cxl_context *ctx, + struct copro_slb *slb) +{ + struct cxl_sste *primary, *sste, *ret = NULL; + unsigned int mask = (ctx->sst_size >> 7) - 1; /* SSTP0[SegTableSize] */ + unsigned int entry; + unsigned int hash; + + if (slb->vsid & SLB_VSID_B_1T) + hash = (slb->esid >> SID_SHIFT_1T) & mask; + else /* 256M */ + hash = (slb->esid >> SID_SHIFT) & mask; + + primary = ctx->sstp + (hash << 3); + + for (entry = 0, sste = primary; entry < 8; entry++, sste++) { + if (!ret && !(be64_to_cpu(sste->esid_data) & SLB_ESID_V)) + ret = sste; + if (sste_matches(sste, slb)) + return NULL; + } + if (ret) + return ret; + + /* Nothing free, select an entry to cast out */ + ret = primary + ctx->sst_lru; + ctx->sst_lru = (ctx->sst_lru + 1) & 0x7; + + return ret; +} + +static void cxl_load_segment(struct cxl_context *ctx, struct copro_slb *slb) +{ + /* mask is the group index, we search primary and secondary here. */ + struct cxl_sste *sste; + unsigned long flags; + + spin_lock_irqsave(&ctx->sste_lock, flags); + sste = find_free_sste(ctx, slb); + if (!sste) + goto out_unlock; + + pr_devel("CXL Populating SST[%li]: %#llx %#llx\n", + sste - ctx->sstp, slb->vsid, slb->esid); + + sste->vsid_data = cpu_to_be64(slb->vsid); + sste->esid_data = cpu_to_be64(slb->esid); +out_unlock: + spin_unlock_irqrestore(&ctx->sste_lock, flags); +} + +static int cxl_fault_segment(struct cxl_context *ctx, struct mm_struct *mm, + u64 ea) +{ + struct copro_slb slb = {0,0}; + int rc; + + if (!(rc = copro_calculate_slb(mm, ea, &slb))) { + cxl_load_segment(ctx, &slb); + } + + return rc; +} + +static void cxl_ack_ae(struct cxl_context *ctx) +{ + unsigned long flags; + + cxl_ack_irq(ctx, CXL_PSL_TFC_An_AE, 0); + + spin_lock_irqsave(&ctx->lock, flags); + ctx->pending_fault = true; + ctx->fault_addr = ctx->dar; + ctx->fault_dsisr = ctx->dsisr; + spin_unlock_irqrestore(&ctx->lock, flags); + + wake_up_all(&ctx->wq); +} + +static int cxl_handle_segment_miss(struct cxl_context *ctx, + struct mm_struct *mm, u64 ea) +{ + int rc; + + pr_devel("CXL interrupt: Segment fault pe: %i ea: %#llx\n", ctx->pe, ea); + + if ((rc = cxl_fault_segment(ctx, mm, ea))) + cxl_ack_ae(ctx); + else { + + mb(); /* Order seg table write to TFC MMIO write */ + cxl_ack_irq(ctx, CXL_PSL_TFC_An_R, 0); + } + + return IRQ_HANDLED; +} + +static void cxl_handle_page_fault(struct cxl_context *ctx, + struct mm_struct *mm, u64 dsisr, u64 dar) +{ + unsigned flt = 0; + int result; + unsigned long access, flags; + + if ((result = copro_handle_mm_fault(mm, dar, dsisr, &flt))) { + pr_devel("copro_handle_mm_fault failed: %#x\n", result); + return cxl_ack_ae(ctx); + } + + /* + * update_mmu_cache() will not have loaded the hash since current->trap + * is not a 0x400 or 0x300, so just call hash_page_mm() here. + */ + access = _PAGE_PRESENT; + if (dsisr & CXL_PSL_DSISR_An_S) + access |= _PAGE_RW; + if ((!ctx->kernel) || ~(dar & (1ULL << 63))) + access |= _PAGE_USER; + local_irq_save(flags); + hash_page_mm(mm, dar, access, 0x300); + local_irq_restore(flags); + + pr_devel("Page fault successfully handled for pe: %i!\n", ctx->pe); + cxl_ack_irq(ctx, CXL_PSL_TFC_An_R, 0); +} + +void cxl_handle_fault(struct work_struct *fault_work) +{ + struct cxl_context *ctx = + container_of(fault_work, struct cxl_context, fault_work); + u64 dsisr = ctx->dsisr; + u64 dar = ctx->dar; + struct task_struct *task; + struct mm_struct *mm; + + if (cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An) != dsisr || + cxl_p2n_read(ctx->afu, CXL_PSL_DAR_An) != dar || + cxl_p2n_read(ctx->afu, CXL_PSL_PEHandle_An) != ctx->pe) { + /* Most likely explanation is harmless - a dedicated process + * has detached and these were cleared by the PSL purge, but + * warn about it just in case */ + dev_notice(&ctx->afu->dev, "cxl_handle_fault: Translation fault regs changed\n"); + return; + } + + pr_devel("CXL BOTTOM HALF handling fault for afu pe: %i. " + "DSISR: %#llx DAR: %#llx\n", ctx->pe, dsisr, dar); + + if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) { + pr_devel("cxl_handle_fault unable to get task %i\n", + pid_nr(ctx->pid)); + cxl_ack_ae(ctx); + return; + } + if (!(mm = get_task_mm(task))) { + pr_devel("cxl_handle_fault unable to get mm %i\n", + pid_nr(ctx->pid)); + cxl_ack_ae(ctx); + goto out; + } + + if (dsisr & CXL_PSL_DSISR_An_DS) + cxl_handle_segment_miss(ctx, mm, dar); + else if (dsisr & CXL_PSL_DSISR_An_DM) + cxl_handle_page_fault(ctx, mm, dsisr, dar); + else + WARN(1, "cxl_handle_fault has nothing to handle\n"); + + mmput(mm); +out: + put_task_struct(task); +} + +static void cxl_prefault_one(struct cxl_context *ctx, u64 ea) +{ + int rc; + struct task_struct *task; + struct mm_struct *mm; + + if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) { + pr_devel("cxl_prefault_one unable to get task %i\n", + pid_nr(ctx->pid)); + return; + } + if (!(mm = get_task_mm(task))) { + pr_devel("cxl_prefault_one unable to get mm %i\n", + pid_nr(ctx->pid)); + put_task_struct(task); + return; + } + + rc = cxl_fault_segment(ctx, mm, ea); + + mmput(mm); + put_task_struct(task); +} + +static u64 next_segment(u64 ea, u64 vsid) +{ + if (vsid & SLB_VSID_B_1T) + ea |= (1ULL << 40) - 1; + else + ea |= (1ULL << 28) - 1; + + return ea + 1; +} + +static void cxl_prefault_vma(struct cxl_context *ctx) +{ + u64 ea, last_esid = 0; + struct copro_slb slb; + struct vm_area_struct *vma; + int rc; + struct task_struct *task; + struct mm_struct *mm; + + if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) { + pr_devel("cxl_prefault_vma unable to get task %i\n", + pid_nr(ctx->pid)); + return; + } + if (!(mm = get_task_mm(task))) { + pr_devel("cxl_prefault_vm unable to get mm %i\n", + pid_nr(ctx->pid)); + goto out1; + } + + down_read(&mm->mmap_sem); + for (vma = mm->mmap; vma; vma = vma->vm_next) { + for (ea = vma->vm_start; ea < vma->vm_end; + ea = next_segment(ea, slb.vsid)) { + rc = copro_calculate_slb(mm, ea, &slb); + if (rc) + continue; + + if (last_esid == slb.esid) + continue; + + cxl_load_segment(ctx, &slb); + last_esid = slb.esid; + } + } + up_read(&mm->mmap_sem); + + mmput(mm); +out1: + put_task_struct(task); +} + +void cxl_prefault(struct cxl_context *ctx, u64 wed) +{ + switch (ctx->afu->prefault_mode) { + case CXL_PREFAULT_WED: + cxl_prefault_one(ctx, wed); + break; + case CXL_PREFAULT_ALL: + cxl_prefault_vma(ctx); + break; + default: + break; + } +} diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c new file mode 100644 index 000000000000..378b099e7c0b --- /dev/null +++ b/drivers/misc/cxl/file.c @@ -0,0 +1,518 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/sched.h> +#include <linux/poll.h> +#include <linux/pid.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/cputable.h> +#include <asm/current.h> +#include <asm/copro.h> + +#include "cxl.h" + +#define CXL_NUM_MINORS 256 /* Total to reserve */ +#define CXL_DEV_MINORS 13 /* 1 control + 4 AFUs * 3 (dedicated/master/shared) */ + +#define CXL_CARD_MINOR(adapter) (adapter->adapter_num * CXL_DEV_MINORS) +#define CXL_AFU_MINOR_D(afu) (CXL_CARD_MINOR(afu->adapter) + 1 + (3 * afu->slice)) +#define CXL_AFU_MINOR_M(afu) (CXL_AFU_MINOR_D(afu) + 1) +#define CXL_AFU_MINOR_S(afu) (CXL_AFU_MINOR_D(afu) + 2) +#define CXL_AFU_MKDEV_D(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_D(afu)) +#define CXL_AFU_MKDEV_M(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_M(afu)) +#define CXL_AFU_MKDEV_S(afu) MKDEV(MAJOR(cxl_dev), CXL_AFU_MINOR_S(afu)) + +#define CXL_DEVT_ADAPTER(dev) (MINOR(dev) / CXL_DEV_MINORS) +#define CXL_DEVT_AFU(dev) ((MINOR(dev) % CXL_DEV_MINORS - 1) / 3) + +#define CXL_DEVT_IS_CARD(dev) (MINOR(dev) % CXL_DEV_MINORS == 0) + +static dev_t cxl_dev; + +static struct class *cxl_class; + +static int __afu_open(struct inode *inode, struct file *file, bool master) +{ + struct cxl *adapter; + struct cxl_afu *afu; + struct cxl_context *ctx; + int adapter_num = CXL_DEVT_ADAPTER(inode->i_rdev); + int slice = CXL_DEVT_AFU(inode->i_rdev); + int rc = -ENODEV; + + pr_devel("afu_open afu%i.%i\n", slice, adapter_num); + + if (!(adapter = get_cxl_adapter(adapter_num))) + return -ENODEV; + + if (slice > adapter->slices) + goto err_put_adapter; + + spin_lock(&adapter->afu_list_lock); + if (!(afu = adapter->afu[slice])) { + spin_unlock(&adapter->afu_list_lock); + goto err_put_adapter; + } + get_device(&afu->dev); + spin_unlock(&adapter->afu_list_lock); + + if (!afu->current_mode) + goto err_put_afu; + + if (!(ctx = cxl_context_alloc())) { + rc = -ENOMEM; + goto err_put_afu; + } + + if ((rc = cxl_context_init(ctx, afu, master))) + goto err_put_afu; + + pr_devel("afu_open pe: %i\n", ctx->pe); + file->private_data = ctx; + cxl_ctx_get(); + + /* Our ref on the AFU will now hold the adapter */ + put_device(&adapter->dev); + + return 0; + +err_put_afu: + put_device(&afu->dev); +err_put_adapter: + put_device(&adapter->dev); + return rc; +} +static int afu_open(struct inode *inode, struct file *file) +{ + return __afu_open(inode, file, false); +} + +static int afu_master_open(struct inode *inode, struct file *file) +{ + return __afu_open(inode, file, true); +} + +static int afu_release(struct inode *inode, struct file *file) +{ + struct cxl_context *ctx = file->private_data; + + pr_devel("%s: closing cxl file descriptor. pe: %i\n", + __func__, ctx->pe); + cxl_context_detach(ctx); + + put_device(&ctx->afu->dev); + + /* + * At this this point all bottom halfs have finished and we should be + * getting no more IRQs from the hardware for this context. Once it's + * removed from the IDR (and RCU synchronised) it's safe to free the + * sstp and context. + */ + cxl_context_free(ctx); + + cxl_ctx_put(); + return 0; +} + +static long afu_ioctl_start_work(struct cxl_context *ctx, + struct cxl_ioctl_start_work __user *uwork) +{ + struct cxl_ioctl_start_work work; + u64 amr = 0; + int rc; + + pr_devel("%s: pe: %i\n", __func__, ctx->pe); + + mutex_lock(&ctx->status_mutex); + if (ctx->status != OPENED) { + rc = -EIO; + goto out; + } + + if (copy_from_user(&work, uwork, + sizeof(struct cxl_ioctl_start_work))) { + rc = -EFAULT; + goto out; + } + + /* + * if any of the reserved fields are set or any of the unused + * flags are set it's invalid + */ + if (work.reserved1 || work.reserved2 || work.reserved3 || + work.reserved4 || work.reserved5 || work.reserved6 || + (work.flags & ~CXL_START_WORK_ALL)) { + rc = -EINVAL; + goto out; + } + + if (!(work.flags & CXL_START_WORK_NUM_IRQS)) + work.num_interrupts = ctx->afu->pp_irqs; + else if ((work.num_interrupts < ctx->afu->pp_irqs) || + (work.num_interrupts > ctx->afu->irqs_max)) { + rc = -EINVAL; + goto out; + } + if ((rc = afu_register_irqs(ctx, work.num_interrupts))) + goto out; + + if (work.flags & CXL_START_WORK_AMR) + amr = work.amr & mfspr(SPRN_UAMOR); + + /* + * We grab the PID here and not in the file open to allow for the case + * where a process (master, some daemon, etc) has opened the chardev on + * behalf of another process, so the AFU's mm gets bound to the process + * that performs this ioctl and not the process that opened the file. + */ + ctx->pid = get_pid(get_task_pid(current, PIDTYPE_PID)); + + if ((rc = cxl_attach_process(ctx, false, work.work_element_descriptor, + amr))) + goto out; + + ctx->status = STARTED; + rc = 0; +out: + mutex_unlock(&ctx->status_mutex); + return rc; +} +static long afu_ioctl_process_element(struct cxl_context *ctx, + int __user *upe) +{ + pr_devel("%s: pe: %i\n", __func__, ctx->pe); + + if (copy_to_user(upe, &ctx->pe, sizeof(__u32))) + return -EFAULT; + + return 0; +} + +static long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct cxl_context *ctx = file->private_data; + + if (ctx->status == CLOSED) + return -EIO; + + pr_devel("afu_ioctl\n"); + switch (cmd) { + case CXL_IOCTL_START_WORK: + return afu_ioctl_start_work(ctx, (struct cxl_ioctl_start_work __user *)arg); + case CXL_IOCTL_GET_PROCESS_ELEMENT: + return afu_ioctl_process_element(ctx, (__u32 __user *)arg); + } + return -EINVAL; +} + +static long afu_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return afu_ioctl(file, cmd, arg); +} + +static int afu_mmap(struct file *file, struct vm_area_struct *vm) +{ + struct cxl_context *ctx = file->private_data; + + /* AFU must be started before we can MMIO */ + if (ctx->status != STARTED) + return -EIO; + + return cxl_context_iomap(ctx, vm); +} + +static unsigned int afu_poll(struct file *file, struct poll_table_struct *poll) +{ + struct cxl_context *ctx = file->private_data; + int mask = 0; + unsigned long flags; + + + poll_wait(file, &ctx->wq, poll); + + pr_devel("afu_poll wait done pe: %i\n", ctx->pe); + + spin_lock_irqsave(&ctx->lock, flags); + if (ctx->pending_irq || ctx->pending_fault || + ctx->pending_afu_err) + mask |= POLLIN | POLLRDNORM; + else if (ctx->status == CLOSED) + /* Only error on closed when there are no futher events pending + */ + mask |= POLLERR; + spin_unlock_irqrestore(&ctx->lock, flags); + + pr_devel("afu_poll pe: %i returning %#x\n", ctx->pe, mask); + + return mask; +} + +static inline int ctx_event_pending(struct cxl_context *ctx) +{ + return (ctx->pending_irq || ctx->pending_fault || + ctx->pending_afu_err || (ctx->status == CLOSED)); +} + +static ssize_t afu_read(struct file *file, char __user *buf, size_t count, + loff_t *off) +{ + struct cxl_context *ctx = file->private_data; + struct cxl_event event; + unsigned long flags; + int rc; + DEFINE_WAIT(wait); + + if (count < CXL_READ_MIN_SIZE) + return -EINVAL; + + spin_lock_irqsave(&ctx->lock, flags); + + for (;;) { + prepare_to_wait(&ctx->wq, &wait, TASK_INTERRUPTIBLE); + if (ctx_event_pending(ctx)) + break; + + if (file->f_flags & O_NONBLOCK) { + rc = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + rc = -ERESTARTSYS; + goto out; + } + + spin_unlock_irqrestore(&ctx->lock, flags); + pr_devel("afu_read going to sleep...\n"); + schedule(); + pr_devel("afu_read woken up\n"); + spin_lock_irqsave(&ctx->lock, flags); + } + + finish_wait(&ctx->wq, &wait); + + memset(&event, 0, sizeof(event)); + event.header.process_element = ctx->pe; + event.header.size = sizeof(struct cxl_event_header); + if (ctx->pending_irq) { + pr_devel("afu_read delivering AFU interrupt\n"); + event.header.size += sizeof(struct cxl_event_afu_interrupt); + event.header.type = CXL_EVENT_AFU_INTERRUPT; + event.irq.irq = find_first_bit(ctx->irq_bitmap, ctx->irq_count) + 1; + clear_bit(event.irq.irq - 1, ctx->irq_bitmap); + if (bitmap_empty(ctx->irq_bitmap, ctx->irq_count)) + ctx->pending_irq = false; + } else if (ctx->pending_fault) { + pr_devel("afu_read delivering data storage fault\n"); + event.header.size += sizeof(struct cxl_event_data_storage); + event.header.type = CXL_EVENT_DATA_STORAGE; + event.fault.addr = ctx->fault_addr; + event.fault.dsisr = ctx->fault_dsisr; + ctx->pending_fault = false; + } else if (ctx->pending_afu_err) { + pr_devel("afu_read delivering afu error\n"); + event.header.size += sizeof(struct cxl_event_afu_error); + event.header.type = CXL_EVENT_AFU_ERROR; + event.afu_error.error = ctx->afu_err; + ctx->pending_afu_err = false; + } else if (ctx->status == CLOSED) { + pr_devel("afu_read fatal error\n"); + spin_unlock_irqrestore(&ctx->lock, flags); + return -EIO; + } else + WARN(1, "afu_read must be buggy\n"); + + spin_unlock_irqrestore(&ctx->lock, flags); + + if (copy_to_user(buf, &event, event.header.size)) + return -EFAULT; + return event.header.size; + +out: + finish_wait(&ctx->wq, &wait); + spin_unlock_irqrestore(&ctx->lock, flags); + return rc; +} + +static const struct file_operations afu_fops = { + .owner = THIS_MODULE, + .open = afu_open, + .poll = afu_poll, + .read = afu_read, + .release = afu_release, + .unlocked_ioctl = afu_ioctl, + .compat_ioctl = afu_compat_ioctl, + .mmap = afu_mmap, +}; + +static const struct file_operations afu_master_fops = { + .owner = THIS_MODULE, + .open = afu_master_open, + .poll = afu_poll, + .read = afu_read, + .release = afu_release, + .unlocked_ioctl = afu_ioctl, + .compat_ioctl = afu_compat_ioctl, + .mmap = afu_mmap, +}; + + +static char *cxl_devnode(struct device *dev, umode_t *mode) +{ + if (CXL_DEVT_IS_CARD(dev->devt)) { + /* + * These minor numbers will eventually be used to program the + * PSL and AFUs once we have dynamic reprogramming support + */ + return NULL; + } + return kasprintf(GFP_KERNEL, "cxl/%s", dev_name(dev)); +} + +extern struct class *cxl_class; + +static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev, + struct device **chardev, char *postfix, char *desc, + const struct file_operations *fops) +{ + struct device *dev; + int rc; + + cdev_init(cdev, fops); + if ((rc = cdev_add(cdev, devt, 1))) { + dev_err(&afu->dev, "Unable to add %s chardev: %i\n", desc, rc); + return rc; + } + + dev = device_create(cxl_class, &afu->dev, devt, afu, + "afu%i.%i%s", afu->adapter->adapter_num, afu->slice, postfix); + if (IS_ERR(dev)) { + dev_err(&afu->dev, "Unable to create %s chardev in sysfs: %i\n", desc, rc); + rc = PTR_ERR(dev); + goto err; + } + + *chardev = dev; + + return 0; +err: + cdev_del(cdev); + return rc; +} + +int cxl_chardev_d_afu_add(struct cxl_afu *afu) +{ + return cxl_add_chardev(afu, CXL_AFU_MKDEV_D(afu), &afu->afu_cdev_d, + &afu->chardev_d, "d", "dedicated", + &afu_master_fops); /* Uses master fops */ +} + +int cxl_chardev_m_afu_add(struct cxl_afu *afu) +{ + return cxl_add_chardev(afu, CXL_AFU_MKDEV_M(afu), &afu->afu_cdev_m, + &afu->chardev_m, "m", "master", + &afu_master_fops); +} + +int cxl_chardev_s_afu_add(struct cxl_afu *afu) +{ + return cxl_add_chardev(afu, CXL_AFU_MKDEV_S(afu), &afu->afu_cdev_s, + &afu->chardev_s, "s", "shared", + &afu_fops); +} + +void cxl_chardev_afu_remove(struct cxl_afu *afu) +{ + if (afu->chardev_d) { + cdev_del(&afu->afu_cdev_d); + device_unregister(afu->chardev_d); + afu->chardev_d = NULL; + } + if (afu->chardev_m) { + cdev_del(&afu->afu_cdev_m); + device_unregister(afu->chardev_m); + afu->chardev_m = NULL; + } + if (afu->chardev_s) { + cdev_del(&afu->afu_cdev_s); + device_unregister(afu->chardev_s); + afu->chardev_s = NULL; + } +} + +int cxl_register_afu(struct cxl_afu *afu) +{ + afu->dev.class = cxl_class; + + return device_register(&afu->dev); +} + +int cxl_register_adapter(struct cxl *adapter) +{ + adapter->dev.class = cxl_class; + + /* + * Future: When we support dynamically reprogramming the PSL & AFU we + * will expose the interface to do that via a chardev: + * adapter->dev.devt = CXL_CARD_MKDEV(adapter); + */ + + return device_register(&adapter->dev); +} + +int __init cxl_file_init(void) +{ + int rc; + + /* + * If these change we really need to update API. Either change some + * flags or update API version number CXL_API_VERSION. + */ + BUILD_BUG_ON(CXL_API_VERSION != 1); + BUILD_BUG_ON(sizeof(struct cxl_ioctl_start_work) != 64); + BUILD_BUG_ON(sizeof(struct cxl_event_header) != 8); + BUILD_BUG_ON(sizeof(struct cxl_event_afu_interrupt) != 8); + BUILD_BUG_ON(sizeof(struct cxl_event_data_storage) != 32); + BUILD_BUG_ON(sizeof(struct cxl_event_afu_error) != 16); + + if ((rc = alloc_chrdev_region(&cxl_dev, 0, CXL_NUM_MINORS, "cxl"))) { + pr_err("Unable to allocate CXL major number: %i\n", rc); + return rc; + } + + pr_devel("CXL device allocated, MAJOR %i\n", MAJOR(cxl_dev)); + + cxl_class = class_create(THIS_MODULE, "cxl"); + if (IS_ERR(cxl_class)) { + pr_err("Unable to create CXL class\n"); + rc = PTR_ERR(cxl_class); + goto err; + } + cxl_class->devnode = cxl_devnode; + + return 0; + +err: + unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS); + return rc; +} + +void cxl_file_exit(void) +{ + unregister_chrdev_region(cxl_dev, CXL_NUM_MINORS); + class_destroy(cxl_class); +} diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c new file mode 100644 index 000000000000..336020c8e1af --- /dev/null +++ b/drivers/misc/cxl/irq.c @@ -0,0 +1,402 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/slab.h> +#include <linux/pid.h> +#include <asm/cputable.h> +#include <misc/cxl.h> + +#include "cxl.h" + +/* XXX: This is implementation specific */ +static irqreturn_t handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr, u64 errstat) +{ + u64 fir1, fir2, fir_slice, serr, afu_debug; + + fir1 = cxl_p1_read(ctx->afu->adapter, CXL_PSL_FIR1); + fir2 = cxl_p1_read(ctx->afu->adapter, CXL_PSL_FIR2); + fir_slice = cxl_p1n_read(ctx->afu, CXL_PSL_FIR_SLICE_An); + serr = cxl_p1n_read(ctx->afu, CXL_PSL_SERR_An); + afu_debug = cxl_p1n_read(ctx->afu, CXL_AFU_DEBUG_An); + + dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat); + dev_crit(&ctx->afu->dev, "PSL_FIR1: 0x%.16llx\n", fir1); + dev_crit(&ctx->afu->dev, "PSL_FIR2: 0x%.16llx\n", fir2); + dev_crit(&ctx->afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr); + dev_crit(&ctx->afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice); + dev_crit(&ctx->afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug); + + dev_crit(&ctx->afu->dev, "STOPPING CXL TRACE\n"); + cxl_stop_trace(ctx->afu->adapter); + + return cxl_ack_irq(ctx, 0, errstat); +} + +irqreturn_t cxl_slice_irq_err(int irq, void *data) +{ + struct cxl_afu *afu = data; + u64 fir_slice, errstat, serr, afu_debug; + + WARN(irq, "CXL SLICE ERROR interrupt %i\n", irq); + + serr = cxl_p1n_read(afu, CXL_PSL_SERR_An); + fir_slice = cxl_p1n_read(afu, CXL_PSL_FIR_SLICE_An); + errstat = cxl_p2n_read(afu, CXL_PSL_ErrStat_An); + afu_debug = cxl_p1n_read(afu, CXL_AFU_DEBUG_An); + dev_crit(&afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr); + dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice); + dev_crit(&afu->dev, "CXL_PSL_ErrStat_An: 0x%.16llx\n", errstat); + dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug); + + cxl_p1n_write(afu, CXL_PSL_SERR_An, serr); + + return IRQ_HANDLED; +} + +static irqreturn_t cxl_irq_err(int irq, void *data) +{ + struct cxl *adapter = data; + u64 fir1, fir2, err_ivte; + + WARN(1, "CXL ERROR interrupt %i\n", irq); + + err_ivte = cxl_p1_read(adapter, CXL_PSL_ErrIVTE); + dev_crit(&adapter->dev, "PSL_ErrIVTE: 0x%.16llx\n", err_ivte); + + dev_crit(&adapter->dev, "STOPPING CXL TRACE\n"); + cxl_stop_trace(adapter); + + fir1 = cxl_p1_read(adapter, CXL_PSL_FIR1); + fir2 = cxl_p1_read(adapter, CXL_PSL_FIR2); + + dev_crit(&adapter->dev, "PSL_FIR1: 0x%.16llx\nPSL_FIR2: 0x%.16llx\n", fir1, fir2); + + return IRQ_HANDLED; +} + +static irqreturn_t schedule_cxl_fault(struct cxl_context *ctx, u64 dsisr, u64 dar) +{ + ctx->dsisr = dsisr; + ctx->dar = dar; + schedule_work(&ctx->fault_work); + return IRQ_HANDLED; +} + +static irqreturn_t cxl_irq(int irq, void *data) +{ + struct cxl_context *ctx = data; + struct cxl_irq_info irq_info; + u64 dsisr, dar; + int result; + + if ((result = cxl_get_irq(ctx, &irq_info))) { + WARN(1, "Unable to get CXL IRQ Info: %i\n", result); + return IRQ_HANDLED; + } + + dsisr = irq_info.dsisr; + dar = irq_info.dar; + + pr_devel("CXL interrupt %i for afu pe: %i DSISR: %#llx DAR: %#llx\n", irq, ctx->pe, dsisr, dar); + + if (dsisr & CXL_PSL_DSISR_An_DS) { + /* + * We don't inherently need to sleep to handle this, but we do + * need to get a ref to the task's mm, which we can't do from + * irq context without the potential for a deadlock since it + * takes the task_lock. An alternate option would be to keep a + * reference to the task's mm the entire time it has cxl open, + * but to do that we need to solve the issue where we hold a + * ref to the mm, but the mm can hold a ref to the fd after an + * mmap preventing anything from being cleaned up. + */ + pr_devel("Scheduling segment miss handling for later pe: %i\n", ctx->pe); + return schedule_cxl_fault(ctx, dsisr, dar); + } + + if (dsisr & CXL_PSL_DSISR_An_M) + pr_devel("CXL interrupt: PTE not found\n"); + if (dsisr & CXL_PSL_DSISR_An_P) + pr_devel("CXL interrupt: Storage protection violation\n"); + if (dsisr & CXL_PSL_DSISR_An_A) + pr_devel("CXL interrupt: AFU lock access to write through or cache inhibited storage\n"); + if (dsisr & CXL_PSL_DSISR_An_S) + pr_devel("CXL interrupt: Access was afu_wr or afu_zero\n"); + if (dsisr & CXL_PSL_DSISR_An_K) + pr_devel("CXL interrupt: Access not permitted by virtual page class key protection\n"); + + if (dsisr & CXL_PSL_DSISR_An_DM) { + /* + * In some cases we might be able to handle the fault + * immediately if hash_page would succeed, but we still need + * the task's mm, which as above we can't get without a lock + */ + pr_devel("Scheduling page fault handling for later pe: %i\n", ctx->pe); + return schedule_cxl_fault(ctx, dsisr, dar); + } + if (dsisr & CXL_PSL_DSISR_An_ST) + WARN(1, "CXL interrupt: Segment Table PTE not found\n"); + if (dsisr & CXL_PSL_DSISR_An_UR) + pr_devel("CXL interrupt: AURP PTE not found\n"); + if (dsisr & CXL_PSL_DSISR_An_PE) + return handle_psl_slice_error(ctx, dsisr, irq_info.errstat); + if (dsisr & CXL_PSL_DSISR_An_AE) { + pr_devel("CXL interrupt: AFU Error %.llx\n", irq_info.afu_err); + + if (ctx->pending_afu_err) { + /* + * This shouldn't happen - the PSL treats these errors + * as fatal and will have reset the AFU, so there's not + * much point buffering multiple AFU errors. + * OTOH if we DO ever see a storm of these come in it's + * probably best that we log them somewhere: + */ + dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error " + "undelivered to pe %i: %.llx\n", + ctx->pe, irq_info.afu_err); + } else { + spin_lock(&ctx->lock); + ctx->afu_err = irq_info.afu_err; + ctx->pending_afu_err = 1; + spin_unlock(&ctx->lock); + + wake_up_all(&ctx->wq); + } + + cxl_ack_irq(ctx, CXL_PSL_TFC_An_A, 0); + } + if (dsisr & CXL_PSL_DSISR_An_OC) + pr_devel("CXL interrupt: OS Context Warning\n"); + + WARN(1, "Unhandled CXL PSL IRQ\n"); + return IRQ_HANDLED; +} + +static irqreturn_t cxl_irq_multiplexed(int irq, void *data) +{ + struct cxl_afu *afu = data; + struct cxl_context *ctx; + int ph = cxl_p2n_read(afu, CXL_PSL_PEHandle_An) & 0xffff; + int ret; + + rcu_read_lock(); + ctx = idr_find(&afu->contexts_idr, ph); + if (ctx) { + ret = cxl_irq(irq, ctx); + rcu_read_unlock(); + return ret; + } + rcu_read_unlock(); + + WARN(1, "Unable to demultiplex CXL PSL IRQ\n"); + return IRQ_HANDLED; +} + +static irqreturn_t cxl_irq_afu(int irq, void *data) +{ + struct cxl_context *ctx = data; + irq_hw_number_t hwirq = irqd_to_hwirq(irq_get_irq_data(irq)); + int irq_off, afu_irq = 1; + __u16 range; + int r; + + for (r = 1; r < CXL_IRQ_RANGES; r++) { + irq_off = hwirq - ctx->irqs.offset[r]; + range = ctx->irqs.range[r]; + if (irq_off >= 0 && irq_off < range) { + afu_irq += irq_off; + break; + } + afu_irq += range; + } + if (unlikely(r >= CXL_IRQ_RANGES)) { + WARN(1, "Recieved AFU IRQ out of range for pe %i (virq %i hwirq %lx)\n", + ctx->pe, irq, hwirq); + return IRQ_HANDLED; + } + + pr_devel("Received AFU interrupt %i for pe: %i (virq %i hwirq %lx)\n", + afu_irq, ctx->pe, irq, hwirq); + + if (unlikely(!ctx->irq_bitmap)) { + WARN(1, "Recieved AFU IRQ for context with no IRQ bitmap\n"); + return IRQ_HANDLED; + } + spin_lock(&ctx->lock); + set_bit(afu_irq - 1, ctx->irq_bitmap); + ctx->pending_irq = true; + spin_unlock(&ctx->lock); + + wake_up_all(&ctx->wq); + + return IRQ_HANDLED; +} + +unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq, + irq_handler_t handler, void *cookie) +{ + unsigned int virq; + int result; + + /* IRQ Domain? */ + virq = irq_create_mapping(NULL, hwirq); + if (!virq) { + dev_warn(&adapter->dev, "cxl_map_irq: irq_create_mapping failed\n"); + return 0; + } + + cxl_setup_irq(adapter, hwirq, virq); + + pr_devel("hwirq %#lx mapped to virq %u\n", hwirq, virq); + + result = request_irq(virq, handler, 0, "cxl", cookie); + if (result) { + dev_warn(&adapter->dev, "cxl_map_irq: request_irq failed: %i\n", result); + return 0; + } + + return virq; +} + +void cxl_unmap_irq(unsigned int virq, void *cookie) +{ + free_irq(virq, cookie); + irq_dispose_mapping(virq); +} + +static int cxl_register_one_irq(struct cxl *adapter, + irq_handler_t handler, + void *cookie, + irq_hw_number_t *dest_hwirq, + unsigned int *dest_virq) +{ + int hwirq, virq; + + if ((hwirq = cxl_alloc_one_irq(adapter)) < 0) + return hwirq; + + if (!(virq = cxl_map_irq(adapter, hwirq, handler, cookie))) + goto err; + + *dest_hwirq = hwirq; + *dest_virq = virq; + + return 0; + +err: + cxl_release_one_irq(adapter, hwirq); + return -ENOMEM; +} + +int cxl_register_psl_err_irq(struct cxl *adapter) +{ + int rc; + + if ((rc = cxl_register_one_irq(adapter, cxl_irq_err, adapter, + &adapter->err_hwirq, + &adapter->err_virq))) + return rc; + + cxl_p1_write(adapter, CXL_PSL_ErrIVTE, adapter->err_hwirq & 0xffff); + + return 0; +} + +void cxl_release_psl_err_irq(struct cxl *adapter) +{ + cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000); + cxl_unmap_irq(adapter->err_virq, adapter); + cxl_release_one_irq(adapter, adapter->err_hwirq); +} + +int cxl_register_serr_irq(struct cxl_afu *afu) +{ + u64 serr; + int rc; + + if ((rc = cxl_register_one_irq(afu->adapter, cxl_slice_irq_err, afu, + &afu->serr_hwirq, + &afu->serr_virq))) + return rc; + + serr = cxl_p1n_read(afu, CXL_PSL_SERR_An); + serr = (serr & 0x00ffffffffff0000ULL) | (afu->serr_hwirq & 0xffff); + cxl_p1n_write(afu, CXL_PSL_SERR_An, serr); + + return 0; +} + +void cxl_release_serr_irq(struct cxl_afu *afu) +{ + cxl_p1n_write(afu, CXL_PSL_SERR_An, 0x0000000000000000); + cxl_unmap_irq(afu->serr_virq, afu); + cxl_release_one_irq(afu->adapter, afu->serr_hwirq); +} + +int cxl_register_psl_irq(struct cxl_afu *afu) +{ + return cxl_register_one_irq(afu->adapter, cxl_irq_multiplexed, afu, + &afu->psl_hwirq, &afu->psl_virq); +} + +void cxl_release_psl_irq(struct cxl_afu *afu) +{ + cxl_unmap_irq(afu->psl_virq, afu); + cxl_release_one_irq(afu->adapter, afu->psl_hwirq); +} + +int afu_register_irqs(struct cxl_context *ctx, u32 count) +{ + irq_hw_number_t hwirq; + int rc, r, i; + + if ((rc = cxl_alloc_irq_ranges(&ctx->irqs, ctx->afu->adapter, count))) + return rc; + + /* Multiplexed PSL Interrupt */ + ctx->irqs.offset[0] = ctx->afu->psl_hwirq; + ctx->irqs.range[0] = 1; + + ctx->irq_count = count; + ctx->irq_bitmap = kcalloc(BITS_TO_LONGS(count), + sizeof(*ctx->irq_bitmap), GFP_KERNEL); + if (!ctx->irq_bitmap) + return -ENOMEM; + for (r = 1; r < CXL_IRQ_RANGES; r++) { + hwirq = ctx->irqs.offset[r]; + for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { + cxl_map_irq(ctx->afu->adapter, hwirq, + cxl_irq_afu, ctx); + } + } + + return 0; +} + +void afu_release_irqs(struct cxl_context *ctx) +{ + irq_hw_number_t hwirq; + unsigned int virq; + int r, i; + + for (r = 1; r < CXL_IRQ_RANGES; r++) { + hwirq = ctx->irqs.offset[r]; + for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { + virq = irq_find_mapping(NULL, hwirq); + if (virq) + cxl_unmap_irq(virq, ctx); + } + } + + cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter); +} diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c new file mode 100644 index 000000000000..4cde9b661642 --- /dev/null +++ b/drivers/misc/cxl/main.c @@ -0,0 +1,230 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/spinlock.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/idr.h> +#include <linux/pci.h> +#include <asm/cputable.h> +#include <misc/cxl.h> + +#include "cxl.h" + +static DEFINE_SPINLOCK(adapter_idr_lock); +static DEFINE_IDR(cxl_adapter_idr); + +uint cxl_verbose; +module_param_named(verbose, cxl_verbose, uint, 0600); +MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); + +static inline void _cxl_slbia(struct cxl_context *ctx, struct mm_struct *mm) +{ + struct task_struct *task; + unsigned long flags; + if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) { + pr_devel("%s unable to get task %i\n", + __func__, pid_nr(ctx->pid)); + return; + } + + if (task->mm != mm) + goto out_put; + + pr_devel("%s matched mm - card: %i afu: %i pe: %i\n", __func__, + ctx->afu->adapter->adapter_num, ctx->afu->slice, ctx->pe); + + spin_lock_irqsave(&ctx->sste_lock, flags); + memset(ctx->sstp, 0, ctx->sst_size); + spin_unlock_irqrestore(&ctx->sste_lock, flags); + mb(); + cxl_afu_slbia(ctx->afu); +out_put: + put_task_struct(task); +} + +static inline void cxl_slbia_core(struct mm_struct *mm) +{ + struct cxl *adapter; + struct cxl_afu *afu; + struct cxl_context *ctx; + int card, slice, id; + + pr_devel("%s called\n", __func__); + + spin_lock(&adapter_idr_lock); + idr_for_each_entry(&cxl_adapter_idr, adapter, card) { + /* XXX: Make this lookup faster with link from mm to ctx */ + spin_lock(&adapter->afu_list_lock); + for (slice = 0; slice < adapter->slices; slice++) { + afu = adapter->afu[slice]; + if (!afu->enabled) + continue; + rcu_read_lock(); + idr_for_each_entry(&afu->contexts_idr, ctx, id) + _cxl_slbia(ctx, mm); + rcu_read_unlock(); + } + spin_unlock(&adapter->afu_list_lock); + } + spin_unlock(&adapter_idr_lock); +} + +static struct cxl_calls cxl_calls = { + .cxl_slbia = cxl_slbia_core, + .owner = THIS_MODULE, +}; + +int cxl_alloc_sst(struct cxl_context *ctx) +{ + unsigned long vsid; + u64 ea_mask, size, sstp0, sstp1; + + sstp0 = 0; + sstp1 = 0; + + ctx->sst_size = PAGE_SIZE; + ctx->sst_lru = 0; + ctx->sstp = (struct cxl_sste *)get_zeroed_page(GFP_KERNEL); + if (!ctx->sstp) { + pr_err("cxl_alloc_sst: Unable to allocate segment table\n"); + return -ENOMEM; + } + pr_devel("SSTP allocated at 0x%p\n", ctx->sstp); + + vsid = get_kernel_vsid((u64)ctx->sstp, mmu_kernel_ssize) << 12; + + sstp0 |= (u64)mmu_kernel_ssize << CXL_SSTP0_An_B_SHIFT; + sstp0 |= (SLB_VSID_KERNEL | mmu_psize_defs[mmu_linear_psize].sllp) << 50; + + size = (((u64)ctx->sst_size >> 8) - 1) << CXL_SSTP0_An_SegTableSize_SHIFT; + if (unlikely(size & ~CXL_SSTP0_An_SegTableSize_MASK)) { + WARN(1, "Impossible segment table size\n"); + return -EINVAL; + } + sstp0 |= size; + + if (mmu_kernel_ssize == MMU_SEGSIZE_256M) + ea_mask = 0xfffff00ULL; + else + ea_mask = 0xffffffff00ULL; + + sstp0 |= vsid >> (50-14); /* Top 14 bits of VSID */ + sstp1 |= (vsid << (64-(50-14))) & ~ea_mask; + sstp1 |= (u64)ctx->sstp & ea_mask; + sstp1 |= CXL_SSTP1_An_V; + + pr_devel("Looked up %#llx: slbfee. %#llx (ssize: %x, vsid: %#lx), copied to SSTP0: %#llx, SSTP1: %#llx\n", + (u64)ctx->sstp, (u64)ctx->sstp & ESID_MASK, mmu_kernel_ssize, vsid, sstp0, sstp1); + + /* Store calculated sstp hardware points for use later */ + ctx->sstp0 = sstp0; + ctx->sstp1 = sstp1; + + return 0; +} + +/* Find a CXL adapter by it's number and increase it's refcount */ +struct cxl *get_cxl_adapter(int num) +{ + struct cxl *adapter; + + spin_lock(&adapter_idr_lock); + if ((adapter = idr_find(&cxl_adapter_idr, num))) + get_device(&adapter->dev); + spin_unlock(&adapter_idr_lock); + + return adapter; +} + +int cxl_alloc_adapter_nr(struct cxl *adapter) +{ + int i; + + idr_preload(GFP_KERNEL); + spin_lock(&adapter_idr_lock); + i = idr_alloc(&cxl_adapter_idr, adapter, 0, 0, GFP_NOWAIT); + spin_unlock(&adapter_idr_lock); + idr_preload_end(); + if (i < 0) + return i; + + adapter->adapter_num = i; + + return 0; +} + +void cxl_remove_adapter_nr(struct cxl *adapter) +{ + idr_remove(&cxl_adapter_idr, adapter->adapter_num); +} + +int cxl_afu_select_best_mode(struct cxl_afu *afu) +{ + if (afu->modes_supported & CXL_MODE_DIRECTED) + return cxl_afu_activate_mode(afu, CXL_MODE_DIRECTED); + + if (afu->modes_supported & CXL_MODE_DEDICATED) + return cxl_afu_activate_mode(afu, CXL_MODE_DEDICATED); + + dev_warn(&afu->dev, "No supported programming modes available\n"); + /* We don't fail this so the user can inspect sysfs */ + return 0; +} + +static int __init init_cxl(void) +{ + int rc = 0; + + if (!cpu_has_feature(CPU_FTR_HVMODE)) + return -EPERM; + + if ((rc = cxl_file_init())) + return rc; + + cxl_debugfs_init(); + + if ((rc = register_cxl_calls(&cxl_calls))) + goto err; + + if ((rc = pci_register_driver(&cxl_pci_driver))) + goto err1; + + return 0; +err1: + unregister_cxl_calls(&cxl_calls); +err: + cxl_debugfs_exit(); + cxl_file_exit(); + + return rc; +} + +static void exit_cxl(void) +{ + pci_unregister_driver(&cxl_pci_driver); + + cxl_debugfs_exit(); + cxl_file_exit(); + unregister_cxl_calls(&cxl_calls); +} + +module_init(init_cxl); +module_exit(exit_cxl); + +MODULE_DESCRIPTION("IBM Coherent Accelerator"); +MODULE_AUTHOR("Ian Munsie <imunsie@au1.ibm.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c new file mode 100644 index 000000000000..d47532e8f4f1 --- /dev/null +++ b/drivers/misc/cxl/native.c @@ -0,0 +1,683 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/spinlock.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/mutex.h> +#include <linux/mm.h> +#include <linux/uaccess.h> +#include <asm/synch.h> +#include <misc/cxl.h> + +#include "cxl.h" + +static int afu_control(struct cxl_afu *afu, u64 command, + u64 result, u64 mask, bool enabled) +{ + u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); + unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + + spin_lock(&afu->afu_cntl_lock); + pr_devel("AFU command starting: %llx\n", command); + + cxl_p2n_write(afu, CXL_AFU_Cntl_An, AFU_Cntl | command); + + AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); + while ((AFU_Cntl & mask) != result) { + if (time_after_eq(jiffies, timeout)) { + dev_warn(&afu->dev, "WARNING: AFU control timed out!\n"); + spin_unlock(&afu->afu_cntl_lock); + return -EBUSY; + } + pr_devel_ratelimited("AFU control... (0x%.16llx)\n", + AFU_Cntl | command); + cpu_relax(); + AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); + }; + pr_devel("AFU command complete: %llx\n", command); + afu->enabled = enabled; + spin_unlock(&afu->afu_cntl_lock); + + return 0; +} + +static int afu_enable(struct cxl_afu *afu) +{ + pr_devel("AFU enable request\n"); + + return afu_control(afu, CXL_AFU_Cntl_An_E, + CXL_AFU_Cntl_An_ES_Enabled, + CXL_AFU_Cntl_An_ES_MASK, true); +} + +int cxl_afu_disable(struct cxl_afu *afu) +{ + pr_devel("AFU disable request\n"); + + return afu_control(afu, 0, CXL_AFU_Cntl_An_ES_Disabled, + CXL_AFU_Cntl_An_ES_MASK, false); +} + +/* This will disable as well as reset */ +int cxl_afu_reset(struct cxl_afu *afu) +{ + pr_devel("AFU reset request\n"); + + return afu_control(afu, CXL_AFU_Cntl_An_RA, + CXL_AFU_Cntl_An_RS_Complete | CXL_AFU_Cntl_An_ES_Disabled, + CXL_AFU_Cntl_An_RS_MASK | CXL_AFU_Cntl_An_ES_MASK, + false); +} + +static int afu_check_and_enable(struct cxl_afu *afu) +{ + if (afu->enabled) + return 0; + return afu_enable(afu); +} + +int cxl_psl_purge(struct cxl_afu *afu) +{ + u64 PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An); + u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); + u64 dsisr, dar; + u64 start, end; + unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + + pr_devel("PSL purge request\n"); + + if ((AFU_Cntl & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) { + WARN(1, "psl_purge request while AFU not disabled!\n"); + cxl_afu_disable(afu); + } + + cxl_p1n_write(afu, CXL_PSL_SCNTL_An, + PSL_CNTL | CXL_PSL_SCNTL_An_Pc); + start = local_clock(); + PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An); + while ((PSL_CNTL & CXL_PSL_SCNTL_An_Ps_MASK) + == CXL_PSL_SCNTL_An_Ps_Pending) { + if (time_after_eq(jiffies, timeout)) { + dev_warn(&afu->dev, "WARNING: PSL Purge timed out!\n"); + return -EBUSY; + } + dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); + pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%.16llx PSL_DSISR: 0x%.16llx\n", PSL_CNTL, dsisr); + if (dsisr & CXL_PSL_DSISR_TRANS) { + dar = cxl_p2n_read(afu, CXL_PSL_DAR_An); + dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%.16llx, DAR: 0x%.16llx\n", dsisr, dar); + cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE); + } else if (dsisr) { + dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%.16llx\n", dsisr); + cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A); + } else { + 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); + + cxl_p1n_write(afu, CXL_PSL_SCNTL_An, + PSL_CNTL & ~CXL_PSL_SCNTL_An_Pc); + return 0; +} + +static int spa_max_procs(int spa_size) +{ + /* + * From the CAIA: + * end_of_SPA_area = SPA_Base + ((n+4) * 128) + (( ((n*8) + 127) >> 7) * 128) + 255 + * Most of that junk is really just an overly-complicated way of saying + * the last 256 bytes are __aligned(128), so it's really: + * end_of_SPA_area = end_of_PSL_queue_area + __aligned(128) 255 + * and + * end_of_PSL_queue_area = SPA_Base + ((n+4) * 128) + (n*8) - 1 + * so + * sizeof(SPA) = ((n+4) * 128) + (n*8) + __aligned(128) 256 + * Ignore the alignment (which is safe in this case as long as we are + * careful with our rounding) and solve for n: + */ + return ((spa_size / 8) - 96) / 17; +} + +static int alloc_spa(struct cxl_afu *afu) +{ + u64 spap; + + /* Work out how many pages to allocate */ + afu->spa_order = 0; + do { + afu->spa_order++; + afu->spa_size = (1 << afu->spa_order) * PAGE_SIZE; + afu->spa_max_procs = spa_max_procs(afu->spa_size); + } while (afu->spa_max_procs < afu->num_procs); + + WARN_ON(afu->spa_size > 0x100000); /* Max size supported by the hardware */ + + if (!(afu->spa = (struct cxl_process_element *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, afu->spa_order))) { + pr_err("cxl_alloc_spa: Unable to allocate scheduled process area\n"); + return -ENOMEM; + } + pr_devel("spa pages: %i afu->spa_max_procs: %i afu->num_procs: %i\n", + 1<<afu->spa_order, afu->spa_max_procs, afu->num_procs); + + afu->sw_command_status = (__be64 *)((char *)afu->spa + + ((afu->spa_max_procs + 3) * 128)); + + spap = virt_to_phys(afu->spa) & CXL_PSL_SPAP_Addr; + spap |= ((afu->spa_size >> (12 - CXL_PSL_SPAP_Size_Shift)) - 1) & CXL_PSL_SPAP_Size; + spap |= CXL_PSL_SPAP_V; + pr_devel("cxl: SPA allocated at 0x%p. Max processes: %i, sw_command_status: 0x%p CXL_PSL_SPAP_An=0x%016llx\n", afu->spa, afu->spa_max_procs, afu->sw_command_status, spap); + cxl_p1n_write(afu, CXL_PSL_SPAP_An, spap); + + return 0; +} + +static void release_spa(struct cxl_afu *afu) +{ + free_pages((unsigned long) afu->spa, afu->spa_order); +} + +int cxl_tlb_slb_invalidate(struct cxl *adapter) +{ + unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + + pr_devel("CXL adapter wide TLBIA & SLBIA\n"); + + cxl_p1_write(adapter, CXL_PSL_AFUSEL, CXL_PSL_AFUSEL_A); + + cxl_p1_write(adapter, CXL_PSL_TLBIA, CXL_TLB_SLB_IQ_ALL); + while (cxl_p1_read(adapter, CXL_PSL_TLBIA) & CXL_TLB_SLB_P) { + if (time_after_eq(jiffies, timeout)) { + dev_warn(&adapter->dev, "WARNING: CXL adapter wide TLBIA timed out!\n"); + return -EBUSY; + } + cpu_relax(); + } + + cxl_p1_write(adapter, CXL_PSL_SLBIA, CXL_TLB_SLB_IQ_ALL); + while (cxl_p1_read(adapter, CXL_PSL_SLBIA) & CXL_TLB_SLB_P) { + if (time_after_eq(jiffies, timeout)) { + dev_warn(&adapter->dev, "WARNING: CXL adapter wide SLBIA timed out!\n"); + return -EBUSY; + } + cpu_relax(); + } + return 0; +} + +int cxl_afu_slbia(struct cxl_afu *afu) +{ + unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT); + + pr_devel("cxl_afu_slbia issuing SLBIA command\n"); + cxl_p2n_write(afu, CXL_SLBIA_An, CXL_TLB_SLB_IQ_ALL); + while (cxl_p2n_read(afu, CXL_SLBIA_An) & CXL_TLB_SLB_P) { + if (time_after_eq(jiffies, timeout)) { + dev_warn(&afu->dev, "WARNING: CXL AFU SLBIA timed out!\n"); + return -EBUSY; + } + cpu_relax(); + } + return 0; +} + +static int cxl_write_sstp(struct cxl_afu *afu, u64 sstp0, u64 sstp1) +{ + int rc; + + /* 1. Disable SSTP by writing 0 to SSTP1[V] */ + cxl_p2n_write(afu, CXL_SSTP1_An, 0); + + /* 2. Invalidate all SLB entries */ + if ((rc = cxl_afu_slbia(afu))) + return rc; + + /* 3. Set SSTP0_An */ + cxl_p2n_write(afu, CXL_SSTP0_An, sstp0); + + /* 4. Set SSTP1_An */ + cxl_p2n_write(afu, CXL_SSTP1_An, sstp1); + + return 0; +} + +/* Using per slice version may improve performance here. (ie. SLBIA_An) */ +static void slb_invalid(struct cxl_context *ctx) +{ + struct cxl *adapter = ctx->afu->adapter; + u64 slbia; + + WARN_ON(!mutex_is_locked(&ctx->afu->spa_mutex)); + + cxl_p1_write(adapter, CXL_PSL_LBISEL, + ((u64)be32_to_cpu(ctx->elem->common.pid) << 32) | + be32_to_cpu(ctx->elem->lpid)); + cxl_p1_write(adapter, CXL_PSL_SLBIA, CXL_TLB_SLB_IQ_LPIDPID); + + while (1) { + slbia = cxl_p1_read(adapter, CXL_PSL_SLBIA); + if (!(slbia & CXL_TLB_SLB_P)) + break; + cpu_relax(); + } +} + +static int do_process_element_cmd(struct cxl_context *ctx, + u64 cmd, u64 pe_state) +{ + u64 state; + + WARN_ON(!ctx->afu->enabled); + + ctx->elem->software_state = cpu_to_be32(pe_state); + smp_wmb(); + *(ctx->afu->sw_command_status) = cpu_to_be64(cmd | 0 | ctx->pe); + smp_mb(); + cxl_p1n_write(ctx->afu, CXL_PSL_LLCMD_An, cmd | ctx->pe); + while (1) { + state = be64_to_cpup(ctx->afu->sw_command_status); + if (state == ~0ULL) { + pr_err("cxl: Error adding process element to AFU\n"); + return -1; + } + if ((state & (CXL_SPA_SW_CMD_MASK | CXL_SPA_SW_STATE_MASK | CXL_SPA_SW_LINK_MASK)) == + (cmd | (cmd >> 16) | ctx->pe)) + break; + /* + * The command won't finish in the PSL if there are + * outstanding DSIs. Hence we need to yield here in + * case there are outstanding DSIs that we need to + * service. Tuning possiblity: we could wait for a + * while before sched + */ + schedule(); + + } + return 0; +} + +static int add_process_element(struct cxl_context *ctx) +{ + int rc = 0; + + mutex_lock(&ctx->afu->spa_mutex); + pr_devel("%s Adding pe: %i started\n", __func__, ctx->pe); + if (!(rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_ADD, CXL_PE_SOFTWARE_STATE_V))) + ctx->pe_inserted = true; + pr_devel("%s Adding pe: %i finished\n", __func__, ctx->pe); + mutex_unlock(&ctx->afu->spa_mutex); + return rc; +} + +static int terminate_process_element(struct cxl_context *ctx) +{ + int rc = 0; + + /* fast path terminate if it's already invalid */ + if (!(ctx->elem->software_state & cpu_to_be32(CXL_PE_SOFTWARE_STATE_V))) + return rc; + + mutex_lock(&ctx->afu->spa_mutex); + pr_devel("%s Terminate pe: %i started\n", __func__, ctx->pe); + rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_TERMINATE, + CXL_PE_SOFTWARE_STATE_V | CXL_PE_SOFTWARE_STATE_T); + ctx->elem->software_state = 0; /* Remove Valid bit */ + pr_devel("%s Terminate pe: %i finished\n", __func__, ctx->pe); + mutex_unlock(&ctx->afu->spa_mutex); + return rc; +} + +static int remove_process_element(struct cxl_context *ctx) +{ + int rc = 0; + + mutex_lock(&ctx->afu->spa_mutex); + pr_devel("%s Remove pe: %i started\n", __func__, ctx->pe); + if (!(rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_REMOVE, 0))) + ctx->pe_inserted = false; + slb_invalid(ctx); + pr_devel("%s Remove pe: %i finished\n", __func__, ctx->pe); + mutex_unlock(&ctx->afu->spa_mutex); + + return rc; +} + + +static void assign_psn_space(struct cxl_context *ctx) +{ + if (!ctx->afu->pp_size || ctx->master) { + ctx->psn_phys = ctx->afu->psn_phys; + ctx->psn_size = ctx->afu->adapter->ps_size; + } else { + ctx->psn_phys = ctx->afu->psn_phys + + (ctx->afu->pp_offset + ctx->afu->pp_size * ctx->pe); + ctx->psn_size = ctx->afu->pp_size; + } +} + +static int activate_afu_directed(struct cxl_afu *afu) +{ + int rc; + + dev_info(&afu->dev, "Activating AFU directed mode\n"); + + if (alloc_spa(afu)) + return -ENOMEM; + + cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_AFU); + cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL); + cxl_p1n_write(afu, CXL_PSL_ID_An, CXL_PSL_ID_An_F | CXL_PSL_ID_An_L); + + afu->current_mode = CXL_MODE_DIRECTED; + afu->num_procs = afu->max_procs_virtualised; + + if ((rc = cxl_chardev_m_afu_add(afu))) + return rc; + + if ((rc = cxl_sysfs_afu_m_add(afu))) + goto err; + + if ((rc = cxl_chardev_s_afu_add(afu))) + goto err1; + + return 0; +err1: + cxl_sysfs_afu_m_remove(afu); +err: + cxl_chardev_afu_remove(afu); + return rc; +} + +#ifdef CONFIG_CPU_LITTLE_ENDIAN +#define set_endian(sr) ((sr) |= CXL_PSL_SR_An_LE) +#else +#define set_endian(sr) ((sr) &= ~(CXL_PSL_SR_An_LE)) +#endif + +static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) +{ + u64 sr; + int r, result; + + assign_psn_space(ctx); + + ctx->elem->ctxtime = 0; /* disable */ + ctx->elem->lpid = cpu_to_be32(mfspr(SPRN_LPID)); + ctx->elem->haurp = 0; /* disable */ + ctx->elem->sdr = cpu_to_be64(mfspr(SPRN_SDR1)); + + sr = 0; + if (ctx->master) + sr |= CXL_PSL_SR_An_MP; + if (mfspr(SPRN_LPCR) & LPCR_TC) + sr |= CXL_PSL_SR_An_TC; + /* HV=0, PR=1, R=1 for userspace + * For kernel contexts: this would need to change + */ + sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R; + set_endian(sr); + sr &= ~(CXL_PSL_SR_An_HV); + if (!test_tsk_thread_flag(current, TIF_32BIT)) + sr |= CXL_PSL_SR_An_SF; + ctx->elem->common.pid = cpu_to_be32(current->pid); + ctx->elem->common.tid = 0; + ctx->elem->sr = cpu_to_be64(sr); + + ctx->elem->common.csrp = 0; /* disable */ + ctx->elem->common.aurp0 = 0; /* disable */ + ctx->elem->common.aurp1 = 0; /* disable */ + + cxl_prefault(ctx, wed); + + ctx->elem->common.sstp0 = cpu_to_be64(ctx->sstp0); + ctx->elem->common.sstp1 = cpu_to_be64(ctx->sstp1); + + for (r = 0; r < CXL_IRQ_RANGES; r++) { + ctx->elem->ivte_offsets[r] = cpu_to_be16(ctx->irqs.offset[r]); + ctx->elem->ivte_ranges[r] = cpu_to_be16(ctx->irqs.range[r]); + } + + ctx->elem->common.amr = cpu_to_be64(amr); + ctx->elem->common.wed = cpu_to_be64(wed); + + /* first guy needs to enable */ + if ((result = afu_check_and_enable(ctx->afu))) + return result; + + add_process_element(ctx); + + return 0; +} + +static int deactivate_afu_directed(struct cxl_afu *afu) +{ + dev_info(&afu->dev, "Deactivating AFU directed mode\n"); + + afu->current_mode = 0; + afu->num_procs = 0; + + cxl_sysfs_afu_m_remove(afu); + cxl_chardev_afu_remove(afu); + + cxl_afu_reset(afu); + cxl_afu_disable(afu); + cxl_psl_purge(afu); + + release_spa(afu); + + return 0; +} + +static int activate_dedicated_process(struct cxl_afu *afu) +{ + dev_info(&afu->dev, "Activating dedicated process mode\n"); + + cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_Process); + + cxl_p1n_write(afu, CXL_PSL_CtxTime_An, 0); /* disable */ + cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0); /* disable */ + cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL); + cxl_p1n_write(afu, CXL_PSL_LPID_An, mfspr(SPRN_LPID)); + cxl_p1n_write(afu, CXL_HAURP_An, 0); /* disable */ + cxl_p1n_write(afu, CXL_PSL_SDR_An, mfspr(SPRN_SDR1)); + + cxl_p2n_write(afu, CXL_CSRP_An, 0); /* disable */ + cxl_p2n_write(afu, CXL_AURP0_An, 0); /* disable */ + cxl_p2n_write(afu, CXL_AURP1_An, 0); /* disable */ + + afu->current_mode = CXL_MODE_DEDICATED; + afu->num_procs = 1; + + return cxl_chardev_d_afu_add(afu); +} + +static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr) +{ + struct cxl_afu *afu = ctx->afu; + u64 sr; + int rc; + + sr = 0; + set_endian(sr); + if (ctx->master) + sr |= CXL_PSL_SR_An_MP; + if (mfspr(SPRN_LPCR) & LPCR_TC) + sr |= CXL_PSL_SR_An_TC; + sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R; + if (!test_tsk_thread_flag(current, TIF_32BIT)) + sr |= CXL_PSL_SR_An_SF; + cxl_p2n_write(afu, CXL_PSL_PID_TID_An, (u64)current->pid << 32); + cxl_p1n_write(afu, CXL_PSL_SR_An, sr); + + if ((rc = cxl_write_sstp(afu, ctx->sstp0, ctx->sstp1))) + return rc; + + cxl_prefault(ctx, wed); + + cxl_p1n_write(afu, CXL_PSL_IVTE_Offset_An, + (((u64)ctx->irqs.offset[0] & 0xffff) << 48) | + (((u64)ctx->irqs.offset[1] & 0xffff) << 32) | + (((u64)ctx->irqs.offset[2] & 0xffff) << 16) | + ((u64)ctx->irqs.offset[3] & 0xffff)); + cxl_p1n_write(afu, CXL_PSL_IVTE_Limit_An, (u64) + (((u64)ctx->irqs.range[0] & 0xffff) << 48) | + (((u64)ctx->irqs.range[1] & 0xffff) << 32) | + (((u64)ctx->irqs.range[2] & 0xffff) << 16) | + ((u64)ctx->irqs.range[3] & 0xffff)); + + cxl_p2n_write(afu, CXL_PSL_AMR_An, amr); + + /* master only context for dedicated */ + assign_psn_space(ctx); + + if ((rc = cxl_afu_reset(afu))) + return rc; + + cxl_p2n_write(afu, CXL_PSL_WED_An, wed); + + return afu_enable(afu); +} + +static int deactivate_dedicated_process(struct cxl_afu *afu) +{ + dev_info(&afu->dev, "Deactivating dedicated process mode\n"); + + afu->current_mode = 0; + afu->num_procs = 0; + + cxl_chardev_afu_remove(afu); + + return 0; +} + +int _cxl_afu_deactivate_mode(struct cxl_afu *afu, int mode) +{ + if (mode == CXL_MODE_DIRECTED) + return deactivate_afu_directed(afu); + if (mode == CXL_MODE_DEDICATED) + return deactivate_dedicated_process(afu); + return 0; +} + +int cxl_afu_deactivate_mode(struct cxl_afu *afu) +{ + return _cxl_afu_deactivate_mode(afu, afu->current_mode); +} + +int cxl_afu_activate_mode(struct cxl_afu *afu, int mode) +{ + if (!mode) + return 0; + if (!(mode & afu->modes_supported)) + return -EINVAL; + + if (mode == CXL_MODE_DIRECTED) + return activate_afu_directed(afu); + if (mode == CXL_MODE_DEDICATED) + return activate_dedicated_process(afu); + + return -EINVAL; +} + +int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr) +{ + ctx->kernel = kernel; + if (ctx->afu->current_mode == CXL_MODE_DIRECTED) + return attach_afu_directed(ctx, wed, amr); + + if (ctx->afu->current_mode == CXL_MODE_DEDICATED) + return attach_dedicated(ctx, wed, amr); + + return -EINVAL; +} + +static inline int detach_process_native_dedicated(struct cxl_context *ctx) +{ + cxl_afu_reset(ctx->afu); + cxl_afu_disable(ctx->afu); + cxl_psl_purge(ctx->afu); + return 0; +} + +/* + * TODO: handle case when this is called inside a rcu_read_lock() which may + * happen when we unbind the driver (ie. cxl_context_detach_all()) . Terminate + * & remove use a mutex lock and schedule which will not good with lock held. + * May need to write do_process_element_cmd() that handles outstanding page + * faults synchronously. + */ +static inline int detach_process_native_afu_directed(struct cxl_context *ctx) +{ + if (!ctx->pe_inserted) + return 0; + if (terminate_process_element(ctx)) + return -1; + if (remove_process_element(ctx)) + return -1; + + return 0; +} + +int cxl_detach_process(struct cxl_context *ctx) +{ + if (ctx->afu->current_mode == CXL_MODE_DEDICATED) + return detach_process_native_dedicated(ctx); + + return detach_process_native_afu_directed(ctx); +} + +int cxl_get_irq(struct cxl_context *ctx, struct cxl_irq_info *info) +{ + u64 pidtid; + + info->dsisr = cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An); + info->dar = cxl_p2n_read(ctx->afu, CXL_PSL_DAR_An); + info->dsr = cxl_p2n_read(ctx->afu, CXL_PSL_DSR_An); + pidtid = cxl_p2n_read(ctx->afu, CXL_PSL_PID_TID_An); + info->pid = pidtid >> 32; + info->tid = pidtid & 0xffffffff; + info->afu_err = cxl_p2n_read(ctx->afu, CXL_AFU_ERR_An); + info->errstat = cxl_p2n_read(ctx->afu, CXL_PSL_ErrStat_An); + + return 0; +} + +static void recover_psl_err(struct cxl_afu *afu, u64 errstat) +{ + u64 dsisr; + + pr_devel("RECOVERING FROM PSL ERROR... (0x%.16llx)\n", errstat); + + /* Clear PSL_DSISR[PE] */ + dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); + cxl_p2n_write(afu, CXL_PSL_DSISR_An, dsisr & ~CXL_PSL_DSISR_An_PE); + + /* Write 1s to clear error status bits */ + cxl_p2n_write(afu, CXL_PSL_ErrStat_An, errstat); +} + +int cxl_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask) +{ + if (tfc) + cxl_p2n_write(ctx->afu, CXL_PSL_TFC_An, tfc); + if (psl_reset_mask) + recover_psl_err(ctx->afu, psl_reset_mask); + + return 0; +} + +int cxl_check_error(struct cxl_afu *afu) +{ + return (cxl_p1n_read(afu, CXL_PSL_SCNTL_An) == ~0ULL); +} diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c new file mode 100644 index 000000000000..10c98ab7f46e --- /dev/null +++ b/drivers/misc/cxl/pci.c @@ -0,0 +1,1000 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/pci_regs.h> +#include <linux/pci_ids.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/pci.h> +#include <linux/of.h> +#include <linux/delay.h> +#include <asm/opal.h> +#include <asm/msi_bitmap.h> +#include <asm/pci-bridge.h> /* for struct pci_controller */ +#include <asm/pnv-pci.h> + +#include "cxl.h" + + +#define CXL_PCI_VSEC_ID 0x1280 +#define CXL_VSEC_MIN_SIZE 0x80 + +#define CXL_READ_VSEC_LENGTH(dev, vsec, dest) \ + { \ + pci_read_config_word(dev, vsec + 0x6, dest); \ + *dest >>= 4; \ + } +#define CXL_READ_VSEC_NAFUS(dev, vsec, dest) \ + pci_read_config_byte(dev, vsec + 0x8, dest) + +#define CXL_READ_VSEC_STATUS(dev, vsec, dest) \ + pci_read_config_byte(dev, vsec + 0x9, dest) +#define CXL_STATUS_SECOND_PORT 0x80 +#define CXL_STATUS_MSI_X_FULL 0x40 +#define CXL_STATUS_MSI_X_SINGLE 0x20 +#define CXL_STATUS_FLASH_RW 0x08 +#define CXL_STATUS_FLASH_RO 0x04 +#define CXL_STATUS_LOADABLE_AFU 0x02 +#define CXL_STATUS_LOADABLE_PSL 0x01 +/* If we see these features we won't try to use the card */ +#define CXL_UNSUPPORTED_FEATURES \ + (CXL_STATUS_MSI_X_FULL | CXL_STATUS_MSI_X_SINGLE) + +#define CXL_READ_VSEC_MODE_CONTROL(dev, vsec, dest) \ + pci_read_config_byte(dev, vsec + 0xa, dest) +#define CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val) \ + pci_write_config_byte(dev, vsec + 0xa, val) +#define CXL_VSEC_PROTOCOL_MASK 0xe0 +#define CXL_VSEC_PROTOCOL_1024TB 0x80 +#define CXL_VSEC_PROTOCOL_512TB 0x40 +#define CXL_VSEC_PROTOCOL_256TB 0x20 /* Power 8 uses this */ +#define CXL_VSEC_PROTOCOL_ENABLE 0x01 + +#define CXL_READ_VSEC_PSL_REVISION(dev, vsec, dest) \ + pci_read_config_word(dev, vsec + 0xc, dest) +#define CXL_READ_VSEC_CAIA_MINOR(dev, vsec, dest) \ + pci_read_config_byte(dev, vsec + 0xe, dest) +#define CXL_READ_VSEC_CAIA_MAJOR(dev, vsec, dest) \ + pci_read_config_byte(dev, vsec + 0xf, dest) +#define CXL_READ_VSEC_BASE_IMAGE(dev, vsec, dest) \ + pci_read_config_word(dev, vsec + 0x10, dest) + +#define CXL_READ_VSEC_IMAGE_STATE(dev, vsec, dest) \ + pci_read_config_byte(dev, vsec + 0x13, dest) +#define CXL_WRITE_VSEC_IMAGE_STATE(dev, vsec, val) \ + pci_write_config_byte(dev, vsec + 0x13, val) +#define CXL_VSEC_USER_IMAGE_LOADED 0x80 /* RO */ +#define CXL_VSEC_PERST_LOADS_IMAGE 0x20 /* RW */ +#define CXL_VSEC_PERST_SELECT_USER 0x10 /* RW */ + +#define CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, dest) \ + pci_read_config_dword(dev, vsec + 0x20, dest) +#define CXL_READ_VSEC_AFU_DESC_SIZE(dev, vsec, dest) \ + pci_read_config_dword(dev, vsec + 0x24, dest) +#define CXL_READ_VSEC_PS_OFF(dev, vsec, dest) \ + pci_read_config_dword(dev, vsec + 0x28, dest) +#define CXL_READ_VSEC_PS_SIZE(dev, vsec, dest) \ + pci_read_config_dword(dev, vsec + 0x2c, dest) + + +/* This works a little different than the p1/p2 register accesses to make it + * easier to pull out individual fields */ +#define AFUD_READ(afu, off) in_be64(afu->afu_desc_mmio + off) +#define EXTRACT_PPC_BIT(val, bit) (!!(val & PPC_BIT(bit))) +#define EXTRACT_PPC_BITS(val, bs, be) ((val & PPC_BITMASK(bs, be)) >> PPC_BITLSHIFT(be)) + +#define AFUD_READ_INFO(afu) AFUD_READ(afu, 0x0) +#define AFUD_NUM_INTS_PER_PROC(val) EXTRACT_PPC_BITS(val, 0, 15) +#define AFUD_NUM_PROCS(val) EXTRACT_PPC_BITS(val, 16, 31) +#define AFUD_NUM_CRS(val) EXTRACT_PPC_BITS(val, 32, 47) +#define AFUD_MULTIMODE(val) EXTRACT_PPC_BIT(val, 48) +#define AFUD_PUSH_BLOCK_TRANSFER(val) EXTRACT_PPC_BIT(val, 55) +#define AFUD_DEDICATED_PROCESS(val) EXTRACT_PPC_BIT(val, 59) +#define AFUD_AFU_DIRECTED(val) EXTRACT_PPC_BIT(val, 61) +#define AFUD_TIME_SLICED(val) EXTRACT_PPC_BIT(val, 63) +#define AFUD_READ_CR(afu) AFUD_READ(afu, 0x20) +#define AFUD_CR_LEN(val) EXTRACT_PPC_BITS(val, 8, 63) +#define AFUD_READ_CR_OFF(afu) AFUD_READ(afu, 0x28) +#define AFUD_READ_PPPSA(afu) AFUD_READ(afu, 0x30) +#define AFUD_PPPSA_PP(val) EXTRACT_PPC_BIT(val, 6) +#define AFUD_PPPSA_PSA(val) EXTRACT_PPC_BIT(val, 7) +#define AFUD_PPPSA_LEN(val) EXTRACT_PPC_BITS(val, 8, 63) +#define AFUD_READ_PPPSA_OFF(afu) AFUD_READ(afu, 0x38) +#define AFUD_READ_EB(afu) AFUD_READ(afu, 0x40) +#define AFUD_EB_LEN(val) EXTRACT_PPC_BITS(val, 8, 63) +#define AFUD_READ_EB_OFF(afu) AFUD_READ(afu, 0x48) + +static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = { + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), }, + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), }, + { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x04cf), }, + { PCI_DEVICE_CLASS(0x120000, ~0), }, + + { } +}; +MODULE_DEVICE_TABLE(pci, cxl_pci_tbl); + + +/* + * Mostly using these wrappers to avoid confusion: + * priv 1 is BAR2, while priv 2 is BAR0 + */ +static inline resource_size_t p1_base(struct pci_dev *dev) +{ + return pci_resource_start(dev, 2); +} + +static inline resource_size_t p1_size(struct pci_dev *dev) +{ + return pci_resource_len(dev, 2); +} + +static inline resource_size_t p2_base(struct pci_dev *dev) +{ + return pci_resource_start(dev, 0); +} + +static inline resource_size_t p2_size(struct pci_dev *dev) +{ + return pci_resource_len(dev, 0); +} + +static int find_cxl_vsec(struct pci_dev *dev) +{ + int vsec = 0; + u16 val; + + while ((vsec = pci_find_next_ext_capability(dev, vsec, PCI_EXT_CAP_ID_VNDR))) { + pci_read_config_word(dev, vsec + 0x4, &val); + if (val == CXL_PCI_VSEC_ID) + return vsec; + } + return 0; + +} + +static void dump_cxl_config_space(struct pci_dev *dev) +{ + int vsec; + u32 val; + + dev_info(&dev->dev, "dump_cxl_config_space\n"); + + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &val); + dev_info(&dev->dev, "BAR0: %#.8x\n", val); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &val); + dev_info(&dev->dev, "BAR1: %#.8x\n", val); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_2, &val); + dev_info(&dev->dev, "BAR2: %#.8x\n", val); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_3, &val); + dev_info(&dev->dev, "BAR3: %#.8x\n", val); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_4, &val); + dev_info(&dev->dev, "BAR4: %#.8x\n", val); + pci_read_config_dword(dev, PCI_BASE_ADDRESS_5, &val); + dev_info(&dev->dev, "BAR5: %#.8x\n", val); + + dev_info(&dev->dev, "p1 regs: %#llx, len: %#llx\n", + p1_base(dev), p1_size(dev)); + dev_info(&dev->dev, "p2 regs: %#llx, len: %#llx\n", + p1_base(dev), p2_size(dev)); + dev_info(&dev->dev, "BAR 4/5: %#llx, len: %#llx\n", + pci_resource_start(dev, 4), pci_resource_len(dev, 4)); + + if (!(vsec = find_cxl_vsec(dev))) + return; + +#define show_reg(name, what) \ + dev_info(&dev->dev, "cxl vsec: %30s: %#x\n", name, what) + + pci_read_config_dword(dev, vsec + 0x0, &val); + show_reg("Cap ID", (val >> 0) & 0xffff); + show_reg("Cap Ver", (val >> 16) & 0xf); + show_reg("Next Cap Ptr", (val >> 20) & 0xfff); + pci_read_config_dword(dev, vsec + 0x4, &val); + show_reg("VSEC ID", (val >> 0) & 0xffff); + show_reg("VSEC Rev", (val >> 16) & 0xf); + show_reg("VSEC Length", (val >> 20) & 0xfff); + pci_read_config_dword(dev, vsec + 0x8, &val); + show_reg("Num AFUs", (val >> 0) & 0xff); + show_reg("Status", (val >> 8) & 0xff); + show_reg("Mode Control", (val >> 16) & 0xff); + show_reg("Reserved", (val >> 24) & 0xff); + pci_read_config_dword(dev, vsec + 0xc, &val); + show_reg("PSL Rev", (val >> 0) & 0xffff); + show_reg("CAIA Ver", (val >> 16) & 0xffff); + pci_read_config_dword(dev, vsec + 0x10, &val); + show_reg("Base Image Rev", (val >> 0) & 0xffff); + show_reg("Reserved", (val >> 16) & 0x0fff); + show_reg("Image Control", (val >> 28) & 0x3); + show_reg("Reserved", (val >> 30) & 0x1); + show_reg("Image Loaded", (val >> 31) & 0x1); + + pci_read_config_dword(dev, vsec + 0x14, &val); + show_reg("Reserved", val); + pci_read_config_dword(dev, vsec + 0x18, &val); + show_reg("Reserved", val); + pci_read_config_dword(dev, vsec + 0x1c, &val); + show_reg("Reserved", val); + + pci_read_config_dword(dev, vsec + 0x20, &val); + show_reg("AFU Descriptor Offset", val); + pci_read_config_dword(dev, vsec + 0x24, &val); + show_reg("AFU Descriptor Size", val); + pci_read_config_dword(dev, vsec + 0x28, &val); + show_reg("Problem State Offset", val); + pci_read_config_dword(dev, vsec + 0x2c, &val); + show_reg("Problem State Size", val); + + pci_read_config_dword(dev, vsec + 0x30, &val); + show_reg("Reserved", val); + pci_read_config_dword(dev, vsec + 0x34, &val); + show_reg("Reserved", val); + pci_read_config_dword(dev, vsec + 0x38, &val); + show_reg("Reserved", val); + pci_read_config_dword(dev, vsec + 0x3c, &val); + show_reg("Reserved", val); + + pci_read_config_dword(dev, vsec + 0x40, &val); + show_reg("PSL Programming Port", val); + pci_read_config_dword(dev, vsec + 0x44, &val); + show_reg("PSL Programming Control", val); + + pci_read_config_dword(dev, vsec + 0x48, &val); + show_reg("Reserved", val); + pci_read_config_dword(dev, vsec + 0x4c, &val); + show_reg("Reserved", val); + + pci_read_config_dword(dev, vsec + 0x50, &val); + show_reg("Flash Address Register", val); + pci_read_config_dword(dev, vsec + 0x54, &val); + show_reg("Flash Size Register", val); + pci_read_config_dword(dev, vsec + 0x58, &val); + show_reg("Flash Status/Control Register", val); + pci_read_config_dword(dev, vsec + 0x58, &val); + show_reg("Flash Data Port", val); + +#undef show_reg +} + +static void dump_afu_descriptor(struct cxl_afu *afu) +{ + u64 val; + +#define show_reg(name, what) \ + dev_info(&afu->dev, "afu desc: %30s: %#llx\n", name, what) + + val = AFUD_READ_INFO(afu); + show_reg("num_ints_per_process", AFUD_NUM_INTS_PER_PROC(val)); + show_reg("num_of_processes", AFUD_NUM_PROCS(val)); + show_reg("num_of_afu_CRs", AFUD_NUM_CRS(val)); + show_reg("req_prog_mode", val & 0xffffULL); + + val = AFUD_READ(afu, 0x8); + show_reg("Reserved", val); + val = AFUD_READ(afu, 0x10); + show_reg("Reserved", val); + val = AFUD_READ(afu, 0x18); + show_reg("Reserved", val); + + val = AFUD_READ_CR(afu); + show_reg("Reserved", (val >> (63-7)) & 0xff); + show_reg("AFU_CR_len", AFUD_CR_LEN(val)); + + val = AFUD_READ_CR_OFF(afu); + show_reg("AFU_CR_offset", val); + + val = AFUD_READ_PPPSA(afu); + show_reg("PerProcessPSA_control", (val >> (63-7)) & 0xff); + show_reg("PerProcessPSA Length", AFUD_PPPSA_LEN(val)); + + val = AFUD_READ_PPPSA_OFF(afu); + show_reg("PerProcessPSA_offset", val); + + val = AFUD_READ_EB(afu); + show_reg("Reserved", (val >> (63-7)) & 0xff); + show_reg("AFU_EB_len", AFUD_EB_LEN(val)); + + val = AFUD_READ_EB_OFF(afu); + show_reg("AFU_EB_offset", val); + +#undef show_reg +} + +static int init_implementation_adapter_regs(struct cxl *adapter, struct pci_dev *dev) +{ + struct device_node *np; + const __be32 *prop; + u64 psl_dsnctl; + u64 chipid; + + if (!(np = pnv_pci_to_phb_node(dev))) + return -ENODEV; + + while (np && !(prop = of_get_property(np, "ibm,chip-id", NULL))) + np = of_get_next_parent(np); + if (!np) + return -ENODEV; + chipid = be32_to_cpup(prop); + of_node_put(np); + + /* Tell PSL where to route data to */ + psl_dsnctl = 0x02E8900002000000ULL | (chipid << (63-5)); + cxl_p1_write(adapter, CXL_PSL_DSNDCTL, psl_dsnctl); + cxl_p1_write(adapter, CXL_PSL_RESLCKTO, 0x20000000200ULL); + /* snoop write mask */ + cxl_p1_write(adapter, CXL_PSL_SNWRALLOC, 0x00000000FFFFFFFFULL); + /* set fir_accum */ + cxl_p1_write(adapter, CXL_PSL_FIR_CNTL, 0x0800000000000000ULL); + /* for debugging with trace arrays */ + cxl_p1_write(adapter, CXL_PSL_TRACE, 0x0000FF7C00000000ULL); + + return 0; +} + +static int init_implementation_afu_regs(struct cxl_afu *afu) +{ + /* read/write masks for this slice */ + cxl_p1n_write(afu, CXL_PSL_APCALLOC_A, 0xFFFFFFFEFEFEFEFEULL); + /* APC read/write masks for this slice */ + cxl_p1n_write(afu, CXL_PSL_COALLOC_A, 0xFF000000FEFEFEFEULL); + /* for debugging with trace arrays */ + cxl_p1n_write(afu, CXL_PSL_SLICE_TRACE, 0x0000FFFF00000000ULL); + cxl_p1n_write(afu, CXL_PSL_RXCTL_A, 0xF000000000000000ULL); + + return 0; +} + +int cxl_setup_irq(struct cxl *adapter, unsigned int hwirq, + unsigned int virq) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + + return pnv_cxl_ioda_msi_setup(dev, hwirq, virq); +} + +int cxl_alloc_one_irq(struct cxl *adapter) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + + return pnv_cxl_alloc_hwirqs(dev, 1); +} + +void cxl_release_one_irq(struct cxl *adapter, int hwirq) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + + return pnv_cxl_release_hwirqs(dev, hwirq, 1); +} + +int cxl_alloc_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter, unsigned int num) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + + return pnv_cxl_alloc_hwirq_ranges(irqs, dev, num); +} + +void cxl_release_irq_ranges(struct cxl_irq_ranges *irqs, struct cxl *adapter) +{ + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + + pnv_cxl_release_hwirq_ranges(irqs, dev); +} + +static int setup_cxl_bars(struct pci_dev *dev) +{ + /* Safety check in case we get backported to < 3.17 without M64 */ + if ((p1_base(dev) < 0x100000000ULL) || + (p2_base(dev) < 0x100000000ULL)) { + dev_err(&dev->dev, "ABORTING: M32 BAR assignment incompatible with CXL\n"); + return -ENODEV; + } + + /* + * BAR 4/5 has a special meaning for CXL and must be programmed with a + * special value corresponding to the CXL protocol address range. + * For POWER 8 that means bits 48:49 must be set to 10 + */ + pci_write_config_dword(dev, PCI_BASE_ADDRESS_4, 0x00000000); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, 0x00020000); + + return 0; +} + +/* pciex node: ibm,opal-m64-window = <0x3d058 0x0 0x3d058 0x0 0x8 0x0>; */ +static int switch_card_to_cxl(struct pci_dev *dev) +{ + int vsec; + u8 val; + int rc; + + dev_info(&dev->dev, "switch card to CXL\n"); + + if (!(vsec = find_cxl_vsec(dev))) { + dev_err(&dev->dev, "ABORTING: CXL VSEC not found!\n"); + return -ENODEV; + } + + if ((rc = CXL_READ_VSEC_MODE_CONTROL(dev, vsec, &val))) { + dev_err(&dev->dev, "failed to read current mode control: %i", rc); + return rc; + } + val &= ~CXL_VSEC_PROTOCOL_MASK; + val |= CXL_VSEC_PROTOCOL_256TB | CXL_VSEC_PROTOCOL_ENABLE; + if ((rc = CXL_WRITE_VSEC_MODE_CONTROL(dev, vsec, val))) { + dev_err(&dev->dev, "failed to enable CXL protocol: %i", rc); + return rc; + } + /* + * The CAIA spec (v0.12 11.6 Bi-modal Device Support) states + * we must wait 100ms after this mode switch before touching + * PCIe config space. + */ + msleep(100); + + return 0; +} + +static int cxl_map_slice_regs(struct cxl_afu *afu, struct cxl *adapter, struct pci_dev *dev) +{ + u64 p1n_base, p2n_base, afu_desc; + const u64 p1n_size = 0x100; + const u64 p2n_size = 0x1000; + + p1n_base = p1_base(dev) + 0x10000 + (afu->slice * p1n_size); + p2n_base = p2_base(dev) + (afu->slice * p2n_size); + afu->psn_phys = p2_base(dev) + (adapter->ps_off + (afu->slice * adapter->ps_size)); + afu_desc = p2_base(dev) + adapter->afu_desc_off + (afu->slice * adapter->afu_desc_size); + + if (!(afu->p1n_mmio = ioremap(p1n_base, p1n_size))) + goto err; + if (!(afu->p2n_mmio = ioremap(p2n_base, p2n_size))) + goto err1; + if (afu_desc) { + if (!(afu->afu_desc_mmio = ioremap(afu_desc, adapter->afu_desc_size))) + goto err2; + } + + return 0; +err2: + iounmap(afu->p2n_mmio); +err1: + iounmap(afu->p1n_mmio); +err: + dev_err(&afu->dev, "Error mapping AFU MMIO regions\n"); + return -ENOMEM; +} + +static void cxl_unmap_slice_regs(struct cxl_afu *afu) +{ + if (afu->p1n_mmio) + iounmap(afu->p2n_mmio); + if (afu->p1n_mmio) + iounmap(afu->p1n_mmio); +} + +static void cxl_release_afu(struct device *dev) +{ + struct cxl_afu *afu = to_cxl_afu(dev); + + pr_devel("cxl_release_afu\n"); + + kfree(afu); +} + +static struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int slice) +{ + struct cxl_afu *afu; + + if (!(afu = kzalloc(sizeof(struct cxl_afu), GFP_KERNEL))) + return NULL; + + afu->adapter = adapter; + afu->dev.parent = &adapter->dev; + afu->dev.release = cxl_release_afu; + afu->slice = slice; + idr_init(&afu->contexts_idr); + spin_lock_init(&afu->contexts_lock); + spin_lock_init(&afu->afu_cntl_lock); + mutex_init(&afu->spa_mutex); + + afu->prefault_mode = CXL_PREFAULT_NONE; + afu->irqs_max = afu->adapter->user_irqs; + + return afu; +} + +/* Expects AFU struct to have recently been zeroed out */ +static int cxl_read_afu_descriptor(struct cxl_afu *afu) +{ + u64 val; + + val = AFUD_READ_INFO(afu); + afu->pp_irqs = AFUD_NUM_INTS_PER_PROC(val); + afu->max_procs_virtualised = AFUD_NUM_PROCS(val); + + if (AFUD_AFU_DIRECTED(val)) + afu->modes_supported |= CXL_MODE_DIRECTED; + if (AFUD_DEDICATED_PROCESS(val)) + afu->modes_supported |= CXL_MODE_DEDICATED; + if (AFUD_TIME_SLICED(val)) + afu->modes_supported |= CXL_MODE_TIME_SLICED; + + val = AFUD_READ_PPPSA(afu); + afu->pp_size = AFUD_PPPSA_LEN(val) * 4096; + afu->psa = AFUD_PPPSA_PSA(val); + if ((afu->pp_psa = AFUD_PPPSA_PP(val))) + afu->pp_offset = AFUD_READ_PPPSA_OFF(afu); + + return 0; +} + +static int cxl_afu_descriptor_looks_ok(struct cxl_afu *afu) +{ + if (afu->psa && afu->adapter->ps_size < + (afu->pp_offset + afu->pp_size*afu->max_procs_virtualised)) { + dev_err(&afu->dev, "per-process PSA can't fit inside the PSA!\n"); + return -ENODEV; + } + + if (afu->pp_psa && (afu->pp_size < PAGE_SIZE)) + dev_warn(&afu->dev, "AFU uses < PAGE_SIZE per-process PSA!"); + + return 0; +} + +static int sanitise_afu_regs(struct cxl_afu *afu) +{ + u64 reg; + + /* + * Clear out any regs that contain either an IVTE or address or may be + * waiting on an acknowledgement to try to be a bit safer as we bring + * it online + */ + reg = cxl_p2n_read(afu, CXL_AFU_Cntl_An); + if ((reg & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) { + dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#.16llx\n", reg); + if (cxl_afu_reset(afu)) + return -EIO; + if (cxl_afu_disable(afu)) + return -EIO; + if (cxl_psl_purge(afu)) + return -EIO; + } + cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0x0000000000000000); + cxl_p1n_write(afu, CXL_PSL_IVTE_Limit_An, 0x0000000000000000); + cxl_p1n_write(afu, CXL_PSL_IVTE_Offset_An, 0x0000000000000000); + cxl_p1n_write(afu, CXL_PSL_AMBAR_An, 0x0000000000000000); + cxl_p1n_write(afu, CXL_PSL_SPOffset_An, 0x0000000000000000); + cxl_p1n_write(afu, CXL_HAURP_An, 0x0000000000000000); + cxl_p2n_write(afu, CXL_CSRP_An, 0x0000000000000000); + cxl_p2n_write(afu, CXL_AURP1_An, 0x0000000000000000); + cxl_p2n_write(afu, CXL_AURP0_An, 0x0000000000000000); + cxl_p2n_write(afu, CXL_SSTP1_An, 0x0000000000000000); + cxl_p2n_write(afu, CXL_SSTP0_An, 0x0000000000000000); + reg = cxl_p2n_read(afu, CXL_PSL_DSISR_An); + if (reg) { + dev_warn(&afu->dev, "AFU had pending DSISR: %#.16llx\n", reg); + if (reg & CXL_PSL_DSISR_TRANS) + cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE); + else + cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A); + } + reg = cxl_p1n_read(afu, CXL_PSL_SERR_An); + if (reg) { + if (reg & ~0xffff) + dev_warn(&afu->dev, "AFU had pending SERR: %#.16llx\n", reg); + cxl_p1n_write(afu, CXL_PSL_SERR_An, reg & ~0xffff); + } + reg = cxl_p2n_read(afu, CXL_PSL_ErrStat_An); + if (reg) { + dev_warn(&afu->dev, "AFU had pending error status: %#.16llx\n", reg); + cxl_p2n_write(afu, CXL_PSL_ErrStat_An, reg); + } + + return 0; +} + +static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) +{ + struct cxl_afu *afu; + bool free = true; + int rc; + + if (!(afu = cxl_alloc_afu(adapter, slice))) + return -ENOMEM; + + if ((rc = dev_set_name(&afu->dev, "afu%i.%i", adapter->adapter_num, slice))) + goto err1; + + if ((rc = cxl_map_slice_regs(afu, adapter, dev))) + goto err1; + + if ((rc = sanitise_afu_regs(afu))) + goto err2; + + /* We need to reset the AFU before we can read the AFU descriptor */ + if ((rc = cxl_afu_reset(afu))) + goto err2; + + if (cxl_verbose) + dump_afu_descriptor(afu); + + if ((rc = cxl_read_afu_descriptor(afu))) + goto err2; + + if ((rc = cxl_afu_descriptor_looks_ok(afu))) + goto err2; + + if ((rc = init_implementation_afu_regs(afu))) + goto err2; + + if ((rc = cxl_register_serr_irq(afu))) + goto err2; + + if ((rc = cxl_register_psl_irq(afu))) + goto err3; + + /* Don't care if this fails */ + cxl_debugfs_afu_add(afu); + + /* + * After we call this function we must not free the afu directly, even + * if it returns an error! + */ + if ((rc = cxl_register_afu(afu))) + goto err_put1; + + if ((rc = cxl_sysfs_afu_add(afu))) + goto err_put1; + + + if ((rc = cxl_afu_select_best_mode(afu))) + goto err_put2; + + adapter->afu[afu->slice] = afu; + + return 0; + +err_put2: + cxl_sysfs_afu_remove(afu); +err_put1: + device_unregister(&afu->dev); + free = false; + cxl_debugfs_afu_remove(afu); + cxl_release_psl_irq(afu); +err3: + cxl_release_serr_irq(afu); +err2: + cxl_unmap_slice_regs(afu); +err1: + if (free) + kfree(afu); + return rc; +} + +static void cxl_remove_afu(struct cxl_afu *afu) +{ + pr_devel("cxl_remove_afu\n"); + + if (!afu) + return; + + cxl_sysfs_afu_remove(afu); + cxl_debugfs_afu_remove(afu); + + spin_lock(&afu->adapter->afu_list_lock); + afu->adapter->afu[afu->slice] = NULL; + spin_unlock(&afu->adapter->afu_list_lock); + + cxl_context_detach_all(afu); + cxl_afu_deactivate_mode(afu); + + cxl_release_psl_irq(afu); + cxl_release_serr_irq(afu); + cxl_unmap_slice_regs(afu); + + device_unregister(&afu->dev); +} + + +static int cxl_map_adapter_regs(struct cxl *adapter, struct pci_dev *dev) +{ + if (pci_request_region(dev, 2, "priv 2 regs")) + goto err1; + if (pci_request_region(dev, 0, "priv 1 regs")) + goto err2; + + pr_devel("cxl_map_adapter_regs: p1: %#.16llx %#llx, p2: %#.16llx %#llx", + p1_base(dev), p1_size(dev), p2_base(dev), p2_size(dev)); + + if (!(adapter->p1_mmio = ioremap(p1_base(dev), p1_size(dev)))) + goto err3; + + if (!(adapter->p2_mmio = ioremap(p2_base(dev), p2_size(dev)))) + goto err4; + + return 0; + +err4: + iounmap(adapter->p1_mmio); + adapter->p1_mmio = NULL; +err3: + pci_release_region(dev, 0); +err2: + pci_release_region(dev, 2); +err1: + return -ENOMEM; +} + +static void cxl_unmap_adapter_regs(struct cxl *adapter) +{ + if (adapter->p1_mmio) + iounmap(adapter->p1_mmio); + if (adapter->p2_mmio) + iounmap(adapter->p2_mmio); +} + +static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev) +{ + int vsec; + u32 afu_desc_off, afu_desc_size; + u32 ps_off, ps_size; + u16 vseclen; + u8 image_state; + + if (!(vsec = find_cxl_vsec(dev))) { + dev_err(&adapter->dev, "ABORTING: CXL VSEC not found!\n"); + return -ENODEV; + } + + CXL_READ_VSEC_LENGTH(dev, vsec, &vseclen); + if (vseclen < CXL_VSEC_MIN_SIZE) { + pr_err("ABORTING: CXL VSEC too short\n"); + return -EINVAL; + } + + CXL_READ_VSEC_STATUS(dev, vsec, &adapter->vsec_status); + CXL_READ_VSEC_PSL_REVISION(dev, vsec, &adapter->psl_rev); + CXL_READ_VSEC_CAIA_MAJOR(dev, vsec, &adapter->caia_major); + CXL_READ_VSEC_CAIA_MINOR(dev, vsec, &adapter->caia_minor); + CXL_READ_VSEC_BASE_IMAGE(dev, vsec, &adapter->base_image); + CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state); + adapter->user_image_loaded = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); + adapter->perst_loads_image = !!(image_state & CXL_VSEC_PERST_LOADS_IMAGE); + adapter->perst_select_user = !!(image_state & CXL_VSEC_PERST_SELECT_USER); + + CXL_READ_VSEC_NAFUS(dev, vsec, &adapter->slices); + CXL_READ_VSEC_AFU_DESC_OFF(dev, vsec, &afu_desc_off); + CXL_READ_VSEC_AFU_DESC_SIZE(dev, vsec, &afu_desc_size); + CXL_READ_VSEC_PS_OFF(dev, vsec, &ps_off); + CXL_READ_VSEC_PS_SIZE(dev, vsec, &ps_size); + + /* Convert everything to bytes, because there is NO WAY I'd look at the + * code a month later and forget what units these are in ;-) */ + adapter->ps_off = ps_off * 64 * 1024; + adapter->ps_size = ps_size * 64 * 1024; + adapter->afu_desc_off = afu_desc_off * 64 * 1024; + adapter->afu_desc_size = afu_desc_size *64 * 1024; + + /* Total IRQs - 1 PSL ERROR - #AFU*(1 slice error + 1 DSI) */ + adapter->user_irqs = pnv_cxl_get_irq_count(dev) - 1 - 2*adapter->slices; + + return 0; +} + +static int cxl_vsec_looks_ok(struct cxl *adapter, struct pci_dev *dev) +{ + if (adapter->vsec_status & CXL_STATUS_SECOND_PORT) + return -EBUSY; + + if (adapter->vsec_status & CXL_UNSUPPORTED_FEATURES) { + dev_err(&adapter->dev, "ABORTING: CXL requires unsupported features\n"); + return -EINVAL; + } + + if (!adapter->slices) { + /* Once we support dynamic reprogramming we can use the card if + * it supports loadable AFUs */ + dev_err(&adapter->dev, "ABORTING: Device has no AFUs\n"); + return -EINVAL; + } + + if (!adapter->afu_desc_off || !adapter->afu_desc_size) { + dev_err(&adapter->dev, "ABORTING: VSEC shows no AFU descriptors\n"); + return -EINVAL; + } + + if (adapter->ps_size > p2_size(dev) - adapter->ps_off) { + dev_err(&adapter->dev, "ABORTING: Problem state size larger than " + "available in BAR2: 0x%llx > 0x%llx\n", + adapter->ps_size, p2_size(dev) - adapter->ps_off); + return -EINVAL; + } + + return 0; +} + +static void cxl_release_adapter(struct device *dev) +{ + struct cxl *adapter = to_cxl_adapter(dev); + + pr_devel("cxl_release_adapter\n"); + + kfree(adapter); +} + +static struct cxl *cxl_alloc_adapter(struct pci_dev *dev) +{ + struct cxl *adapter; + + if (!(adapter = kzalloc(sizeof(struct cxl), GFP_KERNEL))) + return NULL; + + adapter->dev.parent = &dev->dev; + adapter->dev.release = cxl_release_adapter; + pci_set_drvdata(dev, adapter); + spin_lock_init(&adapter->afu_list_lock); + + return adapter; +} + +static int sanitise_adapter_regs(struct cxl *adapter) +{ + cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000); + return cxl_tlb_slb_invalidate(adapter); +} + +static struct cxl *cxl_init_adapter(struct pci_dev *dev) +{ + struct cxl *adapter; + bool free = true; + int rc; + + + if (!(adapter = cxl_alloc_adapter(dev))) + return ERR_PTR(-ENOMEM); + + if ((rc = switch_card_to_cxl(dev))) + goto err1; + + if ((rc = cxl_alloc_adapter_nr(adapter))) + goto err1; + + if ((rc = dev_set_name(&adapter->dev, "card%i", adapter->adapter_num))) + goto err2; + + if ((rc = cxl_read_vsec(adapter, dev))) + goto err2; + + if ((rc = cxl_vsec_looks_ok(adapter, dev))) + goto err2; + + if ((rc = cxl_map_adapter_regs(adapter, dev))) + goto err2; + + if ((rc = sanitise_adapter_regs(adapter))) + goto err2; + + if ((rc = init_implementation_adapter_regs(adapter, dev))) + goto err3; + + if ((rc = pnv_phb_to_cxl(dev))) + goto err3; + + if ((rc = cxl_register_psl_err_irq(adapter))) + goto err3; + + /* Don't care if this one fails: */ + cxl_debugfs_adapter_add(adapter); + + /* + * After we call this function we must not free the adapter directly, + * even if it returns an error! + */ + if ((rc = cxl_register_adapter(adapter))) + goto err_put1; + + if ((rc = cxl_sysfs_adapter_add(adapter))) + goto err_put1; + + return adapter; + +err_put1: + device_unregister(&adapter->dev); + free = false; + cxl_debugfs_adapter_remove(adapter); + cxl_release_psl_err_irq(adapter); +err3: + cxl_unmap_adapter_regs(adapter); +err2: + cxl_remove_adapter_nr(adapter); +err1: + if (free) + kfree(adapter); + return ERR_PTR(rc); +} + +static void cxl_remove_adapter(struct cxl *adapter) +{ + struct pci_dev *pdev = to_pci_dev(adapter->dev.parent); + + pr_devel("cxl_release_adapter\n"); + + cxl_sysfs_adapter_remove(adapter); + cxl_debugfs_adapter_remove(adapter); + cxl_release_psl_err_irq(adapter); + cxl_unmap_adapter_regs(adapter); + cxl_remove_adapter_nr(adapter); + + device_unregister(&adapter->dev); + + pci_release_region(pdev, 0); + pci_release_region(pdev, 2); + pci_disable_device(pdev); +} + +static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct cxl *adapter; + int slice; + int rc; + + pci_dev_get(dev); + + if (cxl_verbose) + dump_cxl_config_space(dev); + + if ((rc = setup_cxl_bars(dev))) + return rc; + + if ((rc = pci_enable_device(dev))) { + dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc); + return rc; + } + + adapter = cxl_init_adapter(dev); + if (IS_ERR(adapter)) { + dev_err(&dev->dev, "cxl_init_adapter failed: %li\n", PTR_ERR(adapter)); + return PTR_ERR(adapter); + } + + for (slice = 0; slice < adapter->slices; slice++) { + if ((rc = cxl_init_afu(adapter, slice, dev))) + dev_err(&dev->dev, "AFU %i failed to initialise: %i\n", slice, rc); + } + + return 0; +} + +static void cxl_remove(struct pci_dev *dev) +{ + struct cxl *adapter = pci_get_drvdata(dev); + int afu; + + dev_warn(&dev->dev, "pci remove\n"); + + /* + * Lock to prevent someone grabbing a ref through the adapter list as + * we are removing it + */ + for (afu = 0; afu < adapter->slices; afu++) + cxl_remove_afu(adapter->afu[afu]); + cxl_remove_adapter(adapter); +} + +struct pci_driver cxl_pci_driver = { + .name = "cxl-pci", + .id_table = cxl_pci_tbl, + .probe = cxl_probe, + .remove = cxl_remove, +}; diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c new file mode 100644 index 000000000000..ce7ec06d87d1 --- /dev/null +++ b/drivers/misc/cxl/sysfs.c @@ -0,0 +1,385 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/sysfs.h> + +#include "cxl.h" + +#define to_afu_chardev_m(d) dev_get_drvdata(d) + +/********* Adapter attributes **********************************************/ + +static ssize_t caia_version_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl *adapter = to_cxl_adapter(device); + + return scnprintf(buf, PAGE_SIZE, "%i.%i\n", adapter->caia_major, + adapter->caia_minor); +} + +static ssize_t psl_revision_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl *adapter = to_cxl_adapter(device); + + return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_rev); +} + +static ssize_t base_image_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl *adapter = to_cxl_adapter(device); + + return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->base_image); +} + +static ssize_t image_loaded_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl *adapter = to_cxl_adapter(device); + + if (adapter->user_image_loaded) + return scnprintf(buf, PAGE_SIZE, "user\n"); + return scnprintf(buf, PAGE_SIZE, "factory\n"); +} + +static struct device_attribute adapter_attrs[] = { + __ATTR_RO(caia_version), + __ATTR_RO(psl_revision), + __ATTR_RO(base_image), + __ATTR_RO(image_loaded), +}; + + +/********* AFU master specific attributes **********************************/ + +static ssize_t mmio_size_show_master(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_afu_chardev_m(device); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); +} + +static ssize_t pp_mmio_off_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_afu_chardev_m(device); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_offset); +} + +static ssize_t pp_mmio_len_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_afu_chardev_m(device); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); +} + +static struct device_attribute afu_master_attrs[] = { + __ATTR(mmio_size, S_IRUGO, mmio_size_show_master, NULL), + __ATTR_RO(pp_mmio_off), + __ATTR_RO(pp_mmio_len), +}; + + +/********* AFU attributes **************************************************/ + +static ssize_t mmio_size_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_cxl_afu(device); + + if (afu->pp_size) + return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); + return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); +} + +static ssize_t reset_store_afu(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cxl_afu *afu = to_cxl_afu(device); + int rc; + + /* Not safe to reset if it is currently in use */ + spin_lock(&afu->contexts_lock); + if (!idr_is_empty(&afu->contexts_idr)) { + rc = -EBUSY; + goto err; + } + + if ((rc = cxl_afu_reset(afu))) + goto err; + + rc = count; +err: + spin_unlock(&afu->contexts_lock); + return rc; +} + +static ssize_t irqs_min_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_cxl_afu(device); + + return scnprintf(buf, PAGE_SIZE, "%i\n", afu->pp_irqs); +} + +static ssize_t irqs_max_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_cxl_afu(device); + + return scnprintf(buf, PAGE_SIZE, "%i\n", afu->irqs_max); +} + +static ssize_t irqs_max_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cxl_afu *afu = to_cxl_afu(device); + ssize_t ret; + int irqs_max; + + ret = sscanf(buf, "%i", &irqs_max); + if (ret != 1) + return -EINVAL; + + if (irqs_max < afu->pp_irqs) + return -EINVAL; + + if (irqs_max > afu->adapter->user_irqs) + return -EINVAL; + + afu->irqs_max = irqs_max; + return count; +} + +static ssize_t modes_supported_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct cxl_afu *afu = to_cxl_afu(device); + char *p = buf, *end = buf + PAGE_SIZE; + + if (afu->modes_supported & CXL_MODE_DEDICATED) + p += scnprintf(p, end - p, "dedicated_process\n"); + if (afu->modes_supported & CXL_MODE_DIRECTED) + p += scnprintf(p, end - p, "afu_directed\n"); + return (p - buf); +} + +static ssize_t prefault_mode_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_cxl_afu(device); + + switch (afu->prefault_mode) { + case CXL_PREFAULT_WED: + return scnprintf(buf, PAGE_SIZE, "work_element_descriptor\n"); + case CXL_PREFAULT_ALL: + return scnprintf(buf, PAGE_SIZE, "all\n"); + default: + return scnprintf(buf, PAGE_SIZE, "none\n"); + } +} + +static ssize_t prefault_mode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cxl_afu *afu = to_cxl_afu(device); + enum prefault_modes mode = -1; + + if (!strncmp(buf, "work_element_descriptor", 23)) + mode = CXL_PREFAULT_WED; + if (!strncmp(buf, "all", 3)) + mode = CXL_PREFAULT_ALL; + if (!strncmp(buf, "none", 4)) + mode = CXL_PREFAULT_NONE; + + if (mode == -1) + return -EINVAL; + + afu->prefault_mode = mode; + return count; +} + +static ssize_t mode_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl_afu *afu = to_cxl_afu(device); + + if (afu->current_mode == CXL_MODE_DEDICATED) + return scnprintf(buf, PAGE_SIZE, "dedicated_process\n"); + if (afu->current_mode == CXL_MODE_DIRECTED) + return scnprintf(buf, PAGE_SIZE, "afu_directed\n"); + return scnprintf(buf, PAGE_SIZE, "none\n"); +} + +static ssize_t mode_store(struct device *device, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cxl_afu *afu = to_cxl_afu(device); + int old_mode, mode = -1; + int rc = -EBUSY; + + /* can't change this if we have a user */ + spin_lock(&afu->contexts_lock); + if (!idr_is_empty(&afu->contexts_idr)) + goto err; + + if (!strncmp(buf, "dedicated_process", 17)) + mode = CXL_MODE_DEDICATED; + if (!strncmp(buf, "afu_directed", 12)) + mode = CXL_MODE_DIRECTED; + if (!strncmp(buf, "none", 4)) + mode = 0; + + if (mode == -1) { + rc = -EINVAL; + goto err; + } + + /* + * cxl_afu_deactivate_mode needs to be done outside the lock, prevent + * other contexts coming in before we are ready: + */ + old_mode = afu->current_mode; + afu->current_mode = 0; + afu->num_procs = 0; + + spin_unlock(&afu->contexts_lock); + + if ((rc = _cxl_afu_deactivate_mode(afu, old_mode))) + return rc; + if ((rc = cxl_afu_activate_mode(afu, mode))) + return rc; + + return count; +err: + spin_unlock(&afu->contexts_lock); + return rc; +} + +static ssize_t api_version_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION); +} + +static ssize_t api_version_compatible_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE); +} + +static struct device_attribute afu_attrs[] = { + __ATTR_RO(mmio_size), + __ATTR_RO(irqs_min), + __ATTR_RW(irqs_max), + __ATTR_RO(modes_supported), + __ATTR_RW(mode), + __ATTR_RW(prefault_mode), + __ATTR_RO(api_version), + __ATTR_RO(api_version_compatible), + __ATTR(reset, S_IWUSR, NULL, reset_store_afu), +}; + + + +int cxl_sysfs_adapter_add(struct cxl *adapter) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) { + if ((rc = device_create_file(&adapter->dev, &adapter_attrs[i]))) + goto err; + } + return 0; +err: + for (i--; i >= 0; i--) + device_remove_file(&adapter->dev, &adapter_attrs[i]); + return rc; +} +void cxl_sysfs_adapter_remove(struct cxl *adapter) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) + device_remove_file(&adapter->dev, &adapter_attrs[i]); +} + +int cxl_sysfs_afu_add(struct cxl_afu *afu) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { + if ((rc = device_create_file(&afu->dev, &afu_attrs[i]))) + goto err; + } + + return 0; + +err: + for (i--; i >= 0; i--) + device_remove_file(&afu->dev, &afu_attrs[i]); + return rc; +} + +void cxl_sysfs_afu_remove(struct cxl_afu *afu) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) + device_remove_file(&afu->dev, &afu_attrs[i]); +} + +int cxl_sysfs_afu_m_add(struct cxl_afu *afu) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) { + if ((rc = device_create_file(afu->chardev_m, &afu_master_attrs[i]))) + goto err; + } + + return 0; + +err: + for (i--; i >= 0; i--) + device_remove_file(afu->chardev_m, &afu_master_attrs[i]); + return rc; +} + +void cxl_sysfs_afu_m_remove(struct cxl_afu *afu) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) + device_remove_file(afu->chardev_m, &afu_master_attrs[i]); +} diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 33f8673d23a6..b432873def96 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -18,7 +18,7 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/slab.h> +#include <linux/device.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/mutex.h> @@ -159,12 +159,11 @@ static int eeprom_probe(struct i2c_client *client, { struct i2c_adapter *adapter = client->adapter; struct eeprom_data *data; - int err; - if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } + data = devm_kzalloc(&client->dev, sizeof(struct eeprom_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; memset(data->data, 0xff, EEPROM_SIZE); i2c_set_clientdata(client, data); @@ -190,22 +189,12 @@ static int eeprom_probe(struct i2c_client *client, } /* create the sysfs eeprom file */ - err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); - if (err) - goto exit_kfree; - - return 0; - -exit_kfree: - kfree(data); -exit: - return err; + return sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); } static int eeprom_remove(struct i2c_client *client) { sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); - kfree(i2c_get_clientdata(client)); return 0; } diff --git a/drivers/misc/fuse/Makefile b/drivers/misc/fuse/Makefile new file mode 100644 index 000000000000..0679c4febc89 --- /dev/null +++ b/drivers/misc/fuse/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 43bbabc96b6c..4cf8f82cfca2 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -45,10 +45,10 @@ MODULE_AUTHOR("Frank Haverkamp <haver@linux.vnet.ibm.com>"); MODULE_AUTHOR("Michael Ruettger <michael@ibmra.de>"); MODULE_AUTHOR("Joerg-Stephan Vogt <jsvogt@de.ibm.com>"); -MODULE_AUTHOR("Michal Jung <mijung@de.ibm.com>"); +MODULE_AUTHOR("Michael Jung <mijung@gmx.net>"); MODULE_DESCRIPTION("GenWQE Card"); -MODULE_VERSION(DRV_VERS_STRING); +MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); static char genwqe_driver_name[] = GENWQE_DEVNAME; @@ -346,8 +346,13 @@ static bool genwqe_setup_vf_jtimer(struct genwqe_dev *cd) unsigned int vf; u32 T = genwqe_T_psec(cd); u64 x; + int totalvfs; - for (vf = 0; vf < pci_sriov_get_totalvfs(pci_dev); vf++) { + totalvfs = pci_sriov_get_totalvfs(pci_dev); + if (totalvfs <= 0) + return false; + + for (vf = 0; vf < totalvfs; vf++) { if (cd->vf_jobtimeout_msec[vf] == 0) continue; @@ -383,8 +388,9 @@ static int genwqe_ffdc_buffs_alloc(struct genwqe_dev *cd) /* currently support only the debug units mentioned here */ cd->ffdc[type].entries = e; - cd->ffdc[type].regs = kmalloc(e * sizeof(struct genwqe_reg), - GFP_KERNEL); + cd->ffdc[type].regs = + kmalloc_array(e, sizeof(struct genwqe_reg), + GFP_KERNEL); /* * regs == NULL is ok, the using code treats this as no regs, * Printing warning is ok in this case. @@ -723,8 +729,8 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) __genwqe_writeq(cd, sfir_addr, sfir); dev_dbg(&pci_dev->dev, - "[HM] Clearing 2ndary FIR 0x%08x " - "with 0x%016llx\n", sfir_addr, sfir); + "[HM] Clearing 2ndary FIR 0x%08x with 0x%016llx\n", + sfir_addr, sfir); /* * note, these cannot be error-Firs @@ -740,9 +746,8 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) __genwqe_writeq(cd, fir_clr_addr, mask); dev_dbg(&pci_dev->dev, - "[HM] Clearing primary FIR 0x%08x " - "with 0x%016llx\n", fir_clr_addr, - mask); + "[HM] Clearing primary FIR 0x%08x with 0x%016llx\n", + fir_clr_addr, mask); } } } @@ -1125,6 +1130,8 @@ static int genwqe_pci_setup(struct genwqe_dev *cd) } cd->num_vfs = pci_sriov_get_totalvfs(pci_dev); + if (cd->num_vfs < 0) + cd->num_vfs = 0; err = genwqe_read_ids(cd); if (err) @@ -1202,8 +1209,8 @@ static int genwqe_probe(struct pci_dev *pci_dev, err = genwqe_health_check_start(cd); if (err < 0) { dev_err(&pci_dev->dev, - "err: cannot start health checking! " - "(err=%d)\n", err); + "err: cannot start health checking! (err=%d)\n", + err); goto out_stop_services; } } @@ -1313,11 +1320,14 @@ static void genwqe_err_resume(struct pci_dev *pci_dev) static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) { + int rc; struct genwqe_dev *cd = dev_get_drvdata(&dev->dev); if (numvfs > 0) { genwqe_setup_vf_jtimer(cd); - pci_enable_sriov(dev, numvfs); + rc = pci_enable_sriov(dev, numvfs); + if (rc < 0) + return rc; return numvfs; } if (numvfs == 0) { diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h index 67abd8cb2247..c64d7cad1085 100644 --- a/drivers/misc/genwqe/card_base.h +++ b/drivers/misc/genwqe/card_base.h @@ -8,7 +8,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -201,7 +201,8 @@ static inline void genwqe_mapping_init(struct dma_mapping *m, * @ddcb_seq: Sequence number of last DDCB * @ddcbs_in_flight: Currently enqueued DDCBs * @ddcbs_completed: Number of already completed DDCBs - * @busy: Number of -EBUSY returns + * @return_on_busy: Number of -EBUSY returns on full queue + * @wait_on_busy: Number of waits on full queue * @ddcb_daddr: DMA address of first DDCB in the queue * @ddcb_vaddr: Kernel virtual address of first DDCB in the queue * @ddcb_req: Associated requests (one per DDCB) @@ -218,7 +219,8 @@ struct ddcb_queue { unsigned int ddcbs_in_flight; /* number of ddcbs in processing */ unsigned int ddcbs_completed; unsigned int ddcbs_max_in_flight; - unsigned int busy; /* how many times -EBUSY? */ + unsigned int return_on_busy; /* how many times -EBUSY? */ + unsigned int wait_on_busy; dma_addr_t ddcb_daddr; /* DMA address */ struct ddcb *ddcb_vaddr; /* kernel virtual addr for DDCBs */ @@ -226,7 +228,7 @@ struct ddcb_queue { wait_queue_head_t *ddcb_waitqs; /* waitqueue per ddcb */ spinlock_t ddcb_lock; /* exclusive access to queue */ - wait_queue_head_t ddcb_waitq; /* wait for ddcb processing */ + wait_queue_head_t busy_waitq; /* wait for ddcb processing */ /* registers or the respective queue to be used */ u32 IO_QUEUE_CONFIG; @@ -306,7 +308,7 @@ struct genwqe_dev { struct pci_dev *pci_dev; /* PCI device */ void __iomem *mmio; /* BAR-0 MMIO start */ unsigned long mmio_len; - u16 num_vfs; + int num_vfs; u32 vf_jobtimeout_msec[GENWQE_MAX_VFS]; int is_privileged; /* access to all regs possible */ @@ -508,7 +510,7 @@ static inline bool dma_mapping_used(struct dma_mapping *m) * buildup and teardown. */ int __genwqe_execute_ddcb(struct genwqe_dev *cd, - struct genwqe_ddcb_cmd *cmd); + struct genwqe_ddcb_cmd *cmd, unsigned int f_flags); /** * __genwqe_execute_raw_ddcb() - Execute DDCB request without addr translation @@ -520,9 +522,12 @@ int __genwqe_execute_ddcb(struct genwqe_dev *cd, * modification. */ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, - struct genwqe_ddcb_cmd *cmd); + struct genwqe_ddcb_cmd *cmd, + unsigned int f_flags); +int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, + struct ddcb_requ *req, + unsigned int f_flags); -int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); int __genwqe_wait_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); int __genwqe_purge_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req); diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c index dc9851a5540e..6d51e5f08664 100644 --- a/drivers/misc/genwqe/card_ddcb.c +++ b/drivers/misc/genwqe/card_ddcb.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -185,8 +185,7 @@ static void print_ddcb_info(struct genwqe_dev *cd, struct ddcb_queue *queue) pddcb = queue->ddcb_vaddr; for (i = 0; i < queue->ddcb_max; i++) { dev_err(&pci_dev->dev, - " %c %-3d: RETC=%03x SEQ=%04x " - "HSI=%02X SHI=%02x PRIV=%06llx CMD=%03x\n", + " %c %-3d: RETC=%03x SEQ=%04x HSI=%02X SHI=%02x PRIV=%06llx CMD=%03x\n", i == queue->ddcb_act ? '>' : ' ', i, be16_to_cpu(pddcb->retc_16), @@ -214,6 +213,7 @@ struct genwqe_ddcb_cmd *ddcb_requ_alloc(void) void ddcb_requ_free(struct genwqe_ddcb_cmd *cmd) { struct ddcb_requ *req = container_of(cmd, struct ddcb_requ, cmd); + kfree(req); } @@ -306,7 +306,7 @@ static int enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_queue *queue, new = (old | DDCB_NEXT_BE32); - wmb(); + wmb(); /* need to ensure write ordering */ icrc_hsi_shi = cmpxchg(&prev_ddcb->icrc_hsi_shi_32, old, new); if (icrc_hsi_shi == old) @@ -317,7 +317,7 @@ static int enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_queue *queue, ddcb_mark_tapped(pddcb); num = (u64)ddcb_no << 8; - wmb(); + wmb(); /* need to ensure write ordering */ __genwqe_writeq(cd, queue->IO_QUEUE_OFFSET, num); /* start queue */ return RET_DDCB_TAPPED; @@ -390,8 +390,9 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, 0x00000000) goto go_home; /* not completed, continue waiting */ - /* Note: DDCB could be purged */ + wmb(); /* Add sync to decouple prev. read operations */ + /* Note: DDCB could be purged */ req = queue->ddcb_req[queue->ddcb_act]; if (req == NULL) { /* this occurs if DDCB is purged, not an error */ @@ -416,9 +417,7 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, status = __genwqe_readq(cd, queue->IO_QUEUE_STATUS); dev_err(&pci_dev->dev, - "[%s] SEQN=%04x HSI=%02x RETC=%03x " - " Q_ERRCNTS=%016llx Q_STATUS=%016llx\n" - " DDCB_DMA_ADDR=%016llx\n", + "[%s] SEQN=%04x HSI=%02x RETC=%03x Q_ERRCNTS=%016llx Q_STATUS=%016llx DDCB_DMA_ADDR=%016llx\n", __func__, be16_to_cpu(pddcb->seqnum_16), pddcb->hsi, retc_16, errcnts, status, queue->ddcb_daddr + ddcb_offs); @@ -439,8 +438,7 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, vcrc_16 = be16_to_cpu(pddcb->vcrc_16); if (vcrc != vcrc_16) { printk_ratelimited(KERN_ERR - "%s %s: err: wrong VCRC pre=%02x vcrc_len=%d " - "bytes vcrc_data=%04x is not vcrc_card=%04x\n", + "%s %s: err: wrong VCRC pre=%02x vcrc_len=%d bytes vcrc_data=%04x is not vcrc_card=%04x\n", GENWQE_DEVNAME, dev_name(&pci_dev->dev), pddcb->pre, VCRC_LENGTH(req->cmd.asv_length), vcrc, vcrc_16); @@ -450,8 +448,10 @@ static int genwqe_check_ddcb_queue(struct genwqe_dev *cd, queue->ddcbs_completed++; queue->ddcbs_in_flight--; - /* wake up process waiting for this DDCB */ + /* wake up process waiting for this DDCB, and + processes on the busy queue */ wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]); + wake_up_interruptible(&queue->busy_waitq); pick_next_one: queue->ddcb_act = (queue->ddcb_act + 1) % queue->ddcb_max; @@ -717,8 +717,7 @@ go_home: genwqe_hexdump(pci_dev, pddcb, sizeof(*pddcb)); dev_err(&pci_dev->dev, - "[%s] err: DDCB#%d not purged and not completed " - "after %d seconds QSTAT=%016llx!!\n", + "[%s] err: DDCB#%d not purged and not completed after %d seconds QSTAT=%016llx!!\n", __func__, req->num, genwqe_ddcb_software_timeout, queue_status); @@ -740,7 +739,7 @@ int genwqe_init_debug_data(struct genwqe_dev *cd, struct genwqe_debug_data *d) } len = sizeof(d->driver_version); - snprintf(d->driver_version, len, "%s", DRV_VERS_STRING); + snprintf(d->driver_version, len, "%s", DRV_VERSION); d->slu_unitcfg = cd->slu_unitcfg; d->app_unitcfg = cd->app_unitcfg; return 0; @@ -748,14 +747,16 @@ int genwqe_init_debug_data(struct genwqe_dev *cd, struct genwqe_debug_data *d) /** * __genwqe_enqueue_ddcb() - Enqueue a DDCB - * @cd: pointer to genwqe device descriptor - * @req: pointer to DDCB execution request + * @cd: pointer to genwqe device descriptor + * @req: pointer to DDCB execution request + * @f_flags: file mode: blocking, non-blocking * * Return: 0 if enqueuing succeeded * -EIO if card is unusable/PCIe problems * -EBUSY if enqueuing failed */ -int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) +int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req, + unsigned int f_flags) { struct ddcb *pddcb; unsigned long flags; @@ -763,6 +764,7 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) struct pci_dev *pci_dev = cd->pci_dev; u16 icrc; + retry: if (cd->card_state != GENWQE_CARD_USED) { printk_ratelimited(KERN_ERR "%s %s: [%s] Card is unusable/PCIe problem Req#%d\n", @@ -788,9 +790,24 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) pddcb = get_next_ddcb(cd, queue, &req->num); /* get ptr and num */ if (pddcb == NULL) { + int rc; + spin_unlock_irqrestore(&queue->ddcb_lock, flags); - queue->busy++; - return -EBUSY; + + if (f_flags & O_NONBLOCK) { + queue->return_on_busy++; + return -EBUSY; + } + + queue->wait_on_busy++; + rc = wait_event_interruptible(queue->busy_waitq, + queue_free_ddcbs(queue) != 0); + dev_dbg(&pci_dev->dev, "[%s] waiting for free DDCB: rc=%d\n", + __func__, rc); + if (rc == -ERESTARTSYS) + return rc; /* interrupted by a signal */ + + goto retry; } if (queue->ddcb_req[req->num] != NULL) { @@ -893,9 +910,11 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) * __genwqe_execute_raw_ddcb() - Setup and execute DDCB * @cd: pointer to genwqe device descriptor * @req: user provided DDCB request + * @f_flags: file mode: blocking, non-blocking */ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, - struct genwqe_ddcb_cmd *cmd) + struct genwqe_ddcb_cmd *cmd, + unsigned int f_flags) { int rc = 0; struct pci_dev *pci_dev = cd->pci_dev; @@ -911,7 +930,7 @@ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, __func__, cmd->asiv_length); return -EINVAL; } - rc = __genwqe_enqueue_ddcb(cd, req); + rc = __genwqe_enqueue_ddcb(cd, req, f_flags); if (rc != 0) return rc; @@ -1017,7 +1036,8 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) queue->ddcbs_in_flight = 0; /* statistics */ queue->ddcbs_max_in_flight = 0; queue->ddcbs_completed = 0; - queue->busy = 0; + queue->return_on_busy = 0; + queue->wait_on_busy = 0; queue->ddcb_seq = 0x100; /* start sequence number */ queue->ddcb_max = genwqe_ddcb_max; /* module parameter */ @@ -1057,7 +1077,7 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue) queue->ddcb_next = 0; /* queue is empty */ spin_lock_init(&queue->ddcb_lock); - init_waitqueue_head(&queue->ddcb_waitq); + init_waitqueue_head(&queue->busy_waitq); val64 = ((u64)(queue->ddcb_max - 1) << 8); /* lastptr */ __genwqe_writeq(cd, queue->IO_QUEUE_CONFIG, 0x07); /* iCRC/vCRC */ @@ -1251,10 +1271,8 @@ int genwqe_setup_service_layer(struct genwqe_dev *cd) } rc = genwqe_set_interrupt_capability(cd, GENWQE_MSI_IRQS); - if (rc) { - rc = -ENODEV; + if (rc) goto stop_kthread; - } /* * We must have all wait-queues initialized when we enable the @@ -1307,6 +1325,7 @@ static int queue_wake_up_all(struct genwqe_dev *cd) for (i = 0; i < queue->ddcb_max; i++) wake_up_interruptible(&queue->ddcb_waitqs[queue->ddcb_act]); + wake_up_interruptible(&queue->busy_waitq); spin_unlock_irqrestore(&queue->ddcb_lock, flags); return 0; @@ -1346,8 +1365,8 @@ int genwqe_finish_queue(struct genwqe_dev *cd) break; dev_dbg(&pci_dev->dev, - " DEBUG [%d/%d] waiting for queue to get empty: " - "%d requests!\n", i, waitmax, in_flight); + " DEBUG [%d/%d] waiting for queue to get empty: %d requests!\n", + i, waitmax, in_flight); /* * Severe severe error situation: The card itself has diff --git a/drivers/misc/genwqe/card_ddcb.h b/drivers/misc/genwqe/card_ddcb.h index c4f26720753e..0361a68d79a6 100644 --- a/drivers/misc/genwqe/card_ddcb.h +++ b/drivers/misc/genwqe/card_ddcb.h @@ -8,7 +8,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/misc/genwqe/card_debugfs.c b/drivers/misc/genwqe/card_debugfs.c index c9b4d6d0eb99..c715534e7fe7 100644 --- a/drivers/misc/genwqe/card_debugfs.c +++ b/drivers/misc/genwqe/card_debugfs.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -244,14 +244,16 @@ static int genwqe_ddcb_info_show(struct seq_file *s, void *unused) " ddcbs_in_flight: %u\n" " ddcbs_max_in_flight: %u\n" " ddcbs_completed: %u\n" - " busy: %u\n" + " return_on_busy: %u\n" + " wait_on_busy: %u\n" " irqs_processed: %u\n", queue->ddcb_max, (long long)queue->ddcb_daddr, (long long)queue->ddcb_daddr + (queue->ddcb_max * DDCB_LENGTH), (long long)queue->ddcb_vaddr, queue->ddcbs_in_flight, queue->ddcbs_max_in_flight, queue->ddcbs_completed, - queue->busy, cd->irqs_processed); + queue->return_on_busy, queue->wait_on_busy, + cd->irqs_processed); /* Hardware State */ seq_printf(s, " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n" @@ -323,7 +325,7 @@ static int genwqe_info_show(struct seq_file *s, void *unused) " Base Clock : %u MHz\n" " Arch/SVN Release: %u/%llx\n" " Bitstream : %llx\n", - GENWQE_DEVNAME, DRV_VERS_STRING, dev_name(&pci_dev->dev), + GENWQE_DEVNAME, DRV_VERSION, dev_name(&pci_dev->dev), genwqe_is_privileged(cd) ? "Physical" : "Virtual or no SR-IOV", cd->card_idx, slu_id, app_id, diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index aae42555e2ca..5918586f2f76 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -213,9 +213,9 @@ static void genwqe_remove_mappings(struct genwqe_file *cfile) * GENWQE_MAPPING_SGL_TEMP should be removed by tidy up code. */ dev_err(&pci_dev->dev, - "[%s] %d. cleanup mapping: u_vaddr=%p " - "u_kaddr=%016lx dma_addr=%lx\n", __func__, i++, - dma_map->u_vaddr, (unsigned long)dma_map->k_vaddr, + "[%s] %d. cleanup mapping: u_vaddr=%p u_kaddr=%016lx dma_addr=%lx\n", + __func__, i++, dma_map->u_vaddr, + (unsigned long)dma_map->k_vaddr, (unsigned long)dma_map->dma_addr); if (dma_map->type == GENWQE_MAPPING_RAW) { @@ -346,6 +346,7 @@ static int genwqe_open(struct inode *inode, struct file *filp) static int genwqe_fasync(int fd, struct file *filp, int mode) { struct genwqe_file *cdev = (struct genwqe_file *)filp->private_data; + return fasync_helper(fd, filp, mode, &cdev->async_queue); } @@ -515,6 +516,7 @@ static int do_flash_update(struct genwqe_file *cfile, u32 crc; u8 cmdopts; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; struct pci_dev *pci_dev = cd->pci_dev; if ((load->size & 0x3) != 0) @@ -609,7 +611,7 @@ static int do_flash_update(struct genwqe_file *cfile, /* For Genwqe5 we get back the calculated CRC */ *(u64 *)&req->asv[0] = 0ULL; /* 0x80 */ - rc = __genwqe_execute_raw_ddcb(cd, req); + rc = __genwqe_execute_raw_ddcb(cd, req, filp->f_flags); load->retc = req->retc; load->attn = req->attn; @@ -649,6 +651,7 @@ static int do_flash_read(struct genwqe_file *cfile, u8 *xbuf; u8 cmdopts; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; struct pci_dev *pci_dev = cd->pci_dev; struct genwqe_ddcb_cmd *cmd; @@ -726,7 +729,7 @@ static int do_flash_read(struct genwqe_file *cfile, /* we only get back the calculated CRC */ *(u64 *)&cmd->asv[0] = 0ULL; /* 0x80 */ - rc = __genwqe_execute_raw_ddcb(cd, cmd); + rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags); load->retc = cmd->retc; load->attn = cmd->attn; @@ -987,13 +990,14 @@ static int genwqe_execute_ddcb(struct genwqe_file *cfile, { int rc; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; struct ddcb_requ *req = container_of(cmd, struct ddcb_requ, cmd); rc = ddcb_cmd_fixups(cfile, req); if (rc != 0) return rc; - rc = __genwqe_execute_raw_ddcb(cd, cmd); + rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags); ddcb_cmd_cleanup(cfile, req); return rc; } @@ -1005,6 +1009,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile, struct genwqe_ddcb_cmd *cmd; struct ddcb_requ *req; struct genwqe_dev *cd = cfile->cd; + struct file *filp = cfile->filp; cmd = ddcb_requ_alloc(); if (cmd == NULL) @@ -1020,7 +1025,7 @@ static int do_execute_ddcb(struct genwqe_file *cfile, if (!raw) rc = genwqe_execute_ddcb(cfile, cmd); else - rc = __genwqe_execute_raw_ddcb(cd, cmd); + rc = __genwqe_execute_raw_ddcb(cd, cmd, filp->f_flags); /* Copy back only the modifed fields. Do not copy ASIV back since the copy got modified by the driver. */ diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c index 7232e40a3ad9..2c33fbca9225 100644 --- a/drivers/misc/genwqe/card_sysfs.c +++ b/drivers/misc/genwqe/card_sysfs.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -91,13 +91,6 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(type); -static ssize_t driver_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%s\n", DRV_VERS_STRING); -} -static DEVICE_ATTR_RO(driver); - static ssize_t tempsens_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -256,7 +249,6 @@ static struct attribute *genwqe_attributes[] = { &dev_attr_next_bitstream.attr, &dev_attr_curr_bitstream.attr, &dev_attr_base_clock.attr, - &dev_attr_driver.attr, &dev_attr_type.attr, &dev_attr_version.attr, &dev_attr_appid.attr, @@ -268,7 +260,6 @@ static struct attribute *genwqe_attributes[] = { }; static struct attribute *genwqe_normal_attributes[] = { - &dev_attr_driver.attr, &dev_attr_type.attr, &dev_attr_version.attr, &dev_attr_appid.attr, diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index a6400f09229c..7cb3b7e41739 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -5,7 +5,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -150,6 +150,7 @@ int genwqe_read_app_id(struct genwqe_dev *cd, char *app_name, int len) memset(app_name, 0, len); for (i = 0, j = 0; j < min(len, 4); j++) { char ch = (char)((app_id >> (24 - j*8)) & 0xff); + if (ch == ' ') continue; app_name[i++] = isprint(ch) ? ch : 'X'; @@ -304,8 +305,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, sgl->nr_pages = DIV_ROUND_UP(sgl->fpage_offs + user_size, PAGE_SIZE); sgl->lpage_size = (user_size - sgl->fpage_size) % PAGE_SIZE; - dev_dbg(&pci_dev->dev, "[%s] uaddr=%p usize=%8ld nr_pages=%ld " - "fpage_offs=%lx fpage_size=%ld lpage_size=%ld\n", + dev_dbg(&pci_dev->dev, "[%s] uaddr=%p usize=%8ld nr_pages=%ld fpage_offs=%lx fpage_size=%ld lpage_size=%ld\n", __func__, user_addr, user_size, sgl->nr_pages, sgl->fpage_offs, sgl->fpage_size, sgl->lpage_size); @@ -662,6 +662,7 @@ int genwqe_user_vunmap(struct genwqe_dev *cd, struct dma_mapping *m, u8 genwqe_card_type(struct genwqe_dev *cd) { u64 card_type = cd->slu_unitcfg; + return (u8)((card_type & IO_SLU_UNITCFG_TYPE_MASK) >> 20); } diff --git a/drivers/misc/genwqe/genwqe_driver.h b/drivers/misc/genwqe/genwqe_driver.h index a506e9aa2d57..15355350e076 100644 --- a/drivers/misc/genwqe/genwqe_driver.h +++ b/drivers/misc/genwqe/genwqe_driver.h @@ -8,7 +8,7 @@ * * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> - * Author: Michael Jung <mijung@de.ibm.com> + * Author: Michael Jung <mijung@gmx.net> * Author: Michael Ruettger <michael@ibmra.de> * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include <asm/byteorder.h> #include <linux/genwqe/genwqe_card.h> -#define DRV_VERS_STRING "2.0.21" +#define DRV_VERSION "2.0.25" /* * Static minor number assignement, until we decide/implement diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 7ffdb589841e..c544f1f50f52 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -79,6 +79,11 @@ static void firmware_load(const struct firmware *fw, void *context) u32 jedec_id; u32 status; + if (fw == NULL) { + dev_err(&spi->dev, "Cannot load firmware, aborting\n"); + return; + } + if (fw->size == 0) { dev_err(&spi->dev, "Error: Firmware size is 0!\n"); return; @@ -242,3 +247,4 @@ module_spi_driver(lattice_ecp3_driver); MODULE_AUTHOR("Stefan Roese <sr@denx.de>"); MODULE_DESCRIPTION("Lattice ECP3 FPGA configuration via SPI"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_NAME); diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 0d6234db00fa..6cdce8477f57 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -20,7 +20,6 @@ #include <linux/types.h> #include <linux/fcntl.h> #include <linux/aio.h> -#include <linux/pci.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/list.h> @@ -29,6 +28,7 @@ #include <linux/uuid.h> #include <linux/jiffies.h> #include <linux/uaccess.h> +#include <linux/slab.h> #include <linux/mei.h> @@ -64,31 +64,32 @@ void mei_amthif_reset_params(struct mei_device *dev) * * @dev: the device structure * + * Return: 0 on success, <0 on failure. */ int mei_amthif_host_init(struct mei_device *dev) { struct mei_cl *cl = &dev->iamthif_cl; + struct mei_me_client *me_cl; unsigned char *msg_buf; - int ret, i; + int ret; dev->iamthif_state = MEI_IAMTHIF_IDLE; mei_cl_init(cl, dev); - i = mei_me_cl_by_uuid(dev, &mei_amthif_guid); - if (i < 0) { - dev_info(&dev->pdev->dev, - "amthif: failed to find the client %d\n", i); + me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); + if (!me_cl) { + dev_info(dev->dev, "amthif: failed to find the client"); return -ENOTTY; } - cl->me_client_id = dev->me_clients[i].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; /* Assign iamthif_mtu to the value received from ME */ - dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length; - dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n", - dev->me_clients[i].props.max_msg_length); + dev->iamthif_mtu = me_cl->props.max_msg_length; + dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu); kfree(dev->iamthif_msg_buf); dev->iamthif_msg_buf = NULL; @@ -96,17 +97,15 @@ int mei_amthif_host_init(struct mei_device *dev) /* allocate storage for ME message buffer */ msg_buf = kcalloc(dev->iamthif_mtu, sizeof(unsigned char), GFP_KERNEL); - if (!msg_buf) { - dev_err(&dev->pdev->dev, "amthif: memory allocation for ME message buffer failed.\n"); + if (!msg_buf) return -ENOMEM; - } dev->iamthif_msg_buf = msg_buf; ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); if (ret < 0) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "amthif: failed link client %d\n", ret); return ret; } @@ -124,18 +123,16 @@ int mei_amthif_host_init(struct mei_device *dev) * @dev: the device structure * @file: pointer to file object * - * returns returned a list entry on success, NULL on failure. + * Return: returned a list entry on success, NULL on failure. */ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, struct file *file) { struct mei_cl_cb *cb; - list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) { - if (cb->cl && cb->cl == &dev->iamthif_cl && - cb->file_object == file) + list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) + if (cb->file_object == file) return cb; - } return NULL; } @@ -144,15 +141,14 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, * mei_amthif_read - read data from AMTHIF client * * @dev: the device structure - * @if_num: minor number * @file: pointer to file object - * @*ubuf: pointer to user data in user space + * @ubuf: pointer to user data in user space * @length: data length to read * @offset: data read offset * * Locking: called under "dev->device_lock" lock * - * returns + * Return: * returned data length on success, * zero if no data to read, * negative on failure. @@ -160,25 +156,19 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, int mei_amthif_read(struct mei_device *dev, struct file *file, char __user *ubuf, size_t length, loff_t *offset) { - int rets; - int wait_ret; - struct mei_cl_cb *cb = NULL; struct mei_cl *cl = file->private_data; + struct mei_cl_cb *cb; unsigned long timeout; - int i; + int rets; + int wait_ret; /* Only possible if we are in timeout */ - if (!cl || cl != &dev->iamthif_cl) { - dev_dbg(&dev->pdev->dev, "bad file ext.\n"); + if (!cl) { + dev_err(dev->dev, "bad file ext.\n"); return -ETIME; } - i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id); - if (i < 0) { - dev_dbg(&dev->pdev->dev, "amthif client not found.\n"); - return -ENOTTY; - } - dev_dbg(&dev->pdev->dev, "checking amthif data\n"); + dev_dbg(dev->dev, "checking amthif data\n"); cb = mei_amthif_find_read_list_entry(dev, file); /* Check for if we can block or not*/ @@ -186,7 +176,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, return -EAGAIN; - dev_dbg(&dev->pdev->dev, "waiting for amthif data\n"); + dev_dbg(dev->dev, "waiting for amthif data\n"); while (cb == NULL) { /* unlock the Mutex */ mutex_unlock(&dev->device_lock); @@ -200,21 +190,21 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, if (wait_ret) return -ERESTARTSYS; - dev_dbg(&dev->pdev->dev, "woke up from sleep\n"); + dev_dbg(dev->dev, "woke up from sleep\n"); } - dev_dbg(&dev->pdev->dev, "Got amthif data\n"); + dev_dbg(dev->dev, "Got amthif data\n"); dev->iamthif_timer = 0; if (cb) { timeout = cb->read_time + mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - dev_dbg(&dev->pdev->dev, "amthif timeout = %lud\n", + dev_dbg(dev->dev, "amthif timeout = %lud\n", timeout); if (time_after(jiffies, timeout)) { - dev_dbg(&dev->pdev->dev, "amthif Time out\n"); + dev_dbg(dev->dev, "amthif Time out\n"); /* 15 sec for the message has expired */ list_del(&cb->list); rets = -ETIME; @@ -234,16 +224,16 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * remove message from deletion list */ - dev_dbg(&dev->pdev->dev, "amthif cb->response_buffer size - %d\n", + dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n", cb->response_buffer.size); - dev_dbg(&dev->pdev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx); + dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx); /* length is being truncated to PAGE_SIZE, however, * the buf_idx may point beyond */ length = min_t(size_t, length, (cb->buf_idx - *offset)); if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { - dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); + dev_dbg(dev->dev, "failed to copy data to userland\n"); rets = -EFAULT; } else { rets = length; @@ -253,7 +243,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, } } free: - dev_dbg(&dev->pdev->dev, "free amthif cb memory.\n"); + dev_dbg(dev->dev, "free amthif cb memory.\n"); *offset = 0; mei_io_cb_free(cb); out: @@ -266,7 +256,7 @@ out: * @dev: the device structure * @cb: mei call back struct * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. * */ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) @@ -277,7 +267,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) if (!dev || !cb) return -ENODEV; - dev_dbg(&dev->pdev->dev, "write data to amthif client.\n"); + dev_dbg(dev->dev, "write data to amthif client.\n"); dev->iamthif_state = MEI_IAMTHIF_WRITING; dev->iamthif_current_cb = cb; @@ -316,12 +306,12 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) return -EIO; dev->iamthif_flow_control_pending = true; dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; - dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n"); + dev_dbg(dev->dev, "add amthif cb to write waiting list\n"); dev->iamthif_current_cb = cb; dev->iamthif_file_object = cb->file_object; list_add_tail(&cb->list, &dev->write_waiting_list.list); } else { - dev_dbg(&dev->pdev->dev, "message does not complete, so add amthif cb to write list.\n"); + dev_dbg(dev->dev, "message does not complete, so add amthif cb to write list.\n"); list_add_tail(&cb->list, &dev->write_list.list); } } else { @@ -336,7 +326,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) * @dev: the device structure * @cb: mei call back struct * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. * */ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) @@ -354,25 +344,23 @@ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) if (!list_empty(&dev->amthif_cmd_list.list) || dev->iamthif_state != MEI_IAMTHIF_IDLE) { - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "amthif state = %d\n", dev->iamthif_state); - dev_dbg(&dev->pdev->dev, "AMTHIF: add cb to the wait list\n"); + dev_dbg(dev->dev, "AMTHIF: add cb to the wait list\n"); list_add_tail(&cb->list, &dev->amthif_cmd_list.list); return 0; } return mei_amthif_send_cmd(dev, cb); } /** - * mei_amthif_run_next_cmd + * mei_amthif_run_next_cmd - send next amt command from queue * * @dev: the device structure - * - * returns 0 on success, <0 on failure. */ void mei_amthif_run_next_cmd(struct mei_device *dev) { - struct mei_cl_cb *pos = NULL; - struct mei_cl_cb *next = NULL; + struct mei_cl_cb *cb; + struct mei_cl_cb *next; int status; if (!dev) @@ -386,21 +374,17 @@ void mei_amthif_run_next_cmd(struct mei_device *dev) dev->iamthif_timer = 0; dev->iamthif_file_object = NULL; - dev_dbg(&dev->pdev->dev, "complete amthif cmd_list cb.\n"); - - list_for_each_entry_safe(pos, next, &dev->amthif_cmd_list.list, list) { - list_del(&pos->list); + dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); - if (pos->cl && pos->cl == &dev->iamthif_cl) { - status = mei_amthif_send_cmd(dev, pos); - if (status) { - dev_dbg(&dev->pdev->dev, - "amthif write failed status = %d\n", + list_for_each_entry_safe(cb, next, &dev->amthif_cmd_list.list, list) { + list_del(&cb->list); + if (!cb->cl) + continue; + status = mei_amthif_send_cmd(dev, cb); + if (status) + dev_warn(dev->dev, "amthif write failed status = %d\n", status); - return; - } - break; - } + break; } } @@ -421,7 +405,7 @@ unsigned int mei_amthif_poll(struct mei_device *dev, dev->iamthif_file_object == file) { mask |= (POLLIN | POLLRDNORM); - dev_dbg(&dev->pdev->dev, "run next amthif cb\n"); + dev_dbg(dev->dev, "run next amthif cb\n"); mei_amthif_run_next_cmd(dev); } mutex_unlock(&dev->device_lock); @@ -434,12 +418,11 @@ unsigned int mei_amthif_poll(struct mei_device *dev, /** * mei_amthif_irq_write - write iamthif command in irq thread context. * - * @dev: the device structure. - * @cb_pos: callback block. * @cl: private data of the file object. + * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * 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) @@ -481,7 +464,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); + dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); rets = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf + dev->iamthif_msg_buf_index); @@ -514,14 +497,14 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, } /** - * mei_amthif_irq_read_message - read routine after ISR to + * mei_amthif_irq_read_msg - read routine after ISR to * handle the read amthif message * * @dev: the device structure * @mei_hdr: header of amthif message * @complete_list: An instance of our list structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_amthif_irq_read_msg(struct mei_device *dev, struct mei_msg_hdr *mei_hdr, @@ -543,10 +526,10 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, if (!mei_hdr->msg_complete) return 0; - dev_dbg(&dev->pdev->dev, "amthif_message_buffer_index =%d\n", + dev_dbg(dev->dev, "amthif_message_buffer_index =%d\n", mei_hdr->length); - dev_dbg(&dev->pdev->dev, "completed amthif read.\n "); + dev_dbg(dev->dev, "completed amthif read.\n "); if (!dev->iamthif_current_cb) return -ENODEV; @@ -559,10 +542,10 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, dev->iamthif_stall_timer = 0; cb->buf_idx = dev->iamthif_msg_buf_index; cb->read_time = jiffies; - if (dev->iamthif_ioctl && cb->cl == &dev->iamthif_cl) { + if (dev->iamthif_ioctl) { /* found the iamthif cb */ - dev_dbg(&dev->pdev->dev, "complete the amthif read cb.\n "); - dev_dbg(&dev->pdev->dev, "add the amthif read cb to complete.\n "); + dev_dbg(dev->dev, "complete the amthif read cb.\n "); + dev_dbg(dev->dev, "add the amthif read cb to complete.\n "); list_add_tail(&cb->list, &complete_list->list); } return 0; @@ -574,7 +557,7 @@ int mei_amthif_irq_read_msg(struct mei_device *dev, * @dev: the device structure. * @slots: free slots. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) { @@ -586,11 +569,11 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) *slots -= msg_slots; if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) { - dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n"); + dev_dbg(dev->dev, "iamthif flow control failed\n"); return -EIO; } - dev_dbg(&dev->pdev->dev, "iamthif flow control success\n"); + dev_dbg(dev->dev, "iamthif flow control success\n"); dev->iamthif_state = MEI_IAMTHIF_READING; dev->iamthif_flow_control_pending = false; dev->iamthif_msg_buf_index = 0; @@ -604,7 +587,7 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) * mei_amthif_complete - complete amthif callback. * * @dev: the device structure. - * @cb_pos: callback block. + * @cb: callback block. */ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) { @@ -615,15 +598,15 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) dev->iamthif_msg_buf, dev->iamthif_msg_buf_index); list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); - dev_dbg(&dev->pdev->dev, "amthif read completed\n"); + dev_dbg(dev->dev, "amthif read completed\n"); dev->iamthif_timer = jiffies; - dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", + dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", dev->iamthif_timer); } else { mei_amthif_run_next_cmd(dev); } - dev_dbg(&dev->pdev->dev, "completing amthif call back.\n"); + dev_dbg(dev->dev, "completing amthif call back.\n"); wake_up_interruptible(&dev->iamthif_cl.wait); } @@ -638,7 +621,7 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) * mei_clear_list is called to clear resources associated with file * when application calls close function or Ctrl-C was pressed * - * returns true if callback removed from the list, false otherwise + * Return: true if callback removed from the list, false otherwise */ static bool mei_clear_list(struct mei_device *dev, const struct file *file, struct list_head *mei_cb_list) @@ -678,7 +661,7 @@ static bool mei_clear_list(struct mei_device *dev, * mei_clear_lists is called to clear resources associated with file * when application calls close function or Ctrl-C was pressed * - * returns true if callback removed from the list, false otherwise + * Return: true if callback removed from the list, false otherwise */ static bool mei_clear_lists(struct mei_device *dev, struct file *file) { @@ -719,7 +702,7 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file) * @dev: device structure * @file: pointer to file structure * -* returns 0 on success, <0 on error +* Return: 0 on success, <0 on error */ int mei_amthif_release(struct mei_device *dev, struct file *file) { @@ -729,11 +712,11 @@ int mei_amthif_release(struct mei_device *dev, struct file *file) if (dev->iamthif_file_object == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { - dev_dbg(&dev->pdev->dev, "amthif canceled iamthif state %d\n", + dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", dev->iamthif_state); dev->iamthif_canceled = true; if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) { - dev_dbg(&dev->pdev->dev, "run next amthif iamthif cb\n"); + dev_dbg(dev->dev, "run next amthif iamthif cb\n"); mei_amthif_run_next_cmd(dev); } } diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 0e993ef28b94..4d20d60ca38d 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -22,7 +22,6 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/interrupt.h> -#include <linux/pci.h> #include <linux/mei_cl_bus.h> #include "mei_dev.h" @@ -70,7 +69,7 @@ static int mei_cl_device_probe(struct device *dev) dev_dbg(dev, "Device probe\n"); - strncpy(id.name, dev_name(dev), sizeof(id.name)); + strlcpy(id.name, dev_name(dev), sizeof(id.name)); return driver->probe(device, &id); } @@ -147,7 +146,7 @@ static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, struct mei_cl *cl; list_for_each_entry(cl, &dev->device_list, device_link) { - if (!uuid_le_cmp(uuid, cl->device_uuid)) + if (!uuid_le_cmp(uuid, cl->cl_uuid)) return cl; } @@ -172,7 +171,7 @@ struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, device->cl = cl; device->ops = ops; - device->dev.parent = &dev->pdev->dev; + device->dev.parent = dev->dev; device->dev.bus = &mei_cl_bus_type; device->dev.type = &mei_cl_device_type; @@ -180,7 +179,7 @@ struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, status = device_register(&device->dev); if (status) { - dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); + dev_err(dev->dev, "Failed to register MEI device\n"); kfree(device); return NULL; } @@ -229,8 +228,8 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { struct mei_device *dev; + struct mei_me_client *me_cl; struct mei_cl_cb *cb; - int id; int rets; if (WARN_ON(!cl || !cl->dev)) @@ -242,11 +241,11 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, return -ENODEV; /* Check if we have an ME client device */ - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) - return id; + me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); + if (!me_cl) + return -ENOTTY; - if (length > dev->me_clients[id].props.max_msg_length) + if (length > me_cl->props.max_msg_length) return -EFBIG; cb = mei_io_cb_init(cl, NULL); @@ -430,7 +429,7 @@ int mei_cl_enable_device(struct mei_cl_device *device) err = mei_cl_connect(cl, NULL); if (err < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, "Could not connect to the ME client"); + dev_err(dev->dev, "Could not connect to the ME client"); return err; } @@ -462,7 +461,7 @@ int mei_cl_disable_device(struct mei_cl_device *device) if (cl->state != MEI_FILE_CONNECTED) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, "Already disconnected"); + dev_err(dev->dev, "Already disconnected"); return 0; } @@ -472,7 +471,7 @@ int mei_cl_disable_device(struct mei_cl_device *device) err = mei_cl_disconnect(cl); if (err < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "Could not disconnect from the ME client"); return err; diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 324e1de93687..bc9ba5359bc6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -14,10 +14,10 @@ * */ -#include <linux/pci.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/delay.h> +#include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/mei.h> @@ -27,47 +27,90 @@ #include "client.h" /** - * mei_me_cl_by_uuid - locate index of me client + * mei_me_cl_by_uuid - locate me client by uuid * * @dev: mei device + * @uuid: me client uuid * * Locking: called under "dev->device_lock" lock * - * returns me client index or -ENOENT if not found + * Return: me client or NULL if not found */ -int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) +struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, + const uuid_le *uuid) { - int i; + struct mei_me_client *me_cl; - for (i = 0; i < dev->me_clients_num; ++i) - if (uuid_le_cmp(*uuid, - dev->me_clients[i].props.protocol_name) == 0) - return i; + list_for_each_entry(me_cl, &dev->me_clients, list) + if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0) + return me_cl; - return -ENOENT; + return NULL; } - /** - * mei_me_cl_by_id return index to me_clients for client_id + * mei_me_cl_by_id - locate me client by client id * * @dev: the device structure * @client_id: me client id * * Locking: called under "dev->device_lock" lock * - * returns index on success, -ENOENT on failure. + * Return: me client or NULL if not found */ +struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) +{ + + struct mei_me_client *me_cl; -int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) + list_for_each_entry(me_cl, &dev->me_clients, list) + if (me_cl->client_id == client_id) + return me_cl; + return NULL; +} + +/** + * mei_me_cl_by_uuid_id - locate me client by client id and uuid + * + * @dev: the device structure + * @uuid: me client uuid + * @client_id: me client id + * + * Locking: called under "dev->device_lock" lock + * + * Return: me client or NULL if not found + */ +struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, + const uuid_le *uuid, u8 client_id) { - int i; + struct mei_me_client *me_cl; - for (i = 0; i < dev->me_clients_num; i++) - if (dev->me_clients[i].client_id == client_id) - return i; + list_for_each_entry(me_cl, &dev->me_clients, list) + if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && + me_cl->client_id == client_id) + return me_cl; + return NULL; +} - return -ENOENT; +/** + * mei_me_cl_remove - remove me client matching uuid and client_id + * + * @dev: the device structure + * @uuid: me client uuid + * @client_id: me client address + */ +void mei_me_cl_remove(struct mei_device *dev, const uuid_le *uuid, u8 client_id) +{ + struct mei_me_client *me_cl, *next; + + list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { + if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 && + me_cl->client_id == client_id) { + list_del(&me_cl->list); + kfree(me_cl); + break; + } + } } @@ -77,7 +120,7 @@ int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) * @cl1: host client 1 * @cl2: host client 2 * - * returns true - if the clients has same host and me ids + * Return: true - if the clients has same host and me ids * false - otherwise */ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, @@ -117,7 +160,7 @@ static void __mei_io_list_flush(struct mei_cl_cb *list, * @list: An instance of our list structure * @cl: host client */ -static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) { __mei_io_list_flush(list, cl, false); } @@ -152,10 +195,10 @@ void mei_io_cb_free(struct mei_cl_cb *cb) /** * mei_io_cb_init - allocate and initialize io callback * - * @cl - mei client + * @cl: mei client * @fp: pointer to file structure * - * returns mei_cl_cb pointer or NULL; + * Return: mei_cl_cb pointer or NULL; */ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) { @@ -179,7 +222,7 @@ struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) * @cb: io callback structure * @length: size of the buffer * - * returns 0 on success + * Return: 0 on success * -EINVAL if cb is NULL * -ENOMEM if allocation failed */ @@ -203,7 +246,7 @@ int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) * @cb: io callback structure * @length: size of the buffer * - * returns 0 on success + * Return: 0 on success * -EINVAL if cb is NULL * -ENOMEM if allocation failed */ @@ -228,6 +271,8 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) * mei_cl_flush_queues - flushes queue lists belonging to cl. * * @cl: host client + * + * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. */ int mei_cl_flush_queues(struct mei_cl *cl) { @@ -273,7 +318,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) * mei_cl_allocate - allocates cl structure and sets it up. * * @dev: mei device - * returns The allocated file or NULL on failure + * Return: The allocated file or NULL on failure */ struct mei_cl *mei_cl_allocate(struct mei_device *dev) { @@ -293,7 +338,7 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) * * @cl: host client * - * returns cb on success, NULL on error + * Return: cb on success, NULL on error */ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) { @@ -311,7 +356,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) * @cl - host client * @id - fixed host id or -1 for generic one * - * returns 0 on success + * Return: 0 on success * -EINVAL on incorrect values * -ENONET if client not found */ @@ -331,13 +376,13 @@ int mei_cl_link(struct mei_cl *cl, int id) MEI_CLIENTS_MAX); if (id >= MEI_CLIENTS_MAX) { - dev_err(&dev->pdev->dev, "id exceeded %d", MEI_CLIENTS_MAX); + dev_err(dev->dev, "id exceeded %d", MEI_CLIENTS_MAX); return -EMFILE; } open_handle_count = dev->open_handle_count + dev->iamthif_open_count; if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { - dev_err(&dev->pdev->dev, "open_handle_count exceeded %d", + dev_err(dev->dev, "open_handle_count exceeded %d", MEI_MAX_OPEN_HANDLE_COUNT); return -EMFILE; } @@ -359,6 +404,8 @@ int mei_cl_link(struct mei_cl *cl, int id) * mei_cl_unlink - remove me_cl from the list * * @cl: host client + * + * Return: always 0 */ int mei_cl_unlink(struct mei_cl *cl) { @@ -395,19 +442,19 @@ void mei_host_client_init(struct work_struct *work) { struct mei_device *dev = container_of(work, struct mei_device, init_work); - struct mei_client_properties *client_props; - int i; + struct mei_me_client *me_cl; + struct mei_client_properties *props; mutex_lock(&dev->device_lock); - for (i = 0; i < dev->me_clients_num; i++) { - client_props = &dev->me_clients[i].props; + list_for_each_entry(me_cl, &dev->me_clients, list) { + props = &me_cl->props; - if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid)) + if (!uuid_le_cmp(props->protocol_name, mei_amthif_guid)) mei_amthif_host_init(dev); - else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) + else if (!uuid_le_cmp(props->protocol_name, mei_wd_guid)) mei_wd_host_init(dev); - else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) + else if (!uuid_le_cmp(props->protocol_name, mei_nfc_guid)) mei_nfc_host_init(dev); } @@ -417,27 +464,27 @@ void mei_host_client_init(struct work_struct *work) mutex_unlock(&dev->device_lock); - pm_runtime_mark_last_busy(&dev->pdev->dev); - dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n"); - pm_runtime_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + dev_dbg(dev->dev, "rpm: autosuspend\n"); + pm_runtime_autosuspend(dev->dev); } /** - * mei_hbuf_acquire: try to acquire host buffer + * mei_hbuf_acquire - try to acquire host buffer * * @dev: the device structure - * returns true if host buffer was acquired + * Return: true if host buffer was acquired */ bool mei_hbuf_acquire(struct mei_device *dev) { if (mei_pg_state(dev) == MEI_PG_ON || dev->pg_event == MEI_PG_EVENT_WAIT) { - dev_dbg(&dev->pdev->dev, "device is in pg\n"); + dev_dbg(dev->dev, "device is in pg\n"); return false; } if (!dev->hbuf_is_ready) { - dev_dbg(&dev->pdev->dev, "hbuf is not ready\n"); + dev_dbg(dev->dev, "hbuf is not ready\n"); return false; } @@ -453,7 +500,7 @@ bool mei_hbuf_acquire(struct mei_device *dev) * * Locking: called under "dev->device_lock" lock * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_cl_disconnect(struct mei_cl *cl) { @@ -471,9 +518,9 @@ int mei_cl_disconnect(struct mei_cl *cl) if (cl->state != MEI_FILE_DISCONNECTING) return 0; - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -484,7 +531,8 @@ int mei_cl_disconnect(struct mei_cl *cl) goto free; } - cb->fop_type = MEI_FOP_CLOSE; + cb->fop_type = MEI_FOP_DISCONNECT; + if (mei_hbuf_acquire(dev)) { if (mei_hbm_cl_disconnect_req(dev, cl)) { rets = -ENODEV; @@ -501,7 +549,7 @@ int mei_cl_disconnect(struct mei_cl *cl) } mutex_unlock(&dev->device_lock); - wait_event_timeout(dev->wait_recvd_msg, + wait_event_timeout(cl->wait, MEI_FILE_DISCONNECTED == cl->state, mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); @@ -519,8 +567,8 @@ int mei_cl_disconnect(struct mei_cl *cl) mei_io_list_flush(&dev->ctrl_wr_list, cl); free: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); mei_io_cb_free(cb); return rets; @@ -533,7 +581,7 @@ free: * * @cl: private data of the file object * - * returns true if other client is connected, false - otherwise. + * Return: true if other client is connected, false - otherwise. */ bool mei_cl_is_other_connecting(struct mei_cl *cl) { @@ -560,10 +608,11 @@ bool mei_cl_is_other_connecting(struct mei_cl *cl) * mei_cl_connect - connect host client to the me one * * @cl: host client + * @file: pointer to file structure * * Locking: called under "dev->device_lock" lock * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_cl_connect(struct mei_cl *cl, struct file *file) { @@ -576,9 +625,9 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) dev = cl->dev; - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -601,11 +650,12 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) cl->timer_count = MEI_CONNECT_TIMEOUT; list_add_tail(&cb->list, &dev->ctrl_rd_list.list); } else { + cl->state = MEI_FILE_INITIALIZING; list_add_tail(&cb->list, &dev->ctrl_wr_list.list); } mutex_unlock(&dev->device_lock); - wait_event_timeout(dev->wait_recvd_msg, + wait_event_timeout(cl->wait, (cl->state == MEI_FILE_CONNECTED || cl->state == MEI_FILE_DISCONNECTED), mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); @@ -625,8 +675,8 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) out: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); mei_io_cb_free(cb); return rets; @@ -637,7 +687,7 @@ out: * * @cl: private data of the file object * - * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. + * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. * -ENOENT if mei_cl is not present * -EINVAL if single_recv_buf == 0 */ @@ -645,26 +695,21 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) { struct mei_device *dev; struct mei_me_client *me_cl; - int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; dev = cl->dev; - if (!dev->me_clients_num) - return 0; - if (cl->mei_flow_ctrl_creds > 0) return 1; - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) { + me_cl = mei_me_cl_by_id(dev, cl->me_client_id); + if (!me_cl) { cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); - return id; + return -ENOENT; } - me_cl = &dev->me_clients[id]; if (me_cl->mei_flow_ctrl_creds) { if (WARN_ON(me_cl->props.single_recv_buf == 0)) return -EINVAL; @@ -678,7 +723,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) * * @cl: private data of the file object * - * @returns + * Return: * 0 on success * -ENOENT when me client is not found * -EINVAL when ctrl credits are <= 0 @@ -687,21 +732,19 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) { struct mei_device *dev; struct mei_me_client *me_cl; - int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; dev = cl->dev; - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) { + me_cl = mei_me_cl_by_id(dev, cl->me_client_id); + if (!me_cl) { cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); - return id; + return -ENOENT; } - me_cl = &dev->me_clients[id]; - if (me_cl->props.single_recv_buf != 0) { + if (me_cl->props.single_recv_buf) { if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) return -EINVAL; me_cl->mei_flow_ctrl_creds--; @@ -717,15 +760,16 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) * mei_cl_read_start - the start read client message function. * * @cl: host client + * @length: number of bytes to read * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_cl_read_start(struct mei_cl *cl, size_t length) { struct mei_device *dev; struct mei_cl_cb *cb; + struct mei_me_client *me_cl; int rets; - int i; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -739,15 +783,15 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) cl_dbg(dev, cl, "read is pending.\n"); return -EBUSY; } - i = mei_me_cl_by_id(dev, cl->me_client_id); - if (i < 0) { + me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); + if (!me_cl) { cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); return -ENOTTY; } - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -759,7 +803,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) } /* always allocate at least client max message */ - length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); + length = max_t(size_t, length, me_cl->props.max_msg_length); rets = mei_io_cb_alloc_resp_buf(cb, length); if (rets) goto out; @@ -779,8 +823,8 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) out: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); if (rets) mei_io_cb_free(cb); @@ -796,7 +840,7 @@ out: * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise error. + * 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) @@ -873,12 +917,13 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, /** * mei_cl_write - submit a write cb to mei device - assumes device_lock is locked + * assumes device_lock is locked * * @cl: host client - * @cl: write callback with filled data + * @cb: write callback with filled data + * @blocking: block until completed * - * returns number of bytes sent on success, <0 on failure. + * 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) { @@ -899,11 +944,11 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) buf = &cb->request_buffer; - cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); + cl_dbg(dev, cl, "size=%d\n", buf->size); - rets = pm_runtime_get(&dev->pdev->dev); + rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(&dev->pdev->dev); + pm_runtime_put_noidle(dev->dev); cl_err(dev, cl, "rpm: get failed %d\n", rets); return rets; } @@ -978,8 +1023,8 @@ out: rets = buf->size; err: cl_dbg(dev, cl, "rpm: autosuspend\n"); - pm_runtime_mark_last_busy(&dev->pdev->dev); - pm_runtime_put_autosuspend(&dev->pdev->dev); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); return rets; } @@ -1015,7 +1060,7 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) /** * mei_cl_all_disconnect - disconnect forcefully all connected clients * - * @dev - mei device + * @dev: mei device */ void mei_cl_all_disconnect(struct mei_device *dev) @@ -1033,11 +1078,12 @@ void mei_cl_all_disconnect(struct mei_device *dev) /** * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted * - * @dev - mei device + * @dev: mei device */ void mei_cl_all_wakeup(struct mei_device *dev) { struct mei_cl *cl; + list_for_each_entry(cl, &dev->file_list, link) { if (waitqueue_active(&cl->rx_wait)) { cl_dbg(dev, cl, "Waking up reading client!\n"); @@ -1052,8 +1098,8 @@ void mei_cl_all_wakeup(struct mei_device *dev) /** * mei_cl_all_write_clear - clear all pending writes - - * @dev - mei device + * + * @dev: mei device */ void mei_cl_all_write_clear(struct mei_device *dev) { diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 96d5de0389f9..d9d0c1525259 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -24,8 +24,15 @@ #include "mei_dev.h" -int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid); -int mei_me_cl_by_id(struct mei_device *dev, u8 client_id); +struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev, + const uuid_le *cuuid); +struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id); + +struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, + const uuid_le *uuid, u8 client_id); + +void mei_me_cl_remove(struct mei_device *dev, + const uuid_le *uuid, u8 client_id); /* * MEI IO Functions @@ -45,6 +52,8 @@ static inline void mei_io_list_init(struct mei_cl_cb *list) { INIT_LIST_HEAD(&list->list); } +void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl); + /* * MEI Host Client Functions */ @@ -101,9 +110,9 @@ void mei_cl_all_write_clear(struct mei_device *dev); #define MEI_CL_PRM(cl) (cl)->host_client_id, (cl)->me_client_id #define cl_dbg(dev, cl, format, arg...) \ - dev_dbg(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + dev_dbg((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) #define cl_err(dev, cl, format, arg...) \ - dev_err(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) + dev_err((dev)->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg) #endif /* _MEI_CLIENT_H_ */ diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index ced5b777c70f..ce1566715f80 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/debugfs.h> -#include <linux/pci.h> #include <linux/mei.h> @@ -28,39 +27,47 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct mei_device *dev = fp->private_data; - struct mei_me_client *cl; - const size_t bufsz = 1024; - char *buf = kzalloc(bufsz, GFP_KERNEL); - int i; + struct mei_me_client *me_cl; + size_t bufsz = 1; + char *buf; + int i = 0; int pos = 0; int ret; - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, - " |id|addr| UUID |con|msg len|\n"); +#define HDR " |id|addr| UUID |con|msg len|sb|\n" mutex_lock(&dev->device_lock); + list_for_each_entry(me_cl, &dev->me_clients, list) + bufsz++; + + bufsz *= sizeof(HDR) + 1; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + mutex_unlock(&dev->device_lock); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, HDR); + /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) goto out; - for (i = 0; i < dev->me_clients_num; i++) { - cl = &dev->me_clients[i]; + list_for_each_entry(me_cl, &dev->me_clients, list) { /* skip me clients that cannot be connected */ - if (cl->props.max_number_of_connections == 0) + if (me_cl->props.max_number_of_connections == 0) continue; pos += scnprintf(buf + pos, bufsz - pos, - "%2d|%2d|%4d|%pUl|%3d|%7d|\n", - i, cl->client_id, - cl->props.fixed_address, - &cl->props.protocol_name, - cl->props.max_number_of_connections, - cl->props.max_msg_length); + "%2d|%2d|%4d|%pUl|%3d|%7d|%2d|\n", + i++, me_cl->client_id, + me_cl->props.fixed_address, + &me_cl->props.protocol_name, + me_cl->props.max_number_of_connections, + me_cl->props.max_msg_length, + me_cl->props.single_recv_buf); } out: mutex_unlock(&dev->device_lock); @@ -98,7 +105,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, mutex_lock(&dev->device_lock); - /* if the driver is not enabled the list won't b consitent */ + /* if the driver is not enabled the list won't be consistent */ if (dev->dev_state != MEI_DEV_ENABLED) goto out; @@ -135,8 +142,13 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, if (!buf) return -ENOMEM; - pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + pos += scnprintf(buf + pos, bufsz - pos, "dev: %s\n", mei_dev_state_str(dev->dev_state)); + pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n", + mei_hbm_state_str(dev->hbm_state)); + pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", + mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED", + mei_pg_state_str(mei_pg_state(dev))); ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); kfree(buf); return ret; @@ -149,7 +161,8 @@ static const struct file_operations mei_dbgfs_fops_devstate = { /** * mei_dbgfs_deregister - Remove the debugfs files and directories - * @mei - pointer to mei device private data + * + * @dev: the mei device structure */ void mei_dbgfs_deregister(struct mei_device *dev) { @@ -160,12 +173,17 @@ void mei_dbgfs_deregister(struct mei_device *dev) } /** - * Add the debugfs files + * mei_dbgfs_register - Add the debugfs files * + * @dev: the mei device structure + * @name: the mei device name + * + * Return: 0 on success, <0 on failure. */ int mei_dbgfs_register(struct mei_device *dev, const char *name) { struct dentry *dir, *f; + dir = debugfs_create_dir(name, NULL); if (!dir) return -ENOMEM; @@ -173,19 +191,19 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) f = debugfs_create_file("meclients", S_IRUSR, dir, dev, &mei_dbgfs_fops_meclients); if (!f) { - dev_err(&dev->pdev->dev, "meclients: registration failed\n"); + dev_err(dev->dev, "meclients: registration failed\n"); goto err; } f = debugfs_create_file("active", S_IRUSR, dir, dev, &mei_dbgfs_fops_active); if (!f) { - dev_err(&dev->pdev->dev, "meclients: registration failed\n"); + dev_err(dev->dev, "meclients: registration failed\n"); goto err; } f = debugfs_create_file("devstate", S_IRUSR, dir, dev, &mei_dbgfs_fops_devstate); if (!f) { - dev_err(&dev->pdev->dev, "devstate: registration failed\n"); + dev_err(dev->dev, "devstate: registration failed\n"); goto err; } dev->dbgfs_dir = dir; diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 804106209d76..49a2653d91a5 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -15,16 +15,34 @@ */ #include <linux/export.h> -#include <linux/pci.h> #include <linux/sched.h> #include <linux/wait.h> -#include <linux/mei.h> #include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include <linux/mei.h> #include "mei_dev.h" #include "hbm.h" #include "client.h" +static const char *mei_hbm_status_str(enum mei_hbm_status status) +{ +#define MEI_HBM_STATUS(status) case MEI_HBMS_##status: return #status + switch (status) { + MEI_HBM_STATUS(SUCCESS); + MEI_HBM_STATUS(CLIENT_NOT_FOUND); + MEI_HBM_STATUS(ALREADY_EXISTS); + MEI_HBM_STATUS(REJECTED); + MEI_HBM_STATUS(INVALID_PARAMETER); + MEI_HBM_STATUS(NOT_ALLOWED); + MEI_HBM_STATUS(ALREADY_STARTED); + MEI_HBM_STATUS(NOT_STARTED); + default: return "unknown"; + } +#undef MEI_HBM_STATUS +}; + static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status) { #define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status @@ -39,13 +57,29 @@ static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status) #undef MEI_CL_CCS } +const char *mei_hbm_state_str(enum mei_hbm_state state) +{ +#define MEI_HBM_STATE(state) case MEI_HBM_##state: return #state + switch (state) { + MEI_HBM_STATE(IDLE); + MEI_HBM_STATE(STARTING); + MEI_HBM_STATE(STARTED); + MEI_HBM_STATE(ENUM_CLIENTS); + MEI_HBM_STATE(CLIENT_PROPERTIES); + MEI_HBM_STATE(STOPPED); + default: + return "unknown"; + } +#undef MEI_HBM_STATE +} + /** * mei_cl_conn_status_to_errno - convert client connect response * status to error code * * @status: client connect response status * - * returns corresponding error code + * Return: corresponding error code */ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status) { @@ -71,60 +105,54 @@ void mei_hbm_idle(struct mei_device *dev) } /** - * mei_hbm_reset - reset hbm counters and book keeping data structurs + * mei_me_cl_remove_all - remove all me clients * * @dev: the device structure */ -void mei_hbm_reset(struct mei_device *dev) +static void mei_me_cl_remove_all(struct mei_device *dev) { - dev->me_clients_num = 0; - dev->me_client_presentation_num = 0; - dev->me_client_index = 0; - - kfree(dev->me_clients); - dev->me_clients = NULL; + struct mei_me_client *me_cl, *next; - mei_hbm_idle(dev); + list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) { + list_del(&me_cl->list); + kfree(me_cl); + } } /** - * mei_hbm_me_cl_allocate - allocates storage for me clients + * mei_hbm_reset - reset hbm counters and book keeping data structurs * * @dev: the device structure - * - * returns 0 on success -ENOMEM on allocation failure */ -static int mei_hbm_me_cl_allocate(struct mei_device *dev) +void mei_hbm_reset(struct mei_device *dev) { - struct mei_me_client *clients; - int b; + dev->me_client_index = 0; - mei_hbm_reset(dev); + mei_me_cl_remove_all(dev); - /* count how many ME clients we have */ - for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) - dev->me_clients_num++; + mei_hbm_idle(dev); +} - if (dev->me_clients_num == 0) - return 0; +/** + * mei_hbm_hdr - construct hbm header + * + * @hdr: hbm header + * @length: payload length + */ - dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n", - dev->me_clients_num * sizeof(struct mei_me_client)); - /* allocate storage for ME clients representation */ - clients = kcalloc(dev->me_clients_num, - sizeof(struct mei_me_client), GFP_KERNEL); - if (!clients) { - dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n"); - return -ENOMEM; - } - dev->me_clients = clients; - return 0; +static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) +{ + hdr->host_addr = 0; + hdr->me_addr = 0; + hdr->length = length; + hdr->msg_complete = 1; + hdr->reserved = 0; } /** * mei_hbm_cl_hdr - construct client hbm header * - * @cl: - client + * @cl: client * @hbm_cmd: host bus message command * @buf: buffer for cl header * @len: buffer length @@ -142,38 +170,87 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len) } /** - * mei_hbm_cl_addr_equal - tells if they have the same address + * mei_hbm_cl_write - write simple hbm client message * - * @cl: - client - * @buf: buffer with cl header + * @dev: the device structure + * @cl: client + * @hbm_cmd: host bus message command + * @len: buffer length * - * returns true if addresses are the same + * Return: 0 on success, <0 on failure. */ static inline -bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf) +int mei_hbm_cl_write(struct mei_device *dev, + struct mei_cl *cl, u8 hbm_cmd, size_t len) +{ + struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; + + mei_hbm_hdr(mei_hdr, len); + mei_hbm_cl_hdr(cl, hbm_cmd, dev->wr_msg.data, len); + + return mei_write_message(dev, mei_hdr, dev->wr_msg.data); +} + +/** + * mei_hbm_cl_addr_equal - check if the client's and + * the message address match + * + * @cl: client + * @cmd: hbm client message + * + * Return: true if addresses are the same + */ +static inline +bool mei_hbm_cl_addr_equal(struct mei_cl *cl, struct mei_hbm_cl_cmd *cmd) { - struct mei_hbm_cl_cmd *cmd = buf; return cl->host_client_id == cmd->host_addr && cl->me_client_id == cmd->me_addr; } +/** + * mei_hbm_cl_find_by_cmd - find recipient client + * + * @dev: the device structure + * @buf: a buffer with hbm cl command + * + * Return: the recipient client or NULL if not found + */ +static inline +struct mei_cl *mei_hbm_cl_find_by_cmd(struct mei_device *dev, void *buf) +{ + struct mei_hbm_cl_cmd *cmd = (struct mei_hbm_cl_cmd *)buf; + struct mei_cl *cl; + + list_for_each_entry(cl, &dev->file_list, link) + if (mei_hbm_cl_addr_equal(cl, cmd)) + return cl; + return NULL; +} + +/** + * mei_hbm_start_wait - wait for start response message. + * + * @dev: the device structure + * + * Return: 0 on success and < 0 on failure + */ int mei_hbm_start_wait(struct mei_device *dev) { int ret; - if (dev->hbm_state > MEI_HBM_START) + + if (dev->hbm_state > MEI_HBM_STARTING) return 0; mutex_unlock(&dev->device_lock); - ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, - dev->hbm_state == MEI_HBM_IDLE || - dev->hbm_state >= MEI_HBM_STARTED, + ret = wait_event_timeout(dev->wait_hbm_start, + dev->hbm_state != MEI_HBM_STARTING, mei_secs_to_jiffies(MEI_HBM_TIMEOUT)); mutex_lock(&dev->device_lock); - if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) { + if (ret == 0 && (dev->hbm_state <= MEI_HBM_STARTING)) { dev->hbm_state = MEI_HBM_IDLE; - dev_err(&dev->pdev->dev, "waiting for mei start failed\n"); + dev_err(dev->dev, "waiting for mei start failed\n"); return -ETIME; } return 0; @@ -184,7 +261,7 @@ int mei_hbm_start_wait(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ int mei_hbm_start_req(struct mei_device *dev) { @@ -193,6 +270,8 @@ int mei_hbm_start_req(struct mei_device *dev) const size_t len = sizeof(struct hbm_host_version_request); int ret; + mei_hbm_reset(dev); + mei_hbm_hdr(mei_hdr, len); /* host start message */ @@ -205,12 +284,12 @@ int mei_hbm_start_req(struct mei_device *dev) dev->hbm_state = MEI_HBM_IDLE; ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { - dev_err(&dev->pdev->dev, "version message write failed: ret = %d\n", + dev_err(dev->dev, "version message write failed: ret = %d\n", ret); return ret; } - dev->hbm_state = MEI_HBM_START; + dev->hbm_state = MEI_HBM_STARTING; dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT; return 0; } @@ -220,7 +299,7 @@ int mei_hbm_start_req(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ static int mei_hbm_enum_clients_req(struct mei_device *dev) { @@ -238,7 +317,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { - dev_err(&dev->pdev->dev, "enumeration request write failed: ret = %d.\n", + dev_err(dev->dev, "enumeration request write failed: ret = %d.\n", ret); return ret; } @@ -247,12 +326,38 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) return 0; } +/* + * mei_hbm_me_cl_add - add new me client to the list + * + * @dev: the device structure + * @res: hbm property response + * + * Return: 0 on success and -ENOMEM on allocation failure + */ + +static int mei_hbm_me_cl_add(struct mei_device *dev, + struct hbm_props_response *res) +{ + struct mei_me_client *me_cl; + + me_cl = kzalloc(sizeof(struct mei_me_client), GFP_KERNEL); + if (!me_cl) + return -ENOMEM; + + me_cl->props = res->client_properties; + me_cl->client_id = res->me_addr; + me_cl->mei_flow_ctrl_creds = 0; + + list_add(&me_cl->list, &dev->me_clients); + return 0; +} + /** * mei_hbm_prop_req - request property for a single client * * @dev: the device structure * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ static int mei_hbm_prop_req(struct mei_device *dev) @@ -262,11 +367,8 @@ static int mei_hbm_prop_req(struct mei_device *dev) struct hbm_props_request *prop_req; const size_t len = sizeof(struct hbm_props_request); unsigned long next_client_index; - unsigned long client_num; int ret; - client_num = dev->me_client_presentation_num; - next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, dev->me_client_index); @@ -278,21 +380,17 @@ static int mei_hbm_prop_req(struct mei_device *dev) return 0; } - dev->me_clients[client_num].client_id = next_client_index; - dev->me_clients[client_num].mei_flow_ctrl_creds = 0; - mei_hbm_hdr(mei_hdr, len); prop_req = (struct hbm_props_request *)dev->wr_msg.data; memset(prop_req, 0, sizeof(struct hbm_props_request)); - prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; - prop_req->address = next_client_index; + prop_req->me_addr = next_client_index; ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { - dev_err(&dev->pdev->dev, "properties request write failed: ret = %d\n", + dev_err(dev->dev, "properties request write failed: ret = %d\n", ret); return ret; } @@ -309,7 +407,8 @@ static int mei_hbm_prop_req(struct mei_device *dev) * @dev: the device structure * @pg_cmd: the pg command code * - * This function returns -EIO on write failure + * Return: -EIO on write failure + * -EOPNOTSUPP if the operation is not supported by the protocol */ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) { @@ -318,6 +417,9 @@ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) const size_t len = sizeof(struct hbm_power_gate); int ret; + if (!dev->hbm_f_pg_supported) + return -EOPNOTSUPP; + mei_hbm_hdr(mei_hdr, len); req = (struct hbm_power_gate *)dev->wr_msg.data; @@ -326,7 +428,7 @@ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) - dev_err(&dev->pdev->dev, "power gate command write failed.\n"); + dev_err(dev->dev, "power gate command write failed.\n"); return ret; } EXPORT_SYMBOL_GPL(mei_hbm_pg); @@ -334,10 +436,9 @@ EXPORT_SYMBOL_GPL(mei_hbm_pg); /** * mei_hbm_stop_req - send stop request message * - * @dev - mei device - * @cl: client info + * @dev: mei device * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ static int mei_hbm_stop_req(struct mei_device *dev) { @@ -361,19 +462,14 @@ static int mei_hbm_stop_req(struct mei_device *dev) * @dev: the device structure * @cl: client info * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_flow_control); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len); - cl_dbg(dev, cl, "sending flow control\n"); - - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); + return mei_hbm_cl_write(dev, cl, MEI_FLOW_CONTROL_CMD, len); } /** @@ -382,31 +478,26 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) * @dev: the device structure * @flow: flow control. * - * return 0 on success, < 0 otherwise + * Return: 0 on success, < 0 otherwise */ static int mei_hbm_add_single_flow_creds(struct mei_device *dev, struct hbm_flow_control *flow) { struct mei_me_client *me_cl; - int id; - id = mei_me_cl_by_id(dev, flow->me_addr); - if (id < 0) { - dev_err(&dev->pdev->dev, "no such me client %d\n", + me_cl = mei_me_cl_by_id(dev, flow->me_addr); + if (!me_cl) { + dev_err(dev->dev, "no such me client %d\n", flow->me_addr); - return id; + return -ENOENT; } - me_cl = &dev->me_clients[id]; - if (me_cl->props.single_recv_buf) { - me_cl->mei_flow_ctrl_creds++; - dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n", - flow->me_addr); - dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n", - me_cl->mei_flow_ctrl_creds); - } else { - BUG(); /* error in flow control */ - } + if (WARN_ON(me_cl->props.single_recv_buf == 0)) + return -EINVAL; + + me_cl->mei_flow_ctrl_creds++; + dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n", + flow->me_addr, me_cl->mei_flow_ctrl_creds); return 0; } @@ -418,7 +509,7 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev, * @flow_control: flow control response bus message */ static void mei_hbm_cl_flow_control_res(struct mei_device *dev, - struct hbm_flow_control *flow_control) + struct hbm_flow_control *flow_control) { struct mei_cl *cl; @@ -428,16 +519,11 @@ static void mei_hbm_cl_flow_control_res(struct mei_device *dev, return; } - /* normal connection */ - list_for_each_entry(cl, &dev->file_list, link) { - if (mei_hbm_cl_addr_equal(cl, flow_control)) { - cl->mei_flow_ctrl_creds++; - dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n", - flow_control->host_addr, flow_control->me_addr); - dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n", - cl->mei_flow_ctrl_creds); - break; - } + cl = mei_hbm_cl_find_by_cmd(dev, flow_control); + if (cl) { + cl->mei_flow_ctrl_creds++; + cl_dbg(dev, cl, "flow control creds = %d.\n", + cl->mei_flow_ctrl_creds); } } @@ -448,17 +534,13 @@ static void mei_hbm_cl_flow_control_res(struct mei_device *dev, * @dev: the device structure * @cl: a client to disconnect from * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_client_connect_request); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len); - - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); + return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_REQ_CMD, len); } /** @@ -467,53 +549,34 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) * @dev: the device structure * @cl: a client to disconnect from * - * This function returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_client_connect_response); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, dev->wr_msg.data, len); - - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); + return mei_hbm_cl_write(dev, cl, CLIENT_DISCONNECT_RES_CMD, len); } /** - * mei_hbm_cl_disconnect_res - disconnect response from ME + * mei_hbm_cl_disconnect_res - update the client state according + * disconnect response * - * @dev: the device structure - * @rs: disconnect response bus message + * @cl: mei host client + * @cmd: disconnect client response host bus message */ -static void mei_hbm_cl_disconnect_res(struct mei_device *dev, - struct hbm_client_connect_response *rs) +static void mei_hbm_cl_disconnect_res(struct mei_cl *cl, + struct mei_hbm_cl_cmd *cmd) { - struct mei_cl *cl; - struct mei_cl_cb *cb, *next; + struct hbm_client_connect_response *rs = + (struct hbm_client_connect_response *)cmd; - dev_dbg(&dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n", + dev_dbg(cl->dev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n", rs->me_addr, rs->host_addr, rs->status); - list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { - cl = cb->cl; - - /* this should not happen */ - if (WARN_ON(!cl)) { - list_del(&cb->list); - return; - } - - if (mei_hbm_cl_addr_equal(cl, rs)) { - list_del(&cb->list); - if (rs->status == MEI_CL_DISCONN_SUCCESS) - cl->state = MEI_FILE_DISCONNECTED; - - cl->status = 0; - cl->timer_count = 0; - break; - } - } + if (rs->status == MEI_CL_DISCONN_SUCCESS) + cl->state = MEI_FILE_DISCONNECTED; + cl->status = 0; } /** @@ -522,38 +585,55 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev, * @dev: the device structure * @cl: a client to connect to * - * returns -EIO on write failure + * Return: -EIO on write failure */ int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl) { - struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; const size_t len = sizeof(struct hbm_client_connect_request); - mei_hbm_hdr(mei_hdr, len); - mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len); + return mei_hbm_cl_write(dev, cl, CLIENT_CONNECT_REQ_CMD, len); +} - return mei_write_message(dev, mei_hdr, dev->wr_msg.data); +/** + * mei_hbm_cl_connect_res - update the client state according + * connection response + * + * @cl: mei host client + * @cmd: connect client response host bus message + */ +static void mei_hbm_cl_connect_res(struct mei_cl *cl, + struct mei_hbm_cl_cmd *cmd) +{ + struct hbm_client_connect_response *rs = + (struct hbm_client_connect_response *)cmd; + + dev_dbg(cl->dev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n", + rs->me_addr, rs->host_addr, + mei_cl_conn_status_str(rs->status)); + + if (rs->status == MEI_CL_CONN_SUCCESS) + cl->state = MEI_FILE_CONNECTED; + else + cl->state = MEI_FILE_DISCONNECTED; + cl->status = mei_cl_conn_status_to_errno(rs->status); } /** - * mei_hbm_cl_connect_res - connect response from the ME + * mei_hbm_cl_res - process hbm response received on behalf + * an client * * @dev: the device structure - * @rs: connect response bus message + * @rs: hbm client message + * @fop_type: file operation type */ -static void mei_hbm_cl_connect_res(struct mei_device *dev, - struct hbm_client_connect_response *rs) +static void mei_hbm_cl_res(struct mei_device *dev, + struct mei_hbm_cl_cmd *rs, + enum mei_cb_file_ops fop_type) { - struct mei_cl *cl; struct mei_cl_cb *cb, *next; - dev_dbg(&dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n", - rs->me_addr, rs->host_addr, - mei_cl_conn_status_str(rs->status)); - cl = NULL; - list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { cl = cb->cl; @@ -563,7 +643,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, continue; } - if (cb->fop_type != MEI_FOP_CONNECT) + if (cb->fop_type != fop_type) continue; if (mei_hbm_cl_addr_equal(cl, rs)) { @@ -575,12 +655,19 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, if (!cl) return; + switch (fop_type) { + case MEI_FOP_CONNECT: + mei_hbm_cl_connect_res(cl, rs); + break; + case MEI_FOP_DISCONNECT: + mei_hbm_cl_disconnect_res(cl, rs); + break; + default: + return; + } + cl->timer_count = 0; - if (rs->status == MEI_CL_CONN_SUCCESS) - cl->state = MEI_FILE_CONNECTED; - else - cl->state = MEI_FILE_DISCONNECTED; - cl->status = mei_cl_conn_status_to_errno(rs->status); + wake_up(&cl->wait); } @@ -591,7 +678,7 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, * @dev: the device structure. * @disconnect_req: disconnect request bus message from the me * - * returns -ENOMEM on allocation failure + * Return: -ENOMEM on allocation failure */ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, struct hbm_client_connect_request *disconnect_req) @@ -599,34 +686,46 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, struct mei_cl *cl; struct mei_cl_cb *cb; - list_for_each_entry(cl, &dev->file_list, link) { - if (mei_hbm_cl_addr_equal(cl, disconnect_req)) { - dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n", - disconnect_req->host_addr, - disconnect_req->me_addr); - cl->state = MEI_FILE_DISCONNECTED; - cl->timer_count = 0; - - cb = mei_io_cb_init(cl, NULL); - if (!cb) - return -ENOMEM; - cb->fop_type = MEI_FOP_DISCONNECT_RSP; - cl_dbg(dev, cl, "add disconnect response as first\n"); - list_add(&cb->list, &dev->ctrl_wr_list.list); + cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req); + if (cl) { + cl_dbg(dev, cl, "disconnect request received\n"); + cl->state = MEI_FILE_DISCONNECTED; + cl->timer_count = 0; - break; - } + cb = mei_io_cb_init(cl, NULL); + if (!cb) + return -ENOMEM; + cb->fop_type = MEI_FOP_DISCONNECT_RSP; + cl_dbg(dev, cl, "add disconnect response as first\n"); + list_add(&cb->list, &dev->ctrl_wr_list.list); } return 0; } +/** + * mei_hbm_config_features - check what hbm features and commands + * are supported by the fw + * + * @dev: the device structure + */ +static void mei_hbm_config_features(struct mei_device *dev) +{ + /* Power Gating Isolation Support */ + dev->hbm_f_pg_supported = 0; + if (dev->version.major_version > HBM_MAJOR_VERSION_PGI) + dev->hbm_f_pg_supported = 1; + + if (dev->version.major_version == HBM_MAJOR_VERSION_PGI && + dev->version.minor_version >= HBM_MINOR_VERSION_PGI) + dev->hbm_f_pg_supported = 1; +} /** * mei_hbm_version_is_supported - checks whether the driver can * support the hbm version of the device * * @dev: the device structure - * returns true if driver can support hbm version of the device + * Return: true if driver can support hbm version of the device */ bool mei_hbm_version_is_supported(struct mei_device *dev) { @@ -640,44 +739,44 @@ bool mei_hbm_version_is_supported(struct mei_device *dev) * handle the read bus message cmd processing. * * @dev: the device structure - * @mei_hdr: header of bus message + * @hdr: header of bus message * - * returns 0 on success and < 0 on failure + * Return: 0 on success and < 0 on failure */ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) { struct mei_bus_message *mei_msg; - struct mei_me_client *me_client; struct hbm_host_version_response *version_res; - struct hbm_client_connect_response *connect_res; - struct hbm_client_connect_response *disconnect_res; - struct hbm_client_connect_request *disconnect_req; - struct hbm_flow_control *flow_control; struct hbm_props_response *props_res; struct hbm_host_enum_response *enum_res; + struct mei_hbm_cl_cmd *cl_cmd; + struct hbm_client_connect_request *disconnect_req; + struct hbm_flow_control *flow_control; + /* read the message to our buffer */ BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf)); mei_read_slots(dev, dev->rd_msg_buf, hdr->length); mei_msg = (struct mei_bus_message *)dev->rd_msg_buf; + cl_cmd = (struct mei_hbm_cl_cmd *)mei_msg; /* ignore spurious message and prevent reset nesting * hbm is put to idle during system reset */ if (dev->hbm_state == MEI_HBM_IDLE) { - dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n"); + dev_dbg(dev->dev, "hbm: state is idle ignore spurious messages\n"); return 0; } switch (mei_msg->hbm_cmd) { case HOST_START_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n"); + dev_dbg(dev->dev, "hbm: start: response message received.\n"); dev->init_clients_timer = 0; version_res = (struct hbm_host_version_response *)mei_msg; - dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n", + dev_dbg(dev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n", HBM_MAJOR_VERSION, HBM_MINOR_VERSION, version_res->me_max_version.major_version, version_res->me_max_version.minor_version); @@ -693,19 +792,21 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) } if (!mei_hbm_version_is_supported(dev)) { - dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n"); + dev_warn(dev->dev, "hbm: start: version mismatch - stopping the driver.\n"); dev->hbm_state = MEI_HBM_STOPPED; if (mei_hbm_stop_req(dev)) { - dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n"); + dev_err(dev->dev, "hbm: start: failed to send stop request\n"); return -EIO; } break; } + mei_hbm_config_features(dev); + if (dev->dev_state != MEI_DEV_INIT_CLIENTS || - dev->hbm_state != MEI_HBM_START) { - dev_err(&dev->pdev->dev, "hbm: start: state mismatch, [%d, %d]\n", + dev->hbm_state != MEI_HBM_STARTING) { + dev_err(dev->dev, "hbm: start: state mismatch, [%d, %d]\n", dev->dev_state, dev->hbm_state); return -EPROTO; } @@ -713,45 +814,39 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) dev->hbm_state = MEI_HBM_STARTED; if (mei_hbm_enum_clients_req(dev)) { - dev_err(&dev->pdev->dev, "hbm: start: failed to send enumeration request\n"); + dev_err(dev->dev, "hbm: start: failed to send enumeration request\n"); return -EIO; } - wake_up_interruptible(&dev->wait_recvd_msg); + wake_up(&dev->wait_hbm_start); break; case CLIENT_CONNECT_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: client connect response: message received.\n"); - - connect_res = (struct hbm_client_connect_response *) mei_msg; - mei_hbm_cl_connect_res(dev, connect_res); - wake_up(&dev->wait_recvd_msg); + dev_dbg(dev->dev, "hbm: client connect response: message received.\n"); + mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_CONNECT); break; case CLIENT_DISCONNECT_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: client disconnect response: message received.\n"); - - disconnect_res = (struct hbm_client_connect_response *) mei_msg; - mei_hbm_cl_disconnect_res(dev, disconnect_res); - wake_up(&dev->wait_recvd_msg); + dev_dbg(dev->dev, "hbm: client disconnect response: message received.\n"); + mei_hbm_cl_res(dev, cl_cmd, MEI_FOP_DISCONNECT); break; case MEI_FLOW_CONTROL_CMD: - dev_dbg(&dev->pdev->dev, "hbm: client flow control response: message received.\n"); + dev_dbg(dev->dev, "hbm: client flow control response: message received.\n"); flow_control = (struct hbm_flow_control *) mei_msg; mei_hbm_cl_flow_control_res(dev, flow_control); break; case MEI_PG_ISOLATION_ENTRY_RES_CMD: - dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n"); + dev_dbg(dev->dev, "power gate isolation entry response received\n"); dev->pg_event = MEI_PG_EVENT_RECEIVED; if (waitqueue_active(&dev->wait_pg)) wake_up(&dev->wait_pg); break; case MEI_PG_ISOLATION_EXIT_REQ_CMD: - dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n"); + dev_dbg(dev->dev, "power gate isolation exit request received\n"); dev->pg_event = MEI_PG_EVENT_RECEIVED; if (waitqueue_active(&dev->wait_pg)) wake_up(&dev->wait_pg); @@ -761,44 +856,33 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) * this is HW initiated exit from PG. * Start runtime pm resume sequence to exit from PG. */ - pm_request_resume(&dev->pdev->dev); + pm_request_resume(dev->dev); break; case HOST_CLIENT_PROPERTIES_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n"); + dev_dbg(dev->dev, "hbm: properties response: message received.\n"); dev->init_clients_timer = 0; - if (dev->me_clients == NULL) { - dev_err(&dev->pdev->dev, "hbm: properties response: mei_clients not allocated\n"); + if (dev->dev_state != MEI_DEV_INIT_CLIENTS || + dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) { + dev_err(dev->dev, "hbm: properties response: state mismatch, [%d, %d]\n", + dev->dev_state, dev->hbm_state); return -EPROTO; } props_res = (struct hbm_props_response *)mei_msg; - me_client = &dev->me_clients[dev->me_client_presentation_num]; if (props_res->status) { - dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d\n", - props_res->status); + dev_err(dev->dev, "hbm: properties response: wrong status = %d %s\n", + props_res->status, + mei_hbm_status_str(props_res->status)); return -EPROTO; } - if (me_client->client_id != props_res->address) { - dev_err(&dev->pdev->dev, "hbm: properties response: address mismatch %d ?= %d\n", - me_client->client_id, props_res->address); - return -EPROTO; - } + mei_hbm_me_cl_add(dev, props_res); - if (dev->dev_state != MEI_DEV_INIT_CLIENTS || - dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) { - dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n", - dev->dev_state, dev->hbm_state); - return -EPROTO; - } - - me_client->props = props_res->client_properties; dev->me_client_index++; - dev->me_client_presentation_num++; /* request property for the next client */ if (mei_hbm_prop_req(dev)) @@ -807,7 +891,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) break; case HOST_ENUM_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n"); + dev_dbg(dev->dev, "hbm: enumeration response: message received\n"); dev->init_clients_timer = 0; @@ -815,20 +899,15 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) BUILD_BUG_ON(sizeof(dev->me_clients_map) < sizeof(enum_res->valid_addresses)); memcpy(dev->me_clients_map, enum_res->valid_addresses, - sizeof(enum_res->valid_addresses)); + sizeof(enum_res->valid_addresses)); if (dev->dev_state != MEI_DEV_INIT_CLIENTS || dev->hbm_state != MEI_HBM_ENUM_CLIENTS) { - dev_err(&dev->pdev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n", + dev_err(dev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n", dev->dev_state, dev->hbm_state); return -EPROTO; } - if (mei_hbm_me_cl_allocate(dev)) { - dev_err(&dev->pdev->dev, "hbm: enumeration response: cannot allocate clients array\n"); - return -ENOMEM; - } - dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES; /* first property request */ @@ -838,34 +917,34 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) break; case HOST_STOP_RES_CMD: - dev_dbg(&dev->pdev->dev, "hbm: stop response: message received\n"); + dev_dbg(dev->dev, "hbm: stop response: message received\n"); dev->init_clients_timer = 0; if (dev->hbm_state != MEI_HBM_STOPPED) { - dev_err(&dev->pdev->dev, "hbm: stop response: state mismatch, [%d, %d]\n", + dev_err(dev->dev, "hbm: stop response: state mismatch, [%d, %d]\n", dev->dev_state, dev->hbm_state); return -EPROTO; } dev->dev_state = MEI_DEV_POWER_DOWN; - dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n"); + dev_info(dev->dev, "hbm: stop response: resetting.\n"); /* force the reset */ return -EPROTO; break; case CLIENT_DISCONNECT_REQ_CMD: - dev_dbg(&dev->pdev->dev, "hbm: disconnect request: message received\n"); + dev_dbg(dev->dev, "hbm: disconnect request: message received\n"); disconnect_req = (struct hbm_client_connect_request *)mei_msg; mei_hbm_fw_disconnect_req(dev, disconnect_req); break; case ME_STOP_REQ_CMD: - dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n"); + dev_dbg(dev->dev, "hbm: stop request: message received\n"); dev->hbm_state = MEI_HBM_STOPPED; if (mei_hbm_stop_req(dev)) { - dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n"); + dev_err(dev->dev, "hbm: stop request: failed to send stop request\n"); return -EIO; } break; diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h index 683eb2835cec..b7cd3d857fd5 100644 --- a/drivers/misc/mei/hbm.h +++ b/drivers/misc/mei/hbm.h @@ -25,29 +25,24 @@ struct mei_cl; * enum mei_hbm_state - host bus message protocol state * * @MEI_HBM_IDLE : protocol not started - * @MEI_HBM_START : start request message was sent + * @MEI_HBM_STARTING : start request message was sent + * @MEI_HBM_STARTED : start reply message was received * @MEI_HBM_ENUM_CLIENTS : enumeration request was sent * @MEI_HBM_CLIENT_PROPERTIES : acquiring clients properties + * @MEI_HBM_STOPPED : stopping exchange */ enum mei_hbm_state { MEI_HBM_IDLE = 0, - MEI_HBM_START, + MEI_HBM_STARTING, MEI_HBM_STARTED, MEI_HBM_ENUM_CLIENTS, MEI_HBM_CLIENT_PROPERTIES, MEI_HBM_STOPPED, }; -int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr); +const char *mei_hbm_state_str(enum mei_hbm_state state); -static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) -{ - hdr->host_addr = 0; - hdr->me_addr = 0; - hdr->length = length; - hdr->msg_complete = 1; - hdr->reserved = 0; -} +int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr); void mei_hbm_idle(struct mei_device *dev); void mei_hbm_reset(struct mei_device *dev); diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index a9a0d08f758e..4f2fd6fc1e23 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -28,10 +28,10 @@ /** * mei_me_reg_read - Reads 32bit data from the mei device * - * @dev: the device structure + * @hw: the me hardware structure * @offset: offset from which to read the data * - * returns register value (u32) + * Return: register value (u32) */ static inline u32 mei_me_reg_read(const struct mei_me_hw *hw, unsigned long offset) @@ -43,7 +43,7 @@ static inline u32 mei_me_reg_read(const struct mei_me_hw *hw, /** * mei_me_reg_write - Writes 32bit data to the mei device * - * @dev: the device structure + * @hw: the me hardware structure * @offset: offset from which to write the data * @value: register value to write (u32) */ @@ -59,7 +59,7 @@ static inline void mei_me_reg_write(const struct mei_me_hw *hw, * * @dev: the device structure * - * returns ME_CB_RW register value (u32) + * Return: ME_CB_RW register value (u32) */ static u32 mei_me_mecbrw_read(const struct mei_device *dev) { @@ -68,9 +68,9 @@ static u32 mei_me_mecbrw_read(const struct mei_device *dev) /** * mei_me_mecsr_read - Reads 32bit data from the ME CSR * - * @dev: the device structure + * @hw: the me hardware structure * - * returns ME_CSR_HA register value (u32) + * Return: ME_CSR_HA register value (u32) */ static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw) { @@ -80,9 +80,9 @@ static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw) /** * mei_hcsr_read - Reads 32bit data from the host CSR * - * @dev: the device structure + * @hw: the me hardware structure * - * returns H_CSR register value (u32) + * Return: H_CSR register value (u32) */ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) { @@ -93,7 +93,8 @@ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw) * mei_hcsr_set - writes H_CSR register to the mei device, * and ignores the H_IS bit for it is write-one-to-zero. * - * @dev: the device structure + * @hw: the me hardware structure + * @hcsr: new register value */ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) { @@ -101,6 +102,36 @@ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr) mei_me_reg_write(hw, H_CSR, hcsr); } +/** + * mei_me_fw_status - read fw status register from pci config space + * + * @dev: mei device + * @fw_status: fw status register values + * + * Return: 0 on success, error otherwise + */ +static int mei_me_fw_status(struct mei_device *dev, + struct mei_fw_status *fw_status) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + struct mei_me_hw *hw = to_me_hw(dev); + const struct mei_fw_status *fw_src = &hw->cfg->fw_status; + int ret; + int i; + + if (!fw_status) + return -EINVAL; + + fw_status->count = fw_src->count; + for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { + ret = pci_read_config_dword(pdev, + fw_src->status[i], &fw_status->status[i]); + if (ret) + return ret; + } + + return 0; +} /** * mei_me_hw_config - configure hw dependent settings @@ -121,17 +152,19 @@ static void mei_me_hw_config(struct mei_device *dev) * mei_me_pg_state - translate internal pg state * to the mei power gating state * - * @hw - me hardware - * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise + * @dev: mei device + * + * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise */ static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + return hw->pg_state; } /** - * mei_clear_interrupts - clear and stop interrupts + * mei_me_intr_clear - clear and stop interrupts * * @dev: the device structure */ @@ -139,6 +172,7 @@ static void mei_me_intr_clear(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); + if ((hcsr & H_IS) == H_IS) mei_me_reg_write(hw, H_CSR, hcsr); } @@ -151,12 +185,13 @@ static void mei_me_intr_enable(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); + hcsr |= H_IE; mei_hcsr_set(hw, hcsr); } /** - * mei_disable_interrupts - disables mei device interrupts + * mei_me_intr_disable - disables mei device interrupts * * @dev: the device structure */ @@ -164,6 +199,7 @@ static void mei_me_intr_disable(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); + hcsr &= ~H_IE; mei_hcsr_set(hw, hcsr); } @@ -190,6 +226,8 @@ static void mei_me_hw_reset_release(struct mei_device *dev) * * @dev: the device structure * @intr_enable: if interrupt should be enabled after reset. + * + * Return: always 0 */ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) { @@ -213,10 +251,10 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) hcsr = mei_hcsr_read(hw); if ((hcsr & H_RST) == 0) - dev_warn(&dev->pdev->dev, "H_RST is not set = 0x%08X", hcsr); + dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr); if ((hcsr & H_RDY) == H_RDY) - dev_warn(&dev->pdev->dev, "H_RDY is not cleared 0x%08X", hcsr); + dev_warn(dev->dev, "H_RDY is not cleared 0x%08X", hcsr); if (intr_enable == false) mei_me_hw_reset_release(dev); @@ -227,26 +265,27 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) /** * mei_me_host_set_ready - enable device * - * @dev - mei device - * returns bool + * @dev: mei device */ - static void mei_me_host_set_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + hw->host_hw_state = mei_hcsr_read(hw); hw->host_hw_state |= H_IE | H_IG | H_RDY; mei_hcsr_set(hw, hw->host_hw_state); } + /** * mei_me_host_is_ready - check whether the host has turned ready * - * @dev - mei device - * returns bool + * @dev: mei device + * Return: bool */ static bool mei_me_host_is_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + hw->host_hw_state = mei_hcsr_read(hw); return (hw->host_hw_state & H_RDY) == H_RDY; } @@ -254,43 +293,53 @@ static bool mei_me_host_is_ready(struct mei_device *dev) /** * mei_me_hw_is_ready - check whether the me(hw) has turned ready * - * @dev - mei device - * returns bool + * @dev: mei device + * Return: bool */ static bool mei_me_hw_is_ready(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); + hw->me_hw_state = mei_me_mecsr_read(hw); return (hw->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA; } +/** + * mei_me_hw_ready_wait - wait until the me(hw) has turned ready + * or timeout is reached + * + * @dev: mei device + * Return: 0 on success, error otherwise + */ static int mei_me_hw_ready_wait(struct mei_device *dev) { - int err; - mutex_unlock(&dev->device_lock); - err = wait_event_interruptible_timeout(dev->wait_hw_ready, + wait_event_timeout(dev->wait_hw_ready, dev->recvd_hw_ready, mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT)); mutex_lock(&dev->device_lock); - if (!err && !dev->recvd_hw_ready) { - if (!err) - err = -ETIME; - dev_err(&dev->pdev->dev, - "wait hw ready failed. status = %d\n", err); - return err; + if (!dev->recvd_hw_ready) { + dev_err(dev->dev, "wait hw ready failed\n"); + return -ETIME; } dev->recvd_hw_ready = false; return 0; } +/** + * mei_me_hw_start - hw start routine + * + * @dev: mei device + * Return: 0 on success, error otherwise + */ static int mei_me_hw_start(struct mei_device *dev) { int ret = mei_me_hw_ready_wait(dev); + if (ret) return ret; - dev_dbg(&dev->pdev->dev, "hw is ready\n"); + dev_dbg(dev->dev, "hw is ready\n"); mei_me_host_set_ready(dev); return ret; @@ -302,7 +351,7 @@ static int mei_me_hw_start(struct mei_device *dev) * * @dev: the device structure * - * returns number of filled slots + * Return: number of filled slots */ static unsigned char mei_hbuf_filled_slots(struct mei_device *dev) { @@ -322,7 +371,7 @@ static unsigned char mei_hbuf_filled_slots(struct mei_device *dev) * * @dev: the device structure * - * returns true if empty, false - otherwise. + * Return: true if empty, false - otherwise. */ static bool mei_me_hbuf_is_empty(struct mei_device *dev) { @@ -334,7 +383,7 @@ static bool mei_me_hbuf_is_empty(struct mei_device *dev) * * @dev: the device structure * - * returns -EOVERFLOW if overflow, otherwise empty slots count + * Return: -EOVERFLOW if overflow, otherwise empty slots count */ static int mei_me_hbuf_empty_slots(struct mei_device *dev) { @@ -350,6 +399,13 @@ static int mei_me_hbuf_empty_slots(struct mei_device *dev) return empty_slots; } +/** + * mei_me_hbuf_max_len - returns size of hw buffer. + * + * @dev: the device structure + * + * Return: size of hw buffer in bytes + */ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) { return dev->hbuf_depth * sizeof(u32) - sizeof(struct mei_msg_hdr); @@ -363,7 +419,7 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) * @header: mei HECI header of message * @buf: message payload will be written * - * This function returns -EIO if write has failed + * Return: -EIO if write has failed */ static int mei_me_write_message(struct mei_device *dev, struct mei_msg_hdr *header, @@ -378,10 +434,10 @@ static int mei_me_write_message(struct mei_device *dev, int i; int empty_slots; - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); + dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); empty_slots = mei_hbuf_empty_slots(dev); - dev_dbg(&dev->pdev->dev, "empty slots = %hu.\n", empty_slots); + dev_dbg(dev->dev, "empty slots = %hu.\n", empty_slots); dw_cnt = mei_data2slots(length); if (empty_slots < 0 || dw_cnt > empty_slots) @@ -395,6 +451,7 @@ static int mei_me_write_message(struct mei_device *dev, rem = length & 0x3; if (rem > 0) { u32 reg = 0; + memcpy(®, &buf[length - rem], rem); mei_me_reg_write(hw, H_CB_WW, reg); } @@ -412,7 +469,7 @@ static int mei_me_write_message(struct mei_device *dev, * * @dev: the device structure * - * returns -EOVERFLOW if overflow, otherwise filled slots count + * Return: -EOVERFLOW if overflow, otherwise filled slots count */ static int mei_me_count_full_read_slots(struct mei_device *dev) { @@ -430,7 +487,7 @@ static int mei_me_count_full_read_slots(struct mei_device *dev) if (filled_slots > buffer_depth) return -EOVERFLOW; - dev_dbg(&dev->pdev->dev, "filled_slots =%08x\n", filled_slots); + dev_dbg(dev->dev, "filled_slots =%08x\n", filled_slots); return (int)filled_slots; } @@ -440,6 +497,8 @@ static int mei_me_count_full_read_slots(struct mei_device *dev) * @dev: the device structure * @buffer: message buffer will be written * @buffer_length: message size will be read + * + * Return: always 0 */ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, unsigned long buffer_length) @@ -453,6 +512,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, if (buffer_length > 0) { u32 reg = mei_me_mecbrw_read(dev); + memcpy(reg_buf, ®, buffer_length); } @@ -462,7 +522,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, } /** - * mei_me_pg_enter - write pg enter register to mei device. + * mei_me_pg_enter - write pg enter register * * @dev: the device structure */ @@ -470,12 +530,13 @@ static void mei_me_pg_enter(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); u32 reg = mei_me_reg_read(hw, H_HPG_CSR); + reg |= H_HPG_CSR_PGI; mei_me_reg_write(hw, H_HPG_CSR, reg); } /** - * mei_me_pg_enter - write pg enter register to mei device. + * mei_me_pg_exit - write pg exit register * * @dev: the device structure */ @@ -495,7 +556,7 @@ static void mei_me_pg_exit(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success an error code otherwise + * Return: 0 on success an error code otherwise */ int mei_me_pg_set_sync(struct mei_device *dev) { @@ -532,7 +593,7 @@ int mei_me_pg_set_sync(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success an error code otherwise + * Return: 0 on success an error code otherwise */ int mei_me_pg_unset_sync(struct mei_device *dev) { @@ -569,7 +630,7 @@ reply: * * @dev: the device structure * - * returns: true is pg supported, false otherwise + * Return: true is pg supported, false otherwise */ static bool mei_me_pg_is_enabled(struct mei_device *dev) { @@ -579,17 +640,13 @@ static bool mei_me_pg_is_enabled(struct mei_device *dev) if ((reg & ME_PGIC_HRA) == 0) goto notsupported; - if (dev->version.major_version < HBM_MAJOR_VERSION_PGI) - goto notsupported; - - if (dev->version.major_version == HBM_MAJOR_VERSION_PGI && - dev->version.minor_version < HBM_MINOR_VERSION_PGI) + if (!dev->hbm_f_pg_supported) goto notsupported; return true; notsupported: - dev_dbg(&dev->pdev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n", + dev_dbg(dev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n", !!(reg & ME_PGIC_HRA), dev->version.major_version, dev->version.minor_version, @@ -605,7 +662,7 @@ notsupported: * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t + * Return: irqreturn_t */ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) @@ -630,7 +687,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t + * Return: irqreturn_t * */ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) @@ -640,19 +697,19 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) s32 slots; int rets = 0; - dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); + 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); /* Ack the interrupt here * In case of MSI we don't go through the quick handler */ - if (pci_dev_msi_enabled(dev->pdev)) + if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) mei_clear_interrupts(dev); /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { - dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n"); + dev_warn(dev->dev, "FW not ready: resetting.\n"); schedule_work(&dev->reset_work); goto end; } @@ -661,19 +718,19 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) if (!mei_host_is_ready(dev)) { if (mei_hw_is_ready(dev)) { mei_me_hw_reset_release(dev); - dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); + dev_dbg(dev->dev, "we need to start the dev.\n"); dev->recvd_hw_ready = true; - wake_up_interruptible(&dev->wait_hw_ready); + wake_up(&dev->wait_hw_ready); } else { - dev_dbg(&dev->pdev->dev, "Spurious Interrupt\n"); + dev_dbg(dev->dev, "Spurious Interrupt\n"); } goto end; } /* check slots available for reading */ slots = mei_count_full_read_slots(dev); while (slots > 0) { - dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots); + dev_dbg(dev->dev, "slots to read = %08x\n", slots); rets = mei_irq_read_handler(dev, &complete_list, &slots); /* There is a race between ME write and interrupt delivery: * Not all data is always available immediately after the @@ -683,7 +740,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) break; if (rets && dev->dev_state != MEI_DEV_RESETTING) { - dev_err(&dev->pdev->dev, "mei_irq_read_handler ret = %d.\n", + dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", rets); schedule_work(&dev->reset_work); goto end; @@ -705,13 +762,14 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) mei_irq_compl_handler(dev, &complete_list); end: - dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets); + dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); mutex_unlock(&dev->device_lock); return IRQ_HANDLED; } static const struct mei_hw_ops mei_me_hw_ops = { + .fw_status = mei_me_fw_status, .pg_state = mei_me_pg_state, .host_is_ready = mei_me_host_is_ready, @@ -741,6 +799,7 @@ static const struct mei_hw_ops mei_me_hw_ops = { static bool mei_me_fw_type_nm(struct pci_dev *pdev) { u32 reg; + pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200; @@ -809,23 +868,22 @@ const struct mei_cfg mei_me_lpt_cfg = { * @pdev: The pci device structure * @cfg: per device generation config * - * returns The mei_device_device pointer on success, NULL on failure. + * Return: The mei_device_device pointer on success, NULL on failure. */ struct mei_device *mei_me_dev_init(struct pci_dev *pdev, const struct mei_cfg *cfg) { struct mei_device *dev; + struct mei_me_hw *hw; dev = kzalloc(sizeof(struct mei_device) + sizeof(struct mei_me_hw), GFP_KERNEL); if (!dev) return NULL; + hw = to_me_hw(dev); - mei_device_init(dev, cfg); - - dev->ops = &mei_me_hw_ops; - - dev->pdev = pdev; + mei_device_init(dev, &pdev->dev, &mei_me_hw_ops); + hw->cfg = cfg; return dev; } diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 12b0f4bbe1f1..e6a59a62573a 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -19,14 +19,44 @@ #ifndef _MEI_INTERFACE_H_ #define _MEI_INTERFACE_H_ -#include <linux/mei.h> #include <linux/irqreturn.h> +#include <linux/pci.h> +#include <linux/mei.h> + #include "mei_dev.h" #include "client.h" +/* + * mei_cfg - mei device configuration + * + * @fw_status: FW status + * @quirk_probe: device exclusion quirk + */ +struct mei_cfg { + const struct mei_fw_status fw_status; + bool (*quirk_probe)(struct pci_dev *pdev); +}; + + +#define MEI_PCI_DEVICE(dev, cfg) \ + .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ + .driver_data = (kernel_ulong_t)&(cfg) + + #define MEI_ME_RPM_TIMEOUT 500 /* ms */ +/** + * struct mei_me_hw - me hw specific data + * + * @cfg: per device generation config and ops + * @mem_addr: io memory address + * @host_hw_state: cached host state + * @me_hw_state: cached me (fw) state + * @pg_state: power gating state + */ struct mei_me_hw { + const struct mei_cfg *cfg; void __iomem *mem_addr; /* * hw states of host and fw(ME) diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index f1cd166094f2..c5e1902e493f 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -28,11 +28,12 @@ #include "hbm.h" /** - * mei_txe_reg_read - Reads 32bit data from the device + * mei_txe_reg_read - Reads 32bit data from the txe device * * @base_addr: registers base address * @offset: register offset * + * Return: register value */ static inline u32 mei_txe_reg_read(void __iomem *base_addr, unsigned long offset) @@ -41,7 +42,7 @@ static inline u32 mei_txe_reg_read(void __iomem *base_addr, } /** - * mei_txe_reg_write - Writes 32bit data to the device + * mei_txe_reg_write - Writes 32bit data to the txe device * * @base_addr: registers base address * @offset: register offset @@ -56,10 +57,12 @@ static inline void mei_txe_reg_write(void __iomem *base_addr, /** * mei_txe_sec_reg_read_silent - Reads 32bit data from the SeC BAR * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * * Doesn't check for aliveness while Reads 32bit data from the SeC BAR + * + * Return: register value */ static inline u32 mei_txe_sec_reg_read_silent(struct mei_txe_hw *hw, unsigned long offset) @@ -70,10 +73,12 @@ static inline u32 mei_txe_sec_reg_read_silent(struct mei_txe_hw *hw, /** * mei_txe_sec_reg_read - Reads 32bit data from the SeC BAR * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * * Reads 32bit data from the SeC BAR and shout loud if aliveness is not set + * + * Return: register value */ static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw, unsigned long offset) @@ -85,7 +90,7 @@ static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw, * mei_txe_sec_reg_write_silent - Writes 32bit data to the SeC BAR * doesn't check for aliveness * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * @value: value to write * @@ -100,7 +105,7 @@ static inline void mei_txe_sec_reg_write_silent(struct mei_txe_hw *hw, /** * mei_txe_sec_reg_write - Writes 32bit data to the SeC BAR * - * @dev: the device structure + * @hw: the txe hardware structure * @offset: register offset * @value: value to write * @@ -115,9 +120,10 @@ static inline void mei_txe_sec_reg_write(struct mei_txe_hw *hw, /** * mei_txe_br_reg_read - Reads 32bit data from the Bridge BAR * - * @hw: the device structure + * @hw: the txe hardware structure * @offset: offset from which to read the data * + * Return: the byte read. */ static inline u32 mei_txe_br_reg_read(struct mei_txe_hw *hw, unsigned long offset) @@ -128,7 +134,7 @@ static inline u32 mei_txe_br_reg_read(struct mei_txe_hw *hw, /** * mei_txe_br_reg_write - Writes 32bit data to the Bridge BAR * - * @hw: the device structure + * @hw: the txe hardware structure * @offset: offset from which to write the data * @value: the byte to write */ @@ -147,7 +153,10 @@ static inline void mei_txe_br_reg_write(struct mei_txe_hw *hw, * Request for aliveness change and returns true if the change is * really needed and false if aliveness is already * in the requested state - * Requires device lock to be held + * + * Locking: called under "dev->device_lock" lock + * + * Return: true if request was send */ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) { @@ -155,7 +164,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) struct mei_txe_hw *hw = to_txe_hw(dev); bool do_req = hw->aliveness != req; - dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n", + dev_dbg(dev->dev, "Aliveness current=%d request=%d\n", hw->aliveness, req); if (do_req) { dev->pg_event = MEI_PG_EVENT_WAIT; @@ -172,26 +181,31 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) * * Extract HICR_HOST_ALIVENESS_RESP_ACK bit from * from HICR_HOST_ALIVENESS_REQ register value + * + * Return: SICR_HOST_ALIVENESS_REQ_REQUESTED bit value */ static u32 mei_txe_aliveness_req_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg; + reg = mei_txe_br_reg_read(hw, SICR_HOST_ALIVENESS_REQ_REG); return reg & SICR_HOST_ALIVENESS_REQ_REQUESTED; } /** * mei_txe_aliveness_get - get aliveness response register value + * * @dev: the device structure * - * Extract HICR_HOST_ALIVENESS_RESP_ACK bit - * from HICR_HOST_ALIVENESS_RESP register value + * Return: HICR_HOST_ALIVENESS_RESP_ACK bit from HICR_HOST_ALIVENESS_RESP + * register */ static u32 mei_txe_aliveness_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg; + reg = mei_txe_br_reg_read(hw, HICR_HOST_ALIVENESS_RESP_REG); return reg & HICR_HOST_ALIVENESS_RESP_ACK; } @@ -203,7 +217,8 @@ static u32 mei_txe_aliveness_get(struct mei_device *dev) * @expected: expected aliveness value * * Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set - * returns > 0 if the expected value was received, -ETIME otherwise + * + * Return: > 0 if the expected value was received, -ETIME otherwise */ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) { @@ -214,7 +229,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) hw->aliveness = mei_txe_aliveness_get(dev); if (hw->aliveness == expected) { dev->pg_event = MEI_PG_EVENT_IDLE; - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "aliveness settled after %d msecs\n", t); return t; } @@ -225,7 +240,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) } while (t < SEC_ALIVENESS_WAIT_TIMEOUT); dev->pg_event = MEI_PG_EVENT_IDLE; - dev_err(&dev->pdev->dev, "aliveness timed out\n"); + dev_err(dev->dev, "aliveness timed out\n"); return -ETIME; } @@ -236,7 +251,8 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) * @expected: expected aliveness value * * Waits for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set - * returns returns 0 on success and < 0 otherwise + * + * Return: 0 on success and < 0 otherwise */ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) { @@ -259,10 +275,10 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) ret = hw->aliveness == expected ? 0 : -ETIME; if (ret) - dev_warn(&dev->pdev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n", + dev_warn(dev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n", err, hw->aliveness, dev->pg_event); else - dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n", + dev_dbg(dev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n", jiffies_to_msecs(timeout - err), hw->aliveness, dev->pg_event); @@ -274,8 +290,9 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) * mei_txe_aliveness_set_sync - sets an wait for aliveness to complete * * @dev: the device structure + * @req: requested aliveness value * - * returns returns 0 on success and < 0 otherwise + * Return: 0 on success and < 0 otherwise */ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req) { @@ -289,7 +306,7 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req) * * @dev: the device structure * - * returns: true is pg supported, false otherwise + * Return: true is pg supported, false otherwise */ static bool mei_txe_pg_is_enabled(struct mei_device *dev) { @@ -302,11 +319,12 @@ static bool mei_txe_pg_is_enabled(struct mei_device *dev) * * @dev: the device structure * - * returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise + * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise */ static inline enum mei_pg_state mei_txe_pg_state(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON; } @@ -326,9 +344,10 @@ static void mei_txe_input_ready_interrupt_enable(struct mei_device *dev) } /** - * mei_txe_input_doorbell_set - * - Sets bit 0 in SEC_IPC_INPUT_DOORBELL.IPC_INPUT_DOORBELL. - * @dev: the device structure + * mei_txe_input_doorbell_set - sets bit 0 in + * SEC_IPC_INPUT_DOORBELL.IPC_INPUT_DOORBELL. + * + * @hw: the txe hardware structure */ static void mei_txe_input_doorbell_set(struct mei_txe_hw *hw) { @@ -340,7 +359,7 @@ static void mei_txe_input_doorbell_set(struct mei_txe_hw *hw) /** * mei_txe_output_ready_set - Sets the SICR_SEC_IPC_OUTPUT_STATUS bit to 1 * - * @dev: the device structure + * @hw: the txe hardware structure */ static void mei_txe_output_ready_set(struct mei_txe_hw *hw) { @@ -353,11 +372,14 @@ static void mei_txe_output_ready_set(struct mei_txe_hw *hw) * mei_txe_is_input_ready - check if TXE is ready for receiving data * * @dev: the device structure + * + * Return: true if INPUT STATUS READY bit is set */ static bool mei_txe_is_input_ready(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 status; + status = mei_txe_sec_reg_read(hw, SEC_IPC_INPUT_STATUS_REG); return !!(SEC_IPC_INPUT_STATUS_RDY & status); } @@ -370,6 +392,7 @@ static bool mei_txe_is_input_ready(struct mei_device *dev) static inline void mei_txe_intr_clear(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_sec_reg_write_silent(hw, SEC_IPC_HOST_INT_STATUS_REG, SEC_IPC_HOST_INT_STATUS_PENDING); mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_STS_MSK); @@ -384,6 +407,7 @@ static inline void mei_txe_intr_clear(struct mei_device *dev) static void mei_txe_intr_disable(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, HHIER_REG, 0); mei_txe_br_reg_write(hw, HIER_REG, 0); } @@ -395,6 +419,7 @@ static void mei_txe_intr_disable(struct mei_device *dev) static void mei_txe_intr_enable(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, HHIER_REG, IPC_HHIER_MSK); mei_txe_br_reg_write(hw, HIER_REG, HIER_INT_EN_MSK); } @@ -407,6 +432,8 @@ static void mei_txe_intr_enable(struct mei_device *dev) * * Checks if there are pending interrupts * only Aliveness, Readiness, Input ready, and Output doorbell are relevant + * + * Return: true if there are pending interrupts */ static bool mei_txe_pending_interrupts(struct mei_device *dev) { @@ -418,7 +445,7 @@ static bool mei_txe_pending_interrupts(struct mei_device *dev) TXE_INTR_OUT_DB)); if (ret) { - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "Pending Interrupts InReady=%01d Readiness=%01d, Aliveness=%01d, OutDoor=%01d\n", !!(hw->intr_cause & TXE_INTR_IN_READY), !!(hw->intr_cause & TXE_INTR_READINESS), @@ -440,6 +467,7 @@ static void mei_txe_input_payload_write(struct mei_device *dev, unsigned long idx, u32 value) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_PAYLOAD_REG + (idx * sizeof(u32)), value); } @@ -451,12 +479,13 @@ static void mei_txe_input_payload_write(struct mei_device *dev, * @dev: the device structure * @idx: index in the device buffer * - * returns register value at index + * Return: register value at index */ static u32 mei_txe_out_data_read(const struct mei_device *dev, unsigned long idx) { struct mei_txe_hw *hw = to_txe_hw(dev); + return mei_txe_br_reg_read(hw, BRIDGE_IPC_OUTPUT_PAYLOAD_REG + (idx * sizeof(u32))); } @@ -464,26 +493,28 @@ static u32 mei_txe_out_data_read(const struct mei_device *dev, /* Readiness */ /** - * mei_txe_readiness_set_host_rdy + * mei_txe_readiness_set_host_rdy - set host readiness bit * * @dev: the device structure */ static void mei_txe_readiness_set_host_rdy(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG, SICR_HOST_IPC_READINESS_HOST_RDY); } /** - * mei_txe_readiness_clear + * mei_txe_readiness_clear - clear host readiness bit * * @dev: the device structure */ static void mei_txe_readiness_clear(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG, SICR_HOST_IPC_READINESS_RDY_CLR); } @@ -492,10 +523,13 @@ static void mei_txe_readiness_clear(struct mei_device *dev) * the HICR_SEC_IPC_READINESS register value * * @dev: the device structure + * + * Return: the HICR_SEC_IPC_READINESS register value */ static u32 mei_txe_readiness_get(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + return mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); } @@ -504,7 +538,9 @@ static u32 mei_txe_readiness_get(struct mei_device *dev) * mei_txe_readiness_is_sec_rdy - check readiness * for HICR_SEC_IPC_READINESS_SEC_RDY * - * @readiness - cached readiness state + * @readiness: cached readiness state + * + * Return: true if readiness bit is set */ static inline bool mei_txe_readiness_is_sec_rdy(u32 readiness) { @@ -515,10 +551,13 @@ static inline bool mei_txe_readiness_is_sec_rdy(u32 readiness) * mei_txe_hw_is_ready - check if the hw is ready * * @dev: the device structure + * + * Return: true if sec is ready */ static bool mei_txe_hw_is_ready(struct mei_device *dev) { u32 readiness = mei_txe_readiness_get(dev); + return mei_txe_readiness_is_sec_rdy(readiness); } @@ -526,11 +565,14 @@ static bool mei_txe_hw_is_ready(struct mei_device *dev) * mei_txe_host_is_ready - check if the host is ready * * @dev: the device structure + * + * Return: true if host is ready */ static inline bool mei_txe_host_is_ready(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); u32 reg = mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); + return !!(reg & HICR_SEC_IPC_READINESS_HOST_RDY); } @@ -539,7 +581,7 @@ static inline bool mei_txe_host_is_ready(struct mei_device *dev) * * @dev: the device structure * - * returns 0 on success and -ETIME on timeout + * Return: 0 on success and -ETIME on timeout */ static int mei_txe_readiness_wait(struct mei_device *dev) { @@ -551,7 +593,7 @@ static int mei_txe_readiness_wait(struct mei_device *dev) msecs_to_jiffies(SEC_RESET_WAIT_TIMEOUT)); mutex_lock(&dev->device_lock); if (!dev->recvd_hw_ready) { - dev_err(&dev->pdev->dev, "wait for readiness failed\n"); + dev_err(dev->dev, "wait for readiness failed\n"); return -ETIME; } @@ -559,6 +601,42 @@ static int mei_txe_readiness_wait(struct mei_device *dev) return 0; } +static const struct mei_fw_status mei_txe_fw_sts = { + .count = 2, + .status[0] = PCI_CFG_TXE_FW_STS0, + .status[1] = PCI_CFG_TXE_FW_STS1 +}; + +/** + * mei_txe_fw_status - read fw status register from pci config space + * + * @dev: mei device + * @fw_status: fw status register values + * + * Return: 0 on success, error otherwise + */ +static int mei_txe_fw_status(struct mei_device *dev, + struct mei_fw_status *fw_status) +{ + const struct mei_fw_status *fw_src = &mei_txe_fw_sts; + struct pci_dev *pdev = to_pci_dev(dev->dev); + int ret; + int i; + + if (!fw_status) + return -EINVAL; + + fw_status->count = fw_src->count; + for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { + ret = pci_read_config_dword(pdev, + fw_src->status[i], &fw_status->status[i]); + if (ret) + return ret; + } + + return 0; +} + /** * mei_txe_hw_config - configure hardware at the start of the devices * @@ -571,13 +649,14 @@ static void mei_txe_hw_config(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + /* Doesn't change in runtime */ dev->hbuf_depth = PAYLOAD_SIZE / 4; hw->aliveness = mei_txe_aliveness_get(dev); hw->readiness = mei_txe_readiness_get(dev); - dev_dbg(&dev->pdev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n", + dev_dbg(dev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n", hw->aliveness, hw->readiness); } @@ -588,7 +667,8 @@ static void mei_txe_hw_config(struct mei_device *dev) * @dev: the device structure * @header: header of message * @buf: message buffer will be written - * returns 1 if success, 0 - otherwise. + * + * Return: 0 if success, <0 - otherwise. */ static int mei_txe_write(struct mei_device *dev, @@ -607,7 +687,7 @@ static int mei_txe_write(struct mei_device *dev, length = header->length; - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); + dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); dw_cnt = mei_data2slots(length); if (dw_cnt > slots) @@ -621,8 +701,9 @@ static int mei_txe_write(struct mei_device *dev, if (!mei_txe_is_input_ready(dev)) { struct mei_fw_status fw_status; + mei_fw_status(dev, &fw_status); - dev_err(&dev->pdev->dev, "Input is not ready " FW_STS_FMT "\n", + dev_err(dev->dev, "Input is not ready " FW_STS_FMT "\n", FW_STS_PRM(fw_status)); return -EAGAIN; } @@ -635,6 +716,7 @@ static int mei_txe_write(struct mei_device *dev, rem = length & 0x3; if (rem > 0) { u32 reg = 0; + memcpy(®, &buf[length - rem], rem); mei_txe_input_payload_write(dev, i + 1, reg); } @@ -653,7 +735,7 @@ static int mei_txe_write(struct mei_device *dev, * * @dev: the device structure * - * returns the PAYLOAD_SIZE - 4 + * Return: the PAYLOAD_SIZE - 4 */ static size_t mei_txe_hbuf_max_len(const struct mei_device *dev) { @@ -665,11 +747,12 @@ static size_t mei_txe_hbuf_max_len(const struct mei_device *dev) * * @dev: the device structure * - * returns always hbuf_depth + * Return: always hbuf_depth */ static int mei_txe_hbuf_empty_slots(struct mei_device *dev) { struct mei_txe_hw *hw = to_txe_hw(dev); + return hw->slots; } @@ -678,7 +761,7 @@ static int mei_txe_hbuf_empty_slots(struct mei_device *dev) * * @dev: the device structure * - * returns always buffer size in dwords count + * Return: always buffer size in dwords count */ static int mei_txe_count_full_read_slots(struct mei_device *dev) { @@ -691,7 +774,7 @@ static int mei_txe_count_full_read_slots(struct mei_device *dev) * * @dev: the device structure * - * returns mei message header + * Return: mei message header */ static u32 mei_txe_read_hdr(const struct mei_device *dev) @@ -705,33 +788,35 @@ static u32 mei_txe_read_hdr(const struct mei_device *dev) * @buf: message buffer will be written * @len: message size will be read * - * returns -EINVAL on error wrong argument and 0 on success + * Return: -EINVAL on error wrong argument and 0 on success */ static int mei_txe_read(struct mei_device *dev, unsigned char *buf, unsigned long len) { struct mei_txe_hw *hw = to_txe_hw(dev); + u32 *reg_buf, reg; + u32 rem; u32 i; - u32 *reg_buf = (u32 *)buf; - u32 rem = len & 0x3; if (WARN_ON(!buf || !len)) return -EINVAL; - dev_dbg(&dev->pdev->dev, - "buffer-length = %lu buf[0]0x%08X\n", + reg_buf = (u32 *)buf; + rem = len & 0x3; + + dev_dbg(dev->dev, "buffer-length = %lu buf[0]0x%08X\n", len, mei_txe_out_data_read(dev, 0)); for (i = 0; i < len / 4; i++) { /* skip header: index starts from 1 */ - u32 reg = mei_txe_out_data_read(dev, i + 1); - dev_dbg(&dev->pdev->dev, "buf[%d] = 0x%08X\n", i, reg); + reg = mei_txe_out_data_read(dev, i + 1); + dev_dbg(dev->dev, "buf[%d] = 0x%08X\n", i, reg); *reg_buf++ = reg; } if (rem) { - u32 reg = mei_txe_out_data_read(dev, i + 1); + reg = mei_txe_out_data_read(dev, i + 1); memcpy(reg_buf, ®, rem); } @@ -745,7 +830,7 @@ static int mei_txe_read(struct mei_device *dev, * @dev: the device structure * @intr_enable: if interrupt should be enabled after reset. * - * returns 0 on success and < 0 in case of error + * Return: 0 on success and < 0 in case of error */ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) { @@ -771,8 +856,7 @@ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) */ if (aliveness_req != hw->aliveness) if (mei_txe_aliveness_poll(dev, aliveness_req) < 0) { - dev_err(&dev->pdev->dev, - "wait for aliveness settle failed ... bailing out\n"); + dev_err(dev->dev, "wait for aliveness settle failed ... bailing out\n"); return -EIO; } @@ -782,14 +866,13 @@ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) if (aliveness_req) { mei_txe_aliveness_set(dev, 0); if (mei_txe_aliveness_poll(dev, 0) < 0) { - dev_err(&dev->pdev->dev, - "wait for aliveness failed ... bailing out\n"); + dev_err(dev->dev, "wait for aliveness failed ... bailing out\n"); return -EIO; } } /* - * Set rediness RDY_CLR bit + * Set readiness RDY_CLR bit */ mei_txe_readiness_clear(dev); @@ -801,7 +884,7 @@ static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) * * @dev: the device structure * - * returns 0 on success and < 0 in case of error + * Return: 0 on success an error code otherwise */ static int mei_txe_hw_start(struct mei_device *dev) { @@ -815,7 +898,7 @@ static int mei_txe_hw_start(struct mei_device *dev) ret = mei_txe_readiness_wait(dev); if (ret < 0) { - dev_err(&dev->pdev->dev, "wating for readiness failed\n"); + dev_err(dev->dev, "waiting for readiness failed\n"); return ret; } @@ -831,7 +914,7 @@ static int mei_txe_hw_start(struct mei_device *dev) ret = mei_txe_aliveness_set_sync(dev, 1); if (ret < 0) { - dev_err(&dev->pdev->dev, "wait for aliveness failed ... bailing out\n"); + dev_err(dev->dev, "wait for aliveness failed ... bailing out\n"); return ret; } @@ -857,6 +940,8 @@ static int mei_txe_hw_start(struct mei_device *dev) * * @dev: the device structure * @do_ack: acknowledge interrupts + * + * Return: true if found interrupts to process. */ static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack) { @@ -912,7 +997,8 @@ out: * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t + * Return: IRQ_WAKE_THREAD if interrupt is designed for the device + * IRQ_NONE otherwise */ irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id) { @@ -930,8 +1016,7 @@ irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id) * @irq: The irq number * @dev_id: pointer to the device structure * - * returns irqreturn_t - * + * Return: IRQ_HANDLED */ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) { @@ -941,7 +1026,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) s32 slots; int rets = 0; - dev_dbg(&dev->pdev->dev, "irq thread: Interrupt Registers HHISR|HISR|SEC=%02X|%04X|%02X\n", + dev_dbg(dev->dev, "irq thread: Interrupt Registers HHISR|HISR|SEC=%02X|%04X|%02X\n", mei_txe_br_reg_read(hw, HHISR_REG), mei_txe_br_reg_read(hw, HISR_REG), mei_txe_sec_reg_read_silent(hw, SEC_IPC_HOST_INT_STATUS_REG)); @@ -951,7 +1036,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) mutex_lock(&dev->device_lock); mei_io_list_init(&complete_list); - if (pci_dev_msi_enabled(dev->pdev)) + if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) mei_txe_check_and_ack_intrs(dev, true); /* show irq events */ @@ -965,17 +1050,17 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) * or TXE driver resetting the HECI interface. */ if (test_and_clear_bit(TXE_INTR_READINESS_BIT, &hw->intr_cause)) { - dev_dbg(&dev->pdev->dev, "Readiness Interrupt was received...\n"); + dev_dbg(dev->dev, "Readiness Interrupt was received...\n"); /* Check if SeC is going through reset */ if (mei_txe_readiness_is_sec_rdy(hw->readiness)) { - dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); + dev_dbg(dev->dev, "we need to start the dev.\n"); dev->recvd_hw_ready = true; } else { dev->recvd_hw_ready = false; if (dev->dev_state != MEI_DEV_RESETTING) { - dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n"); + dev_warn(dev->dev, "FW not ready: resetting.\n"); schedule_work(&dev->reset_work); goto end; @@ -992,7 +1077,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) if (test_and_clear_bit(TXE_INTR_ALIVENESS_BIT, &hw->intr_cause)) { /* Clear the interrupt cause */ - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "Aliveness Interrupt: Status: %d\n", hw->aliveness); dev->pg_event = MEI_PG_EVENT_RECEIVED; if (waitqueue_active(&hw->wait_aliveness_resp)) @@ -1008,7 +1093,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) /* Read from TXE */ rets = mei_irq_read_handler(dev, &complete_list, &slots); if (rets && dev->dev_state != MEI_DEV_RESETTING) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", rets); schedule_work(&dev->reset_work); @@ -1026,7 +1111,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) dev->hbuf_is_ready = mei_hbuf_is_ready(dev); rets = mei_irq_write_handler(dev, &complete_list); if (rets && rets != -EMSGSIZE) - dev_err(&dev->pdev->dev, "mei_irq_write_handler ret = %d.\n", + dev_err(dev->dev, "mei_irq_write_handler ret = %d.\n", rets); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); } @@ -1034,7 +1119,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) mei_irq_compl_handler(dev, &complete_list); end: - dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets); + dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); mutex_unlock(&dev->device_lock); @@ -1046,6 +1131,7 @@ static const struct mei_hw_ops mei_txe_hw_ops = { .host_is_ready = mei_txe_host_is_ready, + .fw_status = mei_txe_fw_status, .pg_state = mei_txe_pg_state, .hw_is_ready = mei_txe_hw_is_ready, @@ -1072,27 +1158,14 @@ static const struct mei_hw_ops mei_txe_hw_ops = { }; -#define MEI_CFG_TXE_FW_STS \ - .fw_status.count = 2, \ - .fw_status.status[0] = PCI_CFG_TXE_FW_STS0, \ - .fw_status.status[1] = PCI_CFG_TXE_FW_STS1 - -const struct mei_cfg mei_txe_cfg = { - MEI_CFG_TXE_FW_STS, -}; - - /** * mei_txe_dev_init - allocates and initializes txe hardware specific structure * - * @pdev - pci device - * @cfg - per device generation config - * - * returns struct mei_device * on success or NULL; + * @pdev: pci device * + * Return: struct mei_device * on success or NULL */ -struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, - const struct mei_cfg *cfg) +struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) { struct mei_device *dev; struct mei_txe_hw *hw; @@ -1102,15 +1175,12 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, if (!dev) return NULL; - mei_device_init(dev, cfg); + mei_device_init(dev, &pdev->dev, &mei_txe_hw_ops); hw = to_txe_hw(dev); init_waitqueue_head(&hw->wait_aliveness_resp); - dev->ops = &mei_txe_hw_ops; - - dev->pdev = pdev; return dev; } @@ -1120,6 +1190,8 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, * @dev: the device structure * @addr: physical address start of the range * @range: physical range size + * + * Return: 0 on success an error code otherwise */ int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range) { @@ -1151,7 +1223,7 @@ int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range) mei_txe_br_reg_write(hw, SATT2_SAP_SIZE_REG, range); mei_txe_br_reg_write(hw, SATT2_BRG_BA_LSB_REG, lo32); mei_txe_br_reg_write(hw, SATT2_CTRL_REG, ctrl); - dev_dbg(&dev->pdev->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n", + dev_dbg(dev->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n", range, lo32, ctrl); return 0; diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h index e244af79167f..ce3ed0b88b0c 100644 --- a/drivers/misc/mei/hw-txe.h +++ b/drivers/misc/mei/hw-txe.h @@ -40,6 +40,7 @@ * @mem_addr: SeC and BRIDGE bars * @aliveness: aliveness (power gating) state of the hardware * @readiness: readiness state of the hardware + * @slots: number of empty slots * @wait_aliveness_resp: aliveness wait queue * @intr_cause: translated interrupt cause */ @@ -61,10 +62,7 @@ static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw) return container_of((void *)hw, struct mei_device, hw); } -extern const struct mei_cfg mei_txe_cfg; - -struct mei_device *mei_txe_dev_init(struct pci_dev *pdev, - const struct mei_cfg *cfg); +struct mei_device *mei_txe_dev_init(struct pci_dev *pdev); irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id); irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id); diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index dd448e58cc87..16fef6dc4dd7 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -97,23 +97,52 @@ enum mei_stop_reason_types { SYSTEM_S5_ENTRY = 0x08 }; + +/** + * enum mei_hbm_status - mei host bus messages return values + * + * @MEI_HBMS_SUCCESS : status success + * @MEI_HBMS_CLIENT_NOT_FOUND : client not found + * @MEI_HBMS_ALREADY_EXISTS : connection already established + * @MEI_HBMS_REJECTED : connection is rejected + * @MEI_HBMS_INVALID_PARAMETER : invalid parameter + * @MEI_HBMS_NOT_ALLOWED : operation not allowed + * @MEI_HBMS_ALREADY_STARTED : system is already started + * @MEI_HBMS_NOT_STARTED : system not started + * + * @MEI_HBMS_MAX : sentinel + */ +enum mei_hbm_status { + MEI_HBMS_SUCCESS = 0, + MEI_HBMS_CLIENT_NOT_FOUND = 1, + MEI_HBMS_ALREADY_EXISTS = 2, + MEI_HBMS_REJECTED = 3, + MEI_HBMS_INVALID_PARAMETER = 4, + MEI_HBMS_NOT_ALLOWED = 5, + MEI_HBMS_ALREADY_STARTED = 6, + MEI_HBMS_NOT_STARTED = 7, + + MEI_HBMS_MAX +}; + + /* * Client Connect Status * used by hbm_client_connect_response.status */ enum mei_cl_connect_status { - MEI_CL_CONN_SUCCESS = 0x00, - MEI_CL_CONN_NOT_FOUND = 0x01, - MEI_CL_CONN_ALREADY_STARTED = 0x02, - MEI_CL_CONN_OUT_OF_RESOURCES = 0x03, - MEI_CL_CONN_MESSAGE_SMALL = 0x04 + MEI_CL_CONN_SUCCESS = MEI_HBMS_SUCCESS, + MEI_CL_CONN_NOT_FOUND = MEI_HBMS_CLIENT_NOT_FOUND, + MEI_CL_CONN_ALREADY_STARTED = MEI_HBMS_ALREADY_EXISTS, + MEI_CL_CONN_OUT_OF_RESOURCES = MEI_HBMS_REJECTED, + MEI_CL_CONN_MESSAGE_SMALL = MEI_HBMS_INVALID_PARAMETER, }; /* * Client Disconnect Status */ enum mei_cl_disconnect_status { - MEI_CL_DISCONN_SUCCESS = 0x00 + MEI_CL_DISCONN_SUCCESS = MEI_HBMS_SUCCESS }; /* @@ -138,10 +167,10 @@ struct mei_bus_message { * struct hbm_cl_cmd - client specific host bus command * CONNECT, DISCONNECT, and FlOW CONTROL * - * @hbm_cmd - bus message command header - * @me_addr - address of the client in ME - * @host_addr - address of the client in the driver - * @data + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @data: generic data */ struct mei_hbm_cl_cmd { u8 hbm_cmd; @@ -206,14 +235,13 @@ struct mei_client_properties { struct hbm_props_request { u8 hbm_cmd; - u8 address; + u8 me_addr; u8 reserved[2]; } __packed; - struct hbm_props_response { u8 hbm_cmd; - u8 address; + u8 me_addr; u8 status; u8 reserved[1]; struct mei_client_properties client_properties; @@ -222,8 +250,8 @@ struct hbm_props_response { /** * struct hbm_power_gate - power gate request/response * - * @hbm_cmd - bus message command header - * @reserved[3] + * @hbm_cmd: bus message command header + * @reserved: reserved */ struct hbm_power_gate { u8 hbm_cmd; @@ -233,10 +261,10 @@ struct hbm_power_gate { /** * struct hbm_client_connect_request - connect/disconnect request * - * @hbm_cmd - bus message command header - * @me_addr - address of the client in ME - * @host_addr - address of the client in the driver - * @reserved + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @reserved: reserved */ struct hbm_client_connect_request { u8 hbm_cmd; @@ -248,10 +276,10 @@ struct hbm_client_connect_request { /** * struct hbm_client_connect_response - connect/disconnect response * - * @hbm_cmd - bus message command header - * @me_addr - address of the client in ME - * @host_addr - address of the client in the driver - * @status - status of the request + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @status: status of the request */ struct hbm_client_connect_response { u8 hbm_cmd; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 006929222481..7901d076c127 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -15,7 +15,6 @@ */ #include <linux/export.h> -#include <linux/pci.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/delay.h> @@ -43,13 +42,23 @@ const char *mei_dev_state_str(int state) #undef MEI_DEV_STATE } +const char *mei_pg_state_str(enum mei_pg_state state) +{ +#define MEI_PG_STATE(state) case MEI_PG_##state: return #state + switch (state) { + MEI_PG_STATE(OFF); + MEI_PG_STATE(ON); + default: + return "unknown"; + } +#undef MEI_PG_STATE +} + /** - * mei_cancel_work. Cancel mei background jobs + * mei_cancel_work - Cancel mei background jobs * * @dev: the device structure - * - * returns 0 on success or < 0 if the reset hasn't succeeded */ void mei_cancel_work(struct mei_device *dev) { @@ -64,6 +73,8 @@ EXPORT_SYMBOL_GPL(mei_cancel_work); * mei_reset - resets host and fw. * * @dev: the device structure + * + * Return: 0 on success or < 0 if the reset hasn't succeeded */ int mei_reset(struct mei_device *dev) { @@ -76,8 +87,9 @@ int mei_reset(struct mei_device *dev) state != MEI_DEV_POWER_DOWN && state != MEI_DEV_POWER_UP) { struct mei_fw_status fw_status; + mei_fw_status(dev, &fw_status); - dev_warn(&dev->pdev->dev, + dev_warn(dev->dev, "unexpected reset: dev_state = %s " FW_STS_FMT "\n", mei_dev_state_str(state), FW_STS_PRM(fw_status)); } @@ -95,7 +107,7 @@ int mei_reset(struct mei_device *dev) dev->reset_count++; if (dev->reset_count > MEI_MAX_CONSEC_RESET) { - dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + dev_err(dev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); dev->dev_state = MEI_DEV_DISABLED; return -ENODEV; } @@ -116,7 +128,7 @@ int mei_reset(struct mei_device *dev) mei_cl_all_wakeup(dev); /* remove entry if already in list */ - dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n"); + dev_dbg(dev->dev, "remove iamthif and wd from the file list.\n"); mei_cl_unlink(&dev->wd_cl); mei_cl_unlink(&dev->iamthif_cl); mei_amthif_reset_params(dev); @@ -128,28 +140,28 @@ int mei_reset(struct mei_device *dev) dev->wd_pending = false; if (ret) { - dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret); + dev_err(dev->dev, "hw_reset failed ret = %d\n", ret); return ret; } if (state == MEI_DEV_POWER_DOWN) { - dev_dbg(&dev->pdev->dev, "powering down: end of reset\n"); + dev_dbg(dev->dev, "powering down: end of reset\n"); dev->dev_state = MEI_DEV_DISABLED; return 0; } ret = mei_hw_start(dev); if (ret) { - dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret); + dev_err(dev->dev, "hw_start failed ret = %d\n", ret); return ret; } - dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); + dev_dbg(dev->dev, "link is established start sending messages.\n"); dev->dev_state = MEI_DEV_INIT_CLIENTS; ret = mei_hbm_start_req(dev); if (ret) { - dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret); + dev_err(dev->dev, "hbm_start failed ret = %d\n", ret); dev->dev_state = MEI_DEV_RESETTING; return ret; } @@ -163,11 +175,12 @@ EXPORT_SYMBOL_GPL(mei_reset); * * @dev: the device structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_start(struct mei_device *dev) { int ret; + mutex_lock(&dev->device_lock); /* acknowledge interrupt and stop interrupts */ @@ -175,7 +188,7 @@ int mei_start(struct mei_device *dev) mei_hw_config(dev); - dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); + dev_dbg(dev->dev, "reset in start the mei device.\n"); dev->reset_count = 0; do { @@ -183,43 +196,43 @@ int mei_start(struct mei_device *dev) ret = mei_reset(dev); if (ret == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "reset failed ret = %d", ret); + dev_err(dev->dev, "reset failed ret = %d", ret); goto err; } } while (ret); /* we cannot start the device w/o hbm start message completed */ if (dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "reset failed"); + dev_err(dev->dev, "reset failed"); goto err; } if (mei_hbm_start_wait(dev)) { - dev_err(&dev->pdev->dev, "HBM haven't started"); + dev_err(dev->dev, "HBM haven't started"); goto err; } if (!mei_host_is_ready(dev)) { - dev_err(&dev->pdev->dev, "host is not ready.\n"); + dev_err(dev->dev, "host is not ready.\n"); goto err; } if (!mei_hw_is_ready(dev)) { - dev_err(&dev->pdev->dev, "ME is not ready.\n"); + dev_err(dev->dev, "ME is not ready.\n"); goto err; } if (!mei_hbm_version_is_supported(dev)) { - dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); + dev_dbg(dev->dev, "MEI start failed.\n"); goto err; } - dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); + dev_dbg(dev->dev, "link layer has been established.\n"); mutex_unlock(&dev->device_lock); return 0; err: - dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); + dev_err(dev->dev, "link layer initialization failed.\n"); dev->dev_state = MEI_DEV_DISABLED; mutex_unlock(&dev->device_lock); return -ENODEV; @@ -231,7 +244,7 @@ EXPORT_SYMBOL_GPL(mei_start); * * @dev: the device structure * - * returns 0 on success or -ENODEV if the restart hasn't succeeded + * Return: 0 on success or -ENODEV if the restart hasn't succeeded */ int mei_restart(struct mei_device *dev) { @@ -249,7 +262,7 @@ int mei_restart(struct mei_device *dev) mutex_unlock(&dev->device_lock); if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "device disabled = %d\n", err); + dev_err(dev->dev, "device disabled = %d\n", err); return -ENODEV; } @@ -275,7 +288,7 @@ static void mei_reset_work(struct work_struct *work) mutex_unlock(&dev->device_lock); if (dev->dev_state == MEI_DEV_DISABLED) { - dev_err(&dev->pdev->dev, "device disabled = %d\n", ret); + dev_err(dev->dev, "device disabled = %d\n", ret); return; } @@ -286,7 +299,7 @@ static void mei_reset_work(struct work_struct *work) void mei_stop(struct mei_device *dev) { - dev_dbg(&dev->pdev->dev, "stopping the device.\n"); + dev_dbg(dev->dev, "stopping the device.\n"); mei_cancel_work(dev); @@ -312,7 +325,7 @@ EXPORT_SYMBOL_GPL(mei_stop); * * @dev: the device structure * - * returns true of there is no pending write + * Return: true of there is no pending write */ bool mei_write_is_idle(struct mei_device *dev) { @@ -320,7 +333,7 @@ bool mei_write_is_idle(struct mei_device *dev) list_empty(&dev->ctrl_wr_list.list) && list_empty(&dev->write_list.list)); - dev_dbg(&dev->pdev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n", + dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n", idle, mei_dev_state_str(dev->dev_state), list_empty(&dev->ctrl_wr_list.list), @@ -330,36 +343,25 @@ bool mei_write_is_idle(struct mei_device *dev) } EXPORT_SYMBOL_GPL(mei_write_is_idle); -int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status) -{ - int i; - const struct mei_fw_status *fw_src = &dev->cfg->fw_status; - - if (!fw_status) - return -EINVAL; - - fw_status->count = fw_src->count; - for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { - int ret; - ret = pci_read_config_dword(dev->pdev, - fw_src->status[i], &fw_status->status[i]); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(mei_fw_status); - -void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg) +/** + * mei_device_init -- initialize mei_device structure + * + * @dev: the mei device + * @device: the device structure + * @hw_ops: hw operations + */ +void mei_device_init(struct mei_device *dev, + struct device *device, + const struct mei_hw_ops *hw_ops) { /* setup our list array */ INIT_LIST_HEAD(&dev->file_list); INIT_LIST_HEAD(&dev->device_list); + INIT_LIST_HEAD(&dev->me_clients); mutex_init(&dev->device_lock); init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_pg); - init_waitqueue_head(&dev->wait_recvd_msg); + init_waitqueue_head(&dev->wait_hbm_start); init_waitqueue_head(&dev->wait_stop_wd); dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; @@ -389,7 +391,8 @@ void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg) bitmap_set(dev->host_clients_map, 0, 1); dev->pg_event = MEI_PG_EVENT_IDLE; - dev->cfg = cfg; + dev->ops = hw_ops; + dev->dev = device; } EXPORT_SYMBOL_GPL(mei_device_init); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 4e3cba6da3f5..20c6c511f438 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -16,11 +16,11 @@ #include <linux/export.h> -#include <linux/pci.h> #include <linux/kthread.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/jiffies.h> +#include <linux/slab.h> #include <linux/mei.h> @@ -33,8 +33,8 @@ * mei_irq_compl_handler - dispatch complete handlers * for the completed callbacks * - * @dev - mei device - * @compl_list - list of completed cbs + * @dev: mei device + * @compl_list: list of completed cbs */ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) { @@ -47,7 +47,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) if (!cl) continue; - dev_dbg(&dev->pdev->dev, "completing call back.\n"); + dev_dbg(dev->dev, "completing call back.\n"); if (cl == &dev->iamthif_cl) mei_amthif_complete(dev, cb); else @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(mei_irq_compl_handler); * @cl: host client * @mei_hdr: header of mei client message * - * returns true if matches, false otherwise + * Return: true if matches, false otherwise */ static inline int mei_cl_hbm_equal(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) @@ -72,12 +72,12 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl, } /** * mei_cl_is_reading - checks if the client - is the one to read this message + * is the one to read this message * * @cl: mei client * @mei_hdr: header of mei message * - * returns true on match and false otherwise + * Return: true on match and false otherwise */ static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) { @@ -87,13 +87,13 @@ static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) } /** - * mei_irq_read_client_message - process client message + * mei_cl_irq_read_msg - process client message * * @dev: the device structure * @mei_hdr: header of mei client message * @complete_list: An instance of our list structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_cl_irq_read_msg(struct mei_device *dev, struct mei_msg_hdr *mei_hdr, @@ -126,7 +126,6 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, GFP_KERNEL); if (!buffer) { - cl_err(dev, cl, "allocation failed.\n"); list_del(&cb->list); return -ENOMEM; } @@ -149,10 +148,10 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, break; } - dev_dbg(&dev->pdev->dev, "message read\n"); + dev_dbg(dev->dev, "message read\n"); if (!buffer) { mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); - dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n", + dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", MEI_HDR_PRM(mei_hdr)); } @@ -166,7 +165,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * 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) @@ -195,16 +194,16 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, /** - * mei_cl_irq_close - processes close related operation from + * mei_cl_irq_disconnect - processes close related operation from * interrupt thread context - send disconnect request * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ -static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, +static int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; @@ -235,14 +234,14 @@ static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, /** - * mei_cl_irq_close - processes client read related operation from the + * mei_cl_irq_read - processes client read related operation from the * interrupt thread context - request for flow control credits * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * 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) @@ -279,7 +278,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, * @cb: callback block. * @cmpl_list: complete list. * - * returns 0, OK; otherwise, error. + * Return: 0, OK; otherwise, error. */ static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) @@ -322,7 +321,7 @@ static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, * @cmpl_list: An instance of our list structure * @slots: slots to read. * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_irq_read_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list, s32 *slots) @@ -334,20 +333,20 @@ int mei_irq_read_handler(struct mei_device *dev, if (!dev->rd_msg_hdr) { dev->rd_msg_hdr = mei_read_hdr(dev); (*slots)--; - dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); + dev_dbg(dev->dev, "slots =%08x.\n", *slots); } mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; - dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); + dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); if (mei_hdr->reserved || !dev->rd_msg_hdr) { - dev_err(&dev->pdev->dev, "corrupted message header 0x%08X\n", + dev_err(dev->dev, "corrupted message header 0x%08X\n", dev->rd_msg_hdr); ret = -EBADMSG; goto end; } if (mei_slots2data(*slots) < mei_hdr->length) { - dev_err(&dev->pdev->dev, "less data available than length=%08x.\n", + dev_err(dev->dev, "less data available than length=%08x.\n", *slots); /* we can't read the message */ ret = -ENODATA; @@ -358,7 +357,7 @@ int mei_irq_read_handler(struct mei_device *dev, if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) { ret = mei_hbm_dispatch(dev, mei_hdr); if (ret) { - dev_dbg(&dev->pdev->dev, "mei_hbm_dispatch failed ret = %d\n", + dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n", ret); goto end; } @@ -375,7 +374,7 @@ int mei_irq_read_handler(struct mei_device *dev, /* if no recipient cl was found we assume corrupted header */ if (&cl->link == &dev->file_list) { - dev_err(&dev->pdev->dev, "no destination client found 0x%08X\n", + dev_err(dev->dev, "no destination client found 0x%08X\n", dev->rd_msg_hdr); ret = -EBADMSG; goto end; @@ -387,14 +386,14 @@ int mei_irq_read_handler(struct mei_device *dev, ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); if (ret) { - dev_err(&dev->pdev->dev, "mei_amthif_irq_read_msg failed = %d\n", + dev_err(dev->dev, "mei_amthif_irq_read_msg failed = %d\n", ret); goto end; } } else { ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list); if (ret) { - dev_err(&dev->pdev->dev, "mei_cl_irq_read_msg failed = %d\n", + dev_err(dev->dev, "mei_cl_irq_read_msg failed = %d\n", ret); goto end; } @@ -407,7 +406,7 @@ reset_slots: if (*slots == -EOVERFLOW) { /* overflow - reset */ - dev_err(&dev->pdev->dev, "resetting due to slots overflow.\n"); + dev_err(dev->dev, "resetting due to slots overflow.\n"); /* set the event since message has been read */ ret = -ERANGE; goto end; @@ -425,7 +424,7 @@ EXPORT_SYMBOL_GPL(mei_irq_read_handler); * @dev: the device structure * @cmpl_list: An instance of our list structure * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) { @@ -445,7 +444,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) return -EMSGSIZE; /* complete all waiting for write CB */ - dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); + 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) { @@ -487,7 +486,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete control write list CB */ - dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); + dev_dbg(dev->dev, "complete control write list cb.\n"); list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { cl = cb->cl; if (!cl) { @@ -495,9 +494,9 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) return -ENODEV; } switch (cb->fop_type) { - case MEI_FOP_CLOSE: + case MEI_FOP_DISCONNECT: /* send disconnect message */ - ret = mei_cl_irq_close(cl, cb, cmpl_list); + ret = mei_cl_irq_disconnect(cl, cb, cmpl_list); if (ret) return ret; @@ -528,7 +527,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) } /* complete write list CB */ - dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); + dev_dbg(dev->dev, "complete write list cb.\n"); list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { cl = cb->cl; if (cl == NULL) @@ -556,8 +555,6 @@ void mei_timer(struct work_struct *work) { unsigned long timeout; struct mei_cl *cl; - struct mei_cl_cb *cb_pos = NULL; - struct mei_cl_cb *cb_next = NULL; struct mei_device *dev = container_of(work, struct mei_device, timer_work.work); @@ -571,7 +568,7 @@ void mei_timer(struct work_struct *work) if (dev->init_clients_timer) { if (--dev->init_clients_timer == 0) { - dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n", + dev_err(dev->dev, "timer: init clients timeout hbm_state = %d.\n", dev->hbm_state); mei_reset(dev); goto out; @@ -586,7 +583,7 @@ void mei_timer(struct work_struct *work) list_for_each_entry(cl, &dev->file_list, link) { if (cl->timer_count) { if (--cl->timer_count == 0) { - dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n"); + dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); mei_reset(dev); goto out; } @@ -598,7 +595,7 @@ void mei_timer(struct work_struct *work) if (dev->iamthif_stall_timer) { if (--dev->iamthif_stall_timer == 0) { - dev_err(&dev->pdev->dev, "timer: amthif hanged.\n"); + dev_err(dev->dev, "timer: amthif hanged.\n"); mei_reset(dev); dev->iamthif_msg_buf_size = 0; dev->iamthif_msg_buf_index = 0; @@ -620,27 +617,20 @@ void mei_timer(struct work_struct *work) timeout = dev->iamthif_timer + mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); - dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", + dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", dev->iamthif_timer); - dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout); - dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies); + dev_dbg(dev->dev, "timeout = %ld\n", timeout); + dev_dbg(dev->dev, "jiffies = %ld\n", jiffies); if (time_after(jiffies, timeout)) { /* * User didn't read the AMTHI data on time (15sec) * freeing AMTHI for other requests */ - dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n"); + dev_dbg(dev->dev, "freeing AMTHI for other requests\n"); - list_for_each_entry_safe(cb_pos, cb_next, - &dev->amthif_rd_complete_list.list, list) { - - cl = cb_pos->file_object->private_data; - - /* Finding the AMTHI entry. */ - if (cl == &dev->iamthif_cl) - list_del(&cb_pos->list); - } + mei_io_list_flush(&dev->amthif_rd_complete_list, + &dev->iamthif_cl); mei_io_cb_free(dev->iamthif_current_cb); dev->iamthif_current_cb = NULL; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 401a3d526cd0..beedc91f03a6 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -17,12 +17,12 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/device.h> +#include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/aio.h> -#include <linux/pci.h> #include <linux/poll.h> #include <linux/init.h> #include <linux/ioctl.h> @@ -44,7 +44,7 @@ * @inode: pointer to inode structure * @file: pointer to file structure * - * returns 0 on success, <0 on error + * Return: 0 on success, <0 on error */ static int mei_open(struct inode *inode, struct file *file) { @@ -63,7 +63,7 @@ static int mei_open(struct inode *inode, struct file *file) err = -ENODEV; if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", + dev_dbg(dev->dev, "dev_state != MEI_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); goto err_unlock; } @@ -96,7 +96,7 @@ err_unlock: * @inode: pointer to inode structure * @file: pointer to file structure * - * returns 0 on success, <0 on error + * Return: 0 on success, <0 on error */ static int mei_release(struct inode *inode, struct file *file) { @@ -157,7 +157,7 @@ out: * @length: buffer length * @offset: data offset in buffer * - * returns >=0 data length on success , <0 on error + * Return: >=0 data length on success , <0 on error */ static ssize_t mei_read(struct file *file, char __user *ubuf, size_t length, loff_t *offset) @@ -211,7 +211,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, err = mei_cl_read_start(cl, length); if (err && err != -EBUSY) { - dev_dbg(&dev->pdev->dev, + dev_dbg(dev->dev, "mei start read failure with status = %d\n", err); rets = err; goto out; @@ -254,7 +254,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, } /* now copy the data to user space */ copy_buffer: - dev_dbg(&dev->pdev->dev, "buf.size = %d buf.idx= %ld\n", + dev_dbg(dev->dev, "buf.size = %d buf.idx= %ld\n", cb->response_buffer.size, cb->buf_idx); if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) { rets = -EMSGSIZE; @@ -266,7 +266,7 @@ copy_buffer: length = min_t(size_t, length, cb->buf_idx - *offset); if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { - dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); + dev_dbg(dev->dev, "failed to copy data to userland\n"); rets = -EFAULT; goto free; } @@ -285,7 +285,7 @@ free: cl->reading_state = MEI_IDLE; cl->read_cb = NULL; out: - dev_dbg(&dev->pdev->dev, "end mei read rets= %d\n", rets); + dev_dbg(dev->dev, "end mei read rets= %d\n", rets); mutex_unlock(&dev->device_lock); return rets; } @@ -297,17 +297,17 @@ out: * @length: buffer length * @offset: data offset in buffer * - * returns >=0 data length on success , <0 on error + * Return: >=0 data length on success , <0 on error */ static ssize_t mei_write(struct file *file, const char __user *ubuf, size_t length, loff_t *offset) { struct mei_cl *cl = file->private_data; + struct mei_me_client *me_cl; struct mei_cl_cb *write_cb = NULL; struct mei_device *dev; unsigned long timeout = 0; int rets; - int id; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -321,8 +321,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - id = mei_me_cl_by_id(dev, cl->me_client_id); - if (id < 0) { + me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id); + if (!me_cl) { rets = -ENOTTY; goto out; } @@ -332,13 +332,13 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, goto out; } - if (length > dev->me_clients[id].props.max_msg_length) { + if (length > me_cl->props.max_msg_length) { rets = -EFBIG; goto out; } if (cl->state != MEI_FILE_CONNECTED) { - dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d", + dev_err(dev->dev, "host client = %d, is not connected to ME client = %d", cl->host_client_id, cl->me_client_id); rets = -ENODEV; goto out; @@ -377,7 +377,6 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, write_cb = mei_io_cb_init(cl, file); if (!write_cb) { - dev_err(&dev->pdev->dev, "write cb allocation failed\n"); rets = -ENOMEM; goto out; } @@ -387,7 +386,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); if (rets) { - dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); + dev_dbg(dev->dev, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -396,7 +395,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = mei_amthif_write(dev, write_cb); if (rets) { - dev_err(&dev->pdev->dev, + dev_err(dev->dev, "amthif write failed with status = %d\n", rets); goto out; } @@ -415,27 +414,23 @@ out: /** * mei_ioctl_connect_client - the connect to fw client IOCTL function * - * @dev: the device structure - * @data: IOCTL connect data, input and output parameters * @file: private data of the file object + * @data: IOCTL connect data, input and output parameters * * Locking: called under "dev->device_lock" lock * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_ioctl_connect_client(struct file *file, struct mei_connect_client_data *data) { struct mei_device *dev; struct mei_client *client; + struct mei_me_client *me_cl; struct mei_cl *cl; - int i; int rets; cl = file->private_data; - if (WARN_ON(!cl || !cl->dev)) - return -ENODEV; - dev = cl->dev; if (dev->dev_state != MEI_DEV_ENABLED) { @@ -450,28 +445,29 @@ static int mei_ioctl_connect_client(struct file *file, } /* find ME client we're trying to connect to */ - i = mei_me_cl_by_uuid(dev, &data->in_client_uuid); - if (i < 0 || dev->me_clients[i].props.fixed_address) { - dev_dbg(&dev->pdev->dev, "Cannot connect to FW Client UUID = %pUl\n", + me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid); + if (!me_cl || me_cl->props.fixed_address) { + dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", &data->in_client_uuid); rets = -ENOTTY; goto end; } - cl->me_client_id = dev->me_clients[i].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; - dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", + dev_dbg(dev->dev, "Connect to FW Client ID = %d\n", cl->me_client_id); - dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n", - dev->me_clients[i].props.protocol_version); - dev_dbg(&dev->pdev->dev, "FW Client - Max Msg Len = %d\n", - dev->me_clients[i].props.max_msg_length); + dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n", + me_cl->props.protocol_version); + dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n", + me_cl->props.max_msg_length); /* if we're connecting to amthif client then we will use the * existing connection */ if (uuid_le_cmp(data->in_client_uuid, mei_amthif_guid) == 0) { - dev_dbg(&dev->pdev->dev, "FW Client is amthi\n"); + dev_dbg(dev->dev, "FW Client is amthi\n"); if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { rets = -ENODEV; goto end; @@ -484,10 +480,8 @@ static int mei_ioctl_connect_client(struct file *file, file->private_data = &dev->iamthif_cl; client = &data->out_client_properties; - client->max_msg_length = - dev->me_clients[i].props.max_msg_length; - client->protocol_version = - dev->me_clients[i].props.protocol_version; + client->max_msg_length = me_cl->props.max_msg_length; + client->protocol_version = me_cl->props.protocol_version; rets = dev->iamthif_cl.status; goto end; @@ -496,9 +490,9 @@ static int mei_ioctl_connect_client(struct file *file, /* prepare the output buffer */ client = &data->out_client_properties; - client->max_msg_length = dev->me_clients[i].props.max_msg_length; - client->protocol_version = dev->me_clients[i].props.protocol_version; - dev_dbg(&dev->pdev->dev, "Can connect?\n"); + client->max_msg_length = me_cl->props.max_msg_length; + client->protocol_version = me_cl->props.protocol_version; + dev_dbg(dev->dev, "Can connect?\n"); rets = mei_cl_connect(cl, file); @@ -507,7 +501,6 @@ end: return rets; } - /** * mei_ioctl - the IOCTL function * @@ -515,24 +508,22 @@ end: * @cmd: ioctl command * @data: pointer to mei message structure * - * returns 0 on success , <0 on error + * Return: 0 on success , <0 on error */ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) { struct mei_device *dev; struct mei_cl *cl = file->private_data; - struct mei_connect_client_data *connect_data = NULL; + struct mei_connect_client_data connect_data; int rets; - if (cmd != IOCTL_MEI_CONNECT_CLIENT) - return -EINVAL; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; dev = cl->dev; - dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd); + dev_dbg(dev->dev, "IOCTL cmd = 0x%x", cmd); mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { @@ -540,38 +531,36 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) goto out; } - dev_dbg(&dev->pdev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); - - connect_data = kzalloc(sizeof(struct mei_connect_client_data), - GFP_KERNEL); - if (!connect_data) { - rets = -ENOMEM; - goto out; - } - dev_dbg(&dev->pdev->dev, "copy connect data from user\n"); - if (copy_from_user(connect_data, (char __user *)data, + switch (cmd) { + case IOCTL_MEI_CONNECT_CLIENT: + dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); + if (copy_from_user(&connect_data, (char __user *)data, sizeof(struct mei_connect_client_data))) { - dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); - rets = -EFAULT; - goto out; - } - - rets = mei_ioctl_connect_client(file, connect_data); + dev_dbg(dev->dev, "failed to copy data from userland\n"); + rets = -EFAULT; + goto out; + } - /* if all is ok, copying the data back to user. */ - if (rets) - goto out; + rets = mei_ioctl_connect_client(file, &connect_data); + if (rets) + goto out; - dev_dbg(&dev->pdev->dev, "copy connect data to user\n"); - if (copy_to_user((char __user *)data, connect_data, + /* if all is ok, copying the data back to user. */ + if (copy_to_user((char __user *)data, &connect_data, sizeof(struct mei_connect_client_data))) { - dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); - rets = -EFAULT; - goto out; + dev_dbg(dev->dev, "failed to copy data to userland\n"); + rets = -EFAULT; + goto out; + } + + break; + + default: + dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd); + rets = -ENOIOCTLCMD; } out: - kfree(connect_data); mutex_unlock(&dev->device_lock); return rets; } @@ -583,7 +572,7 @@ out: * @cmd: ioctl command * @data: pointer to mei message structure * - * returns 0 on success , <0 on error + * Return: 0 on success , <0 on error */ #ifdef CONFIG_COMPAT static long mei_compat_ioctl(struct file *file, @@ -600,7 +589,7 @@ static long mei_compat_ioctl(struct file *file, * @file: pointer to file structure * @wait: pointer to poll_table structure * - * returns poll mask + * Return: poll mask */ static unsigned int mei_poll(struct file *file, poll_table *wait) { @@ -670,7 +659,7 @@ static DEFINE_IDR(mei_idr); * * @dev: device pointer * - * returns allocated minor, or -ENOSPC if no free minor left + * Return: allocated minor, or -ENOSPC if no free minor left */ static int mei_minor_get(struct mei_device *dev) { @@ -681,7 +670,7 @@ static int mei_minor_get(struct mei_device *dev) if (ret >= 0) dev->minor = ret; else if (ret == -ENOSPC) - dev_err(&dev->pdev->dev, "too many mei devices\n"); + dev_err(dev->dev, "too many mei devices\n"); mutex_unlock(&mei_minor_lock); return ret; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 0b0d6135543b..71744b16cc8c 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -129,20 +129,18 @@ enum mei_wd_states { /** * enum mei_cb_file_ops - file operation associated with the callback - * @MEI_FOP_READ - read - * @MEI_FOP_WRITE - write - * @MEI_FOP_CONNECT - connect - * @MEI_FOP_DISCONNECT_RSP - disconnect response - * @MEI_FOP_OPEN - open - * @MEI_FOP_CLOSE - close + * @MEI_FOP_READ: read + * @MEI_FOP_WRITE: write + * @MEI_FOP_CONNECT: connect + * @MEI_FOP_DISCONNECT: disconnect + * @MEI_FOP_DISCONNECT_RSP: disconnect response */ enum mei_cb_file_ops { MEI_FOP_READ = 0, MEI_FOP_WRITE, MEI_FOP_CONNECT, + MEI_FOP_DISCONNECT, MEI_FOP_DISCONNECT_RSP, - MEI_FOP_OPEN, - MEI_FOP_CLOSE }; /* @@ -159,8 +157,8 @@ struct mei_msg_data { /* * struct mei_fw_status - storage of FW status data * - * @count - number of actually available elements in array - * @status - FW status registers + * @count: number of actually available elements in array + * @status: FW status registers */ struct mei_fw_status { int count; @@ -170,11 +168,13 @@ struct mei_fw_status { /** * struct mei_me_client - representation of me (fw) client * - * @props - client properties - * @client_id - me client id - * @mei_flow_ctrl_creds - flow control credits + * @list: link in me client list + * @props: client properties + * @client_id: me client id + * @mei_flow_ctrl_creds: flow control credits */ struct mei_me_client { + struct list_head list; struct mei_client_properties props; u8 client_id; u8 mei_flow_ctrl_creds; @@ -186,8 +186,15 @@ struct mei_cl; /** * struct mei_cl_cb - file operation callback structure * - * @cl - file client who is running this operation - * @fop_type - file operation type + * @list: link in callback queue + * @cl: file client who is running this operation + * @fop_type: file operation type + * @request_buffer: buffer to store request data + * @response_buffer: buffer to store response data + * @buf_idx: last read index + * @read_time: last read operation time stamp (iamthif) + * @file_object: pointer to file structure + * @internal: communication between driver and FW flag */ struct mei_cl_cb { struct list_head list; @@ -201,7 +208,29 @@ struct mei_cl_cb { u32 internal:1; }; -/* MEI client instance carried as file->private_data*/ +/** + * struct mei_cl - me client host representation + * carried in file->private_data + * + * @link: link in the clients list + * @dev: mei parent device + * @state: file operation state + * @tx_wait: wait queue for tx completion + * @rx_wait: wait queue for rx completion + * @wait: wait queue for management operation + * @status: connection status + * @cl_uuid: client uuid name + * @host_client_id: host id + * @me_client_id: me/fw id + * @mei_flow_ctrl_creds: transmit flow credentials + * @timer_count: watchdog timer for operation completion + * @reading_state: state of the rx + * @writing_state: state of the tx + * @read_cb: current pending reading callback + * + * @device: device on the mei client bus + * @device_link: link to bus clients + */ struct mei_cl { struct list_head link; struct mei_device *dev; @@ -210,7 +239,7 @@ struct mei_cl { wait_queue_head_t rx_wait; wait_queue_head_t wait; int status; - /* ID of client connected */ + uuid_le cl_uuid; u8 host_client_id; u8 me_client_id; u8 mei_flow_ctrl_creds; @@ -222,35 +251,35 @@ struct mei_cl { /* MEI CL bus data */ struct mei_cl_device *device; struct list_head device_link; - uuid_le device_uuid; }; /** struct mei_hw_ops * - * @host_is_ready - query for host readiness + * @host_is_ready : query for host readiness - * @hw_is_ready - query if hw is ready - * @hw_reset - reset hw - * @hw_start - start hw after reset - * @hw_config - configure hw + * @hw_is_ready : query if hw is ready + * @hw_reset : reset hw + * @hw_start : start hw after reset + * @hw_config : configure hw - * @pg_state - power gating state of the device - * @pg_is_enabled - is power gating enabled + * @fw_status : get fw status registers + * @pg_state : power gating state of the device + * @pg_is_enabled : is power gating enabled - * @intr_clear - clear pending interrupts - * @intr_enable - enable interrupts - * @intr_disable - disable interrupts + * @intr_clear : clear pending interrupts + * @intr_enable : enable interrupts + * @intr_disable : disable interrupts - * @hbuf_free_slots - query for write buffer empty slots - * @hbuf_is_ready - query if write buffer is empty - * @hbuf_max_len - query for write buffer max len + * @hbuf_free_slots : query for write buffer empty slots + * @hbuf_is_ready : query if write buffer is empty + * @hbuf_max_len : query for write buffer max len - * @write - write a message to FW + * @write : write a message to FW - * @rdbuf_full_slots - query how many slots are filled + * @rdbuf_full_slots : query how many slots are filled - * @read_hdr - get first 4 bytes (header) - * @read - read a buffer from the FW + * @read_hdr : get first 4 bytes (header) + * @read : read a buffer from the FW */ struct mei_hw_ops { @@ -261,6 +290,8 @@ 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_is_enabled)(struct mei_device *dev); @@ -328,11 +359,12 @@ void mei_cl_bus_exit(void); * when being probed and shall use it for doing ME bus I/O. * * @dev: linux driver model device pointer - * @uuid: me client uuid * @cl: mei client * @ops: ME transport ops + * @event_work: async work to execute event callback * @event_cb: Drivers register this callback to get asynchronous ME * events (e.g. Rx buffer pending) notifications. + * @event_context: event callback run context * @events: Events bitmask sent to the driver. * @priv_data: client private data */ @@ -352,7 +384,7 @@ struct mei_cl_device { }; - /** +/** * enum mei_pg_event - power gating transition events * * @MEI_PG_EVENT_IDLE: the driver is not in power gating transition @@ -376,67 +408,106 @@ enum mei_pg_state { MEI_PG_ON = 1, }; -/* - * mei_cfg - * - * @fw_status - FW status - * @quirk_probe - device exclusion quirk - */ -struct mei_cfg { - const struct mei_fw_status fw_status; - bool (*quirk_probe)(struct pci_dev *pdev); -}; - - -#define MEI_PCI_DEVICE(dev, cfg) \ - .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ - .driver_data = (kernel_ulong_t)&(cfg) - +const char *mei_pg_state_str(enum mei_pg_state state); /** * struct mei_device - MEI private device struct - - * @pdev - pointer to pci device struct - * @cdev - character device - * @minor - minor number allocated for device * - * @reset_count - limits the number of consecutive resets - * @hbm_state - state of host bus message protocol - * @pg_event - power gating event - * @mem_addr - mem mapped base register address - - * @hbuf_depth - depth of hardware host/write buffer is slots - * @hbuf_is_ready - query if the host host/write buffer is ready - * @wr_msg - the buffer for hbm control messages - * @cfg - per device generation config and ops + * @dev : device on a bus + * @cdev : character device + * @minor : minor number allocated for device + * + * @read_list : read completion list + * @write_list : write pending list + * @write_waiting_list : write completion list + * @ctrl_wr_list : pending control write list + * @ctrl_rd_list : pending control read list + * + * @file_list : list of opened handles + * @open_handle_count: number of opened handles + * + * @device_lock : big device lock + * @timer_work : MEI timer delayed work (timeouts) + * + * @recvd_hw_ready : hw ready message received flag + * + * @wait_hw_ready : wait queue for receive HW ready message form FW + * @wait_pg : wait queue for receive PG message from FW + * @wait_hbm_start : wait queue for receive HBM start message from FW + * @wait_stop_wd : wait queue for receive WD stop message from FW + * + * @reset_count : number of consecutive resets + * @dev_state : device state + * @hbm_state : state of host bus message protocol + * @init_clients_timer : HBM init handshake timeout + * + * @pg_event : power gating event + * @pg_domain : runtime PM domain + * + * @rd_msg_buf : control messages buffer + * @rd_msg_hdr : read message header storage + * + * @hbuf_depth : depth of hardware host/write buffer is slots + * @hbuf_is_ready : query if the host host/write buffer is ready + * @wr_msg : the buffer for hbm control messages + * + * @version : HBM protocol version in use + * @hbm_f_pg_supported : hbm feature pgi protocol + * + * @me_clients : list of FW clients + * @me_clients_map : FW clients bit map + * @host_clients_map : host clients id pool + * @me_client_index : last FW client index in enumeration + * + * @wd_cl : watchdog client + * @wd_state : watchdog client state + * @wd_pending : watchdog command is pending + * @wd_timeout : watchdog expiration timeout + * @wd_data : watchdog message buffer + * + * @amthif_cmd_list : amthif list for cmd waiting + * @amthif_rd_complete_list : amthif list for reading completed cmd data + * @iamthif_file_object : file for current amthif operation + * @iamthif_cl : amthif host client + * @iamthif_current_cb : amthif current operation callback + * @iamthif_open_count : number of opened amthif connections + * @iamthif_mtu : amthif client max message length + * @iamthif_timer : time stamp of current amthif command completion + * @iamthif_stall_timer : timer to detect amthif hang + * @iamthif_msg_buf : amthif current message buffer + * @iamthif_msg_buf_size : size of current amthif message request buffer + * @iamthif_msg_buf_index : current index in amthif message request buffer + * @iamthif_state : amthif processor state + * @iamthif_flow_control_pending: amthif waits for flow control + * @iamthif_ioctl : wait for completion if amthif control message + * @iamthif_canceled : current amthif command is canceled + * + * @init_work : work item for the device init + * @reset_work : work item for the device reset + * + * @device_list : mei client bus list + * + * @dbgfs_dir : debugfs mei root directory + * + * @ops: : hw specific operations + * @hw : hw specific data */ struct mei_device { - struct pci_dev *pdev; /* pointer to pci device struct */ + struct device *dev; struct cdev cdev; int minor; - /* - * lists of queues - */ - /* array of pointers to aio lists */ - struct mei_cl_cb read_list; /* driver read queue */ - struct mei_cl_cb write_list; /* driver write queue */ - struct mei_cl_cb write_waiting_list; /* write waiting queue */ - struct mei_cl_cb ctrl_wr_list; /* managed write IOCTL list */ - struct mei_cl_cb ctrl_rd_list; /* managed read IOCTL list */ + struct mei_cl_cb read_list; + 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; - /* - * list of files - */ struct list_head file_list; long open_handle_count; - /* - * lock for the device - */ - struct mutex device_lock; /* device lock */ - struct delayed_work timer_work; /* MEI timer delayed work (timeouts) */ + struct mutex device_lock; + struct delayed_work timer_work; bool recvd_hw_ready; /* @@ -444,7 +515,7 @@ struct mei_device { */ wait_queue_head_t wait_hw_ready; wait_queue_head_t wait_pg; - wait_queue_head_t wait_recvd_msg; + wait_queue_head_t wait_hbm_start; wait_queue_head_t wait_stop_wd; /* @@ -463,7 +534,7 @@ struct mei_device { struct dev_pm_domain pg_domain; #endif /* CONFIG_PM_RUNTIME */ - unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */ + unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; u32 rd_msg_hdr; /* write buffer */ @@ -477,12 +548,11 @@ struct mei_device { } wr_msg; struct hbm_version version; + unsigned int hbm_f_pg_supported:1; - struct mei_me_client *me_clients; /* Note: memory has to be allocated */ + struct list_head me_clients; DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX); - unsigned long me_clients_num; - unsigned long me_client_presentation_num; unsigned long me_client_index; struct mei_cl wd_cl; @@ -523,7 +593,6 @@ struct mei_device { const struct mei_hw_ops *ops; - const struct mei_cfg *cfg; char hw[0] __aligned(sizeof(void *)); }; @@ -535,8 +604,10 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec) /** * mei_data2slots - get slots - number of (dwords) from a message length * + size of the mei header - * @length - size of the messages in bytes - * returns - number of slots + * + * @length: size of the messages in bytes + * + * Return: number of slots */ static inline u32 mei_data2slots(size_t length) { @@ -544,9 +615,11 @@ static inline u32 mei_data2slots(size_t length) } /** - * mei_slots2data- get data in slots - bytes from slots - * @slots - number of available slots - * returns - number of bytes in slots + * mei_slots2data - get data in slots - bytes from slots + * + * @slots: number of available slots + * + * Return: number of bytes in slots */ static inline u32 mei_slots2data(int slots) { @@ -556,7 +629,9 @@ static inline u32 mei_slots2data(int slots) /* * mei init function prototypes */ -void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg); +void mei_device_init(struct mei_device *dev, + struct device *device, + const struct mei_hw_ops *hw_ops); int mei_reset(struct mei_device *dev); int mei_start(struct mei_device *dev); int mei_restart(struct mei_device *dev); @@ -622,12 +697,12 @@ int mei_wd_host_init(struct mei_device *dev); /* * mei_watchdog_register - Registering watchdog interface * once we got connection to the WD Client - * @dev - mei device + * @dev: mei device */ int mei_watchdog_register(struct mei_device *dev); /* * mei_watchdog_unregister - Unregistering watchdog interface - * @dev - mei device + * @dev: mei device */ void mei_watchdog_unregister(struct mei_device *dev); @@ -723,7 +798,11 @@ static inline int mei_count_full_read_slots(struct mei_device *dev) return dev->ops->rdbuf_full_slots(dev); } -int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status); +static inline int mei_fw_status(struct mei_device *dev, + struct mei_fw_status *fw_status) +{ + return dev->ops->fw_status(dev, fw_status); +} #define FW_STS_FMT "%08X %08X" #define FW_STS_PRM(fw_status) \ diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index 3095fc514a65..622654323177 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -19,7 +19,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> -#include <linux/pci.h> +#include <linux/slab.h> + #include <linux/mei_cl_bus.h> #include "mei_dev.h" @@ -87,14 +88,20 @@ struct mei_nfc_hci_hdr { #define MEI_NFC_HEADER_SIZE 10 -/** mei_nfc_dev - NFC mei device +/** + * struct mei_nfc_dev - NFC mei device * * @cl: NFC host client * @cl_info: NFC info host client * @init_work: perform connection to the info client + * @send_wq: send completion wait queue * @fw_ivn: NFC Interface Version Number * @vendor_id: NFC manufacturer ID * @radio_type: NFC radio type + * @bus_name: bus name + * + * @req_id: message counter + * @recv_req_id: reception message counter */ struct mei_nfc_dev { struct mei_cl *cl; @@ -163,7 +170,7 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) return 0; default: - dev_err(&dev->pdev->dev, "Unknown radio type 0x%x\n", + dev_err(dev->dev, "Unknown radio type 0x%x\n", ndev->radio_type); return -EINVAL; @@ -175,14 +182,14 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) ndev->bus_name = "pn544"; return 0; default: - dev_err(&dev->pdev->dev, "Unknown radio type 0x%x\n", + dev_err(dev->dev, "Unknown radio type 0x%x\n", ndev->radio_type); return -EINVAL; } default: - dev_err(&dev->pdev->dev, "Unknown vendor ID 0x%x\n", + dev_err(dev->dev, "Unknown vendor ID 0x%x\n", ndev->vendor_id); return -EINVAL; @@ -231,21 +238,21 @@ static int mei_nfc_connect(struct mei_nfc_dev *ndev) ret = __mei_cl_send(cl, (u8 *)cmd, connect_length); if (ret < 0) { - dev_err(&dev->pdev->dev, "Could not send connect cmd\n"); + dev_err(dev->dev, "Could not send connect cmd\n"); goto err; } bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length); if (bytes_recv < 0) { - dev_err(&dev->pdev->dev, "Could not read connect response\n"); + dev_err(dev->dev, "Could not read connect response\n"); ret = bytes_recv; goto err; } - dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n", + dev_info(dev->dev, "IVN 0x%x Vendor ID 0x%x\n", connect_resp->fw_ivn, connect_resp->vendor_id); - dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n", + dev_info(dev->dev, "ME FW %d.%d.%d.%d\n", connect_resp->me_major, connect_resp->me_minor, connect_resp->me_hotfix, connect_resp->me_build); @@ -279,7 +286,7 @@ static int mei_nfc_if_version(struct mei_nfc_dev *ndev) ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd)); if (ret < 0) { - dev_err(&dev->pdev->dev, "Could not send IF version cmd\n"); + dev_err(dev->dev, "Could not send IF version cmd\n"); return ret; } @@ -293,7 +300,7 @@ static int mei_nfc_if_version(struct mei_nfc_dev *ndev) bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { - dev_err(&dev->pdev->dev, "Could not read IF version\n"); + dev_err(dev->dev, "Could not read IF version\n"); ret = -EIO; goto err; } @@ -319,7 +326,7 @@ static int mei_nfc_enable(struct mei_cl_device *cldev) ret = mei_nfc_connect(ndev); if (ret < 0) { - dev_err(&dev->pdev->dev, "Could not connect to NFC"); + dev_err(dev->dev, "Could not connect to NFC"); return ret; } @@ -342,9 +349,10 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) ndev = (struct mei_nfc_dev *) cldev->priv_data; dev = ndev->cl->dev; + err = -ENOMEM; mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL); if (!mei_buf) - return -ENOMEM; + goto out; hdr = (struct mei_nfc_hci_hdr *) mei_buf; hdr->cmd = MEI_NFC_CMD_HCI_SEND; @@ -354,21 +362,19 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) hdr->data_size = length; memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length); - err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE); if (err < 0) - return err; - - kfree(mei_buf); + goto out; if (!wait_event_interruptible_timeout(ndev->send_wq, ndev->recv_req_id == ndev->req_id, HZ)) { - dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); + dev_err(dev->dev, "NFC MEI command timeout\n"); err = -ETIME; } else { ndev->req_id++; } - +out: + kfree(mei_buf); return err; } @@ -419,8 +425,7 @@ static void mei_nfc_init(struct work_struct *work) if (mei_cl_connect(cl_info, NULL) < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, - "Could not connect to the NFC INFO ME client"); + dev_err(dev->dev, "Could not connect to the NFC INFO ME client"); goto err; } @@ -428,21 +433,19 @@ static void mei_nfc_init(struct work_struct *work) mutex_unlock(&dev->device_lock); if (mei_nfc_if_version(ndev) < 0) { - dev_err(&dev->pdev->dev, "Could not get the NFC interface version"); + dev_err(dev->dev, "Could not get the NFC interface version"); goto err; } - dev_info(&dev->pdev->dev, - "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", + dev_info(dev->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", ndev->fw_ivn, ndev->vendor_id, ndev->radio_type); mutex_lock(&dev->device_lock); if (mei_cl_disconnect(cl_info) < 0) { mutex_unlock(&dev->device_lock); - dev_err(&dev->pdev->dev, - "Could not disconnect the NFC INFO ME client"); + dev_err(dev->dev, "Could not disconnect the NFC INFO ME client"); goto err; } @@ -450,15 +453,13 @@ static void mei_nfc_init(struct work_struct *work) mutex_unlock(&dev->device_lock); if (mei_nfc_build_bus_name(ndev) < 0) { - dev_err(&dev->pdev->dev, - "Could not build the bus ID name\n"); + dev_err(dev->dev, "Could not build the bus ID name\n"); return; } cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops); if (!cldev) { - dev_err(&dev->pdev->dev, - "Could not add the NFC device to the MEI bus\n"); + dev_err(dev->dev, "Could not add the NFC device to the MEI bus\n"); goto err; } @@ -473,7 +474,6 @@ err: mei_nfc_free(ndev); mutex_unlock(&dev->device_lock); - return; } @@ -481,7 +481,8 @@ int mei_nfc_host_init(struct mei_device *dev) { struct mei_nfc_dev *ndev = &nfc_dev; struct mei_cl *cl_info, *cl = NULL; - int i, ret; + struct mei_me_client *me_cl; + int ret; /* already initialized */ if (ndev->cl_info) @@ -499,40 +500,38 @@ int mei_nfc_host_init(struct mei_device *dev) } /* check for valid client id */ - i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); - if (i < 0) { - dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); + me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); + if (!me_cl) { + dev_info(dev->dev, "nfc: failed to find the client\n"); ret = -ENOTTY; goto err; } - cl_info->me_client_id = dev->me_clients[i].client_id; + cl_info->me_client_id = me_cl->client_id; + cl_info->cl_uuid = me_cl->props.protocol_name; ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY); if (ret) goto err; - cl_info->device_uuid = mei_nfc_info_guid; list_add_tail(&cl_info->device_link, &dev->device_list); /* check for valid client id */ - i = mei_me_cl_by_uuid(dev, &mei_nfc_guid); - if (i < 0) { - dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); + me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); + if (!me_cl) { + dev_info(dev->dev, "nfc: failed to find the client\n"); ret = -ENOTTY; goto err; } - cl->me_client_id = dev->me_clients[i].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY); if (ret) goto err; - cl->device_uuid = mei_nfc_guid; - - list_add_tail(&cl->device_link, &dev->device_list); ndev->req_id = 1; @@ -552,6 +551,7 @@ err: void mei_nfc_host_exit(struct mei_device *dev) { struct mei_nfc_dev *ndev = &nfc_dev; + cancel_work_sync(&ndev->init_work); } diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 959c313d84a7..f3225b1643ab 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -98,12 +98,12 @@ static inline void mei_me_unset_pm_domain(struct mei_device *dev) {} #endif /* CONFIG_PM_RUNTIME */ /** - * mei_quirk_probe - probe for devices that doesn't valid ME interface + * mei_me_quirk_probe - probe for devices that doesn't valid ME interface * * @pdev: PCI device structure * @cfg: per generation config * - * returns true if ME Interface is valid, false otherwise + * Return: true if ME Interface is valid, false otherwise */ static bool mei_me_quirk_probe(struct pci_dev *pdev, const struct mei_cfg *cfg) @@ -117,12 +117,12 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev, } /** - * mei_probe - Device Initialization Routine + * mei_me_probe - Device Initialization Routine * * @pdev: PCI device structure * @ent: entry in kcs_pci_tbl * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -249,7 +249,7 @@ end: } /** - * mei_remove - Device Removal Routine + * mei_me_remove - Device Removal Routine * * @pdev: PCI device structure * @@ -430,7 +430,7 @@ static int mei_me_pm_runtime_resume(struct device *device) */ static inline void mei_me_set_pm_domain(struct mei_device *dev) { - struct pci_dev *pdev = dev->pdev; + struct pci_dev *pdev = to_pci_dev(dev->dev); if (pdev->dev.bus && pdev->dev.bus->pm) { dev->pg_domain.ops = *pdev->dev.bus->pm; @@ -451,7 +451,7 @@ static inline void mei_me_set_pm_domain(struct mei_device *dev) static inline void mei_me_unset_pm_domain(struct mei_device *dev) { /* stop using pm callbacks if any */ - dev->pdev->dev.pm_domain = NULL; + dev->dev->pm_domain = NULL; } #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 74727dda51c1..bee1c6fb7e75 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -36,7 +36,8 @@ #include "hw-txe.h" static const struct pci_device_id mei_txe_pci_tbl[] = { - {MEI_PCI_DEVICE(0x0F18, mei_txe_cfg)}, /* Baytrail */ + {PCI_VDEVICE(INTEL, 0x0F18)}, /* Baytrail */ + {0, } }; MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); @@ -52,6 +53,7 @@ static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {} 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]); @@ -65,11 +67,10 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) * @pdev: PCI device structure * @ent: entry in mei_txe_pci_tbl * - * returns 0 on success, <0 on failure. + * Return: 0 on success, <0 on failure. */ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data); struct mei_device *dev; struct mei_txe_hw *hw; int err; @@ -100,7 +101,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* allocates and initializes the mei dev structure */ - dev = mei_txe_dev_init(pdev, cfg); + dev = mei_txe_dev_init(pdev); if (!dev) { err = -ENOMEM; goto release_regions; @@ -377,7 +378,7 @@ static int mei_txe_pm_runtime_resume(struct device *device) */ static inline void mei_txe_set_pm_domain(struct mei_device *dev) { - struct pci_dev *pdev = dev->pdev; + struct pci_dev *pdev = to_pci_dev(dev->dev); if (pdev->dev.bus && pdev->dev.bus->pm) { dev->pg_domain.ops = *pdev->dev.bus->pm; @@ -398,7 +399,7 @@ static inline void mei_txe_set_pm_domain(struct mei_device *dev) static inline void mei_txe_unset_pm_domain(struct mei_device *dev) { /* stop using pm callbacks if any */ - dev->pdev->dev.pm_domain = NULL; + dev->dev->pm_domain = NULL; } #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index a84a664dfccb..b836dfffceb5 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> -#include <linux/pci.h> #include <linux/sched.h> #include <linux/watchdog.h> @@ -42,7 +41,7 @@ const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) { - dev_dbg(&dev->pdev->dev, "wd: set timeout=%d.\n", timeout); + dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); } @@ -52,14 +51,14 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) * * @dev: the device structure * - * returns -ENOTTY if wd client cannot be found + * Return: -ENOTTY if wd client cannot be found * -EIO if write has failed * 0 on success */ int mei_wd_host_init(struct mei_device *dev) { struct mei_cl *cl = &dev->wd_cl; - int id; + struct mei_me_client *me_cl; int ret; mei_cl_init(cl, dev); @@ -69,25 +68,26 @@ int mei_wd_host_init(struct mei_device *dev) /* check for valid client id */ - id = mei_me_cl_by_uuid(dev, &mei_wd_guid); - if (id < 0) { - dev_info(&dev->pdev->dev, "wd: failed to find the client\n"); + me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); + if (!me_cl) { + dev_info(dev->dev, "wd: failed to find the client\n"); return -ENOTTY; } - cl->me_client_id = dev->me_clients[id].client_id; + cl->me_client_id = me_cl->client_id; + cl->cl_uuid = me_cl->props.protocol_name; ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); if (ret < 0) { - dev_info(&dev->pdev->dev, "wd: failed link client\n"); + dev_info(dev->dev, "wd: failed link client\n"); return ret; } ret = mei_cl_connect(cl, NULL); if (ret) { - dev_err(&dev->pdev->dev, "wd: failed to connect = %d\n", ret); + dev_err(dev->dev, "wd: failed to connect = %d\n", ret); mei_cl_unlink(cl); return ret; } @@ -105,7 +105,7 @@ int mei_wd_host_init(struct mei_device *dev) * * @dev: the device structure * - * returns 0 if success, + * Return: 0 if success, * -EIO when message send fails * -EINVAL when invalid message is to be sent * -ENODEV on flow control failure @@ -127,19 +127,19 @@ int mei_wd_send(struct mei_device *dev) else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) hdr.length = MEI_WD_STOP_MSG_SIZE; else { - dev_err(&dev->pdev->dev, "wd: invalid message is to be sent, aborting\n"); + dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); return -EINVAL; } ret = mei_write_message(dev, &hdr, dev->wd_data); if (ret) { - dev_err(&dev->pdev->dev, "wd: write message failed\n"); + dev_err(dev->dev, "wd: write message failed\n"); return ret; } ret = mei_cl_flow_ctrl_reduce(cl); if (ret) { - dev_err(&dev->pdev->dev, "wd: flow_ctrl_reduce failed.\n"); + dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); return ret; } @@ -150,9 +150,8 @@ int mei_wd_send(struct mei_device *dev) * mei_wd_stop - sends watchdog stop message to fw. * * @dev: the device structure - * @preserve: indicate if to keep the timeout value * - * returns 0 if success + * Return: 0 if success * on error: * -EIO when message send fails * -EINVAL when invalid message is to be sent @@ -192,11 +191,10 @@ int mei_wd_stop(struct mei_device *dev) if (dev->wd_state != MEI_WD_IDLE) { /* timeout */ ret = -ETIME; - dev_warn(&dev->pdev->dev, - "wd: stop failed to complete ret=%d.\n", ret); + dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); goto err; } - dev_dbg(&dev->pdev->dev, "wd: stop completed after %u msec\n", + dev_dbg(dev->dev, "wd: stop completed after %u msec\n", MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); return 0; err: @@ -208,7 +206,7 @@ err: * * @wd_dev - watchdog device struct * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_start(struct watchdog_device *wd_dev) { @@ -222,15 +220,13 @@ static int mei_wd_ops_start(struct watchdog_device *wd_dev) mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { - dev_dbg(&dev->pdev->dev, - "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", + dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", mei_dev_state_str(dev->dev_state)); goto end_unlock; } if (dev->wd_cl.state != MEI_FILE_CONNECTED) { - dev_dbg(&dev->pdev->dev, - "MEI Driver is not connected to Watchdog Client\n"); + dev_dbg(dev->dev, "MEI Driver is not connected to Watchdog Client\n"); goto end_unlock; } @@ -247,7 +243,7 @@ end_unlock: * * @wd_dev - watchdog device struct * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_stop(struct watchdog_device *wd_dev) { @@ -269,7 +265,7 @@ static int mei_wd_ops_stop(struct watchdog_device *wd_dev) * * @wd_dev - watchdog device struct * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) { @@ -283,7 +279,7 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) mutex_lock(&dev->device_lock); if (dev->wd_cl.state != MEI_FILE_CONNECTED) { - dev_err(&dev->pdev->dev, "wd: not connected.\n"); + dev_err(dev->dev, "wd: not connected.\n"); ret = -ENODEV; goto end; } @@ -296,7 +292,7 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) /* Check if we can send the ping to HW*/ if (ret && mei_hbuf_acquire(dev)) { - dev_dbg(&dev->pdev->dev, "wd: sending ping\n"); + dev_dbg(dev->dev, "wd: sending ping\n"); ret = mei_wd_send(dev); if (ret) @@ -317,7 +313,7 @@ end: * @wd_dev - watchdog device struct * @timeout - timeout value to set * - * returns 0 if success, negative errno code for failure + * Return: 0 if success, negative errno code for failure */ static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout) @@ -379,13 +375,12 @@ int mei_watchdog_register(struct mei_device *dev) ret = watchdog_register_device(&amt_wd_dev); mutex_lock(&dev->device_lock); if (ret) { - dev_err(&dev->pdev->dev, "wd: unable to register watchdog device = %d.\n", + dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", ret); return ret; } - dev_dbg(&dev->pdev->dev, - "wd: successfully register watchdog interface.\n"); + dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); watchdog_set_drvdata(&amt_wd_dev, dev); return 0; } diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index f14b60080c21..e64794730e21 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -462,16 +462,12 @@ static void mic_handle_config_change(struct mic_device_desc __iomem *d, struct mic_device_ctrl __iomem *dc = (void __iomem *)d + mic_aligned_desc_size(d); struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev); - struct virtio_driver *drv; if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) return; dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__); - drv = container_of(mvdev->vdev.dev.driver, - struct virtio_driver, driver); - if (drv->config_changed) - drv->config_changed(&mvdev->vdev); + virtio_config_changed(&mvdev->vdev); iowrite8(1, &dc->guest_ack); } diff --git a/drivers/misc/spear13xx_pcie_gadget.c b/drivers/misc/spear13xx_pcie_gadget.c index 2e13614d41e8..fe3ad0ca9a3e 100644 --- a/drivers/misc/spear13xx_pcie_gadget.c +++ b/drivers/misc/spear13xx_pcie_gadget.c @@ -9,6 +9,7 @@ * warranty of any kind, whether express or implied. */ +#include <linux/device.h> #include <linux/clk.h> #include <linux/slab.h> #include <linux/delay.h> @@ -743,58 +744,33 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) struct config_item *cg_item; struct configfs_subsystem *subsys; - /* get resource for application registers*/ - - res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res0) { - dev_err(&pdev->dev, "no resource defined\n"); - return -EBUSY; - } - if (!request_mem_region(res0->start, resource_size(res0), - pdev->name)) { - dev_err(&pdev->dev, "pcie gadget region already claimed\n"); - return -EBUSY; - } - /* get resource for dbi registers*/ - - res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res1) { - dev_err(&pdev->dev, "no resource defined\n"); - goto err_rel_res0; - } - if (!request_mem_region(res1->start, resource_size(res1), - pdev->name)) { - dev_err(&pdev->dev, "pcie gadget region already claimed\n"); - goto err_rel_res0; - } - - target = kzalloc(sizeof(*target), GFP_KERNEL); + target = devm_kzalloc(&pdev->dev, sizeof(*target), GFP_KERNEL); if (!target) { dev_err(&pdev->dev, "out of memory\n"); - status = -ENOMEM; - goto err_rel_res; + return -ENOMEM; } cg_item = &target->subsys.su_group.cg_item; sprintf(cg_item->ci_namebuf, "pcie_gadget.%d", pdev->id); cg_item->ci_type = &pcie_gadget_target_type; config = &target->config; - config->va_app_base = (void __iomem *)ioremap(res0->start, - resource_size(res0)); - if (!config->va_app_base) { + + /* get resource for application registers*/ + res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + config->va_app_base = devm_ioremap_resource(&pdev->dev, res0); + if (IS_ERR(config->va_app_base)) { dev_err(&pdev->dev, "ioremap fail\n"); - status = -ENOMEM; - goto err_kzalloc; + return PTR_ERR(config->va_app_base); } + /* get resource for dbi registers*/ + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); config->base = (void __iomem *)res1->start; - config->va_dbi_base = (void __iomem *)ioremap(res1->start, - resource_size(res1)); - if (!config->va_dbi_base) { + config->va_dbi_base = devm_ioremap_resource(&pdev->dev, res1); + if (IS_ERR(config->va_dbi_base)) { dev_err(&pdev->dev, "ioremap fail\n"); - status = -ENOMEM; - goto err_iounmap_app; + return PTR_ERR(config->va_dbi_base); } platform_set_drvdata(pdev, target); @@ -802,15 +778,15 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no update irq?\n"); - status = irq; - goto err_iounmap; + return irq; } - status = request_irq(irq, spear_pcie_gadget_irq, 0, pdev->name, NULL); + status = devm_request_irq(&pdev->dev, irq, spear_pcie_gadget_irq, + 0, pdev->name, NULL); if (status) { dev_err(&pdev->dev, "pcie gadget interrupt IRQ%d already claimed\n", irq); - goto err_iounmap; + return status; } /* Register configfs hooks */ @@ -819,7 +795,7 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) mutex_init(&subsys->su_mutex); status = configfs_register_subsystem(subsys); if (status) - goto err_irq; + return status; /* * init basic pcie application registers @@ -835,13 +811,12 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) clk = clk_get_sys("pcie1", NULL); if (IS_ERR(clk)) { pr_err("%s:couldn't get clk for pcie1\n", __func__); - status = PTR_ERR(clk); - goto err_irq; + return PTR_ERR(clk); } status = clk_enable(clk); if (status) { pr_err("%s:couldn't enable clk for pcie1\n", __func__); - goto err_irq; + return status; } } else if (pdev->id == 2) { /* @@ -851,53 +826,26 @@ static int spear_pcie_gadget_probe(struct platform_device *pdev) clk = clk_get_sys("pcie2", NULL); if (IS_ERR(clk)) { pr_err("%s:couldn't get clk for pcie2\n", __func__); - status = PTR_ERR(clk); - goto err_irq; + return PTR_ERR(clk); } status = clk_enable(clk); if (status) { pr_err("%s:couldn't enable clk for pcie2\n", __func__); - goto err_irq; + return status; } } spear13xx_pcie_device_init(config); return 0; -err_irq: - free_irq(irq, NULL); -err_iounmap: - iounmap(config->va_dbi_base); -err_iounmap_app: - iounmap(config->va_app_base); -err_kzalloc: - kfree(target); -err_rel_res: - release_mem_region(res1->start, resource_size(res1)); -err_rel_res0: - release_mem_region(res0->start, resource_size(res0)); - return status; } static int spear_pcie_gadget_remove(struct platform_device *pdev) { - struct resource *res0, *res1; static struct pcie_gadget_target *target; - struct spear_pcie_gadget_config *config; - int irq; - res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq = platform_get_irq(pdev, 0); target = platform_get_drvdata(pdev); - config = &target->config; - free_irq(irq, NULL); - iounmap(config->va_dbi_base); - iounmap(config->va_app_base); - release_mem_region(res1->start, resource_size(res1)); - release_mem_region(res0->start, resource_size(res0)); configfs_unregister_subsystem(&target->subsys); - kfree(target); return 0; } diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 1972d57aadb3..54be83d3efdd 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -153,8 +153,9 @@ static void st_reg_complete(struct st_data_s *st_gdata, char err) (st_gdata->list[i]->priv_data, err); pr_info("protocol %d's cb sent %d\n", i, err); if (err) { /* cleanup registered protocol */ - st_gdata->protos_registered--; st_gdata->is_registered[i] = false; + if (st_gdata->protos_registered) + st_gdata->protos_registered--; } } } @@ -639,14 +640,12 @@ long st_unregister(struct st_proto_s *proto) return -EPROTONOSUPPORT; } - st_gdata->protos_registered--; + if (st_gdata->protos_registered) + st_gdata->protos_registered--; + remove_channel_from_table(st_gdata, proto); spin_unlock_irqrestore(&st_gdata->lock, flags); - /* paranoid check */ - if (st_gdata->protos_registered < ST_EMPTY) - st_gdata->protos_registered = ST_EMPTY; - if ((st_gdata->protos_registered == ST_EMPTY) && (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { pr_info(" all chnl_ids unregistered "); diff --git a/drivers/misc/vmw_vmci/vmci_datagram.c b/drivers/misc/vmw_vmci/vmci_datagram.c index f3cdd904fe4d..822665245588 100644 --- a/drivers/misc/vmw_vmci/vmci_datagram.c +++ b/drivers/misc/vmw_vmci/vmci_datagram.c @@ -328,7 +328,8 @@ int vmci_datagram_dispatch(u32 context_id, BUILD_BUG_ON(sizeof(struct vmci_datagram) != 24); - if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { + if (dg->payload_size > VMCI_MAX_DG_SIZE || + VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { pr_devel("Payload (size=%llu bytes) too big to send\n", (unsigned long long)dg->payload_size); return VMCI_ERROR_INVALID_ARGS; diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index 248399a881af..189b32519748 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -35,7 +35,6 @@ #include "vmci_driver.h" #include "vmci_event.h" -#define PCI_VENDOR_ID_VMWARE 0x15AD #define PCI_DEVICE_ID_VMWARE_VMCI 0x0740 #define VMCI_UTIL_NUM_RESOURCES 1 |