diff options
Diffstat (limited to 'drivers')
238 files changed, 10928 insertions, 2637 deletions
diff --git a/drivers/accessibility/braille/braille_console.c b/drivers/accessibility/braille/braille_console.c index 359bead4b280..fdc6b593f500 100644 --- a/drivers/accessibility/braille/braille_console.c +++ b/drivers/accessibility/braille/braille_console.c @@ -225,6 +225,7 @@ static int keyboard_notifier_call(struct notifier_block *blk, case KBD_POST_KEYSYM: { unsigned char type = KTYP(param->value) - 0xf0; + if (type == KT_SPEC) { unsigned char val = KVAL(param->value); int on_off = -1; @@ -265,6 +266,7 @@ static int vt_notifier_call(struct notifier_block *blk, { struct vt_notifier_param *param = _param; struct vc_data *vc = param->vc; + switch (code) { case VT_ALLOCATE: break; @@ -273,6 +275,7 @@ static int vt_notifier_call(struct notifier_block *blk, case VT_WRITE: { unsigned char c = param->c; + if (vc->vc_num != fg_console) break; switch (c) { diff --git a/drivers/accessibility/speakup/i18n.c b/drivers/accessibility/speakup/i18n.c index 46bd50f3c3a4..bc7b47d1876f 100644 --- a/drivers/accessibility/speakup/i18n.c +++ b/drivers/accessibility/speakup/i18n.c @@ -90,6 +90,13 @@ static char *speakup_default_msgs[MSG_LAST_INDEX] = { [MSG_COLOR_YELLOW] = "yellow", [MSG_COLOR_WHITE] = "white", [MSG_COLOR_GREY] = "grey", + [MSG_COLOR_BRIGHTBLUE] "bright blue", + [MSG_COLOR_BRIGHTGREEN] "bright green", + [MSG_COLOR_BRIGHTCYAN] "bright cyan", + [MSG_COLOR_BRIGHTRED] "bright red", + [MSG_COLOR_BRIGHTMAGENTA] "bright magenta", + [MSG_COLOR_BRIGHTYELLOW] "bright yellow", + [MSG_COLOR_BRIGHTWHITE] "bright white", /* Names of key states. */ [MSG_STATE_DOUBLE] = "double", diff --git a/drivers/accessibility/speakup/i18n.h b/drivers/accessibility/speakup/i18n.h index 2a607d263234..51e3260995d9 100644 --- a/drivers/accessibility/speakup/i18n.h +++ b/drivers/accessibility/speakup/i18n.h @@ -99,7 +99,14 @@ enum msg_index_t { MSG_COLOR_YELLOW, MSG_COLOR_WHITE, MSG_COLOR_GREY, - MSG_COLORS_END = MSG_COLOR_GREY, + MSG_COLOR_BRIGHTBLUE, + MSG_COLOR_BRIGHTGREEN, + MSG_COLOR_BRIGHTCYAN, + MSG_COLOR_BRIGHTRED, + MSG_COLOR_BRIGHTMAGENTA, + MSG_COLOR_BRIGHTYELLOW, + MSG_COLOR_BRIGHTWHITE, + MSG_COLORS_END = MSG_COLOR_BRIGHTWHITE, MSG_STATES_START, MSG_STATE_DOUBLE = MSG_STATES_START, diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 428fceaf9d50..d726537fa16c 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -389,10 +389,6 @@ static void say_attributes(struct vc_data *vc) int fg = spk_attr & 0x0f; int bg = spk_attr >> 4; - if (fg > 8) { - synth_printf("%s ", spk_msg_get(MSG_BRIGHT)); - fg -= 8; - } synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg)); if (bg > 7) { synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING)); diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index c4631e684386..4d4bb810c2ae 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -1050,7 +1050,7 @@ err_out_release_mdesc: return err; } -static int vdc_port_remove(struct vio_dev *vdev) +static void vdc_port_remove(struct vio_dev *vdev) { struct vdc_port *port = dev_get_drvdata(&vdev->dev); @@ -1072,7 +1072,6 @@ static int vdc_port_remove(struct vio_dev *vdev) kfree(port); } - return 0; } static void vdc_requeue_inflight(struct vdc_port *port) diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index e3e2ae41c22b..315e830b6ecd 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -350,7 +350,8 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state * * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object - * + * @alloc_interrupts: if true the function allocates the interrupt pool, + * otherwise the interrupt allocation is delayed * Scans the physical DPRC and synchronizes the state of the Linux * bus driver with the actual state of the MC by adding and removing * devices as appropriate. @@ -373,10 +374,11 @@ int dprc_scan_container(struct fsl_mc_device *mc_bus_dev, return error; } EXPORT_SYMBOL_GPL(dprc_scan_container); + /** * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 * - * @irq: IRQ number of the interrupt being handled + * @irq_num: IRQ number of the interrupt being handled * @arg: Pointer to device structure */ static irqreturn_t dprc_irq0_handler(int irq_num, void *arg) @@ -387,7 +389,7 @@ static irqreturn_t dprc_irq0_handler(int irq_num, void *arg) /** * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0 * - * @irq: IRQ number of the interrupt being handled + * @irq_num: IRQ number of the interrupt being handled * @arg: Pointer to device structure */ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) diff --git a/drivers/bus/fsl-mc/dprc.c b/drivers/bus/fsl-mc/dprc.c index 27b0a01bad9b..d129338b8bc0 100644 --- a/drivers/bus/fsl-mc/dprc.c +++ b/drivers/bus/fsl-mc/dprc.c @@ -334,7 +334,7 @@ int dprc_clear_irq_status(struct fsl_mc_io *mc_io, * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @token: Token of DPRC object - * @attributes Returned container attributes + * @attr: Returned container attributes * * Return: '0' on Success; Error code otherwise. */ @@ -504,7 +504,7 @@ EXPORT_SYMBOL_GPL(dprc_set_obj_irq); * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' * @token: Token of DPRC object - * @obj_type; Object type as returned in dprc_get_obj() + * @obj_type: Object type as returned in dprc_get_obj() * @obj_id: Unique object instance as returned in dprc_get_obj() * @region_index: The specific region to query * @region_desc: Returns the requested region descriptor diff --git a/drivers/bus/fsl-mc/fsl-mc-allocator.c b/drivers/bus/fsl-mc/fsl-mc-allocator.c index 2d7c764bb7dc..6c513556911e 100644 --- a/drivers/bus/fsl-mc/fsl-mc-allocator.c +++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c @@ -254,7 +254,7 @@ EXPORT_SYMBOL_GPL(fsl_mc_resource_free); * @mc_dev: fsl-mc device which is used in conjunction with the * allocated object * @pool_type: pool type - * @new_mc_dev: pointer to area where the pointer to the allocated device + * @new_mc_adev: pointer to area where the pointer to the allocated device * is to be returned * * Allocatable objects are always used in conjunction with some functional @@ -409,7 +409,7 @@ cleanup_msi_irqs: } EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); -/** +/* * Teardown the interrupt pool associated with an fsl-mc bus. * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. */ @@ -436,7 +436,7 @@ void fsl_mc_cleanup_irq_pool(struct fsl_mc_device *mc_bus_dev) } EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); -/** +/* * Allocate the IRQs required by a given fsl-mc device. */ int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) @@ -578,7 +578,7 @@ void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type); } -/** +/* * fsl_mc_allocator_probe - callback invoked when an allocatable device is * being added to the system */ @@ -610,7 +610,7 @@ static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) return 0; } -/** +/* * fsl_mc_allocator_remove - callback invoked when an allocatable device is * being removed from the system */ diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 380ad1fdb745..09c8ab5e0959 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -24,7 +24,7 @@ #include "fsl-mc-private.h" -/** +/* * Default DMA mask for devices on a fsl-mc bus */ #define FSL_MC_DEFAULT_DMA_MASK (~0ULL) @@ -36,6 +36,7 @@ static struct fsl_mc_version mc_version; * @root_mc_bus_dev: fsl-mc device representing the root DPRC * @num_translation_ranges: number of entries in addr_translation_ranges * @translation_ranges: array of bus to system address translation ranges + * @fsl_mc_regs: base address of register bank */ struct fsl_mc { struct fsl_mc_device *root_mc_bus_dev; @@ -117,7 +118,7 @@ out: return found; } -/** +/* * fsl_mc_bus_uevent - callback invoked when a device is added */ static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) @@ -467,7 +468,7 @@ static void fsl_mc_driver_shutdown(struct device *dev) mc_drv->shutdown(mc_dev); } -/** +/* * __fsl_mc_driver_register - registers a child device driver with the * MC bus * @@ -503,7 +504,7 @@ int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver, } EXPORT_SYMBOL_GPL(__fsl_mc_driver_register); -/** +/* * fsl_mc_driver_unregister - unregisters a device driver from the * MC bus */ @@ -563,7 +564,7 @@ struct fsl_mc_version *fsl_mc_get_version(void) } EXPORT_SYMBOL_GPL(fsl_mc_get_version); -/** +/* * fsl_mc_get_root_dprc - function to traverse to the root dprc */ void fsl_mc_get_root_dprc(struct device *dev, @@ -732,7 +733,7 @@ error_cleanup_regions: return error; } -/** +/* * fsl_mc_is_root_dprc - function to check if a given device is a root dprc */ bool fsl_mc_is_root_dprc(struct device *dev) @@ -757,7 +758,7 @@ static void fsl_mc_device_release(struct device *dev) kfree(mc_dev); } -/** +/* * Add a newly discovered fsl-mc device to be visible in Linux */ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, @@ -1058,7 +1059,7 @@ static int get_mc_addr_translation_ranges(struct device *dev, return 0; } -/** +/* * fsl_mc_bus_probe - callback invoked when the root MC bus is being * added */ @@ -1182,7 +1183,7 @@ error_cleanup_mc_io: return error; } -/** +/* * fsl_mc_bus_remove - callback invoked when the root MC bus is being * removed */ diff --git a/drivers/bus/fsl-mc/fsl-mc-msi.c b/drivers/bus/fsl-mc/fsl-mc-msi.c index 8edadf05cbb7..cf974870ba55 100644 --- a/drivers/bus/fsl-mc/fsl-mc-msi.c +++ b/drivers/bus/fsl-mc/fsl-mc-msi.c @@ -148,7 +148,7 @@ static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info) /** * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain - * @np: Optional device-tree node of the interrupt controller + * @fwnode: Optional firmware node of the interrupt controller * @info: MSI domain info * @parent: Parent irq domain * diff --git a/drivers/bus/fsl-mc/mc-io.c b/drivers/bus/fsl-mc/mc-io.c index 305015486b91..95b10a6cf307 100644 --- a/drivers/bus/fsl-mc/mc-io.c +++ b/drivers/bus/fsl-mc/mc-io.c @@ -50,12 +50,12 @@ static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) } /** - * Creates an MC I/O object + * fsl_create_mc_io() - Creates an MC I/O object * * @dev: device to be associated with the MC I/O object * @mc_portal_phys_addr: physical address of the MC portal to use * @mc_portal_size: size in bytes of the MC portal - * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O + * @dpmcp_dev: Pointer to the DPMCP object associated with this MC I/O * object or NULL if none. * @flags: flags for the new MC I/O object * @new_mc_io: Area to return pointer to newly created MC I/O object @@ -123,7 +123,7 @@ error_destroy_mc_io: } /** - * Destroys an MC I/O object + * fsl_destroy_mc_io() - Destroys an MC I/O object * * @mc_io: MC I/O object to destroy */ diff --git a/drivers/bus/fsl-mc/mc-sys.c b/drivers/bus/fsl-mc/mc-sys.c index b291b35e3884..f2052cd0a051 100644 --- a/drivers/bus/fsl-mc/mc-sys.c +++ b/drivers/bus/fsl-mc/mc-sys.c @@ -16,7 +16,7 @@ #include "fsl-mc-private.h" -/** +/* * Timeout in milliseconds to wait for the completion of an MC command */ #define MC_CMD_COMPLETION_TIMEOUT_MS 500 @@ -148,9 +148,10 @@ static inline enum mc_cmd_status mc_read_response(struct fsl_mc_command __iomem } /** - * Waits for the completion of an MC command doing preemptible polling. - * uslepp_range() is called between polling iterations. - * + * mc_polling_wait_preemptible() - Waits for the completion of an MC + * command doing preemptible polling. + * uslepp_range() is called between + * polling iterations. * @mc_io: MC I/O object to be used * @cmd: command buffer to receive MC response * @mc_status: MC command completion status @@ -194,9 +195,9 @@ static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, } /** - * Waits for the completion of an MC command doing atomic polling. - * udelay() is called between polling iterations. - * + * mc_polling_wait_atomic() - Waits for the completion of an MC command + * doing atomic polling. udelay() is called + * between polling iterations. * @mc_io: MC I/O object to be used * @cmd: command buffer to receive MC response * @mc_status: MC command completion status @@ -234,8 +235,8 @@ static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, } /** - * Sends a command to the MC device using the given MC I/O object - * + * mc_send_command() - Sends a command to the MC device using the given + * MC I/O object * @mc_io: MC I/O object to be used * @cmd: command to be sent * diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c index e2e59a341fef..bbf6cd04861e 100644 --- a/drivers/bus/mhi/core/pm.c +++ b/drivers/bus/mhi/core/pm.c @@ -465,23 +465,15 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) /* Trigger MHI RESET so that the device will not access host memory */ if (!MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) { - u32 in_reset = -1; - unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms); - dev_dbg(dev, "Triggering MHI Reset in device\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); /* Wait for the reset bit to be cleared by the device */ - ret = wait_event_timeout(mhi_cntrl->state_event, - mhi_read_reg_field(mhi_cntrl, - mhi_cntrl->regs, - MHICTRL, - MHICTRL_RESET_MASK, - MHICTRL_RESET_SHIFT, - &in_reset) || - !in_reset, timeout); - if (!ret || in_reset) - dev_err(dev, "Device failed to exit MHI Reset state\n"); + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, + MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0, + 25000); + if (ret) + dev_err(dev, "Device failed to clear MHI Reset\n"); /* * Device will clear BHI_INTVEC as a part of RESET processing, @@ -934,6 +926,7 @@ int mhi_pm_resume(struct mhi_controller *mhi_cntrl) ret = wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->dev_state == MHI_STATE_M0 || + mhi_cntrl->dev_state == MHI_STATE_M2 || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), msecs_to_jiffies(mhi_cntrl->timeout_ms)); diff --git a/drivers/bus/mhi/pci_generic.c b/drivers/bus/mhi/pci_generic.c index b3357a8a2fdb..ca3bc40427f8 100644 --- a/drivers/bus/mhi/pci_generic.c +++ b/drivers/bus/mhi/pci_generic.c @@ -665,7 +665,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) err = mhi_register_controller(mhi_cntrl, mhi_cntrl_config); if (err) - return err; + goto err_disable_reporting; /* MHI bus does not power up the controller by default */ err = mhi_prepare_for_power_up(mhi_cntrl); @@ -699,6 +699,8 @@ err_unprepare: mhi_unprepare_after_power_down(mhi_cntrl); err_unregister: mhi_unregister_controller(mhi_cntrl); +err_disable_reporting: + pci_disable_pcie_error_reporting(pdev); return err; } @@ -721,6 +723,7 @@ static void mhi_pci_remove(struct pci_dev *pdev) pm_runtime_get_noresume(&pdev->dev); mhi_unregister_controller(mhi_cntrl); + pci_disable_pcie_error_reporting(pdev); } static void mhi_pci_shutdown(struct pci_dev *pdev) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index b151e0fcdeb5..8e516aad632b 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -357,27 +357,6 @@ config NVRAM To compile this driver as a module, choose M here: the module will be called nvram. -config RAW_DRIVER - tristate "RAW driver (/dev/raw/rawN)" - depends on BLOCK - help - The raw driver permits block devices to be bound to /dev/raw/rawN. - Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. - See the raw(8) manpage for more details. - - Applications should preferably open the device (eg /dev/hda1) - with the O_DIRECT flag. - -config MAX_RAW_DEVS - int "Maximum number of RAW devices to support (1-65536)" - depends on RAW_DRIVER - range 1 65536 - default "256" - help - The maximum number of RAW devices that are supported. - Default is 256. Increase this number in case you need lots of - raw devices. - config DEVPORT bool "/dev/port character device" depends on ISA || PCI diff --git a/drivers/char/Makefile b/drivers/char/Makefile index ffce287ef415..264eb398fdd4 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o -obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_IBM_BSR) += bsr.o @@ -44,6 +43,6 @@ obj-$(CONFIG_TCG_TPM) += tpm/ obj-$(CONFIG_PS3_FLASH) += ps3flash.o -obj-$(CONFIG_XILLYBUS) += xillybus/ +obj-$(CONFIG_XILLYBUS_CLASS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 8b55085650ad..4e5431f01450 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -156,12 +156,12 @@ static irqreturn_t hpet_interrupt(int irq, void *data) * This has the effect of treating non-periodic like periodic. */ if ((devp->hd_flags & (HPET_IE | HPET_PERIODIC)) == HPET_IE) { - unsigned long m, t, mc, base, k; + unsigned long t, mc, base, k; struct hpet __iomem *hpet = devp->hd_hpet; struct hpets *hpetp = devp->hd_hpets; t = devp->hd_ireqfreq; - m = read_counter(&devp->hd_timer->hpet_compare); + read_counter(&devp->hd_timer->hpet_compare); mc = read_counter(&hpet->hpet_mc); /* The time for the next interrupt would logically be t + m, * however, if we are very unlucky and the interrupt is delayed diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c index f4949b689bd5..62bdd5af1339 100644 --- a/drivers/char/hw_random/pseries-rng.c +++ b/drivers/char/hw_random/pseries-rng.c @@ -29,7 +29,7 @@ static int pseries_rng_read(struct hwrng *rng, void *data, size_t max, bool wait return 8; } -/** +/* * pseries_rng_get_desired_dma - Return desired DMA allocate for CMO operations * * This is a required function for a driver to operate in a CMO environment diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 15dc54fa1d47..1c596b5cdb27 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -16,7 +16,6 @@ #include <linux/mman.h> #include <linux/random.h> #include <linux/init.h> -#include <linux/raw.h> #include <linux/tty.h> #include <linux/capability.h> #include <linux/ptrace.h> diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 89681f07bc78..8f1bce0b4fe5 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -544,6 +544,10 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq) io_read_num_rec_bytes(iobase, &num_bytes_read); if (num_bytes_read >= 4) { DEBUGP(2, dev, "NumRecBytes = %i\n", num_bytes_read); + if (num_bytes_read > 4) { + rc = -EIO; + goto exit_setprotocol; + } break; } usleep_range(10000, 11000); @@ -1050,7 +1054,6 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf, struct cm4000_dev *dev = filp->private_data; unsigned int iobase = dev->p_dev->resource[0]->start; unsigned short s; - unsigned char tmp; unsigned char infolen; unsigned char sendT0; unsigned short nsend; @@ -1148,7 +1151,7 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf, set_cardparameter(dev); /* dummy read, reset flag procedure received */ - tmp = inb(REG_FLAGS1(iobase)); + inb(REG_FLAGS1(iobase)); dev->flags1 = 0x20 /* T_Active */ | (sendT0) diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index d5e43606339c..827711911da4 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -221,7 +221,6 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf, unsigned long i; size_t min_bytes_to_read; int rc; - unsigned char uc; DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid); @@ -308,7 +307,7 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf, return -EIO; } - uc = xinb(iobase + REG_OFFSET_BULK_IN); + xinb(iobase + REG_OFFSET_BULK_IN); DEBUGP(2, dev, "<- cm4040_read (successfully)\n"); return min_bytes_to_read; diff --git a/drivers/char/pcmcia/scr24x_cs.c b/drivers/char/pcmcia/scr24x_cs.c index 47feb39af34c..1bdce08fae3d 100644 --- a/drivers/char/pcmcia/scr24x_cs.c +++ b/drivers/char/pcmcia/scr24x_cs.c @@ -265,7 +265,6 @@ static int scr24x_probe(struct pcmcia_device *link) cdev_init(&dev->c_dev, &scr24x_fops); dev->c_dev.owner = THIS_MODULE; - dev->c_dev.ops = &scr24x_fops; ret = cdev_add(&dev->c_dev, MKDEV(MAJOR(scr24x_devt), dev->devno), 1); if (ret < 0) goto err; diff --git a/drivers/char/raw.c b/drivers/char/raw.c deleted file mode 100644 index 5d52a1f4738c..000000000000 --- a/drivers/char/raw.c +++ /dev/null @@ -1,362 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * linux/drivers/char/raw.c - * - * Front-end raw character devices. These can be bound to any block - * devices to provide genuine Unix raw character device semantics. - * - * We reserve minor number 0 for a control interface. ioctl()s on this - * device are used to bind the other minor numbers to block devices. - */ - -#include <linux/init.h> -#include <linux/fs.h> -#include <linux/major.h> -#include <linux/blkdev.h> -#include <linux/backing-dev.h> -#include <linux/module.h> -#include <linux/raw.h> -#include <linux/capability.h> -#include <linux/uio.h> -#include <linux/cdev.h> -#include <linux/device.h> -#include <linux/mutex.h> -#include <linux/gfp.h> -#include <linux/compat.h> -#include <linux/vmalloc.h> - -#include <linux/uaccess.h> - -struct raw_device_data { - dev_t binding; - struct block_device *bdev; - int inuse; -}; - -static struct class *raw_class; -static struct raw_device_data *raw_devices; -static DEFINE_MUTEX(raw_mutex); -static const struct file_operations raw_ctl_fops; /* forward declaration */ - -static int max_raw_minors = CONFIG_MAX_RAW_DEVS; - -module_param(max_raw_minors, int, 0); -MODULE_PARM_DESC(max_raw_minors, "Maximum number of raw devices (1-65536)"); - -/* - * Open/close code for raw IO. - * - * We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to - * point at the blockdev's address_space and set the file handle to use - * O_DIRECT. - * - * Set the device's soft blocksize to the minimum possible. This gives the - * finest possible alignment and has no adverse impact on performance. - */ -static int raw_open(struct inode *inode, struct file *filp) -{ - const int minor = iminor(inode); - struct block_device *bdev; - int err; - - if (minor == 0) { /* It is the control device */ - filp->f_op = &raw_ctl_fops; - return 0; - } - - pr_warn_ratelimited( - "process %s (pid %d) is using the deprecated raw device\n" - "support will be removed in Linux 5.14.\n", - current->comm, current->pid); - - mutex_lock(&raw_mutex); - - /* - * All we need to do on open is check that the device is bound. - */ - err = -ENODEV; - if (!raw_devices[minor].binding) - goto out; - bdev = blkdev_get_by_dev(raw_devices[minor].binding, - filp->f_mode | FMODE_EXCL, raw_open); - if (IS_ERR(bdev)) { - err = PTR_ERR(bdev); - goto out; - } - err = set_blocksize(bdev, bdev_logical_block_size(bdev)); - if (err) - goto out1; - filp->f_flags |= O_DIRECT; - filp->f_mapping = bdev->bd_inode->i_mapping; - if (++raw_devices[minor].inuse == 1) - file_inode(filp)->i_mapping = - bdev->bd_inode->i_mapping; - filp->private_data = bdev; - raw_devices[minor].bdev = bdev; - mutex_unlock(&raw_mutex); - return 0; - -out1: - blkdev_put(bdev, filp->f_mode | FMODE_EXCL); -out: - mutex_unlock(&raw_mutex); - return err; -} - -/* - * When the final fd which refers to this character-special node is closed, we - * make its ->mapping point back at its own i_data. - */ -static int raw_release(struct inode *inode, struct file *filp) -{ - const int minor= iminor(inode); - struct block_device *bdev; - - mutex_lock(&raw_mutex); - bdev = raw_devices[minor].bdev; - if (--raw_devices[minor].inuse == 0) - /* Here inode->i_mapping == bdev->bd_inode->i_mapping */ - inode->i_mapping = &inode->i_data; - mutex_unlock(&raw_mutex); - - blkdev_put(bdev, filp->f_mode | FMODE_EXCL); - return 0; -} - -/* - * Forward ioctls to the underlying block device. - */ -static long -raw_ioctl(struct file *filp, unsigned int command, unsigned long arg) -{ - struct block_device *bdev = filp->private_data; - return blkdev_ioctl(bdev, 0, command, arg); -} - -static int bind_set(int number, u64 major, u64 minor) -{ - dev_t dev = MKDEV(major, minor); - dev_t raw = MKDEV(RAW_MAJOR, number); - struct raw_device_data *rawdev; - int err = 0; - - if (number <= 0 || number >= max_raw_minors) - return -EINVAL; - - if (MAJOR(dev) != major || MINOR(dev) != minor) - return -EINVAL; - - rawdev = &raw_devices[number]; - - /* - * This is like making block devices, so demand the - * same capability - */ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - /* - * For now, we don't need to check that the underlying - * block device is present or not: we can do that when - * the raw device is opened. Just check that the - * major/minor numbers make sense. - */ - - if (MAJOR(dev) == 0 && dev != 0) - return -EINVAL; - - mutex_lock(&raw_mutex); - if (rawdev->inuse) { - mutex_unlock(&raw_mutex); - return -EBUSY; - } - if (rawdev->binding) - module_put(THIS_MODULE); - - rawdev->binding = dev; - if (!dev) { - /* unbind */ - device_destroy(raw_class, raw); - } else { - __module_get(THIS_MODULE); - device_destroy(raw_class, raw); - device_create(raw_class, NULL, raw, NULL, "raw%d", number); - } - mutex_unlock(&raw_mutex); - return err; -} - -static int bind_get(int number, dev_t *dev) -{ - if (number <= 0 || number >= max_raw_minors) - return -EINVAL; - *dev = raw_devices[number].binding; - return 0; -} - -/* - * Deal with ioctls against the raw-device control interface, to bind - * and unbind other raw devices. - */ -static long raw_ctl_ioctl(struct file *filp, unsigned int command, - unsigned long arg) -{ - struct raw_config_request rq; - dev_t dev; - int err; - - switch (command) { - case RAW_SETBIND: - if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) - return -EFAULT; - - return bind_set(rq.raw_minor, rq.block_major, rq.block_minor); - - case RAW_GETBIND: - if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) - return -EFAULT; - - err = bind_get(rq.raw_minor, &dev); - if (err) - return err; - - rq.block_major = MAJOR(dev); - rq.block_minor = MINOR(dev); - - if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) - return -EFAULT; - - return 0; - } - - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -struct raw32_config_request { - compat_int_t raw_minor; - compat_u64 block_major; - compat_u64 block_minor; -}; - -static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct raw32_config_request __user *user_req = compat_ptr(arg); - struct raw32_config_request rq; - dev_t dev; - int err = 0; - - switch (cmd) { - case RAW_SETBIND: - if (copy_from_user(&rq, user_req, sizeof(rq))) - return -EFAULT; - - return bind_set(rq.raw_minor, rq.block_major, rq.block_minor); - - case RAW_GETBIND: - if (copy_from_user(&rq, user_req, sizeof(rq))) - return -EFAULT; - - err = bind_get(rq.raw_minor, &dev); - if (err) - return err; - - rq.block_major = MAJOR(dev); - rq.block_minor = MINOR(dev); - - if (copy_to_user(user_req, &rq, sizeof(rq))) - return -EFAULT; - - return 0; - } - - return -EINVAL; -} -#endif - -static const struct file_operations raw_fops = { - .read_iter = blkdev_read_iter, - .write_iter = blkdev_write_iter, - .fsync = blkdev_fsync, - .open = raw_open, - .release = raw_release, - .unlocked_ioctl = raw_ioctl, - .llseek = default_llseek, - .owner = THIS_MODULE, -}; - -static const struct file_operations raw_ctl_fops = { - .unlocked_ioctl = raw_ctl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = raw_ctl_compat_ioctl, -#endif - .open = raw_open, - .owner = THIS_MODULE, - .llseek = noop_llseek, -}; - -static struct cdev raw_cdev; - -static char *raw_devnode(struct device *dev, umode_t *mode) -{ - return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev)); -} - -static int __init raw_init(void) -{ - dev_t dev = MKDEV(RAW_MAJOR, 0); - int ret; - - if (max_raw_minors < 1 || max_raw_minors > 65536) { - pr_warn("raw: invalid max_raw_minors (must be between 1 and 65536), using %d\n", - CONFIG_MAX_RAW_DEVS); - max_raw_minors = CONFIG_MAX_RAW_DEVS; - } - - raw_devices = vzalloc(array_size(max_raw_minors, - sizeof(struct raw_device_data))); - if (!raw_devices) { - printk(KERN_ERR "Not enough memory for raw device structures\n"); - ret = -ENOMEM; - goto error; - } - - ret = register_chrdev_region(dev, max_raw_minors, "raw"); - if (ret) - goto error; - - cdev_init(&raw_cdev, &raw_fops); - ret = cdev_add(&raw_cdev, dev, max_raw_minors); - if (ret) - goto error_region; - raw_class = class_create(THIS_MODULE, "raw"); - if (IS_ERR(raw_class)) { - printk(KERN_ERR "Error creating raw class.\n"); - cdev_del(&raw_cdev); - ret = PTR_ERR(raw_class); - goto error_region; - } - raw_class->devnode = raw_devnode; - device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl"); - - return 0; - -error_region: - unregister_chrdev_region(dev, max_raw_minors); -error: - vfree(raw_devices); - return ret; -} - -static void __exit raw_exit(void) -{ - device_destroy(raw_class, MKDEV(RAW_MAJOR, 0)); - class_destroy(raw_class); - cdev_del(&raw_cdev); - unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), max_raw_minors); -} - -module_init(raw_init); -module_exit(raw_exit); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/xillybus/Kconfig b/drivers/char/xillybus/Kconfig index 130dbdce858f..a8036dad437e 100644 --- a/drivers/char/xillybus/Kconfig +++ b/drivers/char/xillybus/Kconfig @@ -3,10 +3,14 @@ # Xillybus devices # +config XILLYBUS_CLASS + tristate + config XILLYBUS tristate "Xillybus generic FPGA interface" depends on PCI || OF select CRC32 + select XILLYBUS_CLASS help Xillybus is a generic interface for peripherals designed on programmable logic (FPGA). The driver probes the hardware for @@ -21,7 +25,7 @@ config XILLYBUS_PCIE depends on PCI_MSI help Set to M if you want Xillybus to use PCI Express for communicating - with the FPGA. + with the FPGA. The module will be called xillybus_pcie. config XILLYBUS_OF tristate "Xillybus over Device Tree" @@ -29,6 +33,20 @@ config XILLYBUS_OF help Set to M if you want Xillybus to find its resources from the Open Firmware Flattened Device Tree. If the target is an embedded - system, say M. + system, say M. The module will be called xillybus_of. endif # if XILLYBUS + +# XILLYUSB doesn't depend on XILLYBUS + +config XILLYUSB + tristate "XillyUSB: Xillybus generic FPGA interface for USB" + depends on USB + select CRC32 + select XILLYBUS_CLASS + help + XillyUSB is the Xillybus variant which uses USB for communicating + with the FPGA. + + Set to M if you want Xillybus to use USB for communicating with + the FPGA. The module will be called xillyusb. diff --git a/drivers/char/xillybus/Makefile b/drivers/char/xillybus/Makefile index 099e9a3585fc..16f31d03209d 100644 --- a/drivers/char/xillybus/Makefile +++ b/drivers/char/xillybus/Makefile @@ -3,6 +3,8 @@ # Makefile for Xillybus driver # +obj-$(CONFIG_XILLYBUS_CLASS) += xillybus_class.o obj-$(CONFIG_XILLYBUS) += xillybus_core.o obj-$(CONFIG_XILLYBUS_PCIE) += xillybus_pcie.o obj-$(CONFIG_XILLYBUS_OF) += xillybus_of.o +obj-$(CONFIG_XILLYUSB) += xillyusb.o diff --git a/drivers/char/xillybus/xillybus.h b/drivers/char/xillybus/xillybus.h index 8e3ed4d1bb7f..c63ffc56637c 100644 --- a/drivers/char/xillybus/xillybus.h +++ b/drivers/char/xillybus/xillybus.h @@ -30,7 +30,8 @@ struct xilly_buffer { struct xilly_idt_handle { unsigned char *chandesc; - unsigned char *idt; + unsigned char *names; + int names_len; int entries; }; @@ -94,7 +95,6 @@ struct xilly_endpoint { struct device *dev; struct xilly_endpoint_hardware *ephw; - struct list_head ep_list; int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */ __iomem void *registers; int fatal_error; @@ -102,12 +102,6 @@ struct xilly_endpoint { struct mutex register_mutex; wait_queue_head_t ep_wait; - /* Channels and message handling */ - struct cdev cdev; - - int major; - int lowest_minor; /* Highest minor = lowest_minor + num_channels - 1 */ - int num_channels; /* EXCLUDING message buffer */ struct xilly_channel **channels; int msg_counter; diff --git a/drivers/char/xillybus/xillybus_class.c b/drivers/char/xillybus/xillybus_class.c new file mode 100644 index 000000000000..5046486011c8 --- /dev/null +++ b/drivers/char/xillybus/xillybus_class.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 Xillybus Ltd, http://xillybus.com + * + * Driver for the Xillybus class + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/mutex.h> + +#include "xillybus_class.h" + +MODULE_DESCRIPTION("Driver for Xillybus class"); +MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); +MODULE_ALIAS("xillybus_class"); +MODULE_LICENSE("GPL v2"); + +static DEFINE_MUTEX(unit_mutex); +static LIST_HEAD(unit_list); +static struct class *xillybus_class; + +#define UNITNAMELEN 16 + +struct xilly_unit { + struct list_head list_entry; + void *private_data; + + struct cdev *cdev; + char name[UNITNAMELEN]; + int major; + int lowest_minor; + int num_nodes; +}; + +int xillybus_init_chrdev(struct device *dev, + const struct file_operations *fops, + struct module *owner, + void *private_data, + unsigned char *idt, unsigned int len, + int num_nodes, + const char *prefix, bool enumerate) +{ + int rc; + dev_t mdev; + int i; + char devname[48]; + + struct device *device; + size_t namelen; + struct xilly_unit *unit, *u; + + unit = kzalloc(sizeof(*unit), GFP_KERNEL); + + if (!unit) + return -ENOMEM; + + mutex_lock(&unit_mutex); + + if (!enumerate) + snprintf(unit->name, UNITNAMELEN, "%s", prefix); + + for (i = 0; enumerate; i++) { + snprintf(unit->name, UNITNAMELEN, "%s_%02d", + prefix, i); + + enumerate = false; + list_for_each_entry(u, &unit_list, list_entry) + if (!strcmp(unit->name, u->name)) { + enumerate = true; + break; + } + } + + rc = alloc_chrdev_region(&mdev, 0, num_nodes, unit->name); + + if (rc) { + dev_warn(dev, "Failed to obtain major/minors"); + goto fail_obtain; + } + + unit->major = MAJOR(mdev); + unit->lowest_minor = MINOR(mdev); + unit->num_nodes = num_nodes; + unit->private_data = private_data; + + unit->cdev = cdev_alloc(); + if (!unit->cdev) { + rc = -ENOMEM; + goto unregister_chrdev; + } + unit->cdev->ops = fops; + unit->cdev->owner = owner; + + rc = cdev_add(unit->cdev, MKDEV(unit->major, unit->lowest_minor), + unit->num_nodes); + if (rc) { + dev_err(dev, "Failed to add cdev.\n"); + /* kobject_put() is normally done by cdev_del() */ + kobject_put(&unit->cdev->kobj); + goto unregister_chrdev; + } + + for (i = 0; i < num_nodes; i++) { + namelen = strnlen(idt, len); + + if (namelen == len) { + dev_err(dev, "IDT's list of names is too short. This is exceptionally weird, because its CRC is OK\n"); + rc = -ENODEV; + goto unroll_device_create; + } + + snprintf(devname, sizeof(devname), "%s_%s", + unit->name, idt); + + len -= namelen + 1; + idt += namelen + 1; + + device = device_create(xillybus_class, + NULL, + MKDEV(unit->major, + i + unit->lowest_minor), + NULL, + "%s", devname); + + if (IS_ERR(device)) { + dev_err(dev, "Failed to create %s device. Aborting.\n", + devname); + rc = -ENODEV; + goto unroll_device_create; + } + } + + if (len) { + dev_err(dev, "IDT's list of names is too long. This is exceptionally weird, because its CRC is OK\n"); + rc = -ENODEV; + goto unroll_device_create; + } + + list_add_tail(&unit->list_entry, &unit_list); + + dev_info(dev, "Created %d device files.\n", num_nodes); + + mutex_unlock(&unit_mutex); + + return 0; + +unroll_device_create: + for (i--; i >= 0; i--) + device_destroy(xillybus_class, MKDEV(unit->major, + i + unit->lowest_minor)); + + cdev_del(unit->cdev); + +unregister_chrdev: + unregister_chrdev_region(MKDEV(unit->major, unit->lowest_minor), + unit->num_nodes); + +fail_obtain: + mutex_unlock(&unit_mutex); + + kfree(unit); + + return rc; +} +EXPORT_SYMBOL(xillybus_init_chrdev); + +void xillybus_cleanup_chrdev(void *private_data, + struct device *dev) +{ + int minor; + struct xilly_unit *unit; + bool found = false; + + mutex_lock(&unit_mutex); + + list_for_each_entry(unit, &unit_list, list_entry) + if (unit->private_data == private_data) { + found = true; + break; + } + + if (!found) { + dev_err(dev, "Weird bug: Failed to find unit\n"); + mutex_unlock(&unit_mutex); + return; + } + + for (minor = unit->lowest_minor; + minor < (unit->lowest_minor + unit->num_nodes); + minor++) + device_destroy(xillybus_class, MKDEV(unit->major, minor)); + + cdev_del(unit->cdev); + + unregister_chrdev_region(MKDEV(unit->major, unit->lowest_minor), + unit->num_nodes); + + dev_info(dev, "Removed %d device files.\n", + unit->num_nodes); + + list_del(&unit->list_entry); + kfree(unit); + + mutex_unlock(&unit_mutex); +} +EXPORT_SYMBOL(xillybus_cleanup_chrdev); + +int xillybus_find_inode(struct inode *inode, + void **private_data, int *index) +{ + int minor = iminor(inode); + int major = imajor(inode); + struct xilly_unit *unit; + bool found = false; + + mutex_lock(&unit_mutex); + + list_for_each_entry(unit, &unit_list, list_entry) + if (unit->major == major && + minor >= unit->lowest_minor && + minor < (unit->lowest_minor + unit->num_nodes)) { + found = true; + break; + } + + mutex_unlock(&unit_mutex); + + if (!found) + return -ENODEV; + + *private_data = unit->private_data; + *index = minor - unit->lowest_minor; + + return 0; +} +EXPORT_SYMBOL(xillybus_find_inode); + +static int __init xillybus_class_init(void) +{ + xillybus_class = class_create(THIS_MODULE, "xillybus"); + + if (IS_ERR(xillybus_class)) { + pr_warn("Failed to register xillybus class\n"); + + return PTR_ERR(xillybus_class); + } + return 0; +} + +static void __exit xillybus_class_exit(void) +{ + class_destroy(xillybus_class); +} + +module_init(xillybus_class_init); +module_exit(xillybus_class_exit); diff --git a/drivers/char/xillybus/xillybus_class.h b/drivers/char/xillybus/xillybus_class.h new file mode 100644 index 000000000000..5dbfdfc95c65 --- /dev/null +++ b/drivers/char/xillybus/xillybus_class.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2021 Xillybus Ltd, http://www.xillybus.com + * + * Header file for the Xillybus class + */ + +#ifndef __XILLYBUS_CLASS_H +#define __XILLYBUS_CLASS_H + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/module.h> + +int xillybus_init_chrdev(struct device *dev, + const struct file_operations *fops, + struct module *owner, + void *private_data, + unsigned char *idt, unsigned int len, + int num_nodes, + const char *prefix, bool enumerate); + +void xillybus_cleanup_chrdev(void *private_data, + struct device *dev); + +int xillybus_find_inode(struct inode *inode, + void **private_data, int *index); + +#endif /* __XILLYBUS_CLASS_H */ diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c index 57fa68834981..931d0bf4cec6 100644 --- a/drivers/char/xillybus/xillybus_core.c +++ b/drivers/char/xillybus/xillybus_core.c @@ -21,7 +21,6 @@ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/fs.h> -#include <linux/cdev.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/crc32.h> @@ -30,10 +29,10 @@ #include <linux/slab.h> #include <linux/workqueue.h> #include "xillybus.h" +#include "xillybus_class.h" MODULE_DESCRIPTION("Xillybus core functions"); MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); -MODULE_VERSION("1.07"); MODULE_ALIAS("xillybus_core"); MODULE_LICENSE("GPL v2"); @@ -58,16 +57,6 @@ MODULE_LICENSE("GPL v2"); static const char xillyname[] = "xillybus"; -static struct class *xillybus_class; - -/* - * ep_list_lock is the last lock to be taken; No other lock requests are - * allowed while holding it. It merely protects list_of_endpoints, and not - * the endpoints listed in it. - */ - -static LIST_HEAD(list_of_endpoints); -static struct mutex ep_list_lock; static struct workqueue_struct *xillybus_wq; /* @@ -570,10 +559,8 @@ static int xilly_scan_idt(struct xilly_endpoint *endpoint, unsigned char *scan; int len; - scan = idt; - idt_handle->idt = idt; - - scan++; /* Skip version number */ + scan = idt + 1; + idt_handle->names = scan; while ((scan <= end_of_idt) && *scan) { while ((scan <= end_of_idt) && *scan++) @@ -581,6 +568,8 @@ static int xilly_scan_idt(struct xilly_endpoint *endpoint, count++; } + idt_handle->names_len = scan - idt_handle->names; + scan++; if (scan > end_of_idt) { @@ -1407,36 +1396,20 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf, static int xillybus_open(struct inode *inode, struct file *filp) { - int rc = 0; + int rc; unsigned long flags; - int minor = iminor(inode); - int major = imajor(inode); - struct xilly_endpoint *ep_iter, *endpoint = NULL; + struct xilly_endpoint *endpoint; struct xilly_channel *channel; + int index; - mutex_lock(&ep_list_lock); - - list_for_each_entry(ep_iter, &list_of_endpoints, ep_list) { - if ((ep_iter->major == major) && - (minor >= ep_iter->lowest_minor) && - (minor < (ep_iter->lowest_minor + - ep_iter->num_channels))) { - endpoint = ep_iter; - break; - } - } - mutex_unlock(&ep_list_lock); - - if (!endpoint) { - pr_err("xillybus: open() failed to find a device for major=%d and minor=%d\n", - major, minor); - return -ENODEV; - } + rc = xillybus_find_inode(inode, (void **)&endpoint, &index); + if (rc) + return rc; if (endpoint->fatal_error) return -EIO; - channel = endpoint->channels[1 + minor - endpoint->lowest_minor]; + channel = endpoint->channels[1 + index]; filp->private_data = channel; /* @@ -1799,95 +1772,6 @@ static const struct file_operations xillybus_fops = { .poll = xillybus_poll, }; -static int xillybus_init_chrdev(struct xilly_endpoint *endpoint, - const unsigned char *idt) -{ - int rc; - dev_t dev; - int devnum, i, minor, major; - char devname[48]; - struct device *device; - - rc = alloc_chrdev_region(&dev, 0, /* minor start */ - endpoint->num_channels, - xillyname); - if (rc) { - dev_warn(endpoint->dev, "Failed to obtain major/minors"); - return rc; - } - - endpoint->major = major = MAJOR(dev); - endpoint->lowest_minor = minor = MINOR(dev); - - cdev_init(&endpoint->cdev, &xillybus_fops); - endpoint->cdev.owner = endpoint->ephw->owner; - rc = cdev_add(&endpoint->cdev, MKDEV(major, minor), - endpoint->num_channels); - if (rc) { - dev_warn(endpoint->dev, "Failed to add cdev. Aborting.\n"); - goto unregister_chrdev; - } - - idt++; - - for (i = minor, devnum = 0; - devnum < endpoint->num_channels; - devnum++, i++) { - snprintf(devname, sizeof(devname)-1, "xillybus_%s", idt); - - devname[sizeof(devname)-1] = 0; /* Should never matter */ - - while (*idt++) - /* Skip to next */; - - device = device_create(xillybus_class, - NULL, - MKDEV(major, i), - NULL, - "%s", devname); - - if (IS_ERR(device)) { - dev_warn(endpoint->dev, - "Failed to create %s device. Aborting.\n", - devname); - rc = -ENODEV; - goto unroll_device_create; - } - } - - dev_info(endpoint->dev, "Created %d device files.\n", - endpoint->num_channels); - return 0; /* succeed */ - -unroll_device_create: - devnum--; i--; - for (; devnum >= 0; devnum--, i--) - device_destroy(xillybus_class, MKDEV(major, i)); - - cdev_del(&endpoint->cdev); -unregister_chrdev: - unregister_chrdev_region(MKDEV(major, minor), endpoint->num_channels); - - return rc; -} - -static void xillybus_cleanup_chrdev(struct xilly_endpoint *endpoint) -{ - int minor; - - for (minor = endpoint->lowest_minor; - minor < (endpoint->lowest_minor + endpoint->num_channels); - minor++) - device_destroy(xillybus_class, MKDEV(endpoint->major, minor)); - cdev_del(&endpoint->cdev); - unregister_chrdev_region(MKDEV(endpoint->major, - endpoint->lowest_minor), - endpoint->num_channels); - - dev_info(endpoint->dev, "Removed %d device files.\n", - endpoint->num_channels); -} - struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, struct device *dev, struct xilly_endpoint_hardware @@ -2027,28 +1911,20 @@ int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint) if (rc) goto failed_idt; - /* - * endpoint is now completely configured. We put it on the list - * available to open() before registering the char device(s) - */ - - mutex_lock(&ep_list_lock); - list_add_tail(&endpoint->ep_list, &list_of_endpoints); - mutex_unlock(&ep_list_lock); + rc = xillybus_init_chrdev(dev, &xillybus_fops, + endpoint->ephw->owner, endpoint, + idt_handle.names, + idt_handle.names_len, + endpoint->num_channels, + xillyname, false); - rc = xillybus_init_chrdev(endpoint, idt_handle.idt); if (rc) - goto failed_chrdevs; + goto failed_idt; devres_release_group(dev, bootstrap_resources); return 0; -failed_chrdevs: - mutex_lock(&ep_list_lock); - list_del(&endpoint->ep_list); - mutex_unlock(&ep_list_lock); - failed_idt: xilly_quiesce(endpoint); flush_workqueue(xillybus_wq); @@ -2059,11 +1935,7 @@ EXPORT_SYMBOL(xillybus_endpoint_discovery); void xillybus_endpoint_remove(struct xilly_endpoint *endpoint) { - xillybus_cleanup_chrdev(endpoint); - - mutex_lock(&ep_list_lock); - list_del(&endpoint->ep_list); - mutex_unlock(&ep_list_lock); + xillybus_cleanup_chrdev(endpoint, endpoint->dev); xilly_quiesce(endpoint); @@ -2077,17 +1949,9 @@ EXPORT_SYMBOL(xillybus_endpoint_remove); static int __init xillybus_init(void) { - mutex_init(&ep_list_lock); - - xillybus_class = class_create(THIS_MODULE, xillyname); - if (IS_ERR(xillybus_class)) - return PTR_ERR(xillybus_class); - xillybus_wq = alloc_workqueue(xillyname, 0, 0); - if (!xillybus_wq) { - class_destroy(xillybus_class); + if (!xillybus_wq) return -ENOMEM; - } return 0; } @@ -2096,8 +1960,6 @@ static void __exit xillybus_exit(void) { /* flush_workqueue() was called for each endpoint released */ destroy_workqueue(xillybus_wq); - - class_destroy(xillybus_class); } module_init(xillybus_init); diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 96b6de8a30e5..1a20b286fd1d 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -17,7 +17,6 @@ MODULE_DESCRIPTION("Xillybus driver for Open Firmware"); MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); -MODULE_VERSION("1.06"); MODULE_ALIAS("xillybus_of"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index 18b0c392bc93..bdf1c366b4fc 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -14,7 +14,6 @@ MODULE_DESCRIPTION("Xillybus driver for PCIe"); MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); -MODULE_VERSION("1.06"); MODULE_ALIAS("xillybus_pcie"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/xillybus/xillyusb.c b/drivers/char/xillybus/xillyusb.c new file mode 100644 index 000000000000..e7f88f35c702 --- /dev/null +++ b/drivers/char/xillybus/xillyusb.c @@ -0,0 +1,2259 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Xillybus Ltd, http://xillybus.com + * + * Driver for the XillyUSB FPGA/host framework. + * + * This driver interfaces with a special IP core in an FPGA, setting up + * a pipe between a hardware FIFO in the programmable logic and a device + * file in the host. The number of such pipes and their attributes are + * set up on the logic. This driver detects these automatically and + * creates the device files accordingly. + */ + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/module.h> +#include <asm/byteorder.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/crc32.h> +#include <linux/poll.h> +#include <linux/delay.h> +#include <linux/usb.h> + +#include "xillybus_class.h" + +MODULE_DESCRIPTION("Driver for XillyUSB FPGA IP Core"); +MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); +MODULE_ALIAS("xillyusb"); +MODULE_LICENSE("GPL v2"); + +#define XILLY_RX_TIMEOUT (10 * HZ / 1000) +#define XILLY_RESPONSE_TIMEOUT (500 * HZ / 1000) + +#define BUF_SIZE_ORDER 4 +#define BUFNUM 8 +#define LOG2_IDT_FIFO_SIZE 16 +#define LOG2_INITIAL_FIFO_BUF_SIZE 16 + +#define MSG_EP_NUM 1 +#define IN_EP_NUM 1 + +static const char xillyname[] = "xillyusb"; + +static unsigned int fifo_buf_order; + +#define USB_VENDOR_ID_XILINX 0x03fd +#define USB_VENDOR_ID_ALTERA 0x09fb + +#define USB_PRODUCT_ID_XILLYUSB 0xebbe + +static const struct usb_device_id xillyusb_table[] = { + { USB_DEVICE(USB_VENDOR_ID_XILINX, USB_PRODUCT_ID_XILLYUSB) }, + { USB_DEVICE(USB_VENDOR_ID_ALTERA, USB_PRODUCT_ID_XILLYUSB) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, xillyusb_table); + +struct xillyusb_dev; + +struct xillyfifo { + unsigned int bufsize; /* In bytes, always a power of 2 */ + unsigned int bufnum; + unsigned int size; /* Lazy: Equals bufsize * bufnum */ + unsigned int buf_order; + + int fill; /* Number of bytes in the FIFO */ + spinlock_t lock; + wait_queue_head_t waitq; + + unsigned int readpos; + unsigned int readbuf; + unsigned int writepos; + unsigned int writebuf; + char **mem; +}; + +struct xillyusb_channel; + +struct xillyusb_endpoint { + struct xillyusb_dev *xdev; + + struct mutex ep_mutex; /* serialize operations on endpoint */ + + struct list_head buffers; + struct list_head filled_buffers; + spinlock_t buffers_lock; /* protect these two lists */ + + unsigned int order; + unsigned int buffer_size; + + unsigned int fill_mask; + + int outstanding_urbs; + + struct usb_anchor anchor; + + struct xillyfifo fifo; + + struct work_struct workitem; + + bool shutting_down; + bool drained; + bool wake_on_drain; + + u8 ep_num; +}; + +struct xillyusb_channel { + struct xillyusb_dev *xdev; + + struct xillyfifo *in_fifo; + struct xillyusb_endpoint *out_ep; + struct mutex lock; /* protect @out_ep, @in_fifo, bit fields below */ + + struct mutex in_mutex; /* serialize fops on FPGA to host stream */ + struct mutex out_mutex; /* serialize fops on host to FPGA stream */ + wait_queue_head_t flushq; + + int chan_idx; + + u32 in_consumed_bytes; + u32 in_current_checkpoint; + u32 out_bytes; + + unsigned int in_log2_element_size; + unsigned int out_log2_element_size; + unsigned int in_log2_fifo_size; + unsigned int out_log2_fifo_size; + + unsigned int read_data_ok; /* EOF not arrived (yet) */ + unsigned int poll_used; + unsigned int flushing; + unsigned int flushed; + unsigned int canceled; + + /* Bit fields protected by @lock except for initialization */ + unsigned readable:1; + unsigned writable:1; + unsigned open_for_read:1; + unsigned open_for_write:1; + unsigned in_synchronous:1; + unsigned out_synchronous:1; + unsigned in_seekable:1; + unsigned out_seekable:1; +}; + +struct xillybuffer { + struct list_head entry; + struct xillyusb_endpoint *ep; + void *buf; + unsigned int len; +}; + +struct xillyusb_dev { + struct xillyusb_channel *channels; + + struct usb_device *udev; + struct device *dev; /* For dev_err() and such */ + struct kref kref; + struct workqueue_struct *workq; + + int error; + spinlock_t error_lock; /* protect @error */ + struct work_struct wakeup_workitem; + + int num_channels; + + struct xillyusb_endpoint *msg_ep; + struct xillyusb_endpoint *in_ep; + + struct mutex msg_mutex; /* serialize opcode transmission */ + int in_bytes_left; + int leftover_chan_num; + unsigned int in_counter; + struct mutex process_in_mutex; /* synchronize wakeup_all() */ +}; + +/* FPGA to host opcodes */ +enum { + OPCODE_DATA = 0, + OPCODE_QUIESCE_ACK = 1, + OPCODE_EOF = 2, + OPCODE_REACHED_CHECKPOINT = 3, + OPCODE_CANCELED_CHECKPOINT = 4, +}; + +/* Host to FPGA opcodes */ +enum { + OPCODE_QUIESCE = 0, + OPCODE_REQ_IDT = 1, + OPCODE_SET_CHECKPOINT = 2, + OPCODE_CLOSE = 3, + OPCODE_SET_PUSH = 4, + OPCODE_UPDATE_PUSH = 5, + OPCODE_CANCEL_CHECKPOINT = 6, + OPCODE_SET_ADDR = 7, +}; + +/* + * fifo_write() and fifo_read() are NOT reentrant (i.e. concurrent multiple + * calls to each on the same FIFO is not allowed) however it's OK to have + * threads calling each of the two functions once on the same FIFO, and + * at the same time. + */ + +static int fifo_write(struct xillyfifo *fifo, + const void *data, unsigned int len, + int (*copier)(void *, const void *, int)) +{ + unsigned int done = 0; + unsigned int todo = len; + unsigned int nmax; + unsigned int writepos = fifo->writepos; + unsigned int writebuf = fifo->writebuf; + unsigned long flags; + int rc; + + nmax = fifo->size - READ_ONCE(fifo->fill); + + while (1) { + unsigned int nrail = fifo->bufsize - writepos; + unsigned int n = min(todo, nmax); + + if (n == 0) { + spin_lock_irqsave(&fifo->lock, flags); + fifo->fill += done; + spin_unlock_irqrestore(&fifo->lock, flags); + + fifo->writepos = writepos; + fifo->writebuf = writebuf; + + return done; + } + + if (n > nrail) + n = nrail; + + rc = (*copier)(fifo->mem[writebuf] + writepos, data + done, n); + + if (rc) + return rc; + + done += n; + todo -= n; + + writepos += n; + nmax -= n; + + if (writepos == fifo->bufsize) { + writepos = 0; + writebuf++; + + if (writebuf == fifo->bufnum) + writebuf = 0; + } + } +} + +static int fifo_read(struct xillyfifo *fifo, + void *data, unsigned int len, + int (*copier)(void *, const void *, int)) +{ + unsigned int done = 0; + unsigned int todo = len; + unsigned int fill; + unsigned int readpos = fifo->readpos; + unsigned int readbuf = fifo->readbuf; + unsigned long flags; + int rc; + + /* + * The spinlock here is necessary, because otherwise fifo->fill + * could have been increased by fifo_write() after writing data + * to the buffer, but this data would potentially not have been + * visible on this thread at the time the updated fifo->fill was. + * That could lead to reading invalid data. + */ + + spin_lock_irqsave(&fifo->lock, flags); + fill = fifo->fill; + spin_unlock_irqrestore(&fifo->lock, flags); + + while (1) { + unsigned int nrail = fifo->bufsize - readpos; + unsigned int n = min(todo, fill); + + if (n == 0) { + spin_lock_irqsave(&fifo->lock, flags); + fifo->fill -= done; + spin_unlock_irqrestore(&fifo->lock, flags); + + fifo->readpos = readpos; + fifo->readbuf = readbuf; + + return done; + } + + if (n > nrail) + n = nrail; + + rc = (*copier)(data + done, fifo->mem[readbuf] + readpos, n); + + if (rc) + return rc; + + done += n; + todo -= n; + + readpos += n; + fill -= n; + + if (readpos == fifo->bufsize) { + readpos = 0; + readbuf++; + + if (readbuf == fifo->bufnum) + readbuf = 0; + } + } +} + +/* + * These three wrapper functions are used as the @copier argument to + * fifo_write() and fifo_read(), so that they can work directly with + * user memory as well. + */ + +static int xilly_copy_from_user(void *dst, const void *src, int n) +{ + if (copy_from_user(dst, (const void __user *)src, n)) + return -EFAULT; + + return 0; +} + +static int xilly_copy_to_user(void *dst, const void *src, int n) +{ + if (copy_to_user((void __user *)dst, src, n)) + return -EFAULT; + + return 0; +} + +static int xilly_memcpy(void *dst, const void *src, int n) +{ + memcpy(dst, src, n); + + return 0; +} + +static int fifo_init(struct xillyfifo *fifo, + unsigned int log2_size) +{ + unsigned int log2_bufnum; + unsigned int buf_order; + int i; + + unsigned int log2_fifo_buf_size; + +retry: + log2_fifo_buf_size = fifo_buf_order + PAGE_SHIFT; + + if (log2_size > log2_fifo_buf_size) { + log2_bufnum = log2_size - log2_fifo_buf_size; + buf_order = fifo_buf_order; + fifo->bufsize = 1 << log2_fifo_buf_size; + } else { + log2_bufnum = 0; + buf_order = (log2_size > PAGE_SHIFT) ? + log2_size - PAGE_SHIFT : 0; + fifo->bufsize = 1 << log2_size; + } + + fifo->bufnum = 1 << log2_bufnum; + fifo->size = fifo->bufnum * fifo->bufsize; + fifo->buf_order = buf_order; + + fifo->mem = kmalloc_array(fifo->bufnum, sizeof(void *), GFP_KERNEL); + + if (!fifo->mem) + return -ENOMEM; + + for (i = 0; i < fifo->bufnum; i++) { + fifo->mem[i] = (void *) + __get_free_pages(GFP_KERNEL, buf_order); + + if (!fifo->mem[i]) + goto memfail; + } + + fifo->fill = 0; + fifo->readpos = 0; + fifo->readbuf = 0; + fifo->writepos = 0; + fifo->writebuf = 0; + spin_lock_init(&fifo->lock); + init_waitqueue_head(&fifo->waitq); + return 0; + +memfail: + for (i--; i >= 0; i--) + free_pages((unsigned long)fifo->mem[i], buf_order); + + kfree(fifo->mem); + fifo->mem = NULL; + + if (fifo_buf_order) { + fifo_buf_order--; + goto retry; + } else { + return -ENOMEM; + } +} + +static void fifo_mem_release(struct xillyfifo *fifo) +{ + int i; + + if (!fifo->mem) + return; + + for (i = 0; i < fifo->bufnum; i++) + free_pages((unsigned long)fifo->mem[i], fifo->buf_order); + + kfree(fifo->mem); +} + +/* + * When endpoint_quiesce() returns, the endpoint has no URBs submitted, + * won't accept any new URB submissions, and its related work item doesn't + * and won't run anymore. + */ + +static void endpoint_quiesce(struct xillyusb_endpoint *ep) +{ + mutex_lock(&ep->ep_mutex); + ep->shutting_down = true; + mutex_unlock(&ep->ep_mutex); + + usb_kill_anchored_urbs(&ep->anchor); + cancel_work_sync(&ep->workitem); +} + +/* + * Note that endpoint_dealloc() also frees fifo memory (if allocated), even + * though endpoint_alloc doesn't allocate that memory. + */ + +static void endpoint_dealloc(struct xillyusb_endpoint *ep) +{ + struct list_head *this, *next; + + fifo_mem_release(&ep->fifo); + + /* Join @filled_buffers with @buffers to free these entries too */ + list_splice(&ep->filled_buffers, &ep->buffers); + + list_for_each_safe(this, next, &ep->buffers) { + struct xillybuffer *xb = + list_entry(this, struct xillybuffer, entry); + + free_pages((unsigned long)xb->buf, ep->order); + kfree(xb); + } + + kfree(ep); +} + +static struct xillyusb_endpoint +*endpoint_alloc(struct xillyusb_dev *xdev, + u8 ep_num, + void (*work)(struct work_struct *), + unsigned int order, + int bufnum) +{ + int i; + + struct xillyusb_endpoint *ep; + + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + + if (!ep) + return NULL; + + INIT_LIST_HEAD(&ep->buffers); + INIT_LIST_HEAD(&ep->filled_buffers); + + spin_lock_init(&ep->buffers_lock); + mutex_init(&ep->ep_mutex); + + init_usb_anchor(&ep->anchor); + INIT_WORK(&ep->workitem, work); + + ep->order = order; + ep->buffer_size = 1 << (PAGE_SHIFT + order); + ep->outstanding_urbs = 0; + ep->drained = true; + ep->wake_on_drain = false; + ep->xdev = xdev; + ep->ep_num = ep_num; + ep->shutting_down = false; + + for (i = 0; i < bufnum; i++) { + struct xillybuffer *xb; + unsigned long addr; + + xb = kzalloc(sizeof(*xb), GFP_KERNEL); + + if (!xb) { + endpoint_dealloc(ep); + return NULL; + } + + addr = __get_free_pages(GFP_KERNEL, order); + + if (!addr) { + kfree(xb); + endpoint_dealloc(ep); + return NULL; + } + + xb->buf = (void *)addr; + xb->ep = ep; + list_add_tail(&xb->entry, &ep->buffers); + } + return ep; +} + +static void cleanup_dev(struct kref *kref) +{ + struct xillyusb_dev *xdev = + container_of(kref, struct xillyusb_dev, kref); + + if (xdev->in_ep) + endpoint_dealloc(xdev->in_ep); + + if (xdev->msg_ep) + endpoint_dealloc(xdev->msg_ep); + + if (xdev->workq) + destroy_workqueue(xdev->workq); + + kfree(xdev->channels); /* Argument may be NULL, and that's fine */ + kfree(xdev); +} + +/* + * @process_in_mutex is taken to ensure that bulk_in_work() won't call + * process_bulk_in() after wakeup_all()'s execution: The latter zeroes all + * @read_data_ok entries, which will make process_bulk_in() report false + * errors if executed. The mechanism relies on that xdev->error is assigned + * a non-zero value by report_io_error() prior to queueing wakeup_all(), + * which prevents bulk_in_work() from calling process_bulk_in(). + * + * The fact that wakeup_all() and bulk_in_work() are queued on the same + * workqueue makes their concurrent execution very unlikely, however the + * kernel's API doesn't seem to ensure this strictly. + */ + +static void wakeup_all(struct work_struct *work) +{ + int i; + struct xillyusb_dev *xdev = container_of(work, struct xillyusb_dev, + wakeup_workitem); + + mutex_lock(&xdev->process_in_mutex); + + for (i = 0; i < xdev->num_channels; i++) { + struct xillyusb_channel *chan = &xdev->channels[i]; + + mutex_lock(&chan->lock); + + if (chan->in_fifo) { + /* + * Fake an EOF: Even if such arrives, it won't be + * processed. + */ + chan->read_data_ok = 0; + wake_up_interruptible(&chan->in_fifo->waitq); + } + + if (chan->out_ep) + wake_up_interruptible(&chan->out_ep->fifo.waitq); + + mutex_unlock(&chan->lock); + + wake_up_interruptible(&chan->flushq); + } + + mutex_unlock(&xdev->process_in_mutex); + + wake_up_interruptible(&xdev->msg_ep->fifo.waitq); + + kref_put(&xdev->kref, cleanup_dev); +} + +static void report_io_error(struct xillyusb_dev *xdev, + int errcode) +{ + unsigned long flags; + bool do_once = false; + + spin_lock_irqsave(&xdev->error_lock, flags); + if (!xdev->error) { + xdev->error = errcode; + do_once = true; + } + spin_unlock_irqrestore(&xdev->error_lock, flags); + + if (do_once) { + kref_get(&xdev->kref); /* xdev is used by work item */ + queue_work(xdev->workq, &xdev->wakeup_workitem); + } +} + +/* + * safely_assign_in_fifo() changes the value of chan->in_fifo and ensures + * the previous pointer is never used after its return. + */ + +static void safely_assign_in_fifo(struct xillyusb_channel *chan, + struct xillyfifo *fifo) +{ + mutex_lock(&chan->lock); + chan->in_fifo = fifo; + mutex_unlock(&chan->lock); + + flush_work(&chan->xdev->in_ep->workitem); +} + +static void bulk_in_completer(struct urb *urb) +{ + struct xillybuffer *xb = urb->context; + struct xillyusb_endpoint *ep = xb->ep; + unsigned long flags; + + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + report_io_error(ep->xdev, -EIO); + + spin_lock_irqsave(&ep->buffers_lock, flags); + list_add_tail(&xb->entry, &ep->buffers); + ep->outstanding_urbs--; + spin_unlock_irqrestore(&ep->buffers_lock, flags); + + return; + } + + xb->len = urb->actual_length; + + spin_lock_irqsave(&ep->buffers_lock, flags); + list_add_tail(&xb->entry, &ep->filled_buffers); + spin_unlock_irqrestore(&ep->buffers_lock, flags); + + if (!ep->shutting_down) + queue_work(ep->xdev->workq, &ep->workitem); +} + +static void bulk_out_completer(struct urb *urb) +{ + struct xillybuffer *xb = urb->context; + struct xillyusb_endpoint *ep = xb->ep; + unsigned long flags; + + if (urb->status && + (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN))) + report_io_error(ep->xdev, -EIO); + + spin_lock_irqsave(&ep->buffers_lock, flags); + list_add_tail(&xb->entry, &ep->buffers); + ep->outstanding_urbs--; + spin_unlock_irqrestore(&ep->buffers_lock, flags); + + if (!ep->shutting_down) + queue_work(ep->xdev->workq, &ep->workitem); +} + +static void try_queue_bulk_in(struct xillyusb_endpoint *ep) +{ + struct xillyusb_dev *xdev = ep->xdev; + struct xillybuffer *xb; + struct urb *urb; + + int rc; + unsigned long flags; + unsigned int bufsize = ep->buffer_size; + + mutex_lock(&ep->ep_mutex); + + if (ep->shutting_down || xdev->error) + goto done; + + while (1) { + spin_lock_irqsave(&ep->buffers_lock, flags); + + if (list_empty(&ep->buffers)) { + spin_unlock_irqrestore(&ep->buffers_lock, flags); + goto done; + } + + xb = list_first_entry(&ep->buffers, struct xillybuffer, entry); + list_del(&xb->entry); + ep->outstanding_urbs++; + + spin_unlock_irqrestore(&ep->buffers_lock, flags); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + report_io_error(xdev, -ENOMEM); + goto relist; + } + + usb_fill_bulk_urb(urb, xdev->udev, + usb_rcvbulkpipe(xdev->udev, ep->ep_num), + xb->buf, bufsize, bulk_in_completer, xb); + + usb_anchor_urb(urb, &ep->anchor); + + rc = usb_submit_urb(urb, GFP_KERNEL); + + if (rc) { + report_io_error(xdev, (rc == -ENOMEM) ? -ENOMEM : + -EIO); + goto unanchor; + } + + usb_free_urb(urb); /* This just decrements reference count */ + } + +unanchor: + usb_unanchor_urb(urb); + usb_free_urb(urb); + +relist: + spin_lock_irqsave(&ep->buffers_lock, flags); + list_add_tail(&xb->entry, &ep->buffers); + ep->outstanding_urbs--; + spin_unlock_irqrestore(&ep->buffers_lock, flags); + +done: + mutex_unlock(&ep->ep_mutex); +} + +static void try_queue_bulk_out(struct xillyusb_endpoint *ep) +{ + struct xillyfifo *fifo = &ep->fifo; + struct xillyusb_dev *xdev = ep->xdev; + struct xillybuffer *xb; + struct urb *urb; + + int rc; + unsigned int fill; + unsigned long flags; + bool do_wake = false; + + mutex_lock(&ep->ep_mutex); + + if (ep->shutting_down || xdev->error) + goto done; + + fill = READ_ONCE(fifo->fill) & ep->fill_mask; + + while (1) { + int count; + unsigned int max_read; + + spin_lock_irqsave(&ep->buffers_lock, flags); + + /* + * Race conditions might have the FIFO filled while the + * endpoint is marked as drained here. That doesn't matter, + * because the sole purpose of @drained is to ensure that + * certain data has been sent on the USB channel before + * shutting it down. Hence knowing that the FIFO appears + * to be empty with no outstanding URBs at some moment + * is good enough. + */ + + if (!fill) { + ep->drained = !ep->outstanding_urbs; + if (ep->drained && ep->wake_on_drain) + do_wake = true; + + spin_unlock_irqrestore(&ep->buffers_lock, flags); + goto done; + } + + ep->drained = false; + + if ((fill < ep->buffer_size && ep->outstanding_urbs) || + list_empty(&ep->buffers)) { + spin_unlock_irqrestore(&ep->buffers_lock, flags); + goto done; + } + + xb = list_first_entry(&ep->buffers, struct xillybuffer, entry); + list_del(&xb->entry); + ep->outstanding_urbs++; + + spin_unlock_irqrestore(&ep->buffers_lock, flags); + + max_read = min(fill, ep->buffer_size); + + count = fifo_read(&ep->fifo, xb->buf, max_read, xilly_memcpy); + + /* + * xilly_memcpy always returns 0 => fifo_read can't fail => + * count > 0 + */ + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + report_io_error(xdev, -ENOMEM); + goto relist; + } + + usb_fill_bulk_urb(urb, xdev->udev, + usb_sndbulkpipe(xdev->udev, ep->ep_num), + xb->buf, count, bulk_out_completer, xb); + + usb_anchor_urb(urb, &ep->anchor); + + rc = usb_submit_urb(urb, GFP_KERNEL); + + if (rc) { + report_io_error(xdev, (rc == -ENOMEM) ? -ENOMEM : + -EIO); + goto unanchor; + } + + usb_free_urb(urb); /* This just decrements reference count */ + + fill -= count; + do_wake = true; + } + +unanchor: + usb_unanchor_urb(urb); + usb_free_urb(urb); + +relist: + spin_lock_irqsave(&ep->buffers_lock, flags); + list_add_tail(&xb->entry, &ep->buffers); + ep->outstanding_urbs--; + spin_unlock_irqrestore(&ep->buffers_lock, flags); + +done: + mutex_unlock(&ep->ep_mutex); + + if (do_wake) + wake_up_interruptible(&fifo->waitq); +} + +static void bulk_out_work(struct work_struct *work) +{ + struct xillyusb_endpoint *ep = container_of(work, + struct xillyusb_endpoint, + workitem); + try_queue_bulk_out(ep); +} + +static int process_in_opcode(struct xillyusb_dev *xdev, + int opcode, + int chan_num) +{ + struct xillyusb_channel *chan; + struct device *dev = xdev->dev; + int chan_idx = chan_num >> 1; + + if (chan_idx >= xdev->num_channels) { + dev_err(dev, "Received illegal channel ID %d from FPGA\n", + chan_num); + return -EIO; + } + + chan = &xdev->channels[chan_idx]; + + switch (opcode) { + case OPCODE_EOF: + if (!chan->read_data_ok) { + dev_err(dev, "Received unexpected EOF for channel %d\n", + chan_num); + return -EIO; + } + + /* + * A write memory barrier ensures that the FIFO's fill level + * is visible before read_data_ok turns zero, so the data in + * the FIFO isn't missed by the consumer. + */ + smp_wmb(); + WRITE_ONCE(chan->read_data_ok, 0); + wake_up_interruptible(&chan->in_fifo->waitq); + break; + + case OPCODE_REACHED_CHECKPOINT: + chan->flushing = 0; + wake_up_interruptible(&chan->flushq); + break; + + case OPCODE_CANCELED_CHECKPOINT: + chan->canceled = 1; + wake_up_interruptible(&chan->flushq); + break; + + default: + dev_err(dev, "Received illegal opcode %d from FPGA\n", + opcode); + return -EIO; + } + + return 0; +} + +static int process_bulk_in(struct xillybuffer *xb) +{ + struct xillyusb_endpoint *ep = xb->ep; + struct xillyusb_dev *xdev = ep->xdev; + struct device *dev = xdev->dev; + int dws = xb->len >> 2; + __le32 *p = xb->buf; + u32 ctrlword; + struct xillyusb_channel *chan; + struct xillyfifo *fifo; + int chan_num = 0, opcode; + int chan_idx; + int bytes, count, dwconsume; + int in_bytes_left = 0; + int rc; + + if ((dws << 2) != xb->len) { + dev_err(dev, "Received BULK IN transfer with %d bytes, not a multiple of 4\n", + xb->len); + return -EIO; + } + + if (xdev->in_bytes_left) { + bytes = min(xdev->in_bytes_left, dws << 2); + in_bytes_left = xdev->in_bytes_left - bytes; + chan_num = xdev->leftover_chan_num; + goto resume_leftovers; + } + + while (dws) { + ctrlword = le32_to_cpu(*p++); + dws--; + + chan_num = ctrlword & 0xfff; + count = (ctrlword >> 12) & 0x3ff; + opcode = (ctrlword >> 24) & 0xf; + + if (opcode != OPCODE_DATA) { + unsigned int in_counter = xdev->in_counter++ & 0x3ff; + + if (count != in_counter) { + dev_err(dev, "Expected opcode counter %d, got %d\n", + in_counter, count); + return -EIO; + } + + rc = process_in_opcode(xdev, opcode, chan_num); + + if (rc) + return rc; + + continue; + } + + bytes = min(count + 1, dws << 2); + in_bytes_left = count + 1 - bytes; + +resume_leftovers: + chan_idx = chan_num >> 1; + + if (!(chan_num & 1) || chan_idx >= xdev->num_channels || + !xdev->channels[chan_idx].read_data_ok) { + dev_err(dev, "Received illegal channel ID %d from FPGA\n", + chan_num); + return -EIO; + } + chan = &xdev->channels[chan_idx]; + + fifo = chan->in_fifo; + + if (unlikely(!fifo)) + return -EIO; /* We got really unexpected data */ + + if (bytes != fifo_write(fifo, p, bytes, xilly_memcpy)) { + dev_err(dev, "Misbehaving FPGA overflowed an upstream FIFO!\n"); + return -EIO; + } + + wake_up_interruptible(&fifo->waitq); + + dwconsume = (bytes + 3) >> 2; + dws -= dwconsume; + p += dwconsume; + } + + xdev->in_bytes_left = in_bytes_left; + xdev->leftover_chan_num = chan_num; + return 0; +} + +static void bulk_in_work(struct work_struct *work) +{ + struct xillyusb_endpoint *ep = + container_of(work, struct xillyusb_endpoint, workitem); + struct xillyusb_dev *xdev = ep->xdev; + unsigned long flags; + struct xillybuffer *xb; + bool consumed = false; + int rc = 0; + + mutex_lock(&xdev->process_in_mutex); + + spin_lock_irqsave(&ep->buffers_lock, flags); + + while (1) { + if (rc || list_empty(&ep->filled_buffers)) { + spin_unlock_irqrestore(&ep->buffers_lock, flags); + mutex_unlock(&xdev->process_in_mutex); + + if (rc) + report_io_error(xdev, rc); + else if (consumed) + try_queue_bulk_in(ep); + + return; + } + + xb = list_first_entry(&ep->filled_buffers, struct xillybuffer, + entry); + list_del(&xb->entry); + + spin_unlock_irqrestore(&ep->buffers_lock, flags); + + consumed = true; + + if (!xdev->error) + rc = process_bulk_in(xb); + + spin_lock_irqsave(&ep->buffers_lock, flags); + list_add_tail(&xb->entry, &ep->buffers); + ep->outstanding_urbs--; + } +} + +static int xillyusb_send_opcode(struct xillyusb_dev *xdev, + int chan_num, char opcode, u32 data) +{ + struct xillyusb_endpoint *ep = xdev->msg_ep; + struct xillyfifo *fifo = &ep->fifo; + __le32 msg[2]; + + int rc = 0; + + msg[0] = cpu_to_le32((chan_num & 0xfff) | + ((opcode & 0xf) << 24)); + msg[1] = cpu_to_le32(data); + + mutex_lock(&xdev->msg_mutex); + + /* + * The wait queue is woken with the interruptible variant, so the + * wait function matches, however returning because of an interrupt + * will mess things up considerably, in particular when the caller is + * the release method. And the xdev->error part prevents being stuck + * forever in the event of a bizarre hardware bug: Pull the USB plug. + */ + + while (wait_event_interruptible(fifo->waitq, + fifo->fill <= (fifo->size - 8) || + xdev->error)) + ; /* Empty loop */ + + if (xdev->error) { + rc = xdev->error; + goto unlock_done; + } + + fifo_write(fifo, (void *)msg, 8, xilly_memcpy); + + try_queue_bulk_out(ep); + +unlock_done: + mutex_unlock(&xdev->msg_mutex); + + return rc; +} + +/* + * Note that flush_downstream() merely waits for the data to arrive to + * the application logic at the FPGA -- unlike PCIe Xillybus' counterpart, + * it does nothing to make it happen (and neither is it necessary). + * + * This function is not reentrant for the same @chan, but this is covered + * by the fact that for any given @chan, it's called either by the open, + * write, llseek and flush fops methods, which can't run in parallel (and the + * write + flush and llseek method handlers are protected with out_mutex). + * + * chan->flushed is there to avoid multiple flushes at the same position, + * in particular as a result of programs that close the file descriptor + * e.g. after a dup2() for redirection. + */ + +static int flush_downstream(struct xillyusb_channel *chan, + long timeout, + bool interruptible) +{ + struct xillyusb_dev *xdev = chan->xdev; + int chan_num = chan->chan_idx << 1; + long deadline, left_to_sleep; + int rc; + + if (chan->flushed) + return 0; + + deadline = jiffies + 1 + timeout; + + if (chan->flushing) { + long cancel_deadline = jiffies + 1 + XILLY_RESPONSE_TIMEOUT; + + chan->canceled = 0; + rc = xillyusb_send_opcode(xdev, chan_num, + OPCODE_CANCEL_CHECKPOINT, 0); + + if (rc) + return rc; /* Only real error, never -EINTR */ + + /* Ignoring interrupts. Cancellation must be handled */ + while (!chan->canceled) { + left_to_sleep = cancel_deadline - ((long)jiffies); + + if (left_to_sleep <= 0) { + report_io_error(xdev, -EIO); + return -EIO; + } + + rc = wait_event_interruptible_timeout(chan->flushq, + chan->canceled || + xdev->error, + left_to_sleep); + + if (xdev->error) + return xdev->error; + } + } + + chan->flushing = 1; + + /* + * The checkpoint is given in terms of data elements, not bytes. As + * a result, if less than an element's worth of data is stored in the + * FIFO, it's not flushed, including the flush before closing, which + * means that such data is lost. This is consistent with PCIe Xillybus. + */ + + rc = xillyusb_send_opcode(xdev, chan_num, + OPCODE_SET_CHECKPOINT, + chan->out_bytes >> + chan->out_log2_element_size); + + if (rc) + return rc; /* Only real error, never -EINTR */ + + if (!timeout) { + while (chan->flushing) { + rc = wait_event_interruptible(chan->flushq, + !chan->flushing || + xdev->error); + if (xdev->error) + return xdev->error; + + if (interruptible && rc) + return -EINTR; + } + + goto done; + } + + while (chan->flushing) { + left_to_sleep = deadline - ((long)jiffies); + + if (left_to_sleep <= 0) + return -ETIMEDOUT; + + rc = wait_event_interruptible_timeout(chan->flushq, + !chan->flushing || + xdev->error, + left_to_sleep); + + if (xdev->error) + return xdev->error; + + if (interruptible && rc < 0) + return -EINTR; + } + +done: + chan->flushed = 1; + return 0; +} + +/* request_read_anything(): Ask the FPGA for any little amount of data */ +static int request_read_anything(struct xillyusb_channel *chan, + char opcode) +{ + struct xillyusb_dev *xdev = chan->xdev; + unsigned int sh = chan->in_log2_element_size; + int chan_num = (chan->chan_idx << 1) | 1; + u32 mercy = chan->in_consumed_bytes + (2 << sh) - 1; + + return xillyusb_send_opcode(xdev, chan_num, opcode, mercy >> sh); +} + +static int xillyusb_open(struct inode *inode, struct file *filp) +{ + struct xillyusb_dev *xdev; + struct xillyusb_channel *chan; + struct xillyfifo *in_fifo = NULL; + struct xillyusb_endpoint *out_ep = NULL; + int rc; + int index; + + rc = xillybus_find_inode(inode, (void **)&xdev, &index); + if (rc) + return rc; + + chan = &xdev->channels[index]; + filp->private_data = chan; + + mutex_lock(&chan->lock); + + rc = -ENODEV; + + if (xdev->error) + goto unmutex_fail; + + if (((filp->f_mode & FMODE_READ) && !chan->readable) || + ((filp->f_mode & FMODE_WRITE) && !chan->writable)) + goto unmutex_fail; + + if ((filp->f_flags & O_NONBLOCK) && (filp->f_mode & FMODE_READ) && + chan->in_synchronous) { + dev_err(xdev->dev, + "open() failed: O_NONBLOCK not allowed for read on this device\n"); + goto unmutex_fail; + } + + if ((filp->f_flags & O_NONBLOCK) && (filp->f_mode & FMODE_WRITE) && + chan->out_synchronous) { + dev_err(xdev->dev, + "open() failed: O_NONBLOCK not allowed for write on this device\n"); + goto unmutex_fail; + } + + rc = -EBUSY; + + if (((filp->f_mode & FMODE_READ) && chan->open_for_read) || + ((filp->f_mode & FMODE_WRITE) && chan->open_for_write)) + goto unmutex_fail; + + kref_get(&xdev->kref); + + if (filp->f_mode & FMODE_READ) + chan->open_for_read = 1; + + if (filp->f_mode & FMODE_WRITE) + chan->open_for_write = 1; + + mutex_unlock(&chan->lock); + + if (filp->f_mode & FMODE_WRITE) { + out_ep = endpoint_alloc(xdev, + (chan->chan_idx + 2) | USB_DIR_OUT, + bulk_out_work, BUF_SIZE_ORDER, BUFNUM); + + if (!out_ep) { + rc = -ENOMEM; + goto unopen; + } + + rc = fifo_init(&out_ep->fifo, chan->out_log2_fifo_size); + + if (rc) + goto late_unopen; + + out_ep->fill_mask = -(1 << chan->out_log2_element_size); + chan->out_bytes = 0; + chan->flushed = 0; + + /* + * Sending a flush request to a previously closed stream + * effectively opens it, and also waits until the command is + * confirmed by the FPGA. The latter is necessary because the + * data is sent through a separate BULK OUT endpoint, and the + * xHCI controller is free to reorder transmissions. + * + * This can't go wrong unless there's a serious hardware error + * (or the computer is stuck for 500 ms?) + */ + rc = flush_downstream(chan, XILLY_RESPONSE_TIMEOUT, false); + + if (rc == -ETIMEDOUT) { + rc = -EIO; + report_io_error(xdev, rc); + } + + if (rc) + goto late_unopen; + } + + if (filp->f_mode & FMODE_READ) { + in_fifo = kzalloc(sizeof(*in_fifo), GFP_KERNEL); + + if (!in_fifo) { + rc = -ENOMEM; + goto late_unopen; + } + + rc = fifo_init(in_fifo, chan->in_log2_fifo_size); + + if (rc) { + kfree(in_fifo); + goto late_unopen; + } + } + + mutex_lock(&chan->lock); + if (in_fifo) { + chan->in_fifo = in_fifo; + chan->read_data_ok = 1; + } + if (out_ep) + chan->out_ep = out_ep; + mutex_unlock(&chan->lock); + + if (in_fifo) { + u32 in_checkpoint = 0; + + if (!chan->in_synchronous) + in_checkpoint = in_fifo->size >> + chan->in_log2_element_size; + + chan->in_consumed_bytes = 0; + chan->poll_used = 0; + chan->in_current_checkpoint = in_checkpoint; + rc = xillyusb_send_opcode(xdev, (chan->chan_idx << 1) | 1, + OPCODE_SET_CHECKPOINT, + in_checkpoint); + + if (rc) /* Failure guarantees that opcode wasn't sent */ + goto unfifo; + + /* + * In non-blocking mode, request the FPGA to send any data it + * has right away. Otherwise, the first read() will always + * return -EAGAIN, which is OK strictly speaking, but ugly. + * Checking and unrolling if this fails isn't worth the + * effort -- the error is propagated to the first read() + * anyhow. + */ + if (filp->f_flags & O_NONBLOCK) + request_read_anything(chan, OPCODE_SET_PUSH); + } + + return 0; + +unfifo: + chan->read_data_ok = 0; + safely_assign_in_fifo(chan, NULL); + fifo_mem_release(in_fifo); + kfree(in_fifo); + + if (out_ep) { + mutex_lock(&chan->lock); + chan->out_ep = NULL; + mutex_unlock(&chan->lock); + } + +late_unopen: + if (out_ep) + endpoint_dealloc(out_ep); + +unopen: + mutex_lock(&chan->lock); + + if (filp->f_mode & FMODE_READ) + chan->open_for_read = 0; + + if (filp->f_mode & FMODE_WRITE) + chan->open_for_write = 0; + + mutex_unlock(&chan->lock); + + kref_put(&xdev->kref, cleanup_dev); + + return rc; + +unmutex_fail: + mutex_unlock(&chan->lock); + return rc; +} + +static ssize_t xillyusb_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *f_pos) +{ + struct xillyusb_channel *chan = filp->private_data; + struct xillyusb_dev *xdev = chan->xdev; + struct xillyfifo *fifo = chan->in_fifo; + int chan_num = (chan->chan_idx << 1) | 1; + + long deadline, left_to_sleep; + int bytes_done = 0; + bool sent_set_push = false; + int rc; + + deadline = jiffies + 1 + XILLY_RX_TIMEOUT; + + rc = mutex_lock_interruptible(&chan->in_mutex); + + if (rc) + return rc; + + while (1) { + u32 fifo_checkpoint_bytes, complete_checkpoint_bytes; + u32 complete_checkpoint, fifo_checkpoint; + u32 checkpoint; + s32 diff, leap; + unsigned int sh = chan->in_log2_element_size; + bool checkpoint_for_complete; + + rc = fifo_read(fifo, (__force void *)userbuf + bytes_done, + count - bytes_done, xilly_copy_to_user); + + if (rc < 0) + break; + + bytes_done += rc; + chan->in_consumed_bytes += rc; + + left_to_sleep = deadline - ((long)jiffies); + + /* + * Some 32-bit arithmetic that may wrap. Note that + * complete_checkpoint is rounded up to the closest element + * boundary, because the read() can't be completed otherwise. + * fifo_checkpoint_bytes is rounded down, because it protects + * in_fifo from overflowing. + */ + + fifo_checkpoint_bytes = chan->in_consumed_bytes + fifo->size; + complete_checkpoint_bytes = + chan->in_consumed_bytes + count - bytes_done; + + fifo_checkpoint = fifo_checkpoint_bytes >> sh; + complete_checkpoint = + (complete_checkpoint_bytes + (1 << sh) - 1) >> sh; + + diff = (fifo_checkpoint - complete_checkpoint) << sh; + + if (chan->in_synchronous && diff >= 0) { + checkpoint = complete_checkpoint; + checkpoint_for_complete = true; + } else { + checkpoint = fifo_checkpoint; + checkpoint_for_complete = false; + } + + leap = (checkpoint - chan->in_current_checkpoint) << sh; + + /* + * To prevent flooding of OPCODE_SET_CHECKPOINT commands as + * data is consumed, it's issued only if it moves the + * checkpoint by at least an 8th of the FIFO's size, or if + * it's necessary to complete the number of bytes requested by + * the read() call. + * + * chan->read_data_ok is checked to spare an unnecessary + * submission after receiving EOF, however it's harmless if + * such slips away. + */ + + if (chan->read_data_ok && + (leap > (fifo->size >> 3) || + (checkpoint_for_complete && leap > 0))) { + chan->in_current_checkpoint = checkpoint; + rc = xillyusb_send_opcode(xdev, chan_num, + OPCODE_SET_CHECKPOINT, + checkpoint); + + if (rc) + break; + } + + if (bytes_done == count || + (left_to_sleep <= 0 && bytes_done)) + break; + + /* + * Reaching here means that the FIFO was empty when + * fifo_read() returned, but not necessarily right now. Error + * and EOF are checked and reported only now, so that no data + * that managed its way to the FIFO is lost. + */ + + if (!READ_ONCE(chan->read_data_ok)) { /* FPGA has sent EOF */ + /* Has data slipped into the FIFO since fifo_read()? */ + smp_rmb(); + if (READ_ONCE(fifo->fill)) + continue; + + rc = 0; + break; + } + + if (xdev->error) { + rc = xdev->error; + break; + } + + if (filp->f_flags & O_NONBLOCK) { + rc = -EAGAIN; + break; + } + + if (!sent_set_push) { + rc = xillyusb_send_opcode(xdev, chan_num, + OPCODE_SET_PUSH, + complete_checkpoint); + + if (rc) + break; + + sent_set_push = true; + } + + if (left_to_sleep > 0) { + /* + * Note that when xdev->error is set (e.g. when the + * device is unplugged), read_data_ok turns zero and + * fifo->waitq is awaken. + * Therefore no special attention to xdev->error. + */ + + rc = wait_event_interruptible_timeout + (fifo->waitq, + fifo->fill || !chan->read_data_ok, + left_to_sleep); + } else { /* bytes_done == 0 */ + /* Tell FPGA to send anything it has */ + rc = request_read_anything(chan, OPCODE_UPDATE_PUSH); + + if (rc) + break; + + rc = wait_event_interruptible + (fifo->waitq, + fifo->fill || !chan->read_data_ok); + } + + if (rc < 0) { + rc = -EINTR; + break; + } + } + + if (((filp->f_flags & O_NONBLOCK) || chan->poll_used) && + !READ_ONCE(fifo->fill)) + request_read_anything(chan, OPCODE_SET_PUSH); + + mutex_unlock(&chan->in_mutex); + + if (bytes_done) + return bytes_done; + + return rc; +} + +static int xillyusb_flush(struct file *filp, fl_owner_t id) +{ + struct xillyusb_channel *chan = filp->private_data; + int rc; + + if (!(filp->f_mode & FMODE_WRITE)) + return 0; + + rc = mutex_lock_interruptible(&chan->out_mutex); + + if (rc) + return rc; + + /* + * One second's timeout on flushing. Interrupts are ignored, because if + * the user pressed CTRL-C, that interrupt will still be in flight by + * the time we reach here, and the opportunity to flush is lost. + */ + rc = flush_downstream(chan, HZ, false); + + mutex_unlock(&chan->out_mutex); + + if (rc == -ETIMEDOUT) { + /* The things you do to use dev_warn() and not pr_warn() */ + struct xillyusb_dev *xdev = chan->xdev; + + mutex_lock(&chan->lock); + if (!xdev->error) + dev_warn(xdev->dev, + "Timed out while flushing. Output data may be lost.\n"); + mutex_unlock(&chan->lock); + } + + return rc; +} + +static ssize_t xillyusb_write(struct file *filp, const char __user *userbuf, + size_t count, loff_t *f_pos) +{ + struct xillyusb_channel *chan = filp->private_data; + struct xillyusb_dev *xdev = chan->xdev; + struct xillyfifo *fifo = &chan->out_ep->fifo; + int rc; + + rc = mutex_lock_interruptible(&chan->out_mutex); + + if (rc) + return rc; + + while (1) { + if (xdev->error) { + rc = xdev->error; + break; + } + + if (count == 0) + break; + + rc = fifo_write(fifo, (__force void *)userbuf, count, + xilly_copy_from_user); + + if (rc != 0) + break; + + if (filp->f_flags & O_NONBLOCK) { + rc = -EAGAIN; + break; + } + + if (wait_event_interruptible + (fifo->waitq, + fifo->fill != fifo->size || xdev->error)) { + rc = -EINTR; + break; + } + } + + if (rc < 0) + goto done; + + chan->out_bytes += rc; + + if (rc) { + try_queue_bulk_out(chan->out_ep); + chan->flushed = 0; + } + + if (chan->out_synchronous) { + int flush_rc = flush_downstream(chan, 0, true); + + if (flush_rc && !rc) + rc = flush_rc; + } + +done: + mutex_unlock(&chan->out_mutex); + + return rc; +} + +static int xillyusb_release(struct inode *inode, struct file *filp) +{ + struct xillyusb_channel *chan = filp->private_data; + struct xillyusb_dev *xdev = chan->xdev; + int rc_read = 0, rc_write = 0; + + if (filp->f_mode & FMODE_READ) { + struct xillyfifo *in_fifo = chan->in_fifo; + + rc_read = xillyusb_send_opcode(xdev, (chan->chan_idx << 1) | 1, + OPCODE_CLOSE, 0); + /* + * If rc_read is nonzero, xdev->error indicates a global + * device error. The error is reported later, so that + * resources are freed. + * + * Looping on wait_event_interruptible() kinda breaks the idea + * of being interruptible, and this should have been + * wait_event(). Only it's being waken with + * wake_up_interruptible() for the sake of other uses. If + * there's a global device error, chan->read_data_ok is + * deasserted and the wait queue is awaken, so this is covered. + */ + + while (wait_event_interruptible(in_fifo->waitq, + !chan->read_data_ok)) + ; /* Empty loop */ + + safely_assign_in_fifo(chan, NULL); + fifo_mem_release(in_fifo); + kfree(in_fifo); + + mutex_lock(&chan->lock); + chan->open_for_read = 0; + mutex_unlock(&chan->lock); + } + + if (filp->f_mode & FMODE_WRITE) { + struct xillyusb_endpoint *ep = chan->out_ep; + /* + * chan->flushing isn't zeroed. If the pre-release flush timed + * out, a cancel request will be sent before the next + * OPCODE_SET_CHECKPOINT (i.e. when the file is opened again). + * This is despite that the FPGA forgets about the checkpoint + * request as the file closes. Still, in an exceptional race + * condition, the FPGA could send an OPCODE_REACHED_CHECKPOINT + * just before closing that would reach the host after the + * file has re-opened. + */ + + mutex_lock(&chan->lock); + chan->out_ep = NULL; + mutex_unlock(&chan->lock); + + endpoint_quiesce(ep); + endpoint_dealloc(ep); + + /* See comments on rc_read above */ + rc_write = xillyusb_send_opcode(xdev, chan->chan_idx << 1, + OPCODE_CLOSE, 0); + + mutex_lock(&chan->lock); + chan->open_for_write = 0; + mutex_unlock(&chan->lock); + } + + kref_put(&xdev->kref, cleanup_dev); + + return rc_read ? rc_read : rc_write; +} + +/* + * Xillybus' API allows device nodes to be seekable, giving the user + * application access to a RAM array on the FPGA (or logic emulating it). + */ + +static loff_t xillyusb_llseek(struct file *filp, loff_t offset, int whence) +{ + struct xillyusb_channel *chan = filp->private_data; + struct xillyusb_dev *xdev = chan->xdev; + loff_t pos = filp->f_pos; + int rc = 0; + unsigned int log2_element_size = chan->readable ? + chan->in_log2_element_size : chan->out_log2_element_size; + + /* + * Take both mutexes not allowing interrupts, since it seems like + * common applications don't expect an -EINTR here. Besides, multiple + * access to a single file descriptor on seekable devices is a mess + * anyhow. + */ + + mutex_lock(&chan->out_mutex); + mutex_lock(&chan->in_mutex); + + switch (whence) { + case SEEK_SET: + pos = offset; + break; + case SEEK_CUR: + pos += offset; + break; + case SEEK_END: + pos = offset; /* Going to the end => to the beginning */ + break; + default: + rc = -EINVAL; + goto end; + } + + /* In any case, we must finish on an element boundary */ + if (pos & ((1 << log2_element_size) - 1)) { + rc = -EINVAL; + goto end; + } + + rc = xillyusb_send_opcode(xdev, chan->chan_idx << 1, + OPCODE_SET_ADDR, + pos >> log2_element_size); + + if (rc) + goto end; + + if (chan->writable) { + chan->flushed = 0; + rc = flush_downstream(chan, HZ, false); + } + +end: + mutex_unlock(&chan->out_mutex); + mutex_unlock(&chan->in_mutex); + + if (rc) /* Return error after releasing mutexes */ + return rc; + + filp->f_pos = pos; + + return pos; +} + +static __poll_t xillyusb_poll(struct file *filp, poll_table *wait) +{ + struct xillyusb_channel *chan = filp->private_data; + __poll_t mask = 0; + + if (chan->in_fifo) + poll_wait(filp, &chan->in_fifo->waitq, wait); + + if (chan->out_ep) + poll_wait(filp, &chan->out_ep->fifo.waitq, wait); + + /* + * If this is the first time poll() is called, and the file is + * readable, set the relevant flag. Also tell the FPGA to send all it + * has, to kickstart the mechanism that ensures there's always some + * data in in_fifo unless the stream is dry end-to-end. Note that the + * first poll() may not return a EPOLLIN, even if there's data on the + * FPGA. Rather, the data will arrive soon, and trigger the relevant + * wait queue. + */ + + if (!chan->poll_used && chan->in_fifo) { + chan->poll_used = 1; + request_read_anything(chan, OPCODE_SET_PUSH); + } + + /* + * poll() won't play ball regarding read() channels which + * are synchronous. Allowing that will create situations where data has + * been delivered at the FPGA, and users expecting select() to wake up, + * which it may not. So make it never work. + */ + + if (chan->in_fifo && !chan->in_synchronous && + (READ_ONCE(chan->in_fifo->fill) || !chan->read_data_ok)) + mask |= EPOLLIN | EPOLLRDNORM; + + if (chan->out_ep && + (READ_ONCE(chan->out_ep->fifo.fill) != chan->out_ep->fifo.size)) + mask |= EPOLLOUT | EPOLLWRNORM; + + if (chan->xdev->error) + mask |= EPOLLERR; + + return mask; +} + +static const struct file_operations xillyusb_fops = { + .owner = THIS_MODULE, + .read = xillyusb_read, + .write = xillyusb_write, + .open = xillyusb_open, + .flush = xillyusb_flush, + .release = xillyusb_release, + .llseek = xillyusb_llseek, + .poll = xillyusb_poll, +}; + +static int xillyusb_setup_base_eps(struct xillyusb_dev *xdev) +{ + xdev->msg_ep = endpoint_alloc(xdev, MSG_EP_NUM | USB_DIR_OUT, + bulk_out_work, 1, 2); + if (!xdev->msg_ep) + return -ENOMEM; + + if (fifo_init(&xdev->msg_ep->fifo, 13)) /* 8 kiB */ + goto dealloc; + + xdev->msg_ep->fill_mask = -8; /* 8 bytes granularity */ + + xdev->in_ep = endpoint_alloc(xdev, IN_EP_NUM | USB_DIR_IN, + bulk_in_work, BUF_SIZE_ORDER, BUFNUM); + if (!xdev->in_ep) + goto dealloc; + + try_queue_bulk_in(xdev->in_ep); + + return 0; + +dealloc: + endpoint_dealloc(xdev->msg_ep); /* Also frees FIFO mem if allocated */ + return -ENOMEM; +} + +static int setup_channels(struct xillyusb_dev *xdev, + __le16 *chandesc, + int num_channels) +{ + struct xillyusb_channel *chan; + int i; + + chan = kcalloc(num_channels, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + xdev->channels = chan; + + for (i = 0; i < num_channels; i++, chan++) { + unsigned int in_desc = le16_to_cpu(*chandesc++); + unsigned int out_desc = le16_to_cpu(*chandesc++); + + chan->xdev = xdev; + mutex_init(&chan->in_mutex); + mutex_init(&chan->out_mutex); + mutex_init(&chan->lock); + init_waitqueue_head(&chan->flushq); + + chan->chan_idx = i; + + if (in_desc & 0x80) { /* Entry is valid */ + chan->readable = 1; + chan->in_synchronous = !!(in_desc & 0x40); + chan->in_seekable = !!(in_desc & 0x20); + chan->in_log2_element_size = in_desc & 0x0f; + chan->in_log2_fifo_size = ((in_desc >> 8) & 0x1f) + 16; + } + + /* + * A downstream channel should never exist above index 13, + * as it would request a nonexistent BULK endpoint > 15. + * In the peculiar case that it does, it's ignored silently. + */ + + if ((out_desc & 0x80) && i < 14) { /* Entry is valid */ + chan->writable = 1; + chan->out_synchronous = !!(out_desc & 0x40); + chan->out_seekable = !!(out_desc & 0x20); + chan->out_log2_element_size = out_desc & 0x0f; + chan->out_log2_fifo_size = + ((out_desc >> 8) & 0x1f) + 16; + } + } + + return 0; +} + +static int xillyusb_discovery(struct usb_interface *interface) +{ + int rc; + struct xillyusb_dev *xdev = usb_get_intfdata(interface); + __le16 bogus_chandesc[2]; + struct xillyfifo idt_fifo; + struct xillyusb_channel *chan; + unsigned int idt_len, names_offset; + unsigned char *idt; + int num_channels; + + rc = xillyusb_send_opcode(xdev, ~0, OPCODE_QUIESCE, 0); + + if (rc) { + dev_err(&interface->dev, "Failed to send quiesce request. Aborting.\n"); + return rc; + } + + /* Phase I: Set up one fake upstream channel and obtain IDT */ + + /* Set up a fake IDT with one async IN stream */ + bogus_chandesc[0] = cpu_to_le16(0x80); + bogus_chandesc[1] = cpu_to_le16(0); + + rc = setup_channels(xdev, bogus_chandesc, 1); + + if (rc) + return rc; + + rc = fifo_init(&idt_fifo, LOG2_IDT_FIFO_SIZE); + + if (rc) + return rc; + + chan = xdev->channels; + + chan->in_fifo = &idt_fifo; + chan->read_data_ok = 1; + + xdev->num_channels = 1; + + rc = xillyusb_send_opcode(xdev, ~0, OPCODE_REQ_IDT, 0); + + if (rc) { + dev_err(&interface->dev, "Failed to send IDT request. Aborting.\n"); + goto unfifo; + } + + rc = wait_event_interruptible_timeout(idt_fifo.waitq, + !chan->read_data_ok, + XILLY_RESPONSE_TIMEOUT); + + if (xdev->error) { + rc = xdev->error; + goto unfifo; + } + + if (rc < 0) { + rc = -EINTR; /* Interrupt on probe method? Interesting. */ + goto unfifo; + } + + if (chan->read_data_ok) { + rc = -ETIMEDOUT; + dev_err(&interface->dev, "No response from FPGA. Aborting.\n"); + goto unfifo; + } + + idt_len = READ_ONCE(idt_fifo.fill); + idt = kmalloc(idt_len, GFP_KERNEL); + + if (!idt) { + rc = -ENOMEM; + goto unfifo; + } + + fifo_read(&idt_fifo, idt, idt_len, xilly_memcpy); + + if (crc32_le(~0, idt, idt_len) != 0) { + dev_err(&interface->dev, "IDT failed CRC check. Aborting.\n"); + rc = -ENODEV; + goto unidt; + } + + if (*idt > 0x90) { + dev_err(&interface->dev, "No support for IDT version 0x%02x. Maybe the xillyusb driver needs an upgrade. Aborting.\n", + (int)*idt); + rc = -ENODEV; + goto unidt; + } + + /* Phase II: Set up the streams as defined in IDT */ + + num_channels = le16_to_cpu(*((__le16 *)(idt + 1))); + names_offset = 3 + num_channels * 4; + idt_len -= 4; /* Exclude CRC */ + + if (idt_len < names_offset) { + dev_err(&interface->dev, "IDT too short. This is exceptionally weird, because its CRC is OK\n"); + rc = -ENODEV; + goto unidt; + } + + rc = setup_channels(xdev, (void *)idt + 3, num_channels); + + if (rc) + goto unidt; + + /* + * Except for wildly misbehaving hardware, or if it was disconnected + * just after responding with the IDT, there is no reason for any + * work item to be running now. To be sure that xdev->channels + * is updated on anything that might run in parallel, flush the + * workqueue, which rarely does anything. + */ + flush_workqueue(xdev->workq); + + xdev->num_channels = num_channels; + + fifo_mem_release(&idt_fifo); + kfree(chan); + + rc = xillybus_init_chrdev(&interface->dev, &xillyusb_fops, + THIS_MODULE, xdev, + idt + names_offset, + idt_len - names_offset, + num_channels, + xillyname, true); + + kfree(idt); + + return rc; + +unidt: + kfree(idt); + +unfifo: + safely_assign_in_fifo(chan, NULL); + fifo_mem_release(&idt_fifo); + + return rc; +} + +static int xillyusb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct xillyusb_dev *xdev; + int rc; + + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + kref_init(&xdev->kref); + mutex_init(&xdev->process_in_mutex); + mutex_init(&xdev->msg_mutex); + + xdev->udev = usb_get_dev(interface_to_usbdev(interface)); + xdev->dev = &interface->dev; + xdev->error = 0; + spin_lock_init(&xdev->error_lock); + xdev->in_counter = 0; + xdev->in_bytes_left = 0; + xdev->workq = alloc_workqueue(xillyname, WQ_HIGHPRI, 0); + + if (!xdev->workq) { + dev_err(&interface->dev, "Failed to allocate work queue\n"); + rc = -ENOMEM; + goto fail; + } + + INIT_WORK(&xdev->wakeup_workitem, wakeup_all); + + usb_set_intfdata(interface, xdev); + + rc = xillyusb_setup_base_eps(xdev); + if (rc) + goto fail; + + rc = xillyusb_discovery(interface); + if (rc) + goto latefail; + + return 0; + +latefail: + endpoint_quiesce(xdev->in_ep); + endpoint_quiesce(xdev->msg_ep); + +fail: + usb_set_intfdata(interface, NULL); + kref_put(&xdev->kref, cleanup_dev); + return rc; +} + +static void xillyusb_disconnect(struct usb_interface *interface) +{ + struct xillyusb_dev *xdev = usb_get_intfdata(interface); + struct xillyusb_endpoint *msg_ep = xdev->msg_ep; + struct xillyfifo *fifo = &msg_ep->fifo; + int rc; + int i; + + xillybus_cleanup_chrdev(xdev, &interface->dev); + + /* + * Try to send OPCODE_QUIESCE, which will fail silently if the device + * was disconnected, but makes sense on module unload. + */ + + msg_ep->wake_on_drain = true; + xillyusb_send_opcode(xdev, ~0, OPCODE_QUIESCE, 0); + + /* + * If the device has been disconnected, sending the opcode causes + * a global device error with xdev->error, if such error didn't + * occur earlier. Hence timing out means that the USB link is fine, + * but somehow the message wasn't sent. Should never happen. + */ + + rc = wait_event_interruptible_timeout(fifo->waitq, + msg_ep->drained || xdev->error, + XILLY_RESPONSE_TIMEOUT); + + if (!rc) + dev_err(&interface->dev, + "Weird timeout condition on sending quiesce request.\n"); + + report_io_error(xdev, -ENODEV); /* Discourage further activity */ + + /* + * This device driver is declared with soft_unbind set, or else + * sending OPCODE_QUIESCE above would always fail. The price is + * that the USB framework didn't kill outstanding URBs, so it has + * to be done explicitly before returning from this call. + */ + + for (i = 0; i < xdev->num_channels; i++) { + struct xillyusb_channel *chan = &xdev->channels[i]; + + /* + * Lock taken to prevent chan->out_ep from changing. It also + * ensures xillyusb_open() and xillyusb_flush() don't access + * xdev->dev after being nullified below. + */ + mutex_lock(&chan->lock); + if (chan->out_ep) + endpoint_quiesce(chan->out_ep); + mutex_unlock(&chan->lock); + } + + endpoint_quiesce(xdev->in_ep); + endpoint_quiesce(xdev->msg_ep); + + usb_set_intfdata(interface, NULL); + + xdev->dev = NULL; + + kref_put(&xdev->kref, cleanup_dev); +} + +static struct usb_driver xillyusb_driver = { + .name = xillyname, + .id_table = xillyusb_table, + .probe = xillyusb_probe, + .disconnect = xillyusb_disconnect, + .soft_unbind = 1, +}; + +static int __init xillyusb_init(void) +{ + int rc = 0; + + if (LOG2_INITIAL_FIFO_BUF_SIZE > PAGE_SHIFT) + fifo_buf_order = LOG2_INITIAL_FIFO_BUF_SIZE - PAGE_SHIFT; + else + fifo_buf_order = 0; + + rc = usb_register(&xillyusb_driver); + + return rc; +} + +static void __exit xillyusb_exit(void) +{ + usb_deregister(&xillyusb_driver); +} + +module_init(xillyusb_init); +module_exit(xillyusb_exit); diff --git a/drivers/comedi/drivers/comedi_8254.c b/drivers/comedi/drivers/comedi_8254.c index d1d509e9add9..4bf5daa9e885 100644 --- a/drivers/comedi/drivers/comedi_8254.c +++ b/drivers/comedi/drivers/comedi_8254.c @@ -555,6 +555,7 @@ static int comedi_8254_insn_config(struct comedi_device *dev, /** * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer * @s: comedi_subdevice struct + * @i8254: comedi_8254 struct */ void comedi_8254_subdevice_init(struct comedi_subdevice *s, struct comedi_8254 *i8254) @@ -607,7 +608,7 @@ static struct comedi_8254 *__i8254_init(unsigned long iobase, /** * comedi_8254_init - allocate and initialize the 8254 device for pio access - * @mmio: port I/O base address + * @iobase: port I/O base address * @osc_base: base time of the counter in ns * OPTIONAL - only used by comedi_8254_cascade_ns_to_timer() * @iosize: I/O register size diff --git a/drivers/comedi/drivers/comedi_isadma.c b/drivers/comedi/drivers/comedi_isadma.c index c729094298c2..479b58e209ba 100644 --- a/drivers/comedi/drivers/comedi_isadma.c +++ b/drivers/comedi/drivers/comedi_isadma.c @@ -143,7 +143,7 @@ EXPORT_SYMBOL_GPL(comedi_isadma_set_mode); * comedi_isadma_alloc - allocate and initialize the ISA DMA * @dev: comedi_device struct * @n_desc: the number of cookies to allocate - * @dma_chan: DMA channel for the first cookie + * @dma_chan1: DMA channel for the first cookie * @dma_chan2: DMA channel for the second cookie * @maxsize: the size of the buffer to allocate for each cookie * @dma_dir: the DMA direction diff --git a/drivers/comedi/drivers/ni_routes.c b/drivers/comedi/drivers/ni_routes.c index c426a9286f15..f0f8cd424b30 100644 --- a/drivers/comedi/drivers/ni_routes.c +++ b/drivers/comedi/drivers/ni_routes.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routes.c * Route information for NI boards. @@ -246,7 +245,7 @@ unsigned int ni_get_valid_routes(const struct ni_route_tables *tables, } EXPORT_SYMBOL_GPL(ni_get_valid_routes); -/** +/* * List of NI global signal names that, as destinations, are only routeable * indirectly through the *_arg elements of the comedi_cmd structure. */ @@ -388,7 +387,7 @@ ni_find_route_set(const int destination, } EXPORT_SYMBOL_GPL(ni_find_route_set); -/** +/* * ni_route_set_has_source() - Determines whether the given source is in * included given route_set. * @@ -507,7 +506,7 @@ s8 ni_route_to_register(const int src, const int dest, } EXPORT_SYMBOL_GPL(ni_route_to_register); -/** +/* * ni_find_route_source() - Finds the signal source corresponding to a signal * route (src-->dest) of the specified routing register * value and the specified route destination on the diff --git a/drivers/comedi/drivers/ni_routes.h b/drivers/comedi/drivers/ni_routes.h index b7680fd2afe1..036982315584 100644 --- a/drivers/comedi/drivers/ni_routes.h +++ b/drivers/comedi/drivers/ni_routes.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routes.h * Route information for NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes.c b/drivers/comedi/drivers/ni_routing/ni_device_routes.c index 7b6a74dfe48b..58654c2b12d6 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes.h b/drivers/comedi/drivers/ni_routing/ni_device_routes.h index b9f1c47d19e1..09e4e172c659 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes.h +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/all.h b/drivers/comedi/drivers/ni_routing/ni_device_routes/all.h index 78b24138acb7..001dbb88a874 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/all.h +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/all.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/all.h * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c index f1126a0cb285..7d3064c92643 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6070e.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c index 74a59222963f..e2c462edb8ec 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6220.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6220.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c index 44dcbabf2a99..9e02ec0a66ad 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6221.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6221.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6229.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6229.c index fa5794e4e2b3..33f7fff61f74 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6229.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6229.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6229.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6251.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6251.c index 645fd1cd2de4..dde676b73624 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6251.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6251.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6251.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6254.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6254.c index 056a240cd3a2..167a2da97c14 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6254.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6254.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6254.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6259.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6259.c index e0b5fa78c3bc..ba990f98590c 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6259.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6259.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6259.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6534.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6534.c index a2472ed288cf..f8d2a91b6c0a 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6534.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6534.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6534.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6602.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6602.c index 91de9dac2d6a..2eee91f590eb 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6602.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6602.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6602.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6713.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6713.c index d378b36d2084..c07ef3584a4b 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6713.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6713.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6713.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6723.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6723.c index e0cc57ab06e7..c37373f8f0e1 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6723.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6723.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6723.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6733.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6733.c index f6e1e17ab854..f252fbe19638 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6733.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pci-6733.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pci-6733.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c index 9978d632117f..4ccba4fdf3bc 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxi-6030e.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c index 1b89e27d7aa5..84fdfa2ef9a7 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxi-6224.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c index 10dfc34bc87c..2b99ce0f87a4 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxi-6225.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c index 25db4b7363de..1c5164c46306 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxi-6251.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c index 27da4433fc4a..a3402b1ca6e8 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxi-6733.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c index 8354fe971d59..defcc4cfe1e4 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxie-6251.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c index 2ebb679e0129..d2013b9e6767 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxie-6535.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c index d88504314d7f..89aff39a4fc2 100644 --- a/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c +++ b/drivers/comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_device_routes/pxie-6738.c * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_route_values.c b/drivers/comedi/drivers/ni_routing/ni_route_values.c index 5901762734ed..54a740b39819 100644 --- a/drivers/comedi/drivers/ni_routing/ni_route_values.c +++ b/drivers/comedi/drivers/ni_routing/ni_route_values.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_route_values.c * Route information for NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_route_values.h b/drivers/comedi/drivers/ni_routing/ni_route_values.h index 80e0145fb82b..6e358efa6f7f 100644 --- a/drivers/comedi/drivers/ni_routing/ni_route_values.h +++ b/drivers/comedi/drivers/ni_routing/ni_route_values.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_route_values.h * Route information for NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_route_values/all.h b/drivers/comedi/drivers/ni_routing/ni_route_values/all.h index 7227461500b5..30761e55f746 100644 --- a/drivers/comedi/drivers/ni_routing/ni_route_values/all.h +++ b/drivers/comedi/drivers/ni_routing/ni_route_values/all.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_route_values/all.h * List of valid routes for specific NI boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_route_values/ni_660x.c b/drivers/comedi/drivers/ni_routing/ni_route_values/ni_660x.c index f1c7e6646261..aace60e49507 100644 --- a/drivers/comedi/drivers/ni_routing/ni_route_values/ni_660x.c +++ b/drivers/comedi/drivers/ni_routing/ni_route_values/ni_660x.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_route_values/ni_660x.c * Route information for NI_660X boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_route_values/ni_eseries.c b/drivers/comedi/drivers/ni_routing/ni_route_values/ni_eseries.c index d1ab3c9ce585..7a52f024cdbd 100644 --- a/drivers/comedi/drivers/ni_routing/ni_route_values/ni_eseries.c +++ b/drivers/comedi/drivers/ni_routing/ni_route_values/ni_eseries.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_route_values/ni_eseries.c * Route information for NI_ESERIES boards. diff --git a/drivers/comedi/drivers/ni_routing/ni_route_values/ni_mseries.c b/drivers/comedi/drivers/ni_routing/ni_route_values/ni_mseries.c index c59d8afe0ae9..d1ddd13b33b5 100644 --- a/drivers/comedi/drivers/ni_routing/ni_route_values/ni_mseries.c +++ b/drivers/comedi/drivers/ni_routing/ni_route_values/ni_mseries.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/ni_route_values/ni_mseries.c * Route information for NI_MSERIES boards. diff --git a/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c b/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c index dedb6f2fc678..d55521b5bdcb 100644 --- a/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c +++ b/drivers/comedi/drivers/ni_routing/tools/convert_c_to_py.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ #include <stdint.h> #include <stdbool.h> diff --git a/drivers/comedi/drivers/ni_routing/tools/convert_csv_to_c.py b/drivers/comedi/drivers/ni_routing/tools/convert_csv_to_c.py index 532eb6372a5a..90378fb50580 100755 --- a/drivers/comedi/drivers/ni_routing/tools/convert_csv_to_c.py +++ b/drivers/comedi/drivers/ni_routing/tools/convert_csv_to_c.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0+ -# vim: ts=2:sw=2:et:tw=80:nowrap # This is simply to aide in creating the entries in the order of the value of # the device-global NI signal/terminal constants defined in comedi.h @@ -123,7 +122,6 @@ class DeviceRoutes(CSVCollection): output_file_top = """\ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/{filename} * List of valid routes for specific NI boards. @@ -155,7 +153,6 @@ class DeviceRoutes(CSVCollection): extern_header = """\ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/{filename} * List of valid routes for specific NI boards. @@ -193,7 +190,6 @@ class DeviceRoutes(CSVCollection): single_output_file_top = """\ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/{filename} * List of valid routes for specific NI boards. @@ -299,7 +295,6 @@ class RouteValues(CSVCollection): output_file_top = """\ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/{filename} * Route information for NI boards. @@ -337,7 +332,6 @@ class RouteValues(CSVCollection): extern_header = """\ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/{filename} * List of valid routes for specific NI boards. @@ -375,7 +369,6 @@ class RouteValues(CSVCollection): single_output_file_top = """\ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/ni_routing/{filename} * Route information for {sheet} boards. diff --git a/drivers/comedi/drivers/ni_routing/tools/convert_py_to_csv.py b/drivers/comedi/drivers/ni_routing/tools/convert_py_to_csv.py index b3e6472bac22..a273b33edb8f 100755 --- a/drivers/comedi/drivers/ni_routing/tools/convert_py_to_csv.py +++ b/drivers/comedi/drivers/ni_routing/tools/convert_py_to_csv.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0+ -# vim: ts=2:sw=2:et:tw=80:nowrap from os import path import os, csv diff --git a/drivers/comedi/drivers/ni_routing/tools/csv_collection.py b/drivers/comedi/drivers/ni_routing/tools/csv_collection.py index 12617329a928..db977ecb4307 100644 --- a/drivers/comedi/drivers/ni_routing/tools/csv_collection.py +++ b/drivers/comedi/drivers/ni_routing/tools/csv_collection.py @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ -# vim: ts=2:sw=2:et:tw=80:nowrap import os, csv, glob diff --git a/drivers/comedi/drivers/ni_routing/tools/make_blank_csv.py b/drivers/comedi/drivers/ni_routing/tools/make_blank_csv.py index 89c90a0ba24d..c00eaf803299 100755 --- a/drivers/comedi/drivers/ni_routing/tools/make_blank_csv.py +++ b/drivers/comedi/drivers/ni_routing/tools/make_blank_csv.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0+ -# vim: ts=2:sw=2:et:tw=80:nowrap from os import path import os, csv diff --git a/drivers/comedi/drivers/ni_routing/tools/ni_names.py b/drivers/comedi/drivers/ni_routing/tools/ni_names.py index 5f9b825968b1..d4df5f29e3e5 100644 --- a/drivers/comedi/drivers/ni_routing/tools/ni_names.py +++ b/drivers/comedi/drivers/ni_routing/tools/ni_names.py @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ -# vim: ts=2:sw=2:et:tw=80:nowrap """ This file helps to extract string names of NI signals as included in comedi.h between NI_NAMES_BASE and NI_NAMES_BASE+NI_NUM_NAMES. diff --git a/drivers/comedi/drivers/ni_tio.c b/drivers/comedi/drivers/ni_tio.c index f6154addaa95..da6826d77e60 100644 --- a/drivers/comedi/drivers/ni_tio.c +++ b/drivers/comedi/drivers/ni_tio.c @@ -1501,7 +1501,7 @@ int ni_tio_insn_config(struct comedi_device *dev, } EXPORT_SYMBOL_GPL(ni_tio_insn_config); -/** +/* * Retrieves the register value of the current source of the output selector for * the given destination. * @@ -1541,10 +1541,10 @@ int ni_tio_get_routing(struct ni_gpct_device *counter_dev, unsigned int dest) EXPORT_SYMBOL_GPL(ni_tio_get_routing); /** - * Sets the register value of the selector MUX for the given destination. - * @counter_dev:Pointer to general counter device. - * @destination:Device-global identifier of route destination. - * @register_value: + * ni_tio_set_routing() - Sets the register value of the selector MUX for the given destination. + * @counter_dev: Pointer to general counter device. + * @dest: Device-global identifier of route destination. + * @reg: * The first several bits of this value should store the desired * value to write to the register. All other bits are for * transmitting information that modify the mode of the particular @@ -1580,7 +1580,7 @@ int ni_tio_set_routing(struct ni_gpct_device *counter_dev, unsigned int dest, } EXPORT_SYMBOL_GPL(ni_tio_set_routing); -/** +/* * Sets the given destination MUX to its default value or disable it. * * Return: 0 if successful; -EINVAL if terminal is unknown. diff --git a/drivers/comedi/drivers/tests/comedi_example_test.c b/drivers/comedi/drivers/tests/comedi_example_test.c index e5aaaeab7bdd..81d074bcdea5 100644 --- a/drivers/comedi/drivers/tests/comedi_example_test.c +++ b/drivers/comedi/drivers/tests/comedi_example_test.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/tests/comedi_example_test.c * Example set of unit tests. diff --git a/drivers/comedi/drivers/tests/ni_routes_test.c b/drivers/comedi/drivers/tests/ni_routes_test.c index 32073850d545..652362486ff6 100644 --- a/drivers/comedi/drivers/tests/ni_routes_test.c +++ b/drivers/comedi/drivers/tests/ni_routes_test.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0+ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/tests/ni_routes_test.c * Unit tests for NI routes (ni_routes.c module). diff --git a/drivers/comedi/drivers/tests/unittest.h b/drivers/comedi/drivers/tests/unittest.h index 2da3beea2479..b0b34e058aad 100644 --- a/drivers/comedi/drivers/tests/unittest.h +++ b/drivers/comedi/drivers/tests/unittest.h @@ -1,5 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0+ */ -/* vim: set ts=8 sw=8 noet tw=80 nowrap: */ /* * comedi/drivers/tests/unittest.h * Simple framework for unittests for comedi drivers. diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c index d9a16ba2ccc2..65bffde137e3 100644 --- a/drivers/eisa/eisa-bus.c +++ b/drivers/eisa/eisa-bus.c @@ -155,34 +155,29 @@ void eisa_driver_unregister(struct eisa_driver *edrv) } EXPORT_SYMBOL(eisa_driver_unregister); -static ssize_t eisa_show_sig(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t signature_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct eisa_device *edev = to_eisa_device(dev); return sprintf(buf, "%s\n", edev->id.sig); } +static DEVICE_ATTR_RO(signature); -static DEVICE_ATTR(signature, S_IRUGO, eisa_show_sig, NULL); - -static ssize_t eisa_show_state(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct eisa_device *edev = to_eisa_device(dev); return sprintf(buf, "%d\n", edev->state & EISA_CONFIG_ENABLED); } +static DEVICE_ATTR_RO(enabled); -static DEVICE_ATTR(enabled, S_IRUGO, eisa_show_state, NULL); - -static ssize_t eisa_show_modalias(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct eisa_device *edev = to_eisa_device(dev); return sprintf(buf, EISA_DEVICE_MODALIAS_FMT "\n", edev->id.sig); } - -static DEVICE_ATTR(modalias, S_IRUGO, eisa_show_modalias, NULL); +static DEVICE_ATTR_RO(modalias); static int __init eisa_init_device(struct eisa_root_device *root, struct eisa_device *edev, diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index e3db936becfd..c69d40ae5619 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -154,7 +154,7 @@ config EXTCON_RT8973A from abnormal high input voltage (up to 28V). config EXTCON_SM5502 - tristate "Silicon Mitus SM5502 EXTCON support" + tristate "Silicon Mitus SM5502/SM5504 EXTCON support" depends on I2C select IRQ_DOMAIN select REGMAP_I2C diff --git a/drivers/extcon/extcon-intel-mrfld.c b/drivers/extcon/extcon-intel-mrfld.c index f47016fb28a8..cd1a5f230077 100644 --- a/drivers/extcon/extcon-intel-mrfld.c +++ b/drivers/extcon/extcon-intel-mrfld.c @@ -197,6 +197,7 @@ static int mrfld_extcon_probe(struct platform_device *pdev) struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); struct regmap *regmap = pmic->regmap; struct mrfld_extcon_data *data; + unsigned int status; unsigned int id; int irq, ret; @@ -244,6 +245,14 @@ static int mrfld_extcon_probe(struct platform_device *pdev) /* Get initial state */ mrfld_extcon_role_detect(data); + /* + * Cached status value is used for cable detection, see comments + * in mrfld_extcon_cable_detect(), we need to sync cached value + * with a real state of the hardware. + */ + regmap_read(regmap, BCOVE_SCHGRIRQ1, &status); + data->status = status; + mrfld_extcon_clear(data, BCOVE_MIRQLVL1, BCOVE_LVL1_CHGR); mrfld_extcon_clear(data, BCOVE_MCHGRIRQ1, BCOVE_CHGRIRQ_ALL); diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index bbc592823570..9cddf08e0696 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -773,3 +773,4 @@ module_platform_driver(max8997_muic_driver); MODULE_DESCRIPTION("Maxim MAX8997 Extcon driver"); MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max8997-muic"); diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index db41d1c58efd..93da2d8379b1 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -40,17 +40,13 @@ struct sm5502_muic_info { struct i2c_client *i2c; struct regmap *regmap; + const struct sm5502_type *type; struct regmap_irq_chip_data *irq_data; - struct muic_irq *muic_irqs; - unsigned int num_muic_irqs; int irq; bool irq_attach; bool irq_detach; struct work_struct irq_work; - struct reg_data *reg_data; - unsigned int num_reg_data; - struct mutex mutex; /* @@ -62,6 +58,18 @@ struct sm5502_muic_info { struct delayed_work wq_detcable; }; +struct sm5502_type { + struct muic_irq *muic_irqs; + unsigned int num_muic_irqs; + const struct regmap_irq_chip *irq_chip; + + struct reg_data *reg_data; + unsigned int num_reg_data; + + unsigned int otg_dev_type1; + int (*parse_irq)(struct sm5502_muic_info *info, int irq_type); +}; + /* Default value of SM5502 register to bring up MUIC device. */ static struct reg_data sm5502_reg_data[] = { { @@ -88,7 +96,33 @@ static struct reg_data sm5502_reg_data[] = { | SM5502_REG_INTM2_MHL_MASK, .invert = true, }, - { } +}; + +/* Default value of SM5504 register to bring up MUIC device. */ +static struct reg_data sm5504_reg_data[] = { + { + .reg = SM5502_REG_RESET, + .val = SM5502_REG_RESET_MASK, + .invert = true, + }, { + .reg = SM5502_REG_INTMASK1, + .val = SM5504_REG_INTM1_ATTACH_MASK + | SM5504_REG_INTM1_DETACH_MASK, + .invert = false, + }, { + .reg = SM5502_REG_INTMASK2, + .val = SM5504_REG_INTM2_RID_CHG_MASK + | SM5504_REG_INTM2_UVLO_MASK + | SM5504_REG_INTM2_POR_MASK, + .invert = true, + }, { + .reg = SM5502_REG_CONTROL, + .val = SM5502_REG_CONTROL_MANUAL_SW_MASK + | SM5504_REG_CONTROL_CHGTYP_MASK + | SM5504_REG_CONTROL_USBCHDEN_MASK + | SM5504_REG_CONTROL_ADC_EN_MASK, + .invert = true, + }, }; /* List of detectable cables */ @@ -199,6 +233,55 @@ static const struct regmap_irq_chip sm5502_muic_irq_chip = { .num_irqs = ARRAY_SIZE(sm5502_irqs), }; +/* List of supported interrupt for SM5504 */ +static struct muic_irq sm5504_muic_irqs[] = { + { SM5504_IRQ_INT1_ATTACH, "muic-attach" }, + { SM5504_IRQ_INT1_DETACH, "muic-detach" }, + { SM5504_IRQ_INT1_CHG_DET, "muic-chg-det" }, + { SM5504_IRQ_INT1_DCD_OUT, "muic-dcd-out" }, + { SM5504_IRQ_INT1_OVP_EVENT, "muic-ovp-event" }, + { SM5504_IRQ_INT1_CONNECT, "muic-connect" }, + { SM5504_IRQ_INT1_ADC_CHG, "muic-adc-chg" }, + { SM5504_IRQ_INT2_RID_CHG, "muic-rid-chg" }, + { SM5504_IRQ_INT2_UVLO, "muic-uvlo" }, + { SM5504_IRQ_INT2_POR, "muic-por" }, + { SM5504_IRQ_INT2_OVP_FET, "muic-ovp-fet" }, + { SM5504_IRQ_INT2_OCP_LATCH, "muic-ocp-latch" }, + { SM5504_IRQ_INT2_OCP_EVENT, "muic-ocp-event" }, + { SM5504_IRQ_INT2_OVP_OCP_EVENT, "muic-ovp-ocp-event" }, +}; + +/* Define interrupt list of SM5504 to register regmap_irq */ +static const struct regmap_irq sm5504_irqs[] = { + /* INT1 interrupts */ + { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ATTACH_MASK, }, + { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DETACH_MASK, }, + { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CHG_DET_MASK, }, + { .reg_offset = 0, .mask = SM5504_IRQ_INT1_DCD_OUT_MASK, }, + { .reg_offset = 0, .mask = SM5504_IRQ_INT1_OVP_MASK, }, + { .reg_offset = 0, .mask = SM5504_IRQ_INT1_CONNECT_MASK, }, + { .reg_offset = 0, .mask = SM5504_IRQ_INT1_ADC_CHG_MASK, }, + + /* INT2 interrupts */ + { .reg_offset = 1, .mask = SM5504_IRQ_INT2_RID_CHG_MASK,}, + { .reg_offset = 1, .mask = SM5504_IRQ_INT2_UVLO_MASK, }, + { .reg_offset = 1, .mask = SM5504_IRQ_INT2_POR_MASK, }, + { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_FET_MASK, }, + { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_LATCH_MASK, }, + { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OCP_EVENT_MASK, }, + { .reg_offset = 1, .mask = SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK, }, +}; + +static const struct regmap_irq_chip sm5504_muic_irq_chip = { + .name = "sm5504", + .status_base = SM5502_REG_INT1, + .mask_base = SM5502_REG_INTMASK1, + .mask_invert = false, + .num_regs = 2, + .irqs = sm5504_irqs, + .num_irqs = ARRAY_SIZE(sm5504_irqs), +}; + /* Define regmap configuration of SM5502 for I2C communication */ static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg) { @@ -302,11 +385,9 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) return ret; } - switch (dev_type1) { - case SM5502_REG_DEV_TYPE1_USB_OTG_MASK: + if (dev_type1 == info->type->otg_dev_type1) { cable_type = SM5502_MUIC_ADC_GROUND_USB_OTG; - break; - default: + } else { dev_dbg(info->dev, "cannot identify the cable type: adc(0x%x), dev_type1(0x%x)\n", adc, dev_type1); @@ -359,6 +440,11 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) return ret; } + if (dev_type1 == info->type->otg_dev_type1) { + cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG; + break; + } + switch (dev_type1) { case SM5502_REG_DEV_TYPE1_USB_SDP_MASK: cable_type = SM5502_MUIC_ADC_OPEN_USB; @@ -366,9 +452,6 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK: cable_type = SM5502_MUIC_ADC_OPEN_TA; break; - case SM5502_REG_DEV_TYPE1_USB_OTG_MASK: - cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG; - break; default: dev_dbg(info->dev, "cannot identify the cable type: adc(0x%x)\n", @@ -498,16 +581,44 @@ static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type) return 0; } +static int sm5504_parse_irq(struct sm5502_muic_info *info, int irq_type) +{ + switch (irq_type) { + case SM5504_IRQ_INT1_ATTACH: + info->irq_attach = true; + break; + case SM5504_IRQ_INT1_DETACH: + info->irq_detach = true; + break; + case SM5504_IRQ_INT1_CHG_DET: + case SM5504_IRQ_INT1_DCD_OUT: + case SM5504_IRQ_INT1_OVP_EVENT: + case SM5504_IRQ_INT1_CONNECT: + case SM5504_IRQ_INT1_ADC_CHG: + case SM5504_IRQ_INT2_RID_CHG: + case SM5504_IRQ_INT2_UVLO: + case SM5504_IRQ_INT2_POR: + case SM5504_IRQ_INT2_OVP_FET: + case SM5504_IRQ_INT2_OCP_LATCH: + case SM5504_IRQ_INT2_OCP_EVENT: + case SM5504_IRQ_INT2_OVP_OCP_EVENT: + default: + break; + } + + return 0; +} + static irqreturn_t sm5502_muic_irq_handler(int irq, void *data) { struct sm5502_muic_info *info = data; int i, irq_type = -1, ret; - for (i = 0; i < info->num_muic_irqs; i++) - if (irq == info->muic_irqs[i].virq) - irq_type = info->muic_irqs[i].irq; + for (i = 0; i < info->type->num_muic_irqs; i++) + if (irq == info->type->muic_irqs[i].virq) + irq_type = info->type->muic_irqs[i].irq; - ret = sm5502_parse_irq(info, irq_type); + ret = info->type->parse_irq(info, irq_type); if (ret < 0) { dev_warn(info->dev, "cannot handle is interrupt:%d\n", irq_type); @@ -552,19 +663,18 @@ static void sm5502_init_dev_type(struct sm5502_muic_info *info) version_id, vendor_id); /* Initiazle the register of SM5502 device to bring-up */ - for (i = 0; i < info->num_reg_data; i++) { + for (i = 0; i < info->type->num_reg_data; i++) { unsigned int val = 0; - if (!info->reg_data[i].invert) - val |= ~info->reg_data[i].val; + if (!info->type->reg_data[i].invert) + val |= ~info->type->reg_data[i].val; else - val = info->reg_data[i].val; - regmap_write(info->regmap, info->reg_data[i].reg, val); + val = info->type->reg_data[i].val; + regmap_write(info->regmap, info->type->reg_data[i].reg, val); } } -static int sm5022_muic_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int sm5022_muic_i2c_probe(struct i2c_client *i2c) { struct device_node *np = i2c->dev.of_node; struct sm5502_muic_info *info; @@ -581,10 +691,13 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c, info->dev = &i2c->dev; info->i2c = i2c; info->irq = i2c->irq; - info->muic_irqs = sm5502_muic_irqs; - info->num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs); - info->reg_data = sm5502_reg_data; - info->num_reg_data = ARRAY_SIZE(sm5502_reg_data); + info->type = device_get_match_data(info->dev); + if (!info->type) + return -EINVAL; + if (!info->type->parse_irq) { + dev_err(info->dev, "parse_irq missing in struct sm5502_type\n"); + return -EINVAL; + } mutex_init(&info->mutex); @@ -600,16 +713,17 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c, /* Support irq domain for SM5502 MUIC device */ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; - ret = regmap_add_irq_chip(info->regmap, info->irq, irq_flags, 0, - &sm5502_muic_irq_chip, &info->irq_data); + ret = devm_regmap_add_irq_chip(info->dev, info->regmap, info->irq, + irq_flags, 0, info->type->irq_chip, + &info->irq_data); if (ret != 0) { dev_err(info->dev, "failed to request IRQ %d: %d\n", info->irq, ret); return ret; } - for (i = 0; i < info->num_muic_irqs; i++) { - struct muic_irq *muic_irq = &info->muic_irqs[i]; + for (i = 0; i < info->type->num_muic_irqs; i++) { + struct muic_irq *muic_irq = &info->type->muic_irqs[i]; int virq = 0; virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq); @@ -661,17 +775,29 @@ static int sm5022_muic_i2c_probe(struct i2c_client *i2c, return 0; } -static int sm5502_muic_i2c_remove(struct i2c_client *i2c) -{ - struct sm5502_muic_info *info = i2c_get_clientdata(i2c); - - regmap_del_irq_chip(info->irq, info->irq_data); +static const struct sm5502_type sm5502_data = { + .muic_irqs = sm5502_muic_irqs, + .num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs), + .irq_chip = &sm5502_muic_irq_chip, + .reg_data = sm5502_reg_data, + .num_reg_data = ARRAY_SIZE(sm5502_reg_data), + .otg_dev_type1 = SM5502_REG_DEV_TYPE1_USB_OTG_MASK, + .parse_irq = sm5502_parse_irq, +}; - return 0; -} +static const struct sm5502_type sm5504_data = { + .muic_irqs = sm5504_muic_irqs, + .num_muic_irqs = ARRAY_SIZE(sm5504_muic_irqs), + .irq_chip = &sm5504_muic_irq_chip, + .reg_data = sm5504_reg_data, + .num_reg_data = ARRAY_SIZE(sm5504_reg_data), + .otg_dev_type1 = SM5504_REG_DEV_TYPE1_USB_OTG_MASK, + .parse_irq = sm5504_parse_irq, +}; static const struct of_device_id sm5502_dt_match[] = { - { .compatible = "siliconmitus,sm5502-muic" }, + { .compatible = "siliconmitus,sm5502-muic", .data = &sm5502_data }, + { .compatible = "siliconmitus,sm5504-muic", .data = &sm5504_data }, { }, }; MODULE_DEVICE_TABLE(of, sm5502_dt_match); @@ -702,7 +828,8 @@ static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops, sm5502_muic_suspend, sm5502_muic_resume); static const struct i2c_device_id sm5502_i2c_id[] = { - { "sm5502", TYPE_SM5502 }, + { "sm5502", (kernel_ulong_t)&sm5502_data }, + { "sm5504", (kernel_ulong_t)&sm5504_data }, { } }; MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id); @@ -713,8 +840,7 @@ static struct i2c_driver sm5502_muic_i2c_driver = { .pm = &sm5502_muic_pm_ops, .of_match_table = sm5502_dt_match, }, - .probe = sm5022_muic_i2c_probe, - .remove = sm5502_muic_i2c_remove, + .probe_new = sm5022_muic_i2c_probe, .id_table = sm5502_i2c_id, }; diff --git a/drivers/extcon/extcon-sm5502.h b/drivers/extcon/extcon-sm5502.h index ce1f1ec310c4..9c04315d48e2 100644 --- a/drivers/extcon/extcon-sm5502.h +++ b/drivers/extcon/extcon-sm5502.h @@ -8,10 +8,6 @@ #ifndef __LINUX_EXTCON_SM5502_H #define __LINUX_EXTCON_SM5502_H -enum sm5502_types { - TYPE_SM5502, -}; - /* SM5502 registers */ enum sm5502_reg { SM5502_REG_DEVICE_ID = 0x01, @@ -93,6 +89,13 @@ enum sm5502_reg { #define SM5502_REG_CONTROL_RAW_DATA_MASK (0x1 << SM5502_REG_CONTROL_RAW_DATA_SHIFT) #define SM5502_REG_CONTROL_SW_OPEN_MASK (0x1 << SM5502_REG_CONTROL_SW_OPEN_SHIFT) +#define SM5504_REG_CONTROL_CHGTYP_SHIFT 5 +#define SM5504_REG_CONTROL_USBCHDEN_SHIFT 6 +#define SM5504_REG_CONTROL_ADC_EN_SHIFT 7 +#define SM5504_REG_CONTROL_CHGTYP_MASK (0x1 << SM5504_REG_CONTROL_CHGTYP_SHIFT) +#define SM5504_REG_CONTROL_USBCHDEN_MASK (0x1 << SM5504_REG_CONTROL_USBCHDEN_SHIFT) +#define SM5504_REG_CONTROL_ADC_EN_MASK (0x1 << SM5504_REG_CONTROL_ADC_EN_SHIFT) + #define SM5502_REG_INTM1_ATTACH_SHIFT 0 #define SM5502_REG_INTM1_DETACH_SHIFT 1 #define SM5502_REG_INTM1_KP_SHIFT 2 @@ -123,6 +126,36 @@ enum sm5502_reg { #define SM5502_REG_INTM2_STUCK_KEY_RCV_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT) #define SM5502_REG_INTM2_MHL_MASK (0x1 << SM5502_REG_INTM2_MHL_SHIFT) +#define SM5504_REG_INTM1_ATTACH_SHIFT 0 +#define SM5504_REG_INTM1_DETACH_SHIFT 1 +#define SM5504_REG_INTM1_CHG_DET_SHIFT 2 +#define SM5504_REG_INTM1_DCD_OUT_SHIFT 3 +#define SM5504_REG_INTM1_OVP_EVENT_SHIFT 4 +#define SM5504_REG_INTM1_CONNECT_SHIFT 5 +#define SM5504_REG_INTM1_ADC_CHG_SHIFT 6 +#define SM5504_REG_INTM1_ATTACH_MASK (0x1 << SM5504_REG_INTM1_ATTACH_SHIFT) +#define SM5504_REG_INTM1_DETACH_MASK (0x1 << SM5504_REG_INTM1_DETACH_SHIFT) +#define SM5504_REG_INTM1_CHG_DET_MASK (0x1 << SM5504_REG_INTM1_CHG_DET_SHIFT) +#define SM5504_REG_INTM1_DCD_OUT_MASK (0x1 << SM5504_REG_INTM1_DCD_OUT_SHIFT) +#define SM5504_REG_INTM1_OVP_EVENT_MASK (0x1 << SM5504_REG_INTM1_OVP_EVENT_SHIFT) +#define SM5504_REG_INTM1_CONNECT_MASK (0x1 << SM5504_REG_INTM1_CONNECT_SHIFT) +#define SM5504_REG_INTM1_ADC_CHG_MASK (0x1 << SM5504_REG_INTM1_ADC_CHG_SHIFT) + +#define SM5504_REG_INTM2_RID_CHG_SHIFT 0 +#define SM5504_REG_INTM2_UVLO_SHIFT 1 +#define SM5504_REG_INTM2_POR_SHIFT 2 +#define SM5504_REG_INTM2_OVP_FET_SHIFT 4 +#define SM5504_REG_INTM2_OCP_LATCH_SHIFT 5 +#define SM5504_REG_INTM2_OCP_EVENT_SHIFT 6 +#define SM5504_REG_INTM2_OVP_OCP_EVENT_SHIFT 7 +#define SM5504_REG_INTM2_RID_CHG_MASK (0x1 << SM5504_REG_INTM2_RID_CHG_SHIFT) +#define SM5504_REG_INTM2_UVLO_MASK (0x1 << SM5504_REG_INTM2_UVLO_SHIFT) +#define SM5504_REG_INTM2_POR_MASK (0x1 << SM5504_REG_INTM2_POR_SHIFT) +#define SM5504_REG_INTM2_OVP_FET_MASK (0x1 << SM5504_REG_INTM2_OVP_FET_SHIFT) +#define SM5504_REG_INTM2_OCP_LATCH_MASK (0x1 << SM5504_REG_INTM2_OCP_LATCH_SHIFT) +#define SM5504_REG_INTM2_OCP_EVENT_MASK (0x1 << SM5504_REG_INTM2_OCP_EVENT_SHIFT) +#define SM5504_REG_INTM2_OVP_OCP_EVENT_MASK (0x1 << SM5504_REG_INTM2_OVP_OCP_EVENT_SHIFT) + #define SM5502_REG_ADC_SHIFT 0 #define SM5502_REG_ADC_MASK (0x1f << SM5502_REG_ADC_SHIFT) @@ -199,6 +232,9 @@ enum sm5502_reg { #define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT) #define SM5502_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT) +#define SM5504_REG_DEV_TYPE1_USB_OTG_SHIFT 0 +#define SM5504_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5504_REG_DEV_TYPE1_USB_OTG_SHIFT) + #define SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT 0 #define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT 1 #define SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT 2 @@ -277,4 +313,42 @@ enum sm5502_irq { #define SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK BIT(4) #define SM5502_IRQ_INT2_MHL_MASK BIT(5) +/* SM5504 Interrupts */ +enum sm5504_irq { + /* INT1 */ + SM5504_IRQ_INT1_ATTACH, + SM5504_IRQ_INT1_DETACH, + SM5504_IRQ_INT1_CHG_DET, + SM5504_IRQ_INT1_DCD_OUT, + SM5504_IRQ_INT1_OVP_EVENT, + SM5504_IRQ_INT1_CONNECT, + SM5504_IRQ_INT1_ADC_CHG, + + /* INT2 */ + SM5504_IRQ_INT2_RID_CHG, + SM5504_IRQ_INT2_UVLO, + SM5504_IRQ_INT2_POR, + SM5504_IRQ_INT2_OVP_FET, + SM5504_IRQ_INT2_OCP_LATCH, + SM5504_IRQ_INT2_OCP_EVENT, + SM5504_IRQ_INT2_OVP_OCP_EVENT, + + SM5504_IRQ_NUM, +}; + +#define SM5504_IRQ_INT1_ATTACH_MASK BIT(0) +#define SM5504_IRQ_INT1_DETACH_MASK BIT(1) +#define SM5504_IRQ_INT1_CHG_DET_MASK BIT(2) +#define SM5504_IRQ_INT1_DCD_OUT_MASK BIT(3) +#define SM5504_IRQ_INT1_OVP_MASK BIT(4) +#define SM5504_IRQ_INT1_CONNECT_MASK BIT(5) +#define SM5504_IRQ_INT1_ADC_CHG_MASK BIT(6) +#define SM5504_IRQ_INT2_RID_CHG_MASK BIT(0) +#define SM5504_IRQ_INT2_UVLO_MASK BIT(1) +#define SM5504_IRQ_INT2_POR_MASK BIT(2) +#define SM5504_IRQ_INT2_OVP_FET_MASK BIT(4) +#define SM5504_IRQ_INT2_OCP_LATCH_MASK BIT(5) +#define SM5504_IRQ_INT2_OCP_EVENT_MASK BIT(6) +#define SM5504_IRQ_INT2_OVP_OCP_EVENT_MASK BIT(7) + #endif /* __LINUX_EXTCON_SM5502_H */ diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index 88ed971e32c0..b0d671db178a 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c @@ -511,12 +511,12 @@ remove_card(struct pci_dev *dev) wake_up_interruptible(&client->buffer.wait); spin_unlock_irq(&lynx->client_list_lock); - pci_free_consistent(lynx->pci_device, sizeof(struct pcl), - lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); - pci_free_consistent(lynx->pci_device, sizeof(struct pcl), - lynx->rcv_pcl, lynx->rcv_pcl_bus); - pci_free_consistent(lynx->pci_device, PAGE_SIZE, - lynx->rcv_buffer, lynx->rcv_buffer_bus); + dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), + lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); + dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), + lynx->rcv_pcl, lynx->rcv_pcl_bus); + dma_free_coherent(&lynx->pci_device->dev, PAGE_SIZE, lynx->rcv_buffer, + lynx->rcv_buffer_bus); iounmap(lynx->registers); pci_disable_device(dev); @@ -532,7 +532,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) u32 p, end; int ret, i; - if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { + if (dma_set_mask(&dev->dev, DMA_BIT_MASK(32))) { dev_err(&dev->dev, "DMA address limits not supported for PCILynx hardware\n"); return -ENXIO; @@ -564,12 +564,16 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) goto fail_deallocate_lynx; } - lynx->rcv_start_pcl = pci_alloc_consistent(lynx->pci_device, - sizeof(struct pcl), &lynx->rcv_start_pcl_bus); - lynx->rcv_pcl = pci_alloc_consistent(lynx->pci_device, - sizeof(struct pcl), &lynx->rcv_pcl_bus); - lynx->rcv_buffer = pci_alloc_consistent(lynx->pci_device, - RCV_BUFFER_SIZE, &lynx->rcv_buffer_bus); + lynx->rcv_start_pcl = dma_alloc_coherent(&lynx->pci_device->dev, + sizeof(struct pcl), + &lynx->rcv_start_pcl_bus, + GFP_KERNEL); + lynx->rcv_pcl = dma_alloc_coherent(&lynx->pci_device->dev, + sizeof(struct pcl), + &lynx->rcv_pcl_bus, GFP_KERNEL); + lynx->rcv_buffer = dma_alloc_coherent(&lynx->pci_device->dev, + RCV_BUFFER_SIZE, + &lynx->rcv_buffer_bus, GFP_KERNEL); if (lynx->rcv_start_pcl == NULL || lynx->rcv_pcl == NULL || lynx->rcv_buffer == NULL) { @@ -667,14 +671,15 @@ fail_free_irq: fail_deallocate_buffers: if (lynx->rcv_start_pcl) - pci_free_consistent(lynx->pci_device, sizeof(struct pcl), - lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); + dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), + lynx->rcv_start_pcl, + lynx->rcv_start_pcl_bus); if (lynx->rcv_pcl) - pci_free_consistent(lynx->pci_device, sizeof(struct pcl), - lynx->rcv_pcl, lynx->rcv_pcl_bus); + dma_free_coherent(&lynx->pci_device->dev, sizeof(struct pcl), + lynx->rcv_pcl, lynx->rcv_pcl_bus); if (lynx->rcv_buffer) - pci_free_consistent(lynx->pci_device, PAGE_SIZE, - lynx->rcv_buffer, lynx->rcv_buffer_bus); + dma_free_coherent(&lynx->pci_device->dev, PAGE_SIZE, + lynx->rcv_buffer, lynx->rcv_buffer_bus); iounmap(lynx->registers); fail_deallocate_lynx: diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 3aa489dba30a..2a7687911c09 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1034,24 +1034,32 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) /* add svc client device(s) */ svc = devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL); - if (!svc) - return -ENOMEM; + if (!svc) { + ret = -ENOMEM; + goto err_free_kfifo; + } svc->stratix10_svc_rsu = platform_device_alloc(STRATIX10_RSU, 0); if (!svc->stratix10_svc_rsu) { dev_err(dev, "failed to allocate %s device\n", STRATIX10_RSU); - return -ENOMEM; + ret = -ENOMEM; + goto err_free_kfifo; } ret = platform_device_add(svc->stratix10_svc_rsu); - if (ret) { - platform_device_put(svc->stratix10_svc_rsu); - return ret; - } + if (ret) + goto err_put_device; + dev_set_drvdata(dev, svc); pr_info("Intel Service Layer Driver Initialized\n"); + return 0; + +err_put_device: + platform_device_put(svc->stratix10_svc_rsu); +err_free_kfifo: + kfifo_free(&controller->svc_fifo); return ret; } diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 33e15058d0dc..8cd454ee20c0 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -7,7 +7,7 @@ menuconfig FPGA tristate "FPGA Configuration Framework" help Say Y here if you want support for configuring FPGAs from the - kernel. The FPGA framework adds a FPGA manager class and FPGA + kernel. The FPGA framework adds an FPGA manager class and FPGA manager drivers. if FPGA @@ -134,7 +134,7 @@ config FPGA_REGION tristate "FPGA Region" depends on FPGA_BRIDGE help - FPGA Region common code. A FPGA Region controls a FPGA Manager + FPGA Region common code. An FPGA Region controls an FPGA Manager and the FPGA Bridges associated with either a reconfigurable region of an FPGA or a whole FPGA. diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c index 5b130c4d9882..dfdf21ed34c4 100644 --- a/drivers/fpga/altera-pr-ip-core.c +++ b/drivers/fpga/altera-pr-ip-core.c @@ -199,16 +199,6 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base) } EXPORT_SYMBOL_GPL(alt_pr_register); -void alt_pr_unregister(struct device *dev) -{ - struct fpga_manager *mgr = dev_get_drvdata(dev); - - dev_dbg(dev, "%s\n", __func__); - - fpga_mgr_unregister(mgr); -} -EXPORT_SYMBOL_GPL(alt_pr_unregister); - MODULE_AUTHOR("Matthew Gerlach <matthew.gerlach@linux.intel.com>"); MODULE_DESCRIPTION("Altera Partial Reconfiguration IP Core"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index e9266b2a357f..2bfb2ff86930 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -85,14 +85,14 @@ err_dev: } /** - * of_fpga_bridge_get - get an exclusive reference to a fpga bridge + * of_fpga_bridge_get - get an exclusive reference to an fpga bridge * - * @np: node pointer of a FPGA bridge + * @np: node pointer of an FPGA bridge * @info: fpga image specific information * * Return fpga_bridge struct if successful. * Return -EBUSY if someone already has a reference to the bridge. - * Return -ENODEV if @np is not a FPGA Bridge. + * Return -ENODEV if @np is not an FPGA Bridge. */ struct fpga_bridge *of_fpga_bridge_get(struct device_node *np, struct fpga_image_info *info) @@ -113,11 +113,11 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data) } /** - * fpga_bridge_get - get an exclusive reference to a fpga bridge + * fpga_bridge_get - get an exclusive reference to an fpga bridge * @dev: parent device that fpga bridge was registered with * @info: fpga manager info * - * Given a device, get an exclusive reference to a fpga bridge. + * Given a device, get an exclusive reference to an fpga bridge. * * Return: fpga bridge struct or IS_ERR() condition containing error code. */ @@ -224,7 +224,7 @@ EXPORT_SYMBOL_GPL(fpga_bridges_put); /** * of_fpga_bridge_get_to_list - get a bridge, add it to a list * - * @np: node pointer of a FPGA bridge + * @np: node pointer of an FPGA bridge * @info: fpga image specific information * @bridge_list: list of FPGA bridges * @@ -313,7 +313,7 @@ ATTRIBUTE_GROUPS(fpga_bridge); /** * fpga_bridge_create - create and initialize a struct fpga_bridge - * @dev: FPGA bridge device from pdev + * @parent: FPGA bridge device from pdev * @name: FPGA bridge name * @br_ops: pointer to structure of fpga bridge ops * @priv: FPGA bridge private data @@ -323,7 +323,7 @@ ATTRIBUTE_GROUPS(fpga_bridge); * * Return: struct fpga_bridge or NULL */ -struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, +struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name, const struct fpga_bridge_ops *br_ops, void *priv) { @@ -331,7 +331,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, int id, ret; if (!name || !strlen(name)) { - dev_err(dev, "Attempt to register with no name!\n"); + dev_err(parent, "Attempt to register with no name!\n"); return NULL; } @@ -353,8 +353,8 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, device_initialize(&bridge->dev); bridge->dev.groups = br_ops->groups; bridge->dev.class = fpga_bridge_class; - bridge->dev.parent = dev; - bridge->dev.of_node = dev->of_node; + bridge->dev.parent = parent; + bridge->dev.of_node = parent->of_node; bridge->dev.id = id; ret = dev_set_name(&bridge->dev, "br%d", id); @@ -373,7 +373,7 @@ error_kfree: EXPORT_SYMBOL_GPL(fpga_bridge_create); /** - * fpga_bridge_free - free a fpga bridge created by fpga_bridge_create() + * fpga_bridge_free - free an fpga bridge created by fpga_bridge_create() * @bridge: FPGA bridge struct */ void fpga_bridge_free(struct fpga_bridge *bridge) @@ -392,12 +392,12 @@ static void devm_fpga_bridge_release(struct device *dev, void *res) /** * devm_fpga_bridge_create - create and init a managed struct fpga_bridge - * @dev: FPGA bridge device from pdev + * @parent: FPGA bridge device from pdev * @name: FPGA bridge name * @br_ops: pointer to structure of fpga bridge ops * @priv: FPGA bridge private data * - * This function is intended for use in a FPGA bridge driver's probe function. + * This function is intended for use in an FPGA bridge driver's probe function. * After the bridge driver creates the struct with devm_fpga_bridge_create(), it * should register the bridge with fpga_bridge_register(). The bridge driver's * remove function should call fpga_bridge_unregister(). The bridge struct @@ -408,7 +408,7 @@ static void devm_fpga_bridge_release(struct device *dev, void *res) * Return: struct fpga_bridge or NULL */ struct fpga_bridge -*devm_fpga_bridge_create(struct device *dev, const char *name, +*devm_fpga_bridge_create(struct device *parent, const char *name, const struct fpga_bridge_ops *br_ops, void *priv) { struct fpga_bridge **ptr, *bridge; @@ -417,12 +417,12 @@ struct fpga_bridge if (!ptr) return NULL; - bridge = fpga_bridge_create(dev, name, br_ops, priv); + bridge = fpga_bridge_create(parent, name, br_ops, priv); if (!bridge) { devres_free(ptr); } else { *ptr = bridge; - devres_add(dev, ptr); + devres_add(parent, ptr); } return bridge; @@ -430,7 +430,7 @@ struct fpga_bridge EXPORT_SYMBOL_GPL(devm_fpga_bridge_create); /** - * fpga_bridge_register - register a FPGA bridge + * fpga_bridge_register - register an FPGA bridge * * @bridge: FPGA bridge struct * @@ -454,11 +454,11 @@ int fpga_bridge_register(struct fpga_bridge *bridge) EXPORT_SYMBOL_GPL(fpga_bridge_register); /** - * fpga_bridge_unregister - unregister a FPGA bridge + * fpga_bridge_unregister - unregister an FPGA bridge * * @bridge: FPGA bridge struct * - * This function is intended for use in a FPGA bridge driver's remove function. + * This function is intended for use in an FPGA bridge driver's remove function. */ void fpga_bridge_unregister(struct fpga_bridge *bridge) { diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index b85bc47c91a9..ecb4c3c795fa 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -26,7 +26,7 @@ struct fpga_mgr_devres { }; /** - * fpga_image_info_alloc - Allocate a FPGA image info struct + * fpga_image_info_alloc - Allocate an FPGA image info struct * @dev: owning device * * Return: struct fpga_image_info or NULL @@ -50,7 +50,7 @@ struct fpga_image_info *fpga_image_info_alloc(struct device *dev) EXPORT_SYMBOL_GPL(fpga_image_info_alloc); /** - * fpga_image_info_free - Free a FPGA image info struct + * fpga_image_info_free - Free an FPGA image info struct * @info: FPGA image info struct to free */ void fpga_image_info_free(struct fpga_image_info *info) @@ -470,7 +470,7 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data) } /** - * fpga_mgr_get - Given a device, get a reference to a fpga mgr. + * fpga_mgr_get - Given a device, get a reference to an fpga mgr. * @dev: parent device that fpga mgr was registered with * * Return: fpga manager struct or IS_ERR() condition containing error code. @@ -487,7 +487,7 @@ struct fpga_manager *fpga_mgr_get(struct device *dev) EXPORT_SYMBOL_GPL(fpga_mgr_get); /** - * of_fpga_mgr_get - Given a device node, get a reference to a fpga mgr. + * of_fpga_mgr_get - Given a device node, get a reference to an fpga mgr. * * @node: device node * @@ -506,7 +506,7 @@ struct fpga_manager *of_fpga_mgr_get(struct device_node *node) EXPORT_SYMBOL_GPL(of_fpga_mgr_get); /** - * fpga_mgr_put - release a reference to a fpga manager + * fpga_mgr_put - release a reference to an fpga manager * @mgr: fpga manager structure */ void fpga_mgr_put(struct fpga_manager *mgr) @@ -550,8 +550,8 @@ void fpga_mgr_unlock(struct fpga_manager *mgr) EXPORT_SYMBOL_GPL(fpga_mgr_unlock); /** - * fpga_mgr_create - create and initialize a FPGA manager struct - * @dev: fpga manager device from pdev + * fpga_mgr_create - create and initialize an FPGA manager struct + * @parent: fpga manager device from pdev * @name: fpga manager name * @mops: pointer to structure of fpga manager ops * @priv: fpga manager private data @@ -561,7 +561,7 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock); * * Return: pointer to struct fpga_manager or NULL */ -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, +struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name, const struct fpga_manager_ops *mops, void *priv) { @@ -571,12 +571,12 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, if (!mops || !mops->write_complete || !mops->state || !mops->write_init || (!mops->write && !mops->write_sg) || (mops->write && mops->write_sg)) { - dev_err(dev, "Attempt to register without fpga_manager_ops\n"); + dev_err(parent, "Attempt to register without fpga_manager_ops\n"); return NULL; } if (!name || !strlen(name)) { - dev_err(dev, "Attempt to register with no name!\n"); + dev_err(parent, "Attempt to register with no name!\n"); return NULL; } @@ -597,8 +597,8 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, device_initialize(&mgr->dev); mgr->dev.class = fpga_mgr_class; mgr->dev.groups = mops->groups; - mgr->dev.parent = dev; - mgr->dev.of_node = dev->of_node; + mgr->dev.parent = parent; + mgr->dev.of_node = parent->of_node; mgr->dev.id = id; ret = dev_set_name(&mgr->dev, "fpga%d", id); @@ -617,7 +617,7 @@ error_kfree: EXPORT_SYMBOL_GPL(fpga_mgr_create); /** - * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create() + * fpga_mgr_free - free an FPGA manager created with fpga_mgr_create() * @mgr: fpga manager struct */ void fpga_mgr_free(struct fpga_manager *mgr) @@ -636,12 +636,12 @@ static void devm_fpga_mgr_release(struct device *dev, void *res) /** * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct - * @dev: fpga manager device from pdev + * @parent: fpga manager device from pdev * @name: fpga manager name * @mops: pointer to structure of fpga manager ops * @priv: fpga manager private data * - * This function is intended for use in a FPGA manager driver's probe function. + * This function is intended for use in an FPGA manager driver's probe function. * After the manager driver creates the manager struct with * devm_fpga_mgr_create(), it should register it with fpga_mgr_register(). The * manager driver's remove function should call fpga_mgr_unregister(). The @@ -651,7 +651,7 @@ static void devm_fpga_mgr_release(struct device *dev, void *res) * * Return: pointer to struct fpga_manager or NULL */ -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name, +struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name, const struct fpga_manager_ops *mops, void *priv) { @@ -661,20 +661,20 @@ struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name, if (!dr) return NULL; - dr->mgr = fpga_mgr_create(dev, name, mops, priv); + dr->mgr = fpga_mgr_create(parent, name, mops, priv); if (!dr->mgr) { devres_free(dr); return NULL; } - devres_add(dev, dr); + devres_add(parent, dr); return dr->mgr; } EXPORT_SYMBOL_GPL(devm_fpga_mgr_create); /** - * fpga_mgr_register - register a FPGA manager + * fpga_mgr_register - register an FPGA manager * @mgr: fpga manager struct * * Return: 0 on success, negative error code otherwise. @@ -706,10 +706,10 @@ error_device: EXPORT_SYMBOL_GPL(fpga_mgr_register); /** - * fpga_mgr_unregister - unregister a FPGA manager + * fpga_mgr_unregister - unregister an FPGA manager * @mgr: fpga manager struct * - * This function is intended for use in a FPGA manager driver's remove function. + * This function is intended for use in an FPGA manager driver's remove function. */ void fpga_mgr_unregister(struct fpga_manager *mgr) { diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c index c3134b89c3fe..a4838715221f 100644 --- a/drivers/fpga/fpga-region.c +++ b/drivers/fpga/fpga-region.c @@ -33,14 +33,14 @@ struct fpga_region *fpga_region_class_find( EXPORT_SYMBOL_GPL(fpga_region_class_find); /** - * fpga_region_get - get an exclusive reference to a fpga region + * fpga_region_get - get an exclusive reference to an fpga region * @region: FPGA Region struct * * Caller should call fpga_region_put() when done with region. * * Return fpga_region struct if successful. * Return -EBUSY if someone already has a reference to the region. - * Return -ENODEV if @np is not a FPGA Region. + * Return -ENODEV if @np is not an FPGA Region. */ static struct fpga_region *fpga_region_get(struct fpga_region *region) { @@ -181,7 +181,7 @@ ATTRIBUTE_GROUPS(fpga_region); /** * fpga_region_create - alloc and init a struct fpga_region - * @dev: device parent + * @parent: device parent * @mgr: manager that programs this region * @get_bridges: optional function to get bridges to a list * @@ -192,7 +192,7 @@ ATTRIBUTE_GROUPS(fpga_region); * Return: struct fpga_region or NULL */ struct fpga_region -*fpga_region_create(struct device *dev, +*fpga_region_create(struct device *parent, struct fpga_manager *mgr, int (*get_bridges)(struct fpga_region *)) { @@ -214,8 +214,8 @@ struct fpga_region device_initialize(®ion->dev); region->dev.class = fpga_region_class; - region->dev.parent = dev; - region->dev.of_node = dev->of_node; + region->dev.parent = parent; + region->dev.of_node = parent->of_node; region->dev.id = id; ret = dev_set_name(®ion->dev, "region%d", id); @@ -234,7 +234,7 @@ err_free: EXPORT_SYMBOL_GPL(fpga_region_create); /** - * fpga_region_free - free a FPGA region created by fpga_region_create() + * fpga_region_free - free an FPGA region created by fpga_region_create() * @region: FPGA region */ void fpga_region_free(struct fpga_region *region) @@ -253,11 +253,11 @@ static void devm_fpga_region_release(struct device *dev, void *res) /** * devm_fpga_region_create - create and initialize a managed FPGA region struct - * @dev: device parent + * @parent: device parent * @mgr: manager that programs this region * @get_bridges: optional function to get bridges to a list * - * This function is intended for use in a FPGA region driver's probe function. + * This function is intended for use in an FPGA region driver's probe function. * After the region driver creates the region struct with * devm_fpga_region_create(), it should register it with fpga_region_register(). * The region driver's remove function should call fpga_region_unregister(). @@ -268,7 +268,7 @@ static void devm_fpga_region_release(struct device *dev, void *res) * Return: struct fpga_region or NULL */ struct fpga_region -*devm_fpga_region_create(struct device *dev, +*devm_fpga_region_create(struct device *parent, struct fpga_manager *mgr, int (*get_bridges)(struct fpga_region *)) { @@ -278,12 +278,12 @@ struct fpga_region if (!ptr) return NULL; - region = fpga_region_create(dev, mgr, get_bridges); + region = fpga_region_create(parent, mgr, get_bridges); if (!region) { devres_free(ptr); } else { *ptr = region; - devres_add(dev, ptr); + devres_add(parent, ptr); } return region; @@ -291,7 +291,7 @@ struct fpga_region EXPORT_SYMBOL_GPL(devm_fpga_region_create); /** - * fpga_region_register - register a FPGA region + * fpga_region_register - register an FPGA region * @region: FPGA region * * Return: 0 or -errno @@ -303,10 +303,10 @@ int fpga_region_register(struct fpga_region *region) EXPORT_SYMBOL_GPL(fpga_region_register); /** - * fpga_region_unregister - unregister a FPGA region + * fpga_region_unregister - unregister an FPGA region * @region: FPGA region * - * This function is intended for use in a FPGA region driver's remove function. + * This function is intended for use in an FPGA region driver's remove function. */ void fpga_region_unregister(struct fpga_region *region) { diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c index 114a64d2b7a4..1afb41aa20d7 100644 --- a/drivers/fpga/machxo2-spi.c +++ b/drivers/fpga/machxo2-spi.c @@ -374,11 +374,13 @@ static int machxo2_spi_probe(struct spi_device *spi) return devm_fpga_mgr_register(dev, mgr); } +#ifdef CONFIG_OF static const struct of_device_id of_match[] = { { .compatible = "lattice,machxo2-slave-spi", }, {} }; MODULE_DEVICE_TABLE(of, of_match); +#endif static const struct spi_device_id lattice_ids[] = { { "machxo2-slave-spi", 0 }, diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index e405309baadc..e3c25576b6b9 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -181,7 +181,7 @@ static int child_regions_with_firmware(struct device_node *overlay) * @region: FPGA region * @overlay: overlay applied to the FPGA region * - * Given an overlay applied to a FPGA region, parse the FPGA image specific + * Given an overlay applied to an FPGA region, parse the FPGA image specific * info in the overlay and do some checking. * * Returns: @@ -273,7 +273,7 @@ ret_no_info: * @region: FPGA region that the overlay was applied to * @nd: overlay notification data * - * Called when an overlay targeted to a FPGA Region is about to be applied. + * Called when an overlay targeted to an FPGA Region is about to be applied. * Parses the overlay for properties that influence how the FPGA will be * programmed and does some checking. If the checks pass, programs the FPGA. * If the checks fail, overlay is rejected and does not get added to the @@ -336,8 +336,8 @@ static void of_fpga_region_notify_post_remove(struct fpga_region *region, * @action: notifier action * @arg: reconfig data * - * This notifier handles programming a FPGA when a "firmware-name" property is - * added to a fpga-region. + * This notifier handles programming an FPGA when a "firmware-name" property is + * added to an fpga-region. * * Returns NOTIFY_OK or error if FPGA programming fails. */ diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 657a70c5fc99..a2cea500f7cc 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -271,7 +271,7 @@ static int s10_send_buf(struct fpga_manager *mgr, const char *buf, size_t count) } /* - * Send a FPGA image to privileged layers to write to the FPGA. When done + * Send an FPGA image to privileged layers to write to the FPGA. When done * sending, free all service layer buffers we allocated in write_init. */ static int s10_ops_write(struct fpga_manager *mgr, const char *buf, @@ -454,6 +454,7 @@ static int s10_remove(struct platform_device *pdev) struct s10_priv *priv = mgr->priv; fpga_mgr_unregister(mgr); + fpga_mgr_free(mgr); stratix10_svc_free_channel(priv->chan); return 0; diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 4e60e84cd17a..59ddc9fd5bca 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -724,7 +724,7 @@ static ssize_t cfam_read(struct file *filep, char __user *buf, size_t count, rc = count; fail: *offset = off; - return count; + return rc; } static ssize_t cfam_write(struct file *filep, const char __user *buf, @@ -761,7 +761,7 @@ static ssize_t cfam_write(struct file *filep, const char __user *buf, rc = count; fail: *offset = off; - return count; + return rc; } static loff_t cfam_llseek(struct file *file, loff_t offset, int whence) diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c index 90dbe58ca1ed..8606e55c1721 100644 --- a/drivers/fsi/fsi-master-aspeed.c +++ b/drivers/fsi/fsi-master-aspeed.c @@ -92,7 +92,7 @@ static const u32 fsi_base = 0xa0000000; static u16 aspeed_fsi_divisor = FSI_DIVISOR_DEFAULT; module_param_named(bus_div,aspeed_fsi_divisor, ushort, 0); -#define OPB_POLL_TIMEOUT 10000 +#define OPB_POLL_TIMEOUT 500 static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr, u32 val, u32 transfer_size) @@ -101,11 +101,15 @@ static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr, u32 reg, status; int ret; - writel(CMD_WRITE, base + OPB0_RW); - writel(transfer_size, base + OPB0_XFER_SIZE); - writel(addr, base + OPB0_FSI_ADDR); - writel(val, base + OPB0_FSI_DATA_W); - writel(0x1, base + OPB_IRQ_CLEAR); + /* + * The ordering of these writes up until the trigger + * write does not matter, so use writel_relaxed. + */ + writel_relaxed(CMD_WRITE, base + OPB0_RW); + writel_relaxed(transfer_size, base + OPB0_XFER_SIZE); + writel_relaxed(addr, base + OPB0_FSI_ADDR); + writel_relaxed(val, base + OPB0_FSI_DATA_W); + writel_relaxed(0x1, base + OPB_IRQ_CLEAR); writel(0x1, base + OPB_TRIGGER); ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg, @@ -149,10 +153,14 @@ static int __opb_read(struct fsi_master_aspeed *aspeed, uint32_t addr, u32 result, reg; int status, ret; - writel(CMD_READ, base + OPB0_RW); - writel(transfer_size, base + OPB0_XFER_SIZE); - writel(addr, base + OPB0_FSI_ADDR); - writel(0x1, base + OPB_IRQ_CLEAR); + /* + * The ordering of these writes up until the trigger + * write does not matter, so use writel_relaxed. + */ + writel_relaxed(CMD_READ, base + OPB0_RW); + writel_relaxed(transfer_size, base + OPB0_XFER_SIZE); + writel_relaxed(addr, base + OPB0_FSI_ADDR); + writel_relaxed(0x1, base + OPB_IRQ_CLEAR); writel(0x1, base + OPB_TRIGGER); ret = readl_poll_timeout(base + OPB_IRQ_STATUS, reg, @@ -525,7 +533,6 @@ static int tacoma_cabled_fsi_fixup(struct device *dev) static int fsi_master_aspeed_probe(struct platform_device *pdev) { struct fsi_master_aspeed *aspeed; - struct resource *res; int rc, links, reg; __be32 raw; @@ -541,8 +548,7 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev) aspeed->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - aspeed->base = devm_ioremap_resource(&pdev->dev, res); + aspeed->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(aspeed->base)) return PTR_ERR(aspeed->base); @@ -645,6 +651,7 @@ static const struct of_device_id fsi_master_aspeed_match[] = { { .compatible = "aspeed,ast2600-fsi-master" }, { }, }; +MODULE_DEVICE_TABLE(of, fsi_master_aspeed_match); static struct platform_driver fsi_master_aspeed_driver = { .driver = { diff --git a/drivers/fsi/fsi-master-ast-cf.c b/drivers/fsi/fsi-master-ast-cf.c index 57a779a89b07..24292acdbaf8 100644 --- a/drivers/fsi/fsi-master-ast-cf.c +++ b/drivers/fsi/fsi-master-ast-cf.c @@ -1309,7 +1309,6 @@ static int fsi_master_acf_probe(struct platform_device *pdev) master->cf_mem = devm_ioremap_resource(&pdev->dev, &res); if (IS_ERR(master->cf_mem)) { rc = PTR_ERR(master->cf_mem); - dev_err(&pdev->dev, "Error %d mapping coldfire memory\n", rc); goto err_free; } dev_dbg(&pdev->dev, "DRAM allocation @%x\n", master->cf_mem_addr); @@ -1427,6 +1426,7 @@ static const struct of_device_id fsi_master_acf_match[] = { { .compatible = "aspeed,ast2500-cf-fsi-master" }, { }, }; +MODULE_DEVICE_TABLE(of, fsi_master_acf_match); static struct platform_driver fsi_master_acf = { .driver = { diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c index aa97c4a250cb..7d5f29b4b595 100644 --- a/drivers/fsi/fsi-master-gpio.c +++ b/drivers/fsi/fsi-master-gpio.c @@ -882,6 +882,7 @@ static const struct of_device_id fsi_master_gpio_match[] = { { .compatible = "fsi-master-gpio" }, { }, }; +MODULE_DEVICE_TABLE(of, fsi_master_gpio_match); static struct platform_driver fsi_master_gpio_driver = { .driver = { diff --git a/drivers/fsi/fsi-occ.c b/drivers/fsi/fsi-occ.c index 10ca2e290655..b223f0ef337b 100644 --- a/drivers/fsi/fsi-occ.c +++ b/drivers/fsi/fsi-occ.c @@ -223,7 +223,8 @@ static const struct file_operations occ_fops = { .release = occ_release, }; -static int occ_verify_checksum(struct occ_response *resp, u16 data_length) +static int occ_verify_checksum(struct occ *occ, struct occ_response *resp, + u16 data_length) { /* Fetch the two bytes after the data for the checksum. */ u16 checksum_resp = get_unaligned_be16(&resp->data[data_length]); @@ -238,8 +239,11 @@ static int occ_verify_checksum(struct occ_response *resp, u16 data_length) for (i = 0; i < data_length; ++i) checksum += resp->data[i]; - if (checksum != checksum_resp) + if (checksum != checksum_resp) { + dev_err(occ->dev, "Bad checksum: %04x!=%04x\n", checksum, + checksum_resp); return -EBADMSG; + } return 0; } @@ -495,6 +499,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, goto done; if (resp->return_status == OCC_RESP_CMD_IN_PRG || + resp->return_status == OCC_RESP_CRIT_INIT || resp->seq_no != seq_no) { rc = -ETIMEDOUT; @@ -532,7 +537,7 @@ int fsi_occ_submit(struct device *dev, const void *request, size_t req_len, } *resp_len = resp_data_length + 7; - rc = occ_verify_checksum(resp, resp_data_length); + rc = occ_verify_checksum(occ, resp, resp_data_length); done: mutex_unlock(&occ->occ_lock); @@ -635,6 +640,7 @@ static const struct of_device_id occ_match[] = { }, { }, }; +MODULE_DEVICE_TABLE(of, occ_match); static struct platform_driver occ_driver = { .driver = { diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index bfd5e5da8020..84cb965bfed5 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -325,7 +325,8 @@ static int sbefifo_up_write(struct sbefifo *sbefifo, __be32 word) static int sbefifo_request_reset(struct sbefifo *sbefifo) { struct device *dev = &sbefifo->fsi_dev->dev; - u32 status, timeout; + unsigned long end_time; + u32 status; int rc; dev_dbg(dev, "Requesting FIFO reset\n"); @@ -341,7 +342,8 @@ static int sbefifo_request_reset(struct sbefifo *sbefifo) } /* Wait for it to complete */ - for (timeout = 0; timeout < SBEFIFO_RESET_TIMEOUT; timeout++) { + end_time = jiffies + msecs_to_jiffies(SBEFIFO_RESET_TIMEOUT); + while (!time_after(jiffies, end_time)) { rc = sbefifo_regr(sbefifo, SBEFIFO_UP | SBEFIFO_STS, &status); if (rc) { dev_err(dev, "Failed to read UP fifo status during reset" @@ -355,7 +357,7 @@ static int sbefifo_request_reset(struct sbefifo *sbefifo) return 0; } - msleep(1); + cond_resched(); } dev_err(dev, "FIFO reset timed out\n"); @@ -400,7 +402,7 @@ static int sbefifo_cleanup_hw(struct sbefifo *sbefifo) /* The FIFO already contains a reset request from the SBE ? */ if (down_status & SBEFIFO_STS_RESET_REQ) { dev_info(dev, "Cleanup: FIFO reset request set, resetting\n"); - rc = sbefifo_regw(sbefifo, SBEFIFO_UP, SBEFIFO_PERFORM_RESET); + rc = sbefifo_regw(sbefifo, SBEFIFO_DOWN, SBEFIFO_PERFORM_RESET); if (rc) { sbefifo->broken = true; dev_err(dev, "Cleanup: Reset reg write failed, rc=%d\n", rc); diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c index b45bfab7b7f5..da1486bb6a14 100644 --- a/drivers/fsi/fsi-scom.c +++ b/drivers/fsi/fsi-scom.c @@ -38,9 +38,10 @@ #define SCOM_STATUS_PIB_RESP_MASK 0x00007000 #define SCOM_STATUS_PIB_RESP_SHIFT 12 -#define SCOM_STATUS_ANY_ERR (SCOM_STATUS_PROTECTION | \ - SCOM_STATUS_PARITY | \ - SCOM_STATUS_PIB_ABORT | \ +#define SCOM_STATUS_FSI2PIB_ERROR (SCOM_STATUS_PROTECTION | \ + SCOM_STATUS_PARITY | \ + SCOM_STATUS_PIB_ABORT) +#define SCOM_STATUS_ANY_ERR (SCOM_STATUS_FSI2PIB_ERROR | \ SCOM_STATUS_PIB_RESP_MASK) /* SCOM address encodings */ #define XSCOM_ADDR_IND_FLAG BIT_ULL(63) @@ -60,7 +61,6 @@ #define XSCOM_ADDR_FORM1_HI_SHIFT 20 /* Retries */ -#define SCOM_MAX_RETRIES 100 /* Retries on busy */ #define SCOM_MAX_IND_RETRIES 10 /* Retries indirect not ready */ struct scom_device { @@ -240,14 +240,15 @@ static int handle_fsi2pib_status(struct scom_device *scom, uint32_t status) { uint32_t dummy = -1; - if (status & SCOM_STATUS_PROTECTION) - return -EPERM; - if (status & SCOM_STATUS_PARITY) { + if (status & SCOM_STATUS_FSI2PIB_ERROR) fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy, sizeof(uint32_t)); + + if (status & SCOM_STATUS_PROTECTION) + return -EPERM; + if (status & SCOM_STATUS_PARITY) return -EIO; - } - /* Return -EBUSY on PIB abort to force a retry */ + if (status & SCOM_STATUS_PIB_ABORT) return -EBUSY; return 0; @@ -284,69 +285,39 @@ static int handle_pib_status(struct scom_device *scom, uint8_t status) static int put_scom(struct scom_device *scom, uint64_t value, uint64_t addr) { - uint32_t status, dummy = -1; - int rc, retries; - - for (retries = 0; retries < SCOM_MAX_RETRIES; retries++) { - rc = raw_put_scom(scom, value, addr, &status); - if (rc) { - /* Try resetting the bridge if FSI fails */ - if (rc != -ENODEV && retries == 0) { - fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, - &dummy, sizeof(uint32_t)); - rc = -EBUSY; - } else - return rc; - } else - rc = handle_fsi2pib_status(scom, status); - if (rc && rc != -EBUSY) - break; - if (rc == 0) { - rc = handle_pib_status(scom, - (status & SCOM_STATUS_PIB_RESP_MASK) - >> SCOM_STATUS_PIB_RESP_SHIFT); - if (rc && rc != -EBUSY) - break; - } - if (rc == 0) - break; - msleep(1); - } - return rc; + uint32_t status; + int rc; + + rc = raw_put_scom(scom, value, addr, &status); + if (rc == -ENODEV) + return rc; + + rc = handle_fsi2pib_status(scom, status); + if (rc) + return rc; + + return handle_pib_status(scom, + (status & SCOM_STATUS_PIB_RESP_MASK) + >> SCOM_STATUS_PIB_RESP_SHIFT); } static int get_scom(struct scom_device *scom, uint64_t *value, uint64_t addr) { - uint32_t status, dummy = -1; - int rc, retries; - - for (retries = 0; retries < SCOM_MAX_RETRIES; retries++) { - rc = raw_get_scom(scom, value, addr, &status); - if (rc) { - /* Try resetting the bridge if FSI fails */ - if (rc != -ENODEV && retries == 0) { - fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, - &dummy, sizeof(uint32_t)); - rc = -EBUSY; - } else - return rc; - } else - rc = handle_fsi2pib_status(scom, status); - if (rc && rc != -EBUSY) - break; - if (rc == 0) { - rc = handle_pib_status(scom, - (status & SCOM_STATUS_PIB_RESP_MASK) - >> SCOM_STATUS_PIB_RESP_SHIFT); - if (rc && rc != -EBUSY) - break; - } - if (rc == 0) - break; - msleep(1); - } - return rc; + uint32_t status; + int rc; + + rc = raw_get_scom(scom, value, addr, &status); + if (rc == -ENODEV) + return rc; + + rc = handle_fsi2pib_status(scom, status); + if (rc) + return rc; + + return handle_pib_status(scom, + (status & SCOM_STATUS_PIB_RESP_MASK) + >> SCOM_STATUS_PIB_RESP_SHIFT); } static ssize_t scom_read(struct file *filep, char __user *buf, size_t len, diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 967532afb1c0..0d68a78be980 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -1151,6 +1151,8 @@ int occ_setup(struct occ *occ, const char *name) { int rc; + /* start with 1 to avoid false match with zero-initialized SRAM buffer */ + occ->seq_no = 1; mutex_init(&occ->lock); occ->groups[0] = &occ->group; @@ -1160,8 +1162,9 @@ int occ_setup(struct occ *occ, const char *name) dev_info(occ->bus_dev, "host is not ready\n"); return rc; } else if (rc < 0) { - dev_err(occ->bus_dev, "failed to get OCC poll response: %d\n", - rc); + dev_err(occ->bus_dev, + "failed to get OCC poll response=%02x: %d\n", + occ->resp.return_status, rc); return rc; } diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6c68d34d956e..1002605db8ba 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -608,7 +608,7 @@ static struct coresight_device * coresight_find_enabled_sink(struct coresight_device *csdev) { int i; - struct coresight_device *sink; + struct coresight_device *sink = NULL; if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && @@ -886,7 +886,6 @@ void coresight_release_path(struct list_head *path) } kfree(path); - path = NULL; } /* return true if the device is a suitable type for a default sink */ @@ -1392,7 +1391,7 @@ static int coresight_fixup_device_conns(struct coresight_device *csdev) } } - return 0; + return ret; } static int coresight_remove_match(struct device *dev, void *data) @@ -1730,9 +1729,9 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict, if (idx < 0) { /* Make space for the new entry */ idx = dict->nr_idx; - list = krealloc(dict->fwnode_list, - (idx + 1) * sizeof(*dict->fwnode_list), - GFP_KERNEL); + list = krealloc_array(dict->fwnode_list, + idx + 1, sizeof(*dict->fwnode_list), + GFP_KERNEL); if (ZERO_OR_NULL_PTR(list)) { idx = -ENOMEM; goto done; diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index db881993c211..da27cd4a3c38 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -568,11 +568,6 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, struct etmv4_config *config = &drvdata->config; struct perf_event_attr *attr = &event->attr; - if (!attr) { - ret = -EINVAL; - goto out; - } - /* Clear configuration from previous run */ memset(config, 0, sizeof(struct etmv4_config)); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 45b85edfc690..cd0fb7bfba68 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -530,7 +530,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, buf_ptr = buf->data_pages[cur] + offset; *buf_ptr = readl_relaxed(drvdata->base + TMC_RRD); - if (lost && *barrier) { + if (lost && i < CORESIGHT_BARRIER_PKT_SIZE) { *buf_ptr = *barrier; barrier++; } diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 24d0c974bfd5..66eed2dff818 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -100,16 +100,18 @@ static int intel_th_remove(struct device *dev) struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); struct intel_th_device *thdev = to_intel_th_device(dev); struct intel_th_device *hub = to_intel_th_hub(thdev); - int err; if (thdev->type == INTEL_TH_SWITCH) { struct intel_th *th = to_intel_th(hub); int i, lowest; - /* disconnect outputs */ - err = device_for_each_child(dev, thdev, intel_th_child_remove); - if (err) - return err; + /* + * disconnect outputs + * + * intel_th_child_remove returns 0 unconditionally, so there is + * no need to check the return value of device_for_each_child. + */ + device_for_each_child(dev, thdev, intel_th_child_remove); /* * Remove outputs, that is, hub's children: they are created @@ -215,6 +217,22 @@ static ssize_t port_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(port); +static void intel_th_trace_prepare(struct intel_th_device *thdev) +{ + struct intel_th_device *hub = to_intel_th_hub(thdev); + struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); + + if (hub->type != INTEL_TH_SWITCH) + return; + + if (thdev->type != INTEL_TH_OUTPUT) + return; + + pm_runtime_get_sync(&thdev->dev); + hubdrv->prepare(hub, &thdev->output); + pm_runtime_put(&thdev->dev); +} + static int intel_th_output_activate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = @@ -235,6 +253,7 @@ static int intel_th_output_activate(struct intel_th_device *thdev) if (ret) goto fail_put; + intel_th_trace_prepare(thdev); if (thdrv->activate) ret = thdrv->activate(thdev); else diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 28509b02a0b5..b3308934a687 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -564,6 +564,21 @@ static void gth_tscu_resync(struct gth_device *gth) iowrite32(reg, gth->base + REG_TSCU_TSUCTRL); } +static void intel_th_gth_prepare(struct intel_th_device *thdev, + struct intel_th_output *output) +{ + struct gth_device *gth = dev_get_drvdata(&thdev->dev); + int count; + + /* + * Wait until the output port is in reset before we start + * programming it. + */ + for (count = GTH_PLE_WAITLOOP_DEPTH; + count && !(gth_output_get(gth, output->port) & BIT(5)); count--) + cpu_relax(); +} + /** * intel_th_gth_enable() - enable tracing to an output device * @thdev: GTH device @@ -815,6 +830,7 @@ static struct intel_th_driver intel_th_gth_driver = { .assign = intel_th_gth_assign, .unassign = intel_th_gth_unassign, .set_output = intel_th_gth_set_output, + .prepare = intel_th_gth_prepare, .enable = intel_th_gth_enable, .trig_switch = intel_th_gth_switch, .disable = intel_th_gth_disable, diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 89c67e0e1d34..0ffb42990175 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -143,6 +143,7 @@ intel_th_output_assigned(struct intel_th_device *thdev) * @remove: remove method * @assign: match a given output type device against available outputs * @unassign: deassociate an output type device from an output port + * @prepare: prepare output port for tracing * @enable: enable tracing for a given output device * @disable: disable tracing for a given output device * @irq: interrupt callback @@ -164,6 +165,8 @@ struct intel_th_driver { struct intel_th_device *othdev); void (*unassign)(struct intel_th_device *thdev, struct intel_th_device *othdev); + void (*prepare)(struct intel_th_device *thdev, + struct intel_th_output *output); void (*enable)(struct intel_th_device *thdev, struct intel_th_output *output); void (*trig_switch)(struct intel_th_device *thdev, diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 2edc4666633d..432ade0842f6 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -1024,33 +1024,49 @@ err_nomem: } #ifdef CONFIG_X86 -static void msc_buffer_set_uc(struct msc_window *win, unsigned int nr_segs) +static void msc_buffer_set_uc(struct msc *msc) { struct scatterlist *sg_ptr; + struct msc_window *win; int i; - for_each_sg(win->sgt->sgl, sg_ptr, nr_segs, i) { - /* Set the page as uncached */ - set_memory_uc((unsigned long)sg_virt(sg_ptr), - PFN_DOWN(sg_ptr->length)); + if (msc->mode == MSC_MODE_SINGLE) { + set_memory_uc((unsigned long)msc->base, msc->nr_pages); + return; + } + + list_for_each_entry(win, &msc->win_list, entry) { + for_each_sg(win->sgt->sgl, sg_ptr, win->nr_segs, i) { + /* Set the page as uncached */ + set_memory_uc((unsigned long)sg_virt(sg_ptr), + PFN_DOWN(sg_ptr->length)); + } } } -static void msc_buffer_set_wb(struct msc_window *win) +static void msc_buffer_set_wb(struct msc *msc) { struct scatterlist *sg_ptr; + struct msc_window *win; int i; - for_each_sg(win->sgt->sgl, sg_ptr, win->nr_segs, i) { - /* Reset the page to write-back */ - set_memory_wb((unsigned long)sg_virt(sg_ptr), - PFN_DOWN(sg_ptr->length)); + if (msc->mode == MSC_MODE_SINGLE) { + set_memory_wb((unsigned long)msc->base, msc->nr_pages); + return; + } + + list_for_each_entry(win, &msc->win_list, entry) { + for_each_sg(win->sgt->sgl, sg_ptr, win->nr_segs, i) { + /* Reset the page to write-back */ + set_memory_wb((unsigned long)sg_virt(sg_ptr), + PFN_DOWN(sg_ptr->length)); + } } } #else /* !X86 */ static inline void -msc_buffer_set_uc(struct msc_window *win, unsigned int nr_segs) {} -static inline void msc_buffer_set_wb(struct msc_window *win) {} +msc_buffer_set_uc(struct msc *msc) {} +static inline void msc_buffer_set_wb(struct msc *msc) {} #endif /* CONFIG_X86 */ /** @@ -1097,8 +1113,6 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) if (ret <= 0) goto err_nomem; - msc_buffer_set_uc(win, ret); - win->nr_segs = ret; win->nr_blocks = nr_blocks; @@ -1152,8 +1166,6 @@ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win) msc->base_addr = 0; } - msc_buffer_set_wb(win); - if (msc->mbuf && msc->mbuf->free_window) msc->mbuf->free_window(msc->mbuf_priv, win->sgt); else @@ -1260,6 +1272,8 @@ static int msc_buffer_multi_alloc(struct msc *msc, unsigned long *nr_pages, */ static void msc_buffer_free(struct msc *msc) { + msc_buffer_set_wb(msc); + if (msc->mode == MSC_MODE_SINGLE) msc_buffer_contig_free(msc); else if (msc->mode == MSC_MODE_MULTI) @@ -1303,6 +1317,8 @@ static int msc_buffer_alloc(struct msc *msc, unsigned long *nr_pages, } if (!ret) { + msc_buffer_set_uc(msc); + /* allocation should be visible before the counter goes to 0 */ smp_mb__before_atomic(); diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index cdb3e11462c6..0d7a2500d0b8 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -74,6 +74,15 @@ config INTERCONNECT_QCOM_SC7180 This is a driver for the Qualcomm Network-on-Chip on sc7180-based platforms. +config INTERCONNECT_QCOM_SC7280 + tristate "Qualcomm SC7280 interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on sc7280-based + platforms. + config INTERCONNECT_QCOM_SDM660 tristate "Qualcomm SDM660 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 46fc62447156..2880129a6fe4 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -8,6 +8,7 @@ icc-osm-l3-objs := osm-l3.o qnoc-qcs404-objs := qcs404.o icc-rpmh-obj := icc-rpmh.o qnoc-sc7180-objs := sc7180.o +qnoc-sc7280-objs := sc7280.o qnoc-sdm660-objs := sdm660.o qnoc-sdm845-objs := sdm845.o qnoc-sdx55-objs := sdx55.o @@ -24,6 +25,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o obj-$(CONFIG_INTERCONNECT_QCOM_SC7180) += qnoc-sc7180.o +obj-$(CONFIG_INTERCONNECT_QCOM_SC7280) += qnoc-sc7280.o obj-$(CONFIG_INTERCONNECT_QCOM_SDM660) += qnoc-sdm660.o obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o obj-$(CONFIG_INTERCONNECT_QCOM_SDX55) += qnoc-sdx55.o diff --git a/drivers/interconnect/qcom/sc7280.c b/drivers/interconnect/qcom/sc7280.c new file mode 100644 index 000000000000..8d1b55c3705c --- /dev/null +++ b/drivers/interconnect/qcom/sc7280.c @@ -0,0 +1,1938 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + */ + +#include <linux/device.h> +#include <linux/interconnect.h> +#include <linux/interconnect-provider.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <dt-bindings/interconnect/qcom,sc7280.h> + +#include "bcm-voter.h" +#include "icc-rpmh.h" +#include "sc7280.h" + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .id = SC7280_MASTER_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .id = SC7280_MASTER_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = SC7280_MASTER_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_a1noc_cfg = { + .name = "qnm_a1noc_cfg", + .id = SC7280_MASTER_A1NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_SERVICE_A1NOC }, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .id = SC7280_MASTER_SDCC_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .id = SC7280_MASTER_SDCC_2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .id = SC7280_MASTER_SDCC_4, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .id = SC7280_MASTER_UFS_MEM, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb2 = { + .name = "xm_usb2", + .id = SC7280_MASTER_USB2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = SC7280_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = SC7280_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_a2noc_cfg = { + .name = "qnm_a2noc_cfg", + .id = SC7280_MASTER_A2NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_SERVICE_A2NOC }, +}; + +static struct qcom_icc_node qnm_cnoc_datapath = { + .name = "qnm_cnoc_datapath", + .id = SC7280_MASTER_CNOC_A2NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .id = SC7280_MASTER_CRYPTO, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .id = SC7280_MASTER_IPA, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .id = SC7280_MASTER_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .id = SC7280_MASTER_PCIE_1, + .channels = 1, + .buswidth = 8, + .links = { SC7280_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node xm_qdss_etr = { + .name = "xm_qdss_etr", + .id = SC7280_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = SC7280_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_QUP_CORE_0 }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .id = SC7280_MASTER_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_QUP_CORE_1 }, +}; + +static struct qcom_icc_node qnm_cnoc3_cnoc2 = { + .name = "qnm_cnoc3_cnoc2", + .id = SC7280_MASTER_CNOC3_CNOC2, + .channels = 1, + .buswidth = 8, + .num_links = 44, + .links = { SC7280_SLAVE_AHB2PHY_SOUTH, SC7280_SLAVE_AHB2PHY_NORTH, + SC7280_SLAVE_CAMERA_CFG, SC7280_SLAVE_CLK_CTL, + SC7280_SLAVE_CDSP_CFG, SC7280_SLAVE_RBCPR_CX_CFG, + SC7280_SLAVE_RBCPR_MX_CFG, SC7280_SLAVE_CRYPTO_0_CFG, + SC7280_SLAVE_CX_RDPM, SC7280_SLAVE_DCC_CFG, + SC7280_SLAVE_DISPLAY_CFG, SC7280_SLAVE_GFX3D_CFG, + SC7280_SLAVE_HWKM, SC7280_SLAVE_IMEM_CFG, + SC7280_SLAVE_IPA_CFG, SC7280_SLAVE_IPC_ROUTER_CFG, + SC7280_SLAVE_LPASS, SC7280_SLAVE_CNOC_MSS, + SC7280_SLAVE_MX_RDPM, SC7280_SLAVE_PCIE_0_CFG, + SC7280_SLAVE_PCIE_1_CFG, SC7280_SLAVE_PDM, + SC7280_SLAVE_PIMEM_CFG, SC7280_SLAVE_PKA_WRAPPER_CFG, + SC7280_SLAVE_PMU_WRAPPER_CFG, SC7280_SLAVE_QDSS_CFG, + SC7280_SLAVE_QSPI_0, SC7280_SLAVE_QUP_0, + SC7280_SLAVE_QUP_1, SC7280_SLAVE_SDCC_1, + SC7280_SLAVE_SDCC_2, SC7280_SLAVE_SDCC_4, + SC7280_SLAVE_SECURITY, SC7280_SLAVE_TCSR, + SC7280_SLAVE_TLMM, SC7280_SLAVE_UFS_MEM_CFG, + SC7280_SLAVE_USB2, SC7280_SLAVE_USB3_0, + SC7280_SLAVE_VENUS_CFG, SC7280_SLAVE_VSENSE_CTRL_CFG, + SC7280_SLAVE_A1NOC_CFG, SC7280_SLAVE_A2NOC_CFG, + SC7280_SLAVE_CNOC_MNOC_CFG, SC7280_SLAVE_SNOC_CFG }, +}; + +static struct qcom_icc_node xm_qdss_dap = { + .name = "xm_qdss_dap", + .id = SC7280_MASTER_QDSS_DAP, + .channels = 1, + .buswidth = 8, + .num_links = 45, + .links = { SC7280_SLAVE_AHB2PHY_SOUTH, SC7280_SLAVE_AHB2PHY_NORTH, + SC7280_SLAVE_CAMERA_CFG, SC7280_SLAVE_CLK_CTL, + SC7280_SLAVE_CDSP_CFG, SC7280_SLAVE_RBCPR_CX_CFG, + SC7280_SLAVE_RBCPR_MX_CFG, SC7280_SLAVE_CRYPTO_0_CFG, + SC7280_SLAVE_CX_RDPM, SC7280_SLAVE_DCC_CFG, + SC7280_SLAVE_DISPLAY_CFG, SC7280_SLAVE_GFX3D_CFG, + SC7280_SLAVE_HWKM, SC7280_SLAVE_IMEM_CFG, + SC7280_SLAVE_IPA_CFG, SC7280_SLAVE_IPC_ROUTER_CFG, + SC7280_SLAVE_LPASS, SC7280_SLAVE_CNOC_MSS, + SC7280_SLAVE_MX_RDPM, SC7280_SLAVE_PCIE_0_CFG, + SC7280_SLAVE_PCIE_1_CFG, SC7280_SLAVE_PDM, + SC7280_SLAVE_PIMEM_CFG, SC7280_SLAVE_PKA_WRAPPER_CFG, + SC7280_SLAVE_PMU_WRAPPER_CFG, SC7280_SLAVE_QDSS_CFG, + SC7280_SLAVE_QSPI_0, SC7280_SLAVE_QUP_0, + SC7280_SLAVE_QUP_1, SC7280_SLAVE_SDCC_1, + SC7280_SLAVE_SDCC_2, SC7280_SLAVE_SDCC_4, + SC7280_SLAVE_SECURITY, SC7280_SLAVE_TCSR, + SC7280_SLAVE_TLMM, SC7280_SLAVE_UFS_MEM_CFG, + SC7280_SLAVE_USB2, SC7280_SLAVE_USB3_0, + SC7280_SLAVE_VENUS_CFG, SC7280_SLAVE_VSENSE_CTRL_CFG, + SC7280_SLAVE_A1NOC_CFG, SC7280_SLAVE_A2NOC_CFG, + SC7280_SLAVE_CNOC2_CNOC3, SC7280_SLAVE_CNOC_MNOC_CFG, + SC7280_SLAVE_SNOC_CFG }, +}; + +static struct qcom_icc_node qnm_cnoc2_cnoc3 = { + .name = "qnm_cnoc2_cnoc3", + .id = SC7280_MASTER_CNOC2_CNOC3, + .channels = 1, + .buswidth = 8, + .num_links = 9, + .links = { SC7280_SLAVE_AOSS, SC7280_SLAVE_APPSS, + SC7280_SLAVE_CNOC_A2NOC, SC7280_SLAVE_DDRSS_CFG, + SC7280_SLAVE_BOOT_IMEM, SC7280_SLAVE_IMEM, + SC7280_SLAVE_PIMEM, SC7280_SLAVE_QDSS_STM, + SC7280_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .id = SC7280_MASTER_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 9, + .links = { SC7280_SLAVE_AOSS, SC7280_SLAVE_APPSS, + SC7280_SLAVE_CNOC3_CNOC2, SC7280_SLAVE_DDRSS_CFG, + SC7280_SLAVE_BOOT_IMEM, SC7280_SLAVE_IMEM, + SC7280_SLAVE_PIMEM, SC7280_SLAVE_QDSS_STM, + SC7280_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = SC7280_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SC7280_SLAVE_PCIE_0, SC7280_SLAVE_PCIE_1 }, +}; + +static struct qcom_icc_node qnm_cnoc_dc_noc = { + .name = "qnm_cnoc_dc_noc", + .id = SC7280_MASTER_CNOC_DC_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 2, + .links = { SC7280_SLAVE_LLCC_CFG, SC7280_SLAVE_GEM_NOC_CFG }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .id = SC7280_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .id = SC7280_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .id = SC7280_MASTER_APPSS_PROC, + .channels = 1, + .buswidth = 32, + .num_links = 3, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC, + SC7280_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_cmpnoc = { + .name = "qnm_cmpnoc", + .id = SC7280_MASTER_COMPUTE_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_gemnoc_cfg = { + .name = "qnm_gemnoc_cfg", + .id = SC7280_MASTER_GEM_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 5, + .links = { SC7280_SLAVE_MSS_PROC_MS_MPU_CFG, SC7280_SLAVE_MCDMA_MS_MPU_CFG, + SC7280_SLAVE_SERVICE_GEM_NOC_1, SC7280_SLAVE_SERVICE_GEM_NOC_2, + SC7280_SLAVE_SERVICE_GEM_NOC }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = SC7280_MASTER_GFX3D, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = SC7280_MASTER_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = SC7280_MASTER_MNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 2, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .id = SC7280_MASTER_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 2, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_gc = { + .name = "qnm_snoc_gc", + .id = SC7280_MASTER_SNOC_GC_MEM_NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = SC7280_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SC7280_SLAVE_GEM_NOC_CNOC, SC7280_SLAVE_LLCC, + SC7280_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qhm_config_noc = { + .name = "qhm_config_noc", + .id = SC7280_MASTER_CNOC_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 6, + .links = { SC7280_SLAVE_LPASS_CORE_CFG, SC7280_SLAVE_LPASS_LPI_CFG, + SC7280_SLAVE_LPASS_MPU_CFG, SC7280_SLAVE_LPASS_TOP_CFG, + SC7280_SLAVE_SERVICES_LPASS_AML_NOC, SC7280_SLAVE_SERVICE_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = SC7280_MASTER_LLCC, + .channels = 2, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_EBI1 }, +}; + +static struct qcom_icc_node qnm_mnoc_cfg = { + .name = "qnm_mnoc_cfg", + .id = SC7280_MASTER_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_SERVICE_MNOC }, +}; + +static struct qcom_icc_node qnm_video0 = { + .name = "qnm_video0", + .id = SC7280_MASTER_VIDEO_P0, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cpu = { + .name = "qnm_video_cpu", + .id = SC7280_MASTER_VIDEO_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_camnoc_hf = { + .name = "qxm_camnoc_hf", + .id = SC7280_MASTER_CAMNOC_HF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_camnoc_icp = { + .name = "qxm_camnoc_icp", + .id = SC7280_MASTER_CAMNOC_ICP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_camnoc_sf = { + .name = "qxm_camnoc_sf", + .id = SC7280_MASTER_CAMNOC_SF, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qxm_mdp0 = { + .name = "qxm_mdp0", + .id = SC7280_MASTER_MDP0, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qhm_nsp_noc_config = { + .name = "qhm_nsp_noc_config", + .id = SC7280_MASTER_CDSP_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_SERVICE_NSP_NOC }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .id = SC7280_MASTER_CDSP_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_SLAVE_CDSP_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .id = SC7280_MASTER_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .id = SC7280_MASTER_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_snoc_cfg = { + .name = "qnm_snoc_cfg", + .id = SC7280_MASTER_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_SLAVE_SERVICE_SNOC }, +}; + +static struct qcom_icc_node qxm_pimem = { + .name = "qxm_pimem", + .id = SC7280_MASTER_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = SC7280_MASTER_GIC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .id = SC7280_SLAVE_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_MASTER_A1NOC_SNOC }, +}; + +static struct qcom_icc_node srvc_aggre1_noc = { + .name = "srvc_aggre1_noc", + .id = SC7280_SLAVE_SERVICE_A1NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .id = SC7280_SLAVE_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_MASTER_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .id = SC7280_SLAVE_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_MASTER_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node srvc_aggre2_noc = { + .name = "srvc_aggre2_noc", + .id = SC7280_SLAVE_SERVICE_A2NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = SC7280_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .id = SC7280_SLAVE_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .id = SC7280_SLAVE_AHB2PHY_SOUTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .id = SC7280_SLAVE_AHB2PHY_NORTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = SC7280_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = SC7280_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_compute_cfg = { + .name = "qhs_compute_cfg", + .id = SC7280_SLAVE_CDSP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_CDSP_NOC_CFG }, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .id = SC7280_SLAVE_RBCPR_CX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mx = { + .name = "qhs_cpr_mx", + .id = SC7280_SLAVE_RBCPR_MX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = SC7280_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cx_rdpm = { + .name = "qhs_cx_rdpm", + .id = SC7280_SLAVE_CX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_dcc_cfg = { + .name = "qhs_dcc_cfg", + .id = SC7280_SLAVE_DCC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .id = SC7280_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = SC7280_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_hwkm = { + .name = "qhs_hwkm", + .id = SC7280_SLAVE_HWKM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = SC7280_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .id = SC7280_SLAVE_IPA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .id = SC7280_SLAVE_IPC_ROUTER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_cfg = { + .name = "qhs_lpass_cfg", + .id = SC7280_SLAVE_LPASS, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_CNOC_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .id = SC7280_SLAVE_CNOC_MSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mx_rdpm = { + .name = "qhs_mx_rdpm", + .id = SC7280_SLAVE_MX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie0_cfg = { + .name = "qhs_pcie0_cfg", + .id = SC7280_SLAVE_PCIE_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie1_cfg = { + .name = "qhs_pcie1_cfg", + .id = SC7280_SLAVE_PCIE_1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .id = SC7280_SLAVE_PDM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pimem_cfg = { + .name = "qhs_pimem_cfg", + .id = SC7280_SLAVE_PIMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pka_wrapper_cfg = { + .name = "qhs_pka_wrapper_cfg", + .id = SC7280_SLAVE_PKA_WRAPPER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pmu_wrapper_cfg = { + .name = "qhs_pmu_wrapper_cfg", + .id = SC7280_SLAVE_PMU_WRAPPER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = SC7280_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .id = SC7280_SLAVE_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .id = SC7280_SLAVE_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = SC7280_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc1 = { + .name = "qhs_sdc1", + .id = SC7280_SLAVE_SDCC_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .id = SC7280_SLAVE_SDCC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .id = SC7280_SLAVE_SDCC_4, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_security = { + .name = "qhs_security", + .id = SC7280_SLAVE_SECURITY, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = SC7280_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = SC7280_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .id = SC7280_SLAVE_UFS_MEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb2 = { + .name = "qhs_usb2", + .id = SC7280_SLAVE_USB2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .id = SC7280_SLAVE_USB3_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = SC7280_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .id = SC7280_SLAVE_VSENSE_CTRL_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_a1_noc_cfg = { + .name = "qns_a1_noc_cfg", + .id = SC7280_SLAVE_A1NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_A1NOC_CFG }, +}; + +static struct qcom_icc_node qns_a2_noc_cfg = { + .name = "qns_a2_noc_cfg", + .id = SC7280_SLAVE_A2NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_A2NOC_CFG }, +}; + +static struct qcom_icc_node qns_cnoc2_cnoc3 = { + .name = "qns_cnoc2_cnoc3", + .id = SC7280_SLAVE_CNOC2_CNOC3, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_MASTER_CNOC2_CNOC3 }, +}; + +static struct qcom_icc_node qns_mnoc_cfg = { + .name = "qns_mnoc_cfg", + .id = SC7280_SLAVE_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_CNOC_MNOC_CFG }, +}; + +static struct qcom_icc_node qns_snoc_cfg = { + .name = "qns_snoc_cfg", + .id = SC7280_SLAVE_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_SNOC_CFG }, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = SC7280_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_apss = { + .name = "qhs_apss", + .id = SC7280_SLAVE_APPSS, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qns_cnoc3_cnoc2 = { + .name = "qns_cnoc3_cnoc2", + .id = SC7280_SLAVE_CNOC3_CNOC2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_MASTER_CNOC3_CNOC2 }, +}; + +static struct qcom_icc_node qns_cnoc_a2noc = { + .name = "qns_cnoc_a2noc", + .id = SC7280_SLAVE_CNOC_A2NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_MASTER_CNOC_A2NOC }, +}; + +static struct qcom_icc_node qns_ddrss_cfg = { + .name = "qns_ddrss_cfg", + .id = SC7280_SLAVE_DDRSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_CNOC_DC_NOC }, +}; + +static struct qcom_icc_node qxs_boot_imem = { + .name = "qxs_boot_imem", + .id = SC7280_SLAVE_BOOT_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = SC7280_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_pimem = { + .name = "qxs_pimem", + .id = SC7280_SLAVE_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .id = SC7280_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .id = SC7280_SLAVE_PCIE_1, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = SC7280_SLAVE_QDSS_STM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .id = SC7280_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_llcc = { + .name = "qhs_llcc", + .id = SC7280_SLAVE_LLCC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gemnoc = { + .name = "qns_gemnoc", + .id = SC7280_SLAVE_GEM_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SC7280_MASTER_GEM_NOC_CFG }, +}; + +static struct qcom_icc_node qhs_mdsp_ms_mpu_cfg = { + .name = "qhs_mdsp_ms_mpu_cfg", + .id = SC7280_SLAVE_MSS_PROC_MS_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_modem_ms_mpu_cfg = { + .name = "qhs_modem_ms_mpu_cfg", + .id = SC7280_SLAVE_MCDMA_MS_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .id = SC7280_SLAVE_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_MASTER_GEM_NOC_CNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = SC7280_SLAVE_LLCC, + .channels = 2, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .id = SC7280_SLAVE_MEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node srvc_even_gemnoc = { + .name = "srvc_even_gemnoc", + .id = SC7280_SLAVE_SERVICE_GEM_NOC_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_odd_gemnoc = { + .name = "srvc_odd_gemnoc", + .id = SC7280_SLAVE_SERVICE_GEM_NOC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_sys_gemnoc = { + .name = "srvc_sys_gemnoc", + .id = SC7280_SLAVE_SERVICE_GEM_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_core = { + .name = "qhs_lpass_core", + .id = SC7280_SLAVE_LPASS_CORE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_lpi = { + .name = "qhs_lpass_lpi", + .id = SC7280_SLAVE_LPASS_LPI_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_mpu = { + .name = "qhs_lpass_mpu", + .id = SC7280_SLAVE_LPASS_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_top = { + .name = "qhs_lpass_top", + .id = SC7280_SLAVE_LPASS_TOP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_niu_aml_noc = { + .name = "srvc_niu_aml_noc", + .id = SC7280_SLAVE_SERVICES_LPASS_AML_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_niu_lpass_agnoc = { + .name = "srvc_niu_lpass_agnoc", + .id = SC7280_SLAVE_SERVICE_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = SC7280_SLAVE_EBI1, + .channels = 2, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = SC7280_SLAVE_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .id = SC7280_SLAVE_MNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc = { + .name = "srvc_mnoc", + .id = SC7280_SLAVE_SERVICE_MNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .id = SC7280_SLAVE_CDSP_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SC7280_MASTER_COMPUTE_NOC }, +}; + +static struct qcom_icc_node service_nsp_noc = { + .name = "service_nsp_noc", + .id = SC7280_SLAVE_SERVICE_NSP_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gemnoc_gc = { + .name = "qns_gemnoc_gc", + .id = SC7280_SLAVE_SNOC_GEM_NOC_GC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SC7280_MASTER_SNOC_GC_MEM_NOC }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = SC7280_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SC7280_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_snoc = { + .name = "srvc_snoc", + .id = SC7280_SLAVE_SERVICE_SNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .keepalive = true, + .num_nodes = 2, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 47, + .nodes = { &qnm_cnoc3_cnoc2, &xm_qdss_dap, + &qhs_ahb2phy0, &qhs_ahb2phy1, + &qhs_camera_cfg, &qhs_clk_ctl, + &qhs_compute_cfg, &qhs_cpr_cx, + &qhs_cpr_mx, &qhs_crypto0_cfg, + &qhs_cx_rdpm, &qhs_dcc_cfg, + &qhs_display_cfg, &qhs_gpuss_cfg, + &qhs_hwkm, &qhs_imem_cfg, + &qhs_ipa, &qhs_ipc_router, + &qhs_mss_cfg, &qhs_mx_rdpm, + &qhs_pcie0_cfg, &qhs_pcie1_cfg, + &qhs_pimem_cfg, &qhs_pka_wrapper_cfg, + &qhs_pmu_wrapper_cfg, &qhs_qdss_cfg, + &qhs_qup0, &qhs_qup1, + &qhs_security, &qhs_tcsr, + &qhs_tlmm, &qhs_ufs_mem_cfg, &qhs_usb2, + &qhs_usb3_0, &qhs_venus_cfg, + &qhs_vsense_ctrl_cfg, &qns_a1_noc_cfg, + &qns_a2_noc_cfg, &qns_cnoc2_cnoc3, + &qns_mnoc_cfg, &qns_snoc_cfg, + &qnm_cnoc2_cnoc3, &qhs_aoss, + &qhs_apss, &qns_cnoc3_cnoc2, + &qns_cnoc_a2noc, &qns_ddrss_cfg }, +}; + +static struct qcom_icc_bcm bcm_cn2 = { + .name = "CN2", + .num_nodes = 6, + .nodes = { &qhs_lpass_cfg, &qhs_pdm, + &qhs_qspi, &qhs_sdc1, + &qhs_sdc2, &qhs_sdc4 }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .num_nodes = 1, + .nodes = { &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_co3 = { + .name = "CO3", + .num_nodes = 1, + .nodes = { &qxm_nsp }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .num_nodes = 2, + .nodes = { &qxm_camnoc_hf, &qxm_mdp0 }, +}; + +static struct qcom_icc_bcm bcm_mm4 = { + .name = "MM4", + .num_nodes = 1, + .nodes = { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_mm5 = { + .name = "MM5", + .num_nodes = 3, + .nodes = { &qnm_video0, &qxm_camnoc_icp, + &qxm_camnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh2 = { + .name = "SH2", + .num_nodes = 2, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu }, +}; + +static struct qcom_icc_bcm bcm_sh3 = { + .name = "SH3", + .num_nodes = 1, + .nodes = { &qnm_cmpnoc }, +}; + +static struct qcom_icc_bcm bcm_sh4 = { + .name = "SH4", + .num_nodes = 1, + .nodes = { &chm_apps }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qns_gemnoc_gc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qxs_pimem }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &xs_qdss_stm }, +}; + +static struct qcom_icc_bcm bcm_sn5 = { + .name = "SN5", + .num_nodes = 1, + .nodes = { &xm_pcie3_0 }, +}; + +static struct qcom_icc_bcm bcm_sn6 = { + .name = "SN6", + .num_nodes = 1, + .nodes = { &xm_pcie3_1 }, +}; + +static struct qcom_icc_bcm bcm_sn7 = { + .name = "SN7", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn8 = { + .name = "SN8", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn14 = { + .name = "SN14", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_bcm *aggre1_noc_bcms[] = { + &bcm_sn5, + &bcm_sn6, + &bcm_sn14, +}; + +static struct qcom_icc_node *aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_A1NOC_CFG] = &qnm_a1noc_cfg, + [MASTER_PCIE_0] = &xm_pcie3_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB2] = &xm_usb2, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc, +}; + +static struct qcom_icc_desc sc7280_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), +}; + +static struct qcom_icc_bcm *aggre2_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node *aggre2_noc_nodes[] = { + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_A2NOC_CFG] = &qnm_a2noc_cfg, + [MASTER_CNOC_A2NOC] = &qnm_cnoc_datapath, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QDSS_ETR] = &xm_qdss_etr, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, + [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, +}; + +static struct qcom_icc_desc sc7280_aggre2_noc = { + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), +}; + +static struct qcom_icc_bcm *clk_virt_bcms[] = { + &bcm_qup0, + &bcm_qup1, +}; + +static struct qcom_icc_node *clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, +}; + +static struct qcom_icc_desc sc7280_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm *cnoc2_bcms[] = { + &bcm_cn1, + &bcm_cn2, +}; + +static struct qcom_icc_node *cnoc2_nodes[] = { + [MASTER_CNOC3_CNOC2] = &qnm_cnoc3_cnoc2, + [MASTER_QDSS_DAP] = &xm_qdss_dap, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CDSP_CFG] = &qhs_compute_cfg, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MX_CFG] = &qhs_cpr_mx, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CX_RDPM] = &qhs_cx_rdpm, + [SLAVE_DCC_CFG] = &qhs_dcc_cfg, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_HWKM] = &qhs_hwkm, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_LPASS] = &qhs_lpass_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_MX_RDPM] = &qhs_mx_rdpm, + [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_PIMEM_CFG] = &qhs_pimem_cfg, + [SLAVE_PKA_WRAPPER_CFG] = &qhs_pka_wrapper_cfg, + [SLAVE_PMU_WRAPPER_CFG] = &qhs_pmu_wrapper_cfg, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_SDCC_1] = &qhs_sdc1, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_SECURITY] = &qhs_security, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB2] = &qhs_usb2, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_A1NOC_CFG] = &qns_a1_noc_cfg, + [SLAVE_A2NOC_CFG] = &qns_a2_noc_cfg, + [SLAVE_CNOC2_CNOC3] = &qns_cnoc2_cnoc3, + [SLAVE_CNOC_MNOC_CFG] = &qns_mnoc_cfg, + [SLAVE_SNOC_CFG] = &qns_snoc_cfg, +}; + +static struct qcom_icc_desc sc7280_cnoc2 = { + .nodes = cnoc2_nodes, + .num_nodes = ARRAY_SIZE(cnoc2_nodes), + .bcms = cnoc2_bcms, + .num_bcms = ARRAY_SIZE(cnoc2_bcms), +}; + +static struct qcom_icc_bcm *cnoc3_bcms[] = { + &bcm_cn0, + &bcm_cn1, + &bcm_sn3, + &bcm_sn4, +}; + +static struct qcom_icc_node *cnoc3_nodes[] = { + [MASTER_CNOC2_CNOC3] = &qnm_cnoc2_cnoc3, + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_APPSS] = &qhs_apss, + [SLAVE_CNOC3_CNOC2] = &qns_cnoc3_cnoc2, + [SLAVE_CNOC_A2NOC] = &qns_cnoc_a2noc, + [SLAVE_DDRSS_CFG] = &qns_ddrss_cfg, + [SLAVE_BOOT_IMEM] = &qxs_boot_imem, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static struct qcom_icc_desc sc7280_cnoc3 = { + .nodes = cnoc3_nodes, + .num_nodes = ARRAY_SIZE(cnoc3_nodes), + .bcms = cnoc3_bcms, + .num_bcms = ARRAY_SIZE(cnoc3_bcms), +}; + +static struct qcom_icc_bcm *dc_noc_bcms[] = { +}; + +static struct qcom_icc_node *dc_noc_nodes[] = { + [MASTER_CNOC_DC_NOC] = &qnm_cnoc_dc_noc, + [SLAVE_LLCC_CFG] = &qhs_llcc, + [SLAVE_GEM_NOC_CFG] = &qns_gemnoc, +}; + +static struct qcom_icc_desc sc7280_dc_noc = { + .nodes = dc_noc_nodes, + .num_nodes = ARRAY_SIZE(dc_noc_nodes), + .bcms = dc_noc_bcms, + .num_bcms = ARRAY_SIZE(dc_noc_bcms), +}; + +static struct qcom_icc_bcm *gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh2, + &bcm_sh3, + &bcm_sh4, +}; + +static struct qcom_icc_node *gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_COMPUTE_NOC] = &qnm_cmpnoc, + [MASTER_GEM_NOC_CFG] = &qnm_gemnoc_cfg, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [SLAVE_MSS_PROC_MS_MPU_CFG] = &qhs_mdsp_ms_mpu_cfg, + [SLAVE_MCDMA_MS_MPU_CFG] = &qhs_modem_ms_mpu_cfg, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, + [SLAVE_SERVICE_GEM_NOC_1] = &srvc_even_gemnoc, + [SLAVE_SERVICE_GEM_NOC_2] = &srvc_odd_gemnoc, + [SLAVE_SERVICE_GEM_NOC] = &srvc_sys_gemnoc, +}; + +static struct qcom_icc_desc sc7280_gem_noc = { + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm *lpass_ag_noc_bcms[] = { +}; + +static struct qcom_icc_node *lpass_ag_noc_nodes[] = { + [MASTER_CNOC_LPASS_AG_NOC] = &qhm_config_noc, + [SLAVE_LPASS_CORE_CFG] = &qhs_lpass_core, + [SLAVE_LPASS_LPI_CFG] = &qhs_lpass_lpi, + [SLAVE_LPASS_MPU_CFG] = &qhs_lpass_mpu, + [SLAVE_LPASS_TOP_CFG] = &qhs_lpass_top, + [SLAVE_SERVICES_LPASS_AML_NOC] = &srvc_niu_aml_noc, + [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, +}; + +static struct qcom_icc_desc sc7280_lpass_ag_noc = { + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), + .bcms = lpass_ag_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_ag_noc_bcms), +}; + +static struct qcom_icc_bcm *mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, +}; + +static struct qcom_icc_node *mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static struct qcom_icc_desc sc7280_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm *mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, + &bcm_mm4, + &bcm_mm5, +}; + +static struct qcom_icc_node *mmss_noc_nodes[] = { + [MASTER_CNOC_MNOC_CFG] = &qnm_mnoc_cfg, + [MASTER_VIDEO_P0] = &qnm_video0, + [MASTER_VIDEO_PROC] = &qnm_video_cpu, + [MASTER_CAMNOC_HF] = &qxm_camnoc_hf, + [MASTER_CAMNOC_ICP] = &qxm_camnoc_icp, + [MASTER_CAMNOC_SF] = &qxm_camnoc_sf, + [MASTER_MDP0] = &qxm_mdp0, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC] = &srvc_mnoc, +}; + +static struct qcom_icc_desc sc7280_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm *nsp_noc_bcms[] = { + &bcm_co0, + &bcm_co3, +}; + +static struct qcom_icc_node *nsp_noc_nodes[] = { + [MASTER_CDSP_NOC_CFG] = &qhm_nsp_noc_config, + [MASTER_CDSP_PROC] = &qxm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, + [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, +}; + +static struct qcom_icc_desc sc7280_nsp_noc = { + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm *system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn2, + &bcm_sn7, + &bcm_sn8, +}; + +static struct qcom_icc_node *system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_SNOC_CFG] = &qnm_snoc_cfg, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_GIC] = &xm_gic, + [SLAVE_SNOC_GEM_NOC_GC] = &qns_gemnoc_gc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, +}; + +static struct qcom_icc_desc sc7280_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static int qnoc_probe(struct platform_device *pdev) +{ + const struct qcom_icc_desc *desc; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct qcom_icc_node **qnodes; + struct qcom_icc_provider *qp; + struct icc_node *node; + size_t num_nodes, i; + int ret; + + desc = device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + qnodes = desc->nodes; + num_nodes = desc->num_nodes; + + qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data = devm_kcalloc(&pdev->dev, num_nodes, sizeof(*node), GFP_KERNEL); + if (!data) + return -ENOMEM; + + provider = &qp->provider; + provider->dev = &pdev->dev; + provider->set = qcom_icc_set; + provider->pre_aggregate = qcom_icc_pre_aggregate; + provider->aggregate = qcom_icc_aggregate; + provider->xlate_extended = qcom_icc_xlate_extended; + INIT_LIST_HEAD(&provider->nodes); + provider->data = data; + + qp->dev = &pdev->dev; + qp->bcms = desc->bcms; + qp->num_bcms = desc->num_bcms; + + qp->voter = of_bcm_voter_get(qp->dev, NULL); + if (IS_ERR(qp->voter)) + return PTR_ERR(qp->voter); + + ret = icc_provider_add(provider); + if (ret) { + dev_err(&pdev->dev, "error adding interconnect provider\n"); + return ret; + } + + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + + for (i = 0; i < num_nodes; i++) { + size_t j; + + if (!qnodes[i]) + continue; + + node = icc_node_create(qnodes[i]->id); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err; + } + + node->name = qnodes[i]->name; + node->data = qnodes[i]; + icc_node_add(node, provider); + + for (j = 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] = node; + } + data->num_nodes = num_nodes; + + platform_set_drvdata(pdev, qp); + + return 0; +err: + icc_nodes_remove(provider); + icc_provider_del(provider); + return ret; +} + +static int qnoc_remove(struct platform_device *pdev) +{ + struct qcom_icc_provider *qp = platform_get_drvdata(pdev); + + icc_nodes_remove(&qp->provider); + return icc_provider_del(&qp->provider); +} + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,sc7280-aggre1-noc", + .data = &sc7280_aggre1_noc}, + { .compatible = "qcom,sc7280-aggre2-noc", + .data = &sc7280_aggre2_noc}, + { .compatible = "qcom,sc7280-clk-virt", + .data = &sc7280_clk_virt}, + { .compatible = "qcom,sc7280-cnoc2", + .data = &sc7280_cnoc2}, + { .compatible = "qcom,sc7280-cnoc3", + .data = &sc7280_cnoc3}, + { .compatible = "qcom,sc7280-dc-noc", + .data = &sc7280_dc_noc}, + { .compatible = "qcom,sc7280-gem-noc", + .data = &sc7280_gem_noc}, + { .compatible = "qcom,sc7280-lpass-ag-noc", + .data = &sc7280_lpass_ag_noc}, + { .compatible = "qcom,sc7280-mc-virt", + .data = &sc7280_mc_virt}, + { .compatible = "qcom,sc7280-mmss-noc", + .data = &sc7280_mmss_noc}, + { .compatible = "qcom,sc7280-nsp-noc", + .data = &sc7280_nsp_noc}, + { .compatible = "qcom,sc7280-system-noc", + .data = &sc7280_system_noc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-sc7280", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; +module_platform_driver(qnoc_driver); + +MODULE_DESCRIPTION("SC7280 NoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/interconnect/qcom/sc7280.h b/drivers/interconnect/qcom/sc7280.h new file mode 100644 index 000000000000..175e400305c5 --- /dev/null +++ b/drivers/interconnect/qcom/sc7280.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Qualcomm #define SC7280 interconnect IDs + * + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_SC7280_H +#define __DRIVERS_INTERCONNECT_QCOM_SC7280_H + +#define SC7280_MASTER_GPU_TCU 0 +#define SC7280_MASTER_SYS_TCU 1 +#define SC7280_MASTER_APPSS_PROC 2 +#define SC7280_MASTER_LLCC 3 +#define SC7280_MASTER_CNOC_LPASS_AG_NOC 4 +#define SC7280_MASTER_CDSP_NOC_CFG 5 +#define SC7280_MASTER_QDSS_BAM 6 +#define SC7280_MASTER_QSPI_0 7 +#define SC7280_MASTER_QUP_0 8 +#define SC7280_MASTER_QUP_1 9 +#define SC7280_MASTER_A1NOC_CFG 10 +#define SC7280_MASTER_A2NOC_CFG 11 +#define SC7280_MASTER_A1NOC_SNOC 12 +#define SC7280_MASTER_A2NOC_SNOC 13 +#define SC7280_MASTER_COMPUTE_NOC 14 +#define SC7280_MASTER_CNOC2_CNOC3 15 +#define SC7280_MASTER_CNOC3_CNOC2 16 +#define SC7280_MASTER_CNOC_A2NOC 17 +#define SC7280_MASTER_CNOC_DC_NOC 18 +#define SC7280_MASTER_GEM_NOC_CFG 19 +#define SC7280_MASTER_GEM_NOC_CNOC 20 +#define SC7280_MASTER_GEM_NOC_PCIE_SNOC 21 +#define SC7280_MASTER_GFX3D 22 +#define SC7280_MASTER_CNOC_MNOC_CFG 23 +#define SC7280_MASTER_MNOC_HF_MEM_NOC 24 +#define SC7280_MASTER_MNOC_SF_MEM_NOC 25 +#define SC7280_MASTER_ANOC_PCIE_GEM_NOC 26 +#define SC7280_MASTER_SNOC_CFG 27 +#define SC7280_MASTER_SNOC_GC_MEM_NOC 28 +#define SC7280_MASTER_SNOC_SF_MEM_NOC 29 +#define SC7280_MASTER_VIDEO_P0 30 +#define SC7280_MASTER_VIDEO_PROC 31 +#define SC7280_MASTER_QUP_CORE_0 32 +#define SC7280_MASTER_QUP_CORE_1 33 +#define SC7280_MASTER_CAMNOC_HF 34 +#define SC7280_MASTER_CAMNOC_ICP 35 +#define SC7280_MASTER_CAMNOC_SF 36 +#define SC7280_MASTER_CRYPTO 37 +#define SC7280_MASTER_IPA 38 +#define SC7280_MASTER_MDP0 39 +#define SC7280_MASTER_CDSP_PROC 40 +#define SC7280_MASTER_PIMEM 41 +#define SC7280_MASTER_GIC 42 +#define SC7280_MASTER_PCIE_0 43 +#define SC7280_MASTER_PCIE_1 44 +#define SC7280_MASTER_QDSS_DAP 45 +#define SC7280_MASTER_QDSS_ETR 46 +#define SC7280_MASTER_SDCC_1 47 +#define SC7280_MASTER_SDCC_2 48 +#define SC7280_MASTER_SDCC_4 49 +#define SC7280_MASTER_UFS_MEM 50 +#define SC7280_MASTER_USB2 51 +#define SC7280_MASTER_USB3_0 52 +#define SC7280_SLAVE_EBI1 53 +#define SC7280_SLAVE_AHB2PHY_SOUTH 54 +#define SC7280_SLAVE_AHB2PHY_NORTH 55 +#define SC7280_SLAVE_AOSS 56 +#define SC7280_SLAVE_APPSS 57 +#define SC7280_SLAVE_CAMERA_CFG 58 +#define SC7280_SLAVE_CLK_CTL 59 +#define SC7280_SLAVE_CDSP_CFG 60 +#define SC7280_SLAVE_RBCPR_CX_CFG 61 +#define SC7280_SLAVE_RBCPR_MX_CFG 62 +#define SC7280_SLAVE_CRYPTO_0_CFG 63 +#define SC7280_SLAVE_CX_RDPM 64 +#define SC7280_SLAVE_DCC_CFG 65 +#define SC7280_SLAVE_DISPLAY_CFG 66 +#define SC7280_SLAVE_GFX3D_CFG 67 +#define SC7280_SLAVE_HWKM 68 +#define SC7280_SLAVE_IMEM_CFG 69 +#define SC7280_SLAVE_IPA_CFG 70 +#define SC7280_SLAVE_IPC_ROUTER_CFG 71 +#define SC7280_SLAVE_LLCC_CFG 72 +#define SC7280_SLAVE_LPASS 73 +#define SC7280_SLAVE_LPASS_CORE_CFG 74 +#define SC7280_SLAVE_LPASS_LPI_CFG 75 +#define SC7280_SLAVE_LPASS_MPU_CFG 76 +#define SC7280_SLAVE_LPASS_TOP_CFG 77 +#define SC7280_SLAVE_MSS_PROC_MS_MPU_CFG 78 +#define SC7280_SLAVE_MCDMA_MS_MPU_CFG 79 +#define SC7280_SLAVE_CNOC_MSS 80 +#define SC7280_SLAVE_MX_RDPM 81 +#define SC7280_SLAVE_PCIE_0_CFG 82 +#define SC7280_SLAVE_PCIE_1_CFG 83 +#define SC7280_SLAVE_PDM 84 +#define SC7280_SLAVE_PIMEM_CFG 85 +#define SC7280_SLAVE_PKA_WRAPPER_CFG 86 +#define SC7280_SLAVE_PMU_WRAPPER_CFG 87 +#define SC7280_SLAVE_QDSS_CFG 88 +#define SC7280_SLAVE_QSPI_0 89 +#define SC7280_SLAVE_QUP_0 90 +#define SC7280_SLAVE_QUP_1 91 +#define SC7280_SLAVE_SDCC_1 92 +#define SC7280_SLAVE_SDCC_2 93 +#define SC7280_SLAVE_SDCC_4 94 +#define SC7280_SLAVE_SECURITY 95 +#define SC7280_SLAVE_TCSR 96 +#define SC7280_SLAVE_TLMM 97 +#define SC7280_SLAVE_UFS_MEM_CFG 98 +#define SC7280_SLAVE_USB2 99 +#define SC7280_SLAVE_USB3_0 100 +#define SC7280_SLAVE_VENUS_CFG 101 +#define SC7280_SLAVE_VSENSE_CTRL_CFG 102 +#define SC7280_SLAVE_A1NOC_CFG 103 +#define SC7280_SLAVE_A1NOC_SNOC 104 +#define SC7280_SLAVE_A2NOC_CFG 105 +#define SC7280_SLAVE_A2NOC_SNOC 106 +#define SC7280_SLAVE_CNOC2_CNOC3 107 +#define SC7280_SLAVE_CNOC3_CNOC2 108 +#define SC7280_SLAVE_CNOC_A2NOC 109 +#define SC7280_SLAVE_DDRSS_CFG 110 +#define SC7280_SLAVE_GEM_NOC_CNOC 111 +#define SC7280_SLAVE_GEM_NOC_CFG 112 +#define SC7280_SLAVE_SNOC_GEM_NOC_GC 113 +#define SC7280_SLAVE_SNOC_GEM_NOC_SF 114 +#define SC7280_SLAVE_LLCC 115 +#define SC7280_SLAVE_MNOC_HF_MEM_NOC 116 +#define SC7280_SLAVE_MNOC_SF_MEM_NOC 117 +#define SC7280_SLAVE_CNOC_MNOC_CFG 118 +#define SC7280_SLAVE_CDSP_MEM_NOC 119 +#define SC7280_SLAVE_MEM_NOC_PCIE_SNOC 120 +#define SC7280_SLAVE_ANOC_PCIE_GEM_NOC 121 +#define SC7280_SLAVE_SNOC_CFG 122 +#define SC7280_SLAVE_QUP_CORE_0 123 +#define SC7280_SLAVE_QUP_CORE_1 124 +#define SC7280_SLAVE_BOOT_IMEM 125 +#define SC7280_SLAVE_IMEM 126 +#define SC7280_SLAVE_PIMEM 127 +#define SC7280_SLAVE_SERVICE_NSP_NOC 128 +#define SC7280_SLAVE_SERVICE_A1NOC 129 +#define SC7280_SLAVE_SERVICE_A2NOC 130 +#define SC7280_SLAVE_SERVICE_GEM_NOC_1 131 +#define SC7280_SLAVE_SERVICE_MNOC 132 +#define SC7280_SLAVE_SERVICES_LPASS_AML_NOC 133 +#define SC7280_SLAVE_SERVICE_LPASS_AG_NOC 134 +#define SC7280_SLAVE_SERVICE_GEM_NOC_2 135 +#define SC7280_SLAVE_SERVICE_SNOC 136 +#define SC7280_SLAVE_SERVICE_GEM_NOC 137 +#define SC7280_SLAVE_PCIE_0 138 +#define SC7280_SLAVE_PCIE_1 139 +#define SC7280_SLAVE_QDSS_STM 140 +#define SC7280_SLAVE_TCU 141 + +#endif diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c index ec71063fff76..3461b0a7dc62 100644 --- a/drivers/ipack/carriers/tpci200.c +++ b/drivers/ipack/carriers/tpci200.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** - * tpci200.c - * +/* * driver for the TEWS TPCI-200 device * * Copyright (C) 2009-2012 CERN (www.cern.ch) @@ -596,8 +594,11 @@ static int tpci200_pci_probe(struct pci_dev *pdev, out_err_bus_register: tpci200_uninstall(tpci200); + /* tpci200->info->cfg_regs is unmapped in tpci200_uninstall */ + tpci200->info->cfg_regs = NULL; out_err_install: - iounmap(tpci200->info->cfg_regs); + if (tpci200->info->cfg_regs) + iounmap(tpci200->info->cfg_regs); out_err_ioremap: pci_release_region(pdev, TPCI200_CFG_MEM_BAR); out_err_pci_request: diff --git a/drivers/ipack/carriers/tpci200.h b/drivers/ipack/carriers/tpci200.h index 2619f827e33f..e79ac64abcff 100644 --- a/drivers/ipack/carriers/tpci200.h +++ b/drivers/ipack/carriers/tpci200.h @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/** - * tpci200.h - * +/* * driver for the carrier TEWS TPCI-200 * * Copyright (C) 2009-2012 CERN (www.cern.ch) diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index 3940714e4397..2a3a94f72dfb 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** - * ipoctal.c - * +/* * driver for the GE IP-OCTAL boards * * Copyright (C) 2009-2012 CERN (www.cern.ch) diff --git a/drivers/ipack/devices/ipoctal.h b/drivers/ipack/devices/ipoctal.h index 75f83ba774a4..773dc41bd667 100644 --- a/drivers/ipack/devices/ipoctal.h +++ b/drivers/ipack/devices/ipoctal.h @@ -1,9 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -/** - * ipoctal.h - * +/* * driver for the IPOCTAL boards - + * * Copyright (C) 2009-2012 CERN (www.cern.ch) * Author: Nicolas Serafini, EIC2 SA * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com> diff --git a/drivers/mcb/mcb-lpc.c b/drivers/mcb/mcb-lpc.c index 506676754538..53decd89876e 100644 --- a/drivers/mcb/mcb-lpc.c +++ b/drivers/mcb/mcb-lpc.c @@ -105,17 +105,8 @@ out_put: return ret; } -static struct resource sc24_fpga_resource = { - .start = 0xe000e000, - .end = 0xe000e000 + CHAM_HEADER_SIZE, - .flags = IORESOURCE_MEM, -}; - -static struct resource sc31_fpga_resource = { - .start = 0xf000e000, - .end = 0xf000e000 + CHAM_HEADER_SIZE, - .flags = IORESOURCE_MEM, -}; +static struct resource sc24_fpga_resource = DEFINE_RES_MEM(0xe000e000, CHAM_HEADER_SIZE); +static struct resource sc31_fpga_resource = DEFINE_RES_MEM(0xf000e000, CHAM_HEADER_SIZE); static struct platform_driver mcb_lpc_driver = { .driver = { diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.c b/drivers/misc/bcm-vk/bcm_vk_msg.c index f40cf08a6192..066b9ef7fcd7 100644 --- a/drivers/misc/bcm-vk/bcm_vk_msg.c +++ b/drivers/misc/bcm-vk/bcm_vk_msg.c @@ -354,8 +354,7 @@ static void bcm_vk_drain_all_pend(struct device *dev, for (num = 0; num < chan->q_nr; num++) { list_for_each_entry_safe(entry, tmp, &chan->pendq[num], node) { if ((!ctx) || (entry->ctx->idx == ctx->idx)) { - list_del(&entry->node); - list_add_tail(&entry->node, &del_q); + list_move_tail(&entry->node, &del_q); } } } @@ -701,8 +700,7 @@ int bcm_vk_send_shutdown_msg(struct bcm_vk *vk, u32 shut_type, return -EINVAL; } - entry = kzalloc(sizeof(*entry) + - sizeof(struct vk_msg_blk), GFP_KERNEL); + entry = kzalloc(struct_size(entry, to_v_msg, 1), GFP_KERNEL); if (!entry) return -ENOMEM; diff --git a/drivers/misc/bcm-vk/bcm_vk_msg.h b/drivers/misc/bcm-vk/bcm_vk_msg.h index 4eaad84825d6..56784c8896d8 100644 --- a/drivers/misc/bcm-vk/bcm_vk_msg.h +++ b/drivers/misc/bcm-vk/bcm_vk_msg.h @@ -116,7 +116,7 @@ struct bcm_vk_wkent { u32 usr_msg_id; u32 to_v_blks; u32 seq_num; - struct vk_msg_blk to_v_msg[0]; + struct vk_msg_blk to_v_msg[]; }; /* queue stats counters */ diff --git a/drivers/misc/cardreader/alcor_pci.c b/drivers/misc/cardreader/alcor_pci.c index cd402c89189e..de6d44a158bb 100644 --- a/drivers/misc/cardreader/alcor_pci.c +++ b/drivers/misc/cardreader/alcor_pci.c @@ -139,7 +139,13 @@ static void alcor_pci_init_check_aspm(struct alcor_pci_priv *priv) u32 val32; priv->pdev_cap_off = alcor_pci_find_cap_offset(priv, priv->pdev); - priv->parent_cap_off = alcor_pci_find_cap_offset(priv, + /* + * A device might be attached to root complex directly and + * priv->parent_pdev will be NULL. In this case we don't check its + * capability and disable ASPM completely. + */ + if (priv->parent_pdev) + priv->parent_cap_off = alcor_pci_find_cap_offset(priv, priv->parent_pdev); if ((priv->pdev_cap_off == 0) || (priv->parent_cap_off == 0)) { diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index bd3bd32333c5..3dbdce96fae0 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -569,7 +569,8 @@ static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev, int rc; cdev_init(cdev, fops); - if ((rc = cdev_add(cdev, devt, 1))) { + rc = cdev_add(cdev, devt, 1); + if (rc) { dev_err(&afu->dev, "Unable to add %s chardev: %i\n", desc, rc); return rc; } @@ -577,8 +578,8 @@ static int cxl_add_chardev(struct cxl_afu *afu, dev_t devt, struct cdev *cdev, 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); + dev_err(&afu->dev, "Unable to create %s chardev in sysfs: %i\n", desc, rc); goto err; } diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 0f791bfdc1f5..f0a7531f354c 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -32,12 +32,13 @@ config EEPROM_AT24 will be called at24. config EEPROM_AT25 - tristate "SPI EEPROMs from most vendors" + tristate "SPI EEPROMs (FRAMs) from most vendors" depends on SPI && SYSFS select NVMEM select NVMEM_SYSFS help - Enable this driver to get read/write support to most SPI EEPROMs, + Enable this driver to get read/write support to most SPI EEPROMs + and Cypress FRAMs, after you configure the board init code to know about each eeprom on your target board. diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index b76e4901b4a4..4d09b672ac3c 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models + * and Cypress FRAMs FM25 models * * Copyright (C) 2006 David Brownell */ @@ -16,6 +17,9 @@ #include <linux/spi/spi.h> #include <linux/spi/eeprom.h> #include <linux/property.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/math.h> /* * NOTE: this is an *EEPROM* driver. The vagaries of product naming @@ -27,6 +31,7 @@ * AT25M02, AT25128B */ +#define FM25_SN_LEN 8 /* serial number length */ struct at25_data { struct spi_device *spi; struct mutex lock; @@ -34,6 +39,7 @@ struct at25_data { unsigned addrlen; struct nvmem_config nvmem_config; struct nvmem_device *nvmem; + u8 sernum[FM25_SN_LEN]; }; #define AT25_WREN 0x06 /* latch the write enable */ @@ -42,6 +48,9 @@ struct at25_data { #define AT25_WRSR 0x01 /* write status register */ #define AT25_READ 0x03 /* read byte(s) */ #define AT25_WRITE 0x02 /* write byte(s)/sector */ +#define FM25_SLEEP 0xb9 /* enter sleep mode */ +#define FM25_RDID 0x9f /* read device ID */ +#define FM25_RDSN 0xc3 /* read S/N */ #define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ #define AT25_SR_WEN 0x02 /* write enable (latched) */ @@ -51,6 +60,8 @@ struct at25_data { #define AT25_INSTR_BIT3 0x08 /* Additional address bit in instr */ +#define FM25_ID_LEN 9 /* ID length */ + #define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ /* Specs often allow 5 msec for a page write, sometimes 20 msec; @@ -129,6 +140,51 @@ static int at25_ee_read(void *priv, unsigned int offset, return status; } +/* + * read extra registers as ID or serial number + */ +static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, + int len) +{ + int status; + struct spi_transfer t[2]; + struct spi_message m; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &command; + t[0].len = 1; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + mutex_lock(&at25->lock); + + status = spi_sync(at25->spi, &m); + dev_dbg(&at25->spi->dev, "read %d aux bytes --> %d\n", len, status); + + mutex_unlock(&at25->lock); + return status; +} + +static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct at25_data *at25; + + at25 = dev_get_drvdata(dev); + return sysfs_emit(buf, "%*ph\n", (int)sizeof(at25->sernum), at25->sernum); +} +static DEVICE_ATTR_RO(sernum); + +static struct attribute *sernum_attrs[] = { + &dev_attr_sernum.attr, + NULL, +}; +ATTRIBUTE_GROUPS(sernum); + static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) { struct at25_data *at25 = priv; @@ -303,34 +359,39 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) return 0; } +static const struct of_device_id at25_of_match[] = { + { .compatible = "atmel,at25",}, + { .compatible = "cypress,fm25",}, + { } +}; +MODULE_DEVICE_TABLE(of, at25_of_match); + static int at25_probe(struct spi_device *spi) { struct at25_data *at25 = NULL; struct spi_eeprom chip; int err; int sr; - int addrlen; + u8 id[FM25_ID_LEN]; + u8 sernum[FM25_SN_LEN]; + int i; + const struct of_device_id *match; + bool is_fram = 0; + + match = of_match_device(of_match_ptr(at25_of_match), &spi->dev); + if (match && !strcmp(match->compatible, "cypress,fm25")) + is_fram = 1; /* Chip description */ if (!spi->dev.platform_data) { - err = at25_fw_to_chip(&spi->dev, &chip); - if (err) - return err; + if (!is_fram) { + err = at25_fw_to_chip(&spi->dev, &chip); + if (err) + return err; + } } else chip = *(struct spi_eeprom *)spi->dev.platform_data; - /* For now we only support 8/16/24 bit addressing */ - if (chip.flags & EE_ADDR1) - addrlen = 1; - else if (chip.flags & EE_ADDR2) - addrlen = 2; - else if (chip.flags & EE_ADDR3) - addrlen = 3; - else { - dev_dbg(&spi->dev, "unsupported address type\n"); - return -EINVAL; - } - /* Ping the chip ... the status register is pretty portable, * unlike probing manufacturer IDs. We do expect that system * firmware didn't write it in the past few milliseconds! @@ -349,9 +410,51 @@ static int at25_probe(struct spi_device *spi) at25->chip = chip; at25->spi = spi; spi_set_drvdata(spi, at25); - at25->addrlen = addrlen; - at25->nvmem_config.type = NVMEM_TYPE_EEPROM; + if (is_fram) { + /* Get ID of chip */ + fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); + if (id[6] != 0xc2) { + dev_err(&spi->dev, + "Error: no Cypress FRAM (id %02x)\n", id[6]); + return -ENODEV; + } + /* set size found in ID */ + if (id[7] < 0x21 || id[7] > 0x26) { + dev_err(&spi->dev, "Error: unsupported size (id %02x)\n", id[7]); + return -ENODEV; + } + chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; + + if (at25->chip.byte_len > 64 * 1024) + at25->chip.flags |= EE_ADDR3; + else + at25->chip.flags |= EE_ADDR2; + + if (id[8]) { + fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN); + /* swap byte order */ + for (i = 0; i < FM25_SN_LEN; i++) + at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i]; + } + + at25->chip.page_size = PAGE_SIZE; + strncpy(at25->chip.name, "fm25", sizeof(at25->chip.name)); + } + + /* For now we only support 8/16/24 bit addressing */ + if (at25->chip.flags & EE_ADDR1) + at25->addrlen = 1; + else if (at25->chip.flags & EE_ADDR2) + at25->addrlen = 2; + else if (at25->chip.flags & EE_ADDR3) + at25->addrlen = 3; + else { + dev_dbg(&spi->dev, "unsupported address type\n"); + return -EINVAL; + } + + at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM; at25->nvmem_config.name = dev_name(&spi->dev); at25->nvmem_config.dev = &spi->dev; at25->nvmem_config.read_only = chip.flags & EE_READONLY; @@ -370,27 +473,22 @@ static int at25_probe(struct spi_device *spi) if (IS_ERR(at25->nvmem)) return PTR_ERR(at25->nvmem); - dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n", - (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), - (chip.byte_len < 1024) ? "Byte" : "KByte", - at25->chip.name, - (chip.flags & EE_READONLY) ? " (readonly)" : "", - at25->chip.page_size); + dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n", + (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), + (chip.byte_len < 1024) ? "Byte" : "KByte", + at25->chip.name, is_fram ? "fram" : "eeprom", + (chip.flags & EE_READONLY) ? " (readonly)" : "", + at25->chip.page_size); return 0; } /*-------------------------------------------------------------------------*/ -static const struct of_device_id at25_of_match[] = { - { .compatible = "atmel,at25", }, - { } -}; -MODULE_DEVICE_TABLE(of, at25_of_match); - static struct spi_driver at25_driver = { .driver = { .name = "at25", .of_match_table = at25_of_match, + .dev_groups = sernum_groups, }, .probe = at25_probe, }; diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c index 252e15ba65e1..bb9c4512c968 100644 --- a/drivers/misc/eeprom/ee1004.c +++ b/drivers/misc/eeprom/ee1004.c @@ -32,16 +32,17 @@ */ #define EE1004_ADDR_SET_PAGE 0x36 -#define EE1004_EEPROM_SIZE 512 +#define EE1004_NUM_PAGES 2 #define EE1004_PAGE_SIZE 256 #define EE1004_PAGE_SHIFT 8 +#define EE1004_EEPROM_SIZE (EE1004_PAGE_SIZE * EE1004_NUM_PAGES) /* * Mutex protects ee1004_set_page and ee1004_dev_count, and must be held * from page selection to end of read. */ static DEFINE_MUTEX(ee1004_bus_lock); -static struct i2c_client *ee1004_set_page[2]; +static struct i2c_client *ee1004_set_page[EE1004_NUM_PAGES]; static unsigned int ee1004_dev_count; static int ee1004_current_page; @@ -71,40 +72,58 @@ static int ee1004_get_current_page(void) return 0; } +static int ee1004_set_current_page(struct device *dev, int page) +{ + int ret; + + if (page == ee1004_current_page) + return 0; + + /* Data is ignored */ + ret = i2c_smbus_write_byte(ee1004_set_page[page], 0x00); + /* + * Don't give up just yet. Some memory modules will select the page + * but not ack the command. Check which page is selected now. + */ + if (ret == -ENXIO && ee1004_get_current_page() == page) + ret = 0; + if (ret < 0) { + dev_err(dev, "Failed to select page %d (%d)\n", page, ret); + return ret; + } + + dev_dbg(dev, "Selected page %d\n", page); + ee1004_current_page = page; + + return 0; +} + static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf, unsigned int offset, size_t count) { - int status; + int status, page; + + page = offset >> EE1004_PAGE_SHIFT; + offset &= (1 << EE1004_PAGE_SHIFT) - 1; + + status = ee1004_set_current_page(&client->dev, page); + if (status) + return status; - if (count > I2C_SMBUS_BLOCK_MAX) - count = I2C_SMBUS_BLOCK_MAX; /* Can't cross page boundaries */ - if (unlikely(offset + count > EE1004_PAGE_SIZE)) + if (offset + count > EE1004_PAGE_SIZE) count = EE1004_PAGE_SIZE - offset; - status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset, - count, buf); - dev_dbg(&client->dev, "read %zu@%d --> %d\n", count, offset, status); - - return status; + return i2c_smbus_read_i2c_block_data_or_emulated(client, offset, count, buf); } -static ssize_t ee1004_read(struct file *filp, struct kobject *kobj, +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { - struct device *dev = kobj_to_dev(kobj); - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = kobj_to_i2c_client(kobj); size_t requested = count; - int page; - - if (unlikely(!count)) - return count; - - page = off >> EE1004_PAGE_SHIFT; - if (unlikely(page > 1)) - return 0; - off &= (1 << EE1004_PAGE_SHIFT) - 1; + int ret = 0; /* * Read data from chip, protecting against concurrent access to @@ -113,139 +132,85 @@ static ssize_t ee1004_read(struct file *filp, struct kobject *kobj, mutex_lock(&ee1004_bus_lock); while (count) { - int status; - - /* Select page */ - if (page != ee1004_current_page) { - /* Data is ignored */ - status = i2c_smbus_write_byte(ee1004_set_page[page], - 0x00); - if (status == -ENXIO) { - /* - * Don't give up just yet. Some memory - * modules will select the page but not - * ack the command. Check which page is - * selected now. - */ - if (ee1004_get_current_page() == page) - status = 0; - } - if (status < 0) { - dev_err(dev, "Failed to select page %d (%d)\n", - page, status); - mutex_unlock(&ee1004_bus_lock); - return status; - } - dev_dbg(dev, "Selected page %d\n", page); - ee1004_current_page = page; - } - - status = ee1004_eeprom_read(client, buf, off, count); - if (status < 0) { - mutex_unlock(&ee1004_bus_lock); - return status; - } - buf += status; - off += status; - count -= status; + ret = ee1004_eeprom_read(client, buf, off, count); + if (ret < 0) + goto out; - if (off == EE1004_PAGE_SIZE) { - page++; - off = 0; - } + buf += ret; + off += ret; + count -= ret; } - +out: mutex_unlock(&ee1004_bus_lock); - return requested; + return ret < 0 ? ret : requested; } -static const struct bin_attribute eeprom_attr = { - .attr = { - .name = "eeprom", - .mode = 0444, - }, - .size = EE1004_EEPROM_SIZE, - .read = ee1004_read, +static BIN_ATTR_RO(eeprom, EE1004_EEPROM_SIZE); + +static struct bin_attribute *ee1004_attrs[] = { + &bin_attr_eeprom, + NULL }; -static int ee1004_probe(struct i2c_client *client, - const struct i2c_device_id *id) +BIN_ATTRIBUTE_GROUPS(ee1004); + +static void ee1004_cleanup(int idx) +{ + if (--ee1004_dev_count == 0) + while (--idx >= 0) { + i2c_unregister_device(ee1004_set_page[idx]); + ee1004_set_page[idx] = NULL; + } +} + +static int ee1004_probe(struct i2c_client *client) { int err, cnr = 0; - const char *slow = NULL; /* Make sure we can operate on this adapter */ if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { - if (i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_READ_WORD_DATA)) - slow = "word"; - else if (i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE | - I2C_FUNC_SMBUS_READ_BYTE_DATA)) - slow = "byte"; - else - return -EPFNOSUPPORT; - } + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_I2C_BLOCK) && + !i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) + return -EPFNOSUPPORT; /* Use 2 dummy devices for page select command */ mutex_lock(&ee1004_bus_lock); if (++ee1004_dev_count == 1) { - for (cnr = 0; cnr < 2; cnr++) { - ee1004_set_page[cnr] = i2c_new_dummy_device(client->adapter, - EE1004_ADDR_SET_PAGE + cnr); - if (IS_ERR(ee1004_set_page[cnr])) { - dev_err(&client->dev, - "address 0x%02x unavailable\n", - EE1004_ADDR_SET_PAGE + cnr); - err = PTR_ERR(ee1004_set_page[cnr]); + for (cnr = 0; cnr < EE1004_NUM_PAGES; cnr++) { + struct i2c_client *cl; + + cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr); + if (IS_ERR(cl)) { + err = PTR_ERR(cl); goto err_clients; } + ee1004_set_page[cnr] = cl; } - } else if (i2c_adapter_id(client->adapter) != - i2c_adapter_id(ee1004_set_page[0]->adapter)) { + + /* Remember current page to avoid unneeded page select */ + err = ee1004_get_current_page(); + if (err < 0) + goto err_clients; + dev_dbg(&client->dev, "Currently selected page: %d\n", err); + ee1004_current_page = err; + } else if (client->adapter != ee1004_set_page[0]->adapter) { dev_err(&client->dev, "Driver only supports devices on a single I2C bus\n"); err = -EOPNOTSUPP; goto err_clients; } - - /* Remember current page to avoid unneeded page select */ - err = ee1004_get_current_page(); - if (err < 0) - goto err_clients; - ee1004_current_page = err; - dev_dbg(&client->dev, "Currently selected page: %d\n", - ee1004_current_page); mutex_unlock(&ee1004_bus_lock); - /* Create the sysfs eeprom file */ - err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); - if (err) - goto err_clients_lock; - dev_info(&client->dev, "%u byte EE1004-compliant SPD EEPROM, read-only\n", EE1004_EEPROM_SIZE); - if (slow) - dev_notice(&client->dev, - "Falling back to %s reads, performance will suffer\n", - slow); return 0; - err_clients_lock: - mutex_lock(&ee1004_bus_lock); err_clients: - if (--ee1004_dev_count == 0) { - for (cnr--; cnr >= 0; cnr--) { - i2c_unregister_device(ee1004_set_page[cnr]); - ee1004_set_page[cnr] = NULL; - } - } + ee1004_cleanup(cnr); mutex_unlock(&ee1004_bus_lock); return err; @@ -253,18 +218,9 @@ static int ee1004_probe(struct i2c_client *client, static int ee1004_remove(struct i2c_client *client) { - int i; - - sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); - /* Remove page select clients if this is the last device */ mutex_lock(&ee1004_bus_lock); - if (--ee1004_dev_count == 0) { - for (i = 0; i < 2; i++) { - i2c_unregister_device(ee1004_set_page[i]); - ee1004_set_page[i] = NULL; - } - } + ee1004_cleanup(EE1004_NUM_PAGES); mutex_unlock(&ee1004_bus_lock); return 0; @@ -275,8 +231,9 @@ static int ee1004_remove(struct i2c_client *client) static struct i2c_driver ee1004_driver = { .driver = { .name = "ee1004", + .dev_groups = ee1004_groups, }, - .probe = ee1004_probe, + .probe_new = ee1004_probe, .remove = ee1004_remove, .id_table = ee1004_ids, }; diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 80114f4c80ad..29d8971ec558 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> +#include <linux/log2.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> @@ -28,14 +29,29 @@ struct eeprom_93xx46_devtype_data { unsigned int quirks; + unsigned char flags; +}; + +static const struct eeprom_93xx46_devtype_data at93c46_data = { + .flags = EE_SIZE1K, +}; + +static const struct eeprom_93xx46_devtype_data at93c56_data = { + .flags = EE_SIZE2K, +}; + +static const struct eeprom_93xx46_devtype_data at93c66_data = { + .flags = EE_SIZE4K, }; static const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = { + .flags = EE_SIZE1K, .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ | EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH, }; static const struct eeprom_93xx46_devtype_data microchip_93lc46b_data = { + .flags = EE_SIZE1K, .quirks = EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE, }; @@ -70,6 +86,7 @@ static int eeprom_93xx46_read(void *priv, unsigned int off, struct eeprom_93xx46_dev *edev = priv; char *buf = val; int err = 0; + int bits; if (unlikely(off >= edev->size)) return 0; @@ -83,21 +100,21 @@ static int eeprom_93xx46_read(void *priv, unsigned int off, if (edev->pdata->prepare) edev->pdata->prepare(edev); + /* The opcode in front of the address is three bits. */ + bits = edev->addrlen + 3; + while (count) { struct spi_message m; struct spi_transfer t[2] = { { 0 } }; u16 cmd_addr = OP_READ << edev->addrlen; size_t nbytes = count; - int bits; - if (edev->addrlen == 7) { - cmd_addr |= off & 0x7f; - bits = 10; + if (edev->pdata->flags & EE_ADDR8) { + cmd_addr |= off; if (has_quirk_single_word_read(edev)) nbytes = 1; } else { - cmd_addr |= (off >> 1) & 0x3f; - bits = 9; + cmd_addr |= (off >> 1); if (has_quirk_single_word_read(edev)) nbytes = 2; } @@ -152,14 +169,14 @@ static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) int bits, ret; u16 cmd_addr; + /* The opcode in front of the address is three bits. */ + bits = edev->addrlen + 3; + cmd_addr = OP_START << edev->addrlen; - if (edev->addrlen == 7) { + if (edev->pdata->flags & EE_ADDR8) cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1; - bits = 10; - } else { + else cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS); - bits = 9; - } if (has_quirk_instruction_length(edev)) { cmd_addr <<= 2; @@ -205,15 +222,19 @@ eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, int bits, data_len, ret; u16 cmd_addr; + if (unlikely(off >= edev->size)) + return -EINVAL; + + /* The opcode in front of the address is three bits. */ + bits = edev->addrlen + 3; + cmd_addr = OP_WRITE << edev->addrlen; - if (edev->addrlen == 7) { - cmd_addr |= off & 0x7f; - bits = 10; + if (edev->pdata->flags & EE_ADDR8) { + cmd_addr |= off; data_len = 1; } else { - cmd_addr |= (off >> 1) & 0x3f; - bits = 9; + cmd_addr |= (off >> 1); data_len = 2; } @@ -253,7 +274,7 @@ static int eeprom_93xx46_write(void *priv, unsigned int off, return count; /* only write even number of bytes on 16-bit devices */ - if (edev->addrlen == 6) { + if (edev->pdata->flags & EE_ADDR16) { step = 2; count &= ~1; } @@ -295,14 +316,14 @@ static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) int bits, ret; u16 cmd_addr; + /* The opcode in front of the address is three bits. */ + bits = edev->addrlen + 3; + cmd_addr = OP_START << edev->addrlen; - if (edev->addrlen == 7) { + if (edev->pdata->flags & EE_ADDR8) cmd_addr |= ADDR_ERAL << 1; - bits = 10; - } else { + else cmd_addr |= ADDR_ERAL; - bits = 9; - } if (has_quirk_instruction_length(edev)) { cmd_addr <<= 2; @@ -375,8 +396,11 @@ static void select_deassert(void *context) } static const struct of_device_id eeprom_93xx46_of_table[] = { - { .compatible = "eeprom-93xx46", }, + { .compatible = "eeprom-93xx46", .data = &at93c46_data, }, + { .compatible = "atmel,at93c46", .data = &at93c46_data, }, { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, + { .compatible = "atmel,at93c56", .data = &at93c56_data, }, + { .compatible = "atmel,at93c66", .data = &at93c66_data, }, { .compatible = "microchip,93lc46b", .data = µchip_93lc46b_data, }, {} }; @@ -426,6 +450,7 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) const struct eeprom_93xx46_devtype_data *data = of_id->data; pd->quirks = data->quirks; + pd->flags |= data->flags; } spi->dev.platform_data = pd; @@ -455,10 +480,21 @@ static int eeprom_93xx46_probe(struct spi_device *spi) if (!edev) return -ENOMEM; + if (pd->flags & EE_SIZE1K) + edev->size = 128; + else if (pd->flags & EE_SIZE2K) + edev->size = 256; + else if (pd->flags & EE_SIZE4K) + edev->size = 512; + else { + dev_err(&spi->dev, "unspecified size\n"); + return -EINVAL; + } + if (pd->flags & EE_ADDR8) - edev->addrlen = 7; + edev->addrlen = ilog2(edev->size); else if (pd->flags & EE_ADDR16) - edev->addrlen = 6; + edev->addrlen = ilog2(edev->size) - 1; else { dev_err(&spi->dev, "unspecified address type\n"); return -EINVAL; @@ -469,7 +505,6 @@ static int eeprom_93xx46_probe(struct spi_device *spi) edev->spi = spi; edev->pdata = pd; - edev->size = 128; edev->nvmem_config.type = NVMEM_TYPE_EEPROM; edev->nvmem_config.name = dev_name(&spi->dev); edev->nvmem_config.dev = &spi->dev; @@ -489,8 +524,9 @@ static int eeprom_93xx46_probe(struct spi_device *spi) if (IS_ERR(edev->nvmem)) return PTR_ERR(edev->nvmem); - dev_info(&spi->dev, "%d-bit eeprom %s\n", + dev_info(&spi->dev, "%d-bit eeprom containing %d bytes %s\n", (pd->flags & EE_ADDR8) ? 8 : 16, + edev->size, (pd->flags & EE_READONLY) ? "(readonly)" : ""); if (!(pd->flags & EE_READONLY)) { diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 81c70e5bc168..b0cff4b152da 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1,38 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * This file is provided under a GPLv2 license. When using or - * redistributing this file, you may do so under that license. - * - * GPL LICENSE SUMMARY - * * Copyright (C) 2016 T-Platforms. All Rights Reserved. * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, it can be found <http://www.gnu.org/licenses/>. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * * IDT PCIe-switch NTB Linux driver * * Contact Information: @@ -1126,11 +1095,10 @@ static void idt_get_fw_data(struct idt_89hpesx_dev *pdev) device_for_each_child_node(dev, fwnode) { ee_id = idt_ee_match_id(fwnode); - if (!ee_id) { - dev_warn(dev, "Skip unsupported EEPROM device"); - continue; - } else + if (ee_id) break; + + dev_warn(dev, "Skip unsupported EEPROM device %pfw\n", fwnode); } /* If there is no fwnode EEPROM device, then set zero size */ @@ -1161,6 +1129,7 @@ static void idt_get_fw_data(struct idt_89hpesx_dev *pdev) else /* if (!fwnode_property_read_bool(node, "read-only")) */ pdev->eero = false; + fwnode_handle_put(fwnode); dev_info(dev, "EEPROM of %d bytes found by 0x%x", pdev->eesize, pdev->eeaddr); } diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index af3c497defb1..80c60fb41bbc 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -556,6 +556,13 @@ out: else if (!cs->submitted) cs->fence->error = -EBUSY; + if (unlikely(cs->skip_reset_on_timeout)) { + dev_err(hdev->dev, + "Command submission %llu completed after %llu (s)\n", + cs->sequence, + div_u64(jiffies - cs->submission_time_jiffies, HZ)); + } + if (cs->timestamp) cs->fence->timestamp = ktime_get(); complete_all(&cs->fence->completion); @@ -571,6 +578,8 @@ static void cs_timedout(struct work_struct *work) int rc; struct hl_cs *cs = container_of(work, struct hl_cs, work_tdr.work); + bool skip_reset_on_timeout = cs->skip_reset_on_timeout; + rc = cs_get_unless_zero(cs); if (!rc) return; @@ -581,7 +590,8 @@ static void cs_timedout(struct work_struct *work) } /* Mark the CS is timed out so we won't try to cancel its TDR */ - cs->timedout = true; + if (likely(!skip_reset_on_timeout)) + cs->timedout = true; hdev = cs->ctx->hdev; @@ -613,10 +623,12 @@ static void cs_timedout(struct work_struct *work) cs_put(cs); - if (hdev->reset_on_lockup) - hl_device_reset(hdev, 0); - else - hdev->needs_reset = true; + if (likely(!skip_reset_on_timeout)) { + if (hdev->reset_on_lockup) + hl_device_reset(hdev, HL_RESET_TDR); + else + hdev->needs_reset = true; + } } static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, @@ -650,6 +662,10 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, cs->type = cs_type; cs->timestamp = !!(flags & HL_CS_FLAGS_TIMESTAMP); cs->timeout_jiffies = timeout; + cs->skip_reset_on_timeout = + hdev->skip_reset_on_timeout || + !!(flags & HL_CS_FLAGS_SKIP_RESET_ON_TIMEOUT); + cs->submission_time_jiffies = jiffies; INIT_LIST_HEAD(&cs->job_list); INIT_DELAYED_WORK(&cs->work_tdr, cs_timedout); kref_init(&cs->refcount); @@ -1481,6 +1497,61 @@ out: return rc; } +/* + * hl_cs_signal_sob_wraparound_handler: handle SOB value wrapaound case. + * if the SOB value reaches the max value move to the other SOB reserved + * to the queue. + * Note that this function must be called while hw_queues_lock is taken. + */ +int hl_cs_signal_sob_wraparound_handler(struct hl_device *hdev, u32 q_idx, + struct hl_hw_sob **hw_sob, u32 count) +{ + struct hl_sync_stream_properties *prop; + struct hl_hw_sob *sob = *hw_sob, *other_sob; + u8 other_sob_offset; + + prop = &hdev->kernel_queues[q_idx].sync_stream_prop; + + kref_get(&sob->kref); + + /* check for wraparound */ + if (prop->next_sob_val + count >= HL_MAX_SOB_VAL) { + /* + * Decrement as we reached the max value. + * The release function won't be called here as we've + * just incremented the refcount right before calling this + * function. + */ + kref_put(&sob->kref, hl_sob_reset_error); + + /* + * check the other sob value, if it still in use then fail + * otherwise make the switch + */ + other_sob_offset = (prop->curr_sob_offset + 1) % HL_RSVD_SOBS; + other_sob = &prop->hw_sob[other_sob_offset]; + + if (kref_read(&other_sob->kref) != 1) { + dev_err(hdev->dev, "error: Cannot switch SOBs q_idx: %d\n", + q_idx); + return -EINVAL; + } + + prop->next_sob_val = 1; + + /* only two SOBs are currently in use */ + prop->curr_sob_offset = other_sob_offset; + *hw_sob = other_sob; + + dev_dbg(hdev->dev, "switched to SOB %d, q_idx: %d\n", + prop->curr_sob_offset, q_idx); + } else { + prop->next_sob_val += count; + } + + return 0; +} + static int cs_ioctl_extract_signal_seq(struct hl_device *hdev, struct hl_cs_chunk *chunk, u64 *signal_seq, struct hl_ctx *ctx) { diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index 62d705889ca8..19b6b045219e 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -12,7 +12,6 @@ static void hl_ctx_fini(struct hl_ctx *ctx) { struct hl_device *hdev = ctx->hdev; - u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; int i; /* Release all allocated pending cb's, those cb's were never @@ -57,14 +56,6 @@ static void hl_ctx_fini(struct hl_ctx *ctx) /* Scrub both SRAM and DRAM */ hdev->asic_funcs->scrub_device_mem(hdev, 0, 0); - - if ((!hdev->pldm) && (hdev->pdev) && - (!hdev->asic_funcs->is_device_idle(hdev, - idle_mask, - HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL))) - dev_notice(hdev->dev, - "device not idle after user context is closed (0x%llx, 0x%llx)\n", - idle_mask[0], idle_mask[1]); } else { dev_dbg(hdev->dev, "closing kernel context\n"); hdev->asic_funcs->ctx_fini(ctx); diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 8381155578a0..703d79fb6f3f 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -1278,6 +1278,11 @@ void hl_debugfs_add_device(struct hl_device *hdev) dev_entry->root, &dev_entry->blob_desc); + debugfs_create_x8("skip_reset_on_timeout", + 0644, + dev_entry->root, + &hdev->skip_reset_on_timeout); + for (i = 0, entry = dev_entry->entry_arr ; i < count ; i++, entry++) { debugfs_create_file(hl_debugfs_list[i].name, 0444, diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 00e92b678828..ff4cbde289c0 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -51,6 +51,8 @@ bool hl_device_operational(struct hl_device *hdev, static void hpriv_release(struct kref *ref) { + u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; + bool device_is_idle = true; struct hl_fpriv *hpriv; struct hl_device *hdev; @@ -71,8 +73,20 @@ static void hpriv_release(struct kref *ref) kfree(hpriv); - if (hdev->reset_upon_device_release) - hl_device_reset(hdev, 0); + if ((!hdev->pldm) && (hdev->pdev) && + (!hdev->asic_funcs->is_device_idle(hdev, + idle_mask, + HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL))) { + dev_err(hdev->dev, + "device not idle after user context is closed (0x%llx_%llx)\n", + idle_mask[1], idle_mask[0]); + + device_is_idle = false; + } + + if ((hdev->reset_if_device_not_idle && !device_is_idle) + || hdev->reset_upon_device_release) + hl_device_reset(hdev, HL_RESET_DEVICE_RELEASE); } void hl_hpriv_get(struct hl_fpriv *hpriv) @@ -118,6 +132,9 @@ static int hl_device_release(struct inode *inode, struct file *filp) dev_warn(hdev->dev, "Device is still in use because there are live CS and/or memory mappings\n"); + hdev->last_open_session_duration_jif = + jiffies - hdev->last_successful_open_jif; + return 0; } @@ -868,7 +885,7 @@ static void device_disable_open_processes(struct hl_device *hdev) int hl_device_reset(struct hl_device *hdev, u32 flags) { u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; - bool hard_reset, from_hard_reset_thread; + bool hard_reset, from_hard_reset_thread, hard_instead_soft = false; int i, rc; if (!hdev->init_done) { @@ -880,11 +897,28 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) hard_reset = (flags & HL_RESET_HARD) != 0; from_hard_reset_thread = (flags & HL_RESET_FROM_RESET_THREAD) != 0; - if ((!hard_reset) && (!hdev->supports_soft_reset)) { - dev_dbg(hdev->dev, "Doing hard-reset instead of soft-reset\n"); + if (!hard_reset && !hdev->supports_soft_reset) { + hard_instead_soft = true; + hard_reset = true; + } + + if (hdev->reset_upon_device_release && + (flags & HL_RESET_DEVICE_RELEASE)) { + dev_dbg(hdev->dev, + "Perform %s-reset upon device release\n", + hard_reset ? "hard" : "soft"); + goto do_reset; + } + + if (!hard_reset && !hdev->allow_external_soft_reset) { + hard_instead_soft = true; hard_reset = true; } + if (hard_instead_soft) + dev_dbg(hdev->dev, "Doing hard-reset instead of soft-reset\n"); + +do_reset: /* Re-entry of reset thread */ if (from_hard_reset_thread && hdev->process_kill_trial_cnt) goto kill_processes; @@ -901,6 +935,19 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) return 0; /* + * 'reset cause' is being updated here, because getting here + * means that it's the 1st time and the last time we're here + * ('in_reset' makes sure of it). This makes sure that + * 'reset_cause' will continue holding its 1st recorded reason! + */ + if (flags & HL_RESET_HEARTBEAT) + hdev->curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT; + else if (flags & HL_RESET_TDR) + hdev->curr_reset_cause = HL_RESET_CAUSE_TDR; + else + hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + + /* * if reset is due to heartbeat, device CPU is no responsive in * which case no point sending PCI disable message to it */ @@ -943,9 +990,8 @@ again: hdev->process_kill_trial_cnt = 0; /* - * Because the reset function can't run from interrupt or - * from heartbeat work, we need to call the reset function - * from a dedicated work + * Because the reset function can't run from heartbeat work, + * we need to call the reset function from a dedicated work. */ queue_delayed_work(hdev->device_reset_work.wq, &hdev->device_reset_work.reset_work, 0); @@ -1096,8 +1142,8 @@ kill_processes: if (!hdev->asic_funcs->is_device_idle(hdev, idle_mask, HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL)) { dev_err(hdev->dev, - "device is not idle (mask %#llx %#llx) after reset\n", - idle_mask[0], idle_mask[1]); + "device is not idle (mask 0x%llx_%llx) after reset\n", + idle_mask[1], idle_mask[0]); rc = -EIO; goto out_err; } @@ -1334,8 +1380,9 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) } /* - * From this point, in case of an error, add char devices and create - * sysfs nodes as part of the error flow, to allow debugging. + * From this point, override rc (=0) in case of an error to allow + * debugging (by adding char devices and create sysfs nodes as part of + * the error flow). */ add_cdev_sysfs_on_err = true; @@ -1369,7 +1416,7 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) dev_info(hdev->dev, "Found %s device with %lluGB DRAM\n", hdev->asic_name, - hdev->asic_prop.dram_size / 1024 / 1024 / 1024); + hdev->asic_prop.dram_size / SZ_1G); rc = hl_vm_init(hdev); if (rc) { @@ -1475,6 +1522,7 @@ out_disabled: void hl_device_fini(struct hl_device *hdev) { ktime_t timeout; + u64 reset_sec; int i, rc; dev_info(hdev->dev, "Removing device\n"); @@ -1482,6 +1530,11 @@ void hl_device_fini(struct hl_device *hdev) hdev->device_fini_pending = 1; flush_delayed_work(&hdev->device_reset_work.reset_work); + if (hdev->pldm) + reset_sec = HL_PLDM_HARD_RESET_MAX_TIMEOUT; + else + reset_sec = HL_HARD_RESET_MAX_TIMEOUT; + /* * This function is competing with the reset function, so try to * take the reset atomic and if we are already in middle of reset, @@ -1490,8 +1543,7 @@ void hl_device_fini(struct hl_device *hdev) * ports, the hard reset could take between 10-30 seconds */ - timeout = ktime_add_us(ktime_get(), - HL_HARD_RESET_MAX_TIMEOUT * 1000 * 1000); + timeout = ktime_add_us(ktime_get(), reset_sec * 1000 * 1000); rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); while (rc) { usleep_range(50, 200); diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 0713b2c12d54..2e4d04ec6b53 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -9,42 +9,63 @@ #include "../include/common/hl_boot_if.h" #include <linux/firmware.h> +#include <linux/crc32.h> #include <linux/slab.h> +#include <linux/ctype.h> -#define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */ -/** - * hl_fw_load_fw_to_device() - Load F/W code to device's memory. - * - * @hdev: pointer to hl_device structure. - * @fw_name: the firmware image name - * @dst: IO memory mapped address space to copy firmware to - * @src_offset: offset in src FW to copy from - * @size: amount of bytes to copy (0 to copy the whole binary) - * - * Copy fw code from firmware file to device memory. - * - * Return: 0 on success, non-zero for failure. - */ -int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, - void __iomem *dst, u32 src_offset, u32 size) +#define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */ + +#define FW_CPU_STATUS_POLL_INTERVAL_USEC 10000 + +static char *extract_fw_ver_from_str(const char *fw_str) +{ + char *str, *fw_ver, *whitespace; + + fw_ver = kmalloc(16, GFP_KERNEL); + if (!fw_ver) + return NULL; + + str = strnstr(fw_str, "fw-", VERSION_MAX_LEN); + if (!str) + goto free_fw_ver; + + /* Skip the fw- part */ + str += 3; + + /* Copy until the next whitespace */ + whitespace = strnstr(str, " ", 15); + if (!whitespace) + goto free_fw_ver; + + strscpy(fw_ver, str, whitespace - str + 1); + + return fw_ver; + +free_fw_ver: + kfree(fw_ver); + return NULL; +} + +static int hl_request_fw(struct hl_device *hdev, + const struct firmware **firmware_p, + const char *fw_name) { - const struct firmware *fw; - const void *fw_data; size_t fw_size; int rc; - rc = request_firmware(&fw, fw_name, hdev->dev); + rc = request_firmware(firmware_p, fw_name, hdev->dev); if (rc) { - dev_err(hdev->dev, "Firmware file %s is not found!\n", fw_name); + dev_err(hdev->dev, "Firmware file %s is not found! (error %d)\n", + fw_name, rc); goto out; } - fw_size = fw->size; + fw_size = (*firmware_p)->size; if ((fw_size % 4) != 0) { dev_err(hdev->dev, "Illegal %s firmware size %zu\n", - fw_name, fw_size); + fw_name, fw_size); rc = -EINVAL; - goto out; + goto release_fw; } dev_dbg(hdev->dev, "%s firmware size == %zu\n", fw_name, fw_size); @@ -54,26 +75,125 @@ int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, "FW file size %zu exceeds maximum of %u bytes\n", fw_size, FW_FILE_MAX_SIZE); rc = -EINVAL; - goto out; + goto release_fw; } - if (size - src_offset > fw_size) { + return 0; + +release_fw: + release_firmware(*firmware_p); +out: + return rc; +} + +/** + * hl_release_firmware() - release FW + * + * @fw: fw descriptor + * + * note: this inline function added to serve as a comprehensive mirror for the + * hl_request_fw function. + */ +static inline void hl_release_firmware(const struct firmware *fw) +{ + release_firmware(fw); +} + +/** + * hl_fw_copy_fw_to_device() - copy FW to device + * + * @hdev: pointer to hl_device structure. + * @fw: fw descriptor + * @dst: IO memory mapped address space to copy firmware to + * @src_offset: offset in src FW to copy from + * @size: amount of bytes to copy (0 to copy the whole binary) + * + * actual copy of FW binary data to device, shared by static and dynamic loaders + */ +static int hl_fw_copy_fw_to_device(struct hl_device *hdev, + const struct firmware *fw, void __iomem *dst, + u32 src_offset, u32 size) +{ + const void *fw_data; + + /* size 0 indicates to copy the whole file */ + if (!size) + size = fw->size; + + if (src_offset + size > fw->size) { dev_err(hdev->dev, "size to copy(%u) and offset(%u) are invalid\n", size, src_offset); - rc = -EINVAL; - goto out; + return -EINVAL; } - if (size) - fw_size = size; - fw_data = (const void *) fw->data; - memcpy_toio(dst, fw_data + src_offset, fw_size); + memcpy_toio(dst, fw_data + src_offset, size); + return 0; +} -out: - release_firmware(fw); +/** + * hl_fw_copy_msg_to_device() - copy message to device + * + * @hdev: pointer to hl_device structure. + * @msg: message + * @dst: IO memory mapped address space to copy firmware to + * @src_offset: offset in src message to copy from + * @size: amount of bytes to copy (0 to copy the whole binary) + * + * actual copy of message data to device. + */ +static int hl_fw_copy_msg_to_device(struct hl_device *hdev, + struct lkd_msg_comms *msg, void __iomem *dst, + u32 src_offset, u32 size) +{ + void *msg_data; + + /* size 0 indicates to copy the whole file */ + if (!size) + size = sizeof(struct lkd_msg_comms); + + if (src_offset + size > sizeof(struct lkd_msg_comms)) { + dev_err(hdev->dev, + "size to copy(%u) and offset(%u) are invalid\n", + size, src_offset); + return -EINVAL; + } + + msg_data = (void *) msg; + + memcpy_toio(dst, msg_data + src_offset, size); + + return 0; +} + +/** + * hl_fw_load_fw_to_device() - Load F/W code to device's memory. + * + * @hdev: pointer to hl_device structure. + * @fw_name: the firmware image name + * @dst: IO memory mapped address space to copy firmware to + * @src_offset: offset in src FW to copy from + * @size: amount of bytes to copy (0 to copy the whole binary) + * + * Copy fw code from firmware file to device memory. + * + * Return: 0 on success, non-zero for failure. + */ +int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, + void __iomem *dst, u32 src_offset, u32 size) +{ + const struct firmware *fw; + int rc; + + rc = hl_request_fw(hdev, &fw, fw_name); + if (rc) + return rc; + + rc = hl_fw_copy_fw_to_device(hdev, fw, dst, src_offset, size); + + hl_release_firmware(fw); return rc; } @@ -91,6 +211,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, u16 len, u32 timeout, u64 *result) { struct hl_hw_queue *queue = &hdev->kernel_queues[hw_queue_id]; + struct asic_fixed_properties *prop = &hdev->asic_prop; struct cpucp_packet *pkt; dma_addr_t pkt_dma_addr; u32 tmp, expected_ack_val; @@ -117,7 +238,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, } /* set fence to a non valid value */ - pkt->fence = UINT_MAX; + pkt->fence = cpu_to_le32(UINT_MAX); rc = hl_hw_queue_send_cb_no_cmpl(hdev, hw_queue_id, len, pkt_dma_addr); if (rc) { @@ -125,8 +246,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, goto out; } - if (hdev->asic_prop.fw_app_security_map & - CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN) + if (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN) expected_ack_val = queue->pi; else expected_ack_val = CPUCP_PACKET_FENCE_VAL; @@ -272,10 +392,11 @@ void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, int hl_fw_send_heartbeat(struct hl_device *hdev) { - struct cpucp_packet hb_pkt = {}; + struct cpucp_packet hb_pkt; u64 result; int rc; + memset(&hb_pkt, 0, sizeof(hb_pkt)); hb_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST << CPUCP_PKT_CTL_OPCODE_SHIFT); hb_pkt.value = cpu_to_le64(CPUCP_PACKET_FENCE_VAL); @@ -284,29 +405,24 @@ int hl_fw_send_heartbeat(struct hl_device *hdev) sizeof(hb_pkt), 0, &result); if ((rc) || (result != CPUCP_PACKET_FENCE_VAL)) + return -EIO; + + if (le32_to_cpu(hb_pkt.status_mask) & + CPUCP_PKT_HB_STATUS_EQ_FAULT_MASK) { + dev_warn(hdev->dev, "FW reported EQ fault during heartbeat\n"); rc = -EIO; + } return rc; } -static int fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg, - u32 cpu_security_boot_status_reg) +static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, + u32 sts_val) { - u32 err_val, security_val; bool err_exists = false; - /* Some of the firmware status codes are deprecated in newer f/w - * versions. In those versions, the errors are reported - * in different registers. Therefore, we need to check those - * registers and print the exact errors. Moreover, there - * may be multiple errors, so we need to report on each error - * separately. Some of the error codes might indicate a state - * that is not an error per-se, but it is an error in production - * environment - */ - err_val = RREG32(boot_err0_reg); if (!(err_val & CPU_BOOT_ERR0_ENABLED)) - return 0; + return false; if (err_val & CPU_BOOT_ERR0_DRAM_INIT_FAIL) { dev_err(hdev->dev, @@ -377,44 +493,120 @@ static int fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg, err_exists = true; } + if (err_val & CPU_BOOT_ERR0_PRI_IMG_VER_FAIL) { + dev_warn(hdev->dev, + "Device boot warning - Failed to load preboot primary image\n"); + /* This is a warning so we don't want it to disable the + * device as we have a secondary preboot image + */ + err_val &= ~CPU_BOOT_ERR0_PRI_IMG_VER_FAIL; + } + + if (err_val & CPU_BOOT_ERR0_SEC_IMG_VER_FAIL) { + dev_err(hdev->dev, "Device boot error - Failed to load preboot secondary image\n"); + err_exists = true; + } + if (err_val & CPU_BOOT_ERR0_PLL_FAIL) { dev_err(hdev->dev, "Device boot error - PLL failure\n"); err_exists = true; } if (err_val & CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL) { - dev_err(hdev->dev, - "Device boot error - device unusable\n"); - err_exists = true; + /* Ignore this bit, don't prevent driver loading */ + dev_dbg(hdev->dev, "device unusable status is set\n"); + err_val &= ~CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL; } - security_val = RREG32(cpu_security_boot_status_reg); - if (security_val & CPU_BOOT_DEV_STS0_ENABLED) - dev_dbg(hdev->dev, "Device security status %#x\n", - security_val); + if (sts_val & CPU_BOOT_DEV_STS0_ENABLED) + dev_dbg(hdev->dev, "Device status0 %#x\n", sts_val); if (!err_exists && (err_val & ~CPU_BOOT_ERR0_ENABLED)) { dev_err(hdev->dev, - "Device boot error - unknown error 0x%08x\n", - err_val); + "Device boot error - unknown ERR0 error 0x%08x\n", err_val); err_exists = true; } + /* return error only if it's in the predefined mask */ if (err_exists && ((err_val & ~CPU_BOOT_ERR0_ENABLED) & lower_32_bits(hdev->boot_error_status_mask))) + return true; + + return false; +} + +/* placeholder for ERR1 as no errors defined there yet */ +static bool fw_report_boot_dev1(struct hl_device *hdev, u32 err_val, + u32 sts_val) +{ + /* + * keep this variable to preserve the logic of the function. + * this way it would require less modifications when error will be + * added to DEV_ERR1 + */ + bool err_exists = false; + + if (!(err_val & CPU_BOOT_ERR1_ENABLED)) + return false; + + if (sts_val & CPU_BOOT_DEV_STS1_ENABLED) + dev_dbg(hdev->dev, "Device status1 %#x\n", sts_val); + + if (!err_exists && (err_val & ~CPU_BOOT_ERR1_ENABLED)) { + dev_err(hdev->dev, + "Device boot error - unknown ERR1 error 0x%08x\n", + err_val); + err_exists = true; + } + + /* return error only if it's in the predefined mask */ + if (err_exists && ((err_val & ~CPU_BOOT_ERR1_ENABLED) & + upper_32_bits(hdev->boot_error_status_mask))) + return true; + + return false; +} + +static int fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg, + u32 boot_err1_reg, u32 cpu_boot_dev_status0_reg, + u32 cpu_boot_dev_status1_reg) +{ + u32 err_val, status_val; + bool err_exists = false; + + /* Some of the firmware status codes are deprecated in newer f/w + * versions. In those versions, the errors are reported + * in different registers. Therefore, we need to check those + * registers and print the exact errors. Moreover, there + * may be multiple errors, so we need to report on each error + * separately. Some of the error codes might indicate a state + * that is not an error per-se, but it is an error in production + * environment + */ + err_val = RREG32(boot_err0_reg); + status_val = RREG32(cpu_boot_dev_status0_reg); + err_exists = fw_report_boot_dev0(hdev, err_val, status_val); + + err_val = RREG32(boot_err1_reg); + status_val = RREG32(cpu_boot_dev_status1_reg); + err_exists |= fw_report_boot_dev1(hdev, err_val, status_val); + + if (err_exists) return -EIO; return 0; } int hl_fw_cpucp_info_get(struct hl_device *hdev, - u32 cpu_security_boot_status_reg, - u32 boot_err0_reg) + u32 sts_boot_dev_sts0_reg, + u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, + u32 boot_err1_reg) { struct asic_fixed_properties *prop = &hdev->asic_prop; struct cpucp_packet pkt = {}; - void *cpucp_info_cpu_addr; dma_addr_t cpucp_info_dma_addr; + void *cpucp_info_cpu_addr; + char *kernel_ver; u64 result; int rc; @@ -443,7 +635,8 @@ int hl_fw_cpucp_info_get(struct hl_device *hdev, goto out; } - rc = fw_read_errors(hdev, boot_err0_reg, cpu_security_boot_status_reg); + rc = fw_read_errors(hdev, boot_err0_reg, boot_err1_reg, + sts_boot_dev_sts0_reg, sts_boot_dev_sts1_reg); if (rc) { dev_err(hdev->dev, "Errors in device boot\n"); goto out; @@ -460,10 +653,27 @@ int hl_fw_cpucp_info_get(struct hl_device *hdev, goto out; } + kernel_ver = extract_fw_ver_from_str(prop->cpucp_info.kernel_version); + if (kernel_ver) { + dev_info(hdev->dev, "Linux version %s", kernel_ver); + kfree(kernel_ver); + } + + /* assume EQ code doesn't need to check eqe index */ + hdev->event_queue.check_eqe_index = false; + /* Read FW application security bits again */ - if (hdev->asic_prop.fw_security_status_valid) - hdev->asic_prop.fw_app_security_map = - RREG32(cpu_security_boot_status_reg); + if (hdev->asic_prop.fw_cpu_boot_dev_sts0_valid) { + hdev->asic_prop.fw_app_cpu_boot_dev_sts0 = + RREG32(sts_boot_dev_sts0_reg); + if (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_EQ_INDEX_EN) + hdev->event_queue.check_eqe_index = true; + } + + if (hdev->asic_prop.fw_cpu_boot_dev_sts1_valid) + hdev->asic_prop.fw_app_cpu_boot_dev_sts1 = + RREG32(sts_boot_dev_sts1_reg); out: hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, @@ -501,7 +711,8 @@ static int hl_fw_send_msi_info_msg(struct hl_device *hdev) pkt->length = cpu_to_le32(CPUCP_NUM_OF_MSI_TYPES); - hdev->asic_funcs->get_msi_info((u32 *)&pkt->data); + memset((void *) &pkt->data, 0xFF, data_size); + hdev->asic_funcs->get_msi_info(pkt->data); pkt->cpucp_pkt.ctl = cpu_to_le32(CPUCP_PACKET_MSI_INFO_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); @@ -526,13 +737,15 @@ static int hl_fw_send_msi_info_msg(struct hl_device *hdev) } int hl_fw_cpucp_handshake(struct hl_device *hdev, - u32 cpu_security_boot_status_reg, - u32 boot_err0_reg) + u32 sts_boot_dev_sts0_reg, + u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, + u32 boot_err1_reg) { int rc; - rc = hl_fw_cpucp_info_get(hdev, cpu_security_boot_status_reg, - boot_err0_reg); + rc = hl_fw_cpucp_info_get(hdev, sts_boot_dev_sts0_reg, + sts_boot_dev_sts1_reg, boot_err0_reg, + boot_err1_reg); if (rc) return rc; @@ -667,8 +880,8 @@ int get_used_pll_index(struct hl_device *hdev, u32 input_pll_index, bool dynamic_pll; int fw_pll_idx; - dynamic_pll = prop->fw_security_status_valid && - (prop->fw_app_security_map & CPU_BOOT_DEV_STS0_DYN_PLL_EN); + dynamic_pll = !!(prop->fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_DYN_PLL_EN); if (!dynamic_pll) { /* @@ -759,6 +972,47 @@ int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power) return rc; } +void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev) +{ + struct static_fw_load_mgr *static_loader = + &hdev->fw_loader.static_loader; + int rc; + + if (hdev->asic_prop.dynamic_fw_load) { + rc = hl_fw_dynamic_send_protocol_cmd(hdev, &hdev->fw_loader, + COMMS_RST_DEV, 0, false, + hdev->fw_loader.cpu_timeout); + if (rc) + dev_warn(hdev->dev, "Failed sending COMMS_RST_DEV\n"); + } else { + WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_RST_DEV); + } +} + +void hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev) +{ + struct static_fw_load_mgr *static_loader = + &hdev->fw_loader.static_loader; + int rc; + + if (hdev->device_cpu_is_halted) + return; + + /* Stop device CPU to make sure nothing bad happens */ + if (hdev->asic_prop.dynamic_fw_load) { + rc = hl_fw_dynamic_send_protocol_cmd(hdev, &hdev->fw_loader, + COMMS_GOTO_WFE, 0, true, + hdev->fw_loader.cpu_timeout); + if (rc) + dev_warn(hdev->dev, "Failed sending COMMS_GOTO_WFE\n"); + } else { + WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_GOTO_WFE); + msleep(static_loader->cpu_reset_wait_msec); + } + + hdev->device_cpu_is_halted = true; +} + static void detect_cpu_boot_status(struct hl_device *hdev, u32 status) { /* Some of the status codes below are deprecated in newer f/w @@ -767,63 +1021,59 @@ static void detect_cpu_boot_status(struct hl_device *hdev, u32 status) switch (status) { case CPU_BOOT_STATUS_NA: dev_err(hdev->dev, - "Device boot error - BTL did NOT run\n"); + "Device boot progress - BTL did NOT run\n"); break; case CPU_BOOT_STATUS_IN_WFE: dev_err(hdev->dev, - "Device boot error - Stuck inside WFE loop\n"); + "Device boot progress - Stuck inside WFE loop\n"); break; case CPU_BOOT_STATUS_IN_BTL: dev_err(hdev->dev, - "Device boot error - Stuck in BTL\n"); + "Device boot progress - Stuck in BTL\n"); break; case CPU_BOOT_STATUS_IN_PREBOOT: dev_err(hdev->dev, - "Device boot error - Stuck in Preboot\n"); + "Device boot progress - Stuck in Preboot\n"); break; case CPU_BOOT_STATUS_IN_SPL: dev_err(hdev->dev, - "Device boot error - Stuck in SPL\n"); + "Device boot progress - Stuck in SPL\n"); break; case CPU_BOOT_STATUS_IN_UBOOT: dev_err(hdev->dev, - "Device boot error - Stuck in u-boot\n"); + "Device boot progress - Stuck in u-boot\n"); break; case CPU_BOOT_STATUS_DRAM_INIT_FAIL: dev_err(hdev->dev, - "Device boot error - DRAM initialization failed\n"); + "Device boot progress - DRAM initialization failed\n"); break; case CPU_BOOT_STATUS_UBOOT_NOT_READY: dev_err(hdev->dev, - "Device boot error - u-boot stopped by user\n"); + "Device boot progress - Cannot boot\n"); break; case CPU_BOOT_STATUS_TS_INIT_FAIL: dev_err(hdev->dev, - "Device boot error - Thermal Sensor initialization failed\n"); + "Device boot progress - Thermal Sensor initialization failed\n"); break; default: dev_err(hdev->dev, - "Device boot error - Invalid status code %d\n", + "Device boot progress - Invalid status code %d\n", status); break; } } -int hl_fw_read_preboot_status(struct hl_device *hdev, u32 cpu_boot_status_reg, - u32 cpu_security_boot_status_reg, u32 boot_err0_reg, - u32 timeout) +static int hl_fw_read_preboot_caps(struct hl_device *hdev, + u32 cpu_boot_status_reg, + u32 sts_boot_dev_sts0_reg, + u32 sts_boot_dev_sts1_reg, + u32 boot_err0_reg, u32 boot_err1_reg, + u32 timeout) { struct asic_fixed_properties *prop = &hdev->asic_prop; - u32 status, security_status; + u32 status, reg_val; int rc; - /* pldm was added for cases in which we use preboot on pldm and want - * to load boot fit, but we can't wait for preboot because it runs - * very slowly - */ - if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU) || hdev->pldm) - return 0; - /* Need to check two possible scenarios: * * CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT - for newer firmwares where @@ -842,29 +1092,145 @@ int hl_fw_read_preboot_status(struct hl_device *hdev, u32 cpu_boot_status_reg, (status == CPU_BOOT_STATUS_READY_TO_BOOT) || (status == CPU_BOOT_STATUS_SRAM_AVAIL) || (status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT), - 10000, + FW_CPU_STATUS_POLL_INTERVAL_USEC, timeout); if (rc) { - dev_err(hdev->dev, "Failed to read preboot version\n"); + dev_err(hdev->dev, "CPU boot ready status timeout\n"); detect_cpu_boot_status(hdev, status); /* If we read all FF, then something is totally wrong, no point * of reading specific errors */ if (status != -1) - fw_read_errors(hdev, boot_err0_reg, - cpu_security_boot_status_reg); + fw_read_errors(hdev, boot_err0_reg, boot_err1_reg, + sts_boot_dev_sts0_reg, + sts_boot_dev_sts1_reg); return -EIO; } - rc = hdev->asic_funcs->read_device_fw_version(hdev, FW_COMP_PREBOOT); - if (rc) - return rc; + /* + * the registers DEV_STS* contain FW capabilities/features. + * We can rely on this registers only if bit CPU_BOOT_DEV_STS*_ENABLED + * is set. + * In the first read of this register we store the value of this + * register ONLY if the register is enabled (which will be propagated + * to next stages) and also mark the register as valid. + * In case it is not enabled the stored value will be left 0- all + * caps/features are off + */ + reg_val = RREG32(sts_boot_dev_sts0_reg); + if (reg_val & CPU_BOOT_DEV_STS0_ENABLED) { + prop->fw_cpu_boot_dev_sts0_valid = true; + prop->fw_preboot_cpu_boot_dev_sts0 = reg_val; + } + + reg_val = RREG32(sts_boot_dev_sts1_reg); + if (reg_val & CPU_BOOT_DEV_STS1_ENABLED) { + prop->fw_cpu_boot_dev_sts1_valid = true; + prop->fw_preboot_cpu_boot_dev_sts1 = reg_val; + } - security_status = RREG32(cpu_security_boot_status_reg); + prop->dynamic_fw_load = !!(prop->fw_preboot_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_FW_LD_COM_EN); + + /* initialize FW loader once we know what load protocol is used */ + hdev->asic_funcs->init_firmware_loader(hdev); + + dev_dbg(hdev->dev, "Attempting %s FW load\n", + prop->dynamic_fw_load ? "dynamic" : "legacy"); + return 0; +} - /* We read security status multiple times during boot: +static int hl_fw_static_read_device_fw_version(struct hl_device *hdev, + enum hl_fw_component fwc) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct fw_load_mgr *fw_loader = &hdev->fw_loader; + struct static_fw_load_mgr *static_loader; + char *dest, *boot_ver, *preboot_ver; + u32 ver_off, limit; + const char *name; + char btl_ver[32]; + + static_loader = &hdev->fw_loader.static_loader; + + switch (fwc) { + case FW_COMP_BOOT_FIT: + ver_off = RREG32(static_loader->boot_fit_version_offset_reg); + dest = prop->uboot_ver; + name = "Boot-fit"; + limit = static_loader->boot_fit_version_max_off; + break; + case FW_COMP_PREBOOT: + ver_off = RREG32(static_loader->preboot_version_offset_reg); + dest = prop->preboot_ver; + name = "Preboot"; + limit = static_loader->preboot_version_max_off; + break; + default: + dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc); + return -EIO; + } + + ver_off &= static_loader->sram_offset_mask; + + if (ver_off < limit) { + memcpy_fromio(dest, + hdev->pcie_bar[fw_loader->sram_bar_id] + ver_off, + VERSION_MAX_LEN); + } else { + dev_err(hdev->dev, "%s version offset (0x%x) is above SRAM\n", + name, ver_off); + strscpy(dest, "unavailable", VERSION_MAX_LEN); + return -EIO; + } + + if (fwc == FW_COMP_BOOT_FIT) { + boot_ver = extract_fw_ver_from_str(prop->uboot_ver); + if (boot_ver) { + dev_info(hdev->dev, "boot-fit version %s\n", boot_ver); + kfree(boot_ver); + } + } else if (fwc == FW_COMP_PREBOOT) { + preboot_ver = strnstr(prop->preboot_ver, "Preboot", + VERSION_MAX_LEN); + if (preboot_ver && preboot_ver != prop->preboot_ver) { + strscpy(btl_ver, prop->preboot_ver, + min((int) (preboot_ver - prop->preboot_ver), + 31)); + dev_info(hdev->dev, "%s\n", btl_ver); + } + + preboot_ver = extract_fw_ver_from_str(prop->preboot_ver); + if (preboot_ver) { + dev_info(hdev->dev, "preboot version %s\n", + preboot_ver); + kfree(preboot_ver); + } + } + + return 0; +} + +/** + * hl_fw_preboot_update_state - update internal data structures during + * handshake with preboot + * + * + * @hdev: pointer to the habanalabs device structure + * + * @return 0 on success, otherwise non-zero error code + */ +static void hl_fw_preboot_update_state(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + u32 cpu_boot_dev_sts0, cpu_boot_dev_sts1; + + cpu_boot_dev_sts0 = prop->fw_preboot_cpu_boot_dev_sts0; + cpu_boot_dev_sts1 = prop->fw_preboot_cpu_boot_dev_sts1; + + /* We read boot_dev_sts registers multiple times during boot: * 1. preboot - a. Check whether the security status bits are valid * b. Check whether fw security is enabled * c. Check whether hard reset is done by preboot @@ -874,48 +1240,1121 @@ int hl_fw_read_preboot_status(struct hl_device *hdev, u32 cpu_boot_status_reg, * b. Check whether hard reset is done by fw app * * Preboot: - * Check security status bit (CPU_BOOT_DEV_STS0_ENABLED), if it is set + * Check security status bit (CPU_BOOT_DEV_STS0_ENABLED). If set, then- * check security enabled bit (CPU_BOOT_DEV_STS0_SECURITY_EN) + * If set, then mark GIC controller to be disabled. */ - if (security_status & CPU_BOOT_DEV_STS0_ENABLED) { - prop->fw_security_status_valid = 1; + prop->hard_reset_done_by_fw = + !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); - /* FW security should be derived from PCI ID, we keep this - * check for backward compatibility - */ - if (security_status & CPU_BOOT_DEV_STS0_SECURITY_EN) - prop->fw_security_disabled = false; + dev_dbg(hdev->dev, "Firmware preboot boot device status0 %#x\n", + cpu_boot_dev_sts0); + + dev_dbg(hdev->dev, "Firmware preboot boot device status1 %#x\n", + cpu_boot_dev_sts1); + + dev_dbg(hdev->dev, "Firmware preboot hard-reset is %s\n", + prop->hard_reset_done_by_fw ? "enabled" : "disabled"); + + dev_dbg(hdev->dev, "firmware-level security is %s\n", + prop->fw_security_enabled ? "enabled" : "disabled"); + + dev_dbg(hdev->dev, "GIC controller is %s\n", + prop->gic_interrupts_enable ? "enabled" : "disabled"); +} + +static int hl_fw_static_read_preboot_status(struct hl_device *hdev) +{ + int rc; + + rc = hl_fw_static_read_device_fw_version(hdev, FW_COMP_PREBOOT); + if (rc) + return rc; + + return 0; +} + +int hl_fw_read_preboot_status(struct hl_device *hdev, u32 cpu_boot_status_reg, + u32 sts_boot_dev_sts0_reg, + u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, + u32 boot_err1_reg, u32 timeout) +{ + int rc; + + /* pldm was added for cases in which we use preboot on pldm and want + * to load boot fit, but we can't wait for preboot because it runs + * very slowly + */ + if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU) || hdev->pldm) + return 0; + + /* + * In order to determine boot method (static VS dymanic) we need to + * read the boot caps register + */ + rc = hl_fw_read_preboot_caps(hdev, cpu_boot_status_reg, + sts_boot_dev_sts0_reg, + sts_boot_dev_sts1_reg, boot_err0_reg, + boot_err1_reg, timeout); + if (rc) + return rc; + + hl_fw_preboot_update_state(hdev); - if (security_status & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN) + /* no need to read preboot status in dynamic load */ + if (hdev->asic_prop.dynamic_fw_load) + return 0; + + return hl_fw_static_read_preboot_status(hdev); +} + +/* associate string with COMM status */ +static char *hl_dynamic_fw_status_str[COMMS_STS_INVLD_LAST] = { + [COMMS_STS_NOOP] = "NOOP", + [COMMS_STS_ACK] = "ACK", + [COMMS_STS_OK] = "OK", + [COMMS_STS_ERR] = "ERR", + [COMMS_STS_VALID_ERR] = "VALID_ERR", + [COMMS_STS_TIMEOUT_ERR] = "TIMEOUT_ERR", +}; + +/** + * hl_fw_dynamic_report_error_status - report error status + * + * @hdev: pointer to the habanalabs device structure + * @status: value of FW status register + * @expected_status: the expected status + */ +static void hl_fw_dynamic_report_error_status(struct hl_device *hdev, + u32 status, + enum comms_sts expected_status) +{ + enum comms_sts comm_status = + FIELD_GET(COMMS_STATUS_STATUS_MASK, status); + + if (comm_status < COMMS_STS_INVLD_LAST) + dev_err(hdev->dev, "Device status %s, expected status: %s\n", + hl_dynamic_fw_status_str[comm_status], + hl_dynamic_fw_status_str[expected_status]); + else + dev_err(hdev->dev, "Device status unknown %d, expected status: %s\n", + comm_status, + hl_dynamic_fw_status_str[expected_status]); +} + +/** + * hl_fw_dynamic_send_cmd - send LKD to FW cmd + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @cmd: LKD to FW cmd code + * @size: size of next FW component to be loaded (0 if not necessary) + * + * LDK to FW exact command layout is defined at struct comms_command. + * note: the size argument is used only when the next FW component should be + * loaded, otherwise it shall be 0. the size is used by the FW in later + * protocol stages and when sending only indicating the amount of memory + * to be allocated by the FW to receive the next boot component. + */ +static void hl_fw_dynamic_send_cmd(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + enum comms_cmd cmd, unsigned int size) +{ + struct cpu_dyn_regs *dyn_regs; + u32 val; + + dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; + + val = FIELD_PREP(COMMS_COMMAND_CMD_MASK, cmd); + val |= FIELD_PREP(COMMS_COMMAND_SIZE_MASK, size); + + WREG32(le32_to_cpu(dyn_regs->kmd_msg_to_cpu), val); +} + +/** + * hl_fw_dynamic_extract_fw_response - update the FW response + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @response: FW response + * @status: the status read from CPU status register + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_dynamic_extract_fw_response(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + struct fw_response *response, + u32 status) +{ + response->status = FIELD_GET(COMMS_STATUS_STATUS_MASK, status); + response->ram_offset = FIELD_GET(COMMS_STATUS_OFFSET_MASK, status) << + COMMS_STATUS_OFFSET_ALIGN_SHIFT; + response->ram_type = FIELD_GET(COMMS_STATUS_RAM_TYPE_MASK, status); + + if ((response->ram_type != COMMS_SRAM) && + (response->ram_type != COMMS_DRAM)) { + dev_err(hdev->dev, "FW status: invalid RAM type %u\n", + response->ram_type); + return -EIO; + } + + return 0; +} + +/** + * hl_fw_dynamic_wait_for_status - wait for status in dynamic FW load + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @expected_status: expected status to wait for + * @timeout: timeout for status wait + * + * @return 0 on success, otherwise non-zero error code + * + * waiting for status from FW include polling the FW status register until + * expected status is received or timeout occurs (whatever occurs first). + */ +static int hl_fw_dynamic_wait_for_status(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + enum comms_sts expected_status, + u32 timeout) +{ + struct cpu_dyn_regs *dyn_regs; + u32 status; + int rc; + + dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; + + /* Wait for expected status */ + rc = hl_poll_timeout( + hdev, + le32_to_cpu(dyn_regs->cpu_cmd_status_to_host), + status, + FIELD_GET(COMMS_STATUS_STATUS_MASK, status) == expected_status, + FW_CPU_STATUS_POLL_INTERVAL_USEC, + timeout); + + if (rc) { + hl_fw_dynamic_report_error_status(hdev, status, + expected_status); + return -EIO; + } + + /* + * skip storing FW response for NOOP to preserve the actual desired + * FW status + */ + if (expected_status == COMMS_STS_NOOP) + return 0; + + rc = hl_fw_dynamic_extract_fw_response(hdev, fw_loader, + &fw_loader->dynamic_loader.response, + status); + return rc; +} + +/** + * hl_fw_dynamic_send_clear_cmd - send clear command to FW + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * + * @return 0 on success, otherwise non-zero error code + * + * after command cycle between LKD to FW CPU (i.e. LKD got an expected status + * from FW) we need to clear the CPU status register in order to avoid garbage + * between command cycles. + * This is done by sending clear command and polling the CPU to LKD status + * register to hold the status NOOP + */ +static int hl_fw_dynamic_send_clear_cmd(struct hl_device *hdev, + struct fw_load_mgr *fw_loader) +{ + hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_CLR_STS, 0); + + return hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_NOOP, + fw_loader->cpu_timeout); +} + +/** + * hl_fw_dynamic_send_protocol_cmd - send LKD to FW cmd and wait for ACK + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @cmd: LKD to FW cmd code + * @size: size of next FW component to be loaded (0 if not necessary) + * @wait_ok: if true also wait for OK response from FW + * @timeout: timeout for status wait + * + * @return 0 on success, otherwise non-zero error code + * + * brief: + * when sending protocol command we have the following steps: + * - send clear (clear command and verify clear status register) + * - send the actual protocol command + * - wait for ACK on the protocol command + * - send clear + * - send NOOP + * if, in addition, the specific protocol command should wait for OK then: + * - wait for OK + * - send clear + * - send NOOP + * + * NOTES: + * send clear: this is necessary in order to clear the status register to avoid + * leftovers between command + * NOOP command: necessary to avoid loop on the clear command by the FW + */ +int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + enum comms_cmd cmd, unsigned int size, + bool wait_ok, u32 timeout) +{ + int rc; + + /* first send clear command to clean former commands */ + rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader); + + /* send the actual command */ + hl_fw_dynamic_send_cmd(hdev, fw_loader, cmd, size); + + /* wait for ACK for the command */ + rc = hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_ACK, + timeout); + if (rc) + return rc; + + /* clear command to prepare for NOOP command */ + rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader); + if (rc) + return rc; + + /* send the actual NOOP command */ + hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_NOOP, 0); + + if (!wait_ok) + return 0; + + rc = hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_OK, + timeout); + if (rc) + return rc; + + /* clear command to prepare for NOOP command */ + rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader); + if (rc) + return rc; + + /* send the actual NOOP command */ + hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_NOOP, 0); + + return 0; +} + +/** + * hl_fw_compat_crc32 - CRC compatible with FW + * + * @data: pointer to the data + * @size: size of the data + * + * @return the CRC32 result + * + * NOTE: kernel's CRC32 differ's from standard CRC32 calculation. + * in order to be aligned we need to flip the bits of both the input + * initial CRC and kernel's CRC32 result. + * in addition both sides use initial CRC of 0, + */ +static u32 hl_fw_compat_crc32(u8 *data, size_t size) +{ + return ~crc32_le(~((u32)0), data, size); +} + +/** + * hl_fw_dynamic_validate_memory_bound - validate memory bounds for memory + * transfer (image or descriptor) between + * host and FW + * + * @hdev: pointer to the habanalabs device structure + * @addr: device address of memory transfer + * @size: memory transter size + * @region: PCI memory region + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_dynamic_validate_memory_bound(struct hl_device *hdev, + u64 addr, size_t size, + struct pci_mem_region *region) +{ + u64 end_addr; + + /* now make sure that the memory transfer is within region's bounds */ + end_addr = addr + size; + if (end_addr >= region->region_base + region->region_size) { + dev_err(hdev->dev, + "dynamic FW load: memory transfer end address out of memory region bounds. addr: %llx\n", + end_addr); + return -EIO; + } + + /* + * now make sure memory transfer is within predefined BAR bounds. + * this is to make sure we do not need to set the bar (e.g. for DRAM + * memory transfers) + */ + if (end_addr >= region->region_base - region->offset_in_bar + + region->bar_size) { + dev_err(hdev->dev, + "FW image beyond PCI BAR bounds\n"); + return -EIO; + } + + return 0; +} + +/** + * hl_fw_dynamic_validate_descriptor - validate FW descriptor + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @fw_desc: the descriptor form FW + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + struct lkd_fw_comms_desc *fw_desc) +{ + struct pci_mem_region *region; + enum pci_region region_id; + size_t data_size; + u32 data_crc32; + u8 *data_ptr; + u64 addr; + int rc; + + if (le32_to_cpu(fw_desc->header.magic) != HL_COMMS_DESC_MAGIC) { + dev_err(hdev->dev, "Invalid magic for dynamic FW descriptor (%x)\n", + fw_desc->header.magic); + return -EIO; + } + + if (fw_desc->header.version != HL_COMMS_DESC_VER) { + dev_err(hdev->dev, "Invalid version for dynamic FW descriptor (%x)\n", + fw_desc->header.version); + return -EIO; + } + + /* + * calc CRC32 of data without header. + * note that no alignment/stride address issues here as all structures + * are 64 bit padded + */ + data_size = sizeof(struct lkd_fw_comms_desc) - + sizeof(struct comms_desc_header); + data_ptr = (u8 *)fw_desc + sizeof(struct comms_desc_header); + + if (le16_to_cpu(fw_desc->header.size) != data_size) { + dev_err(hdev->dev, + "Invalid descriptor size 0x%x, expected size 0x%zx\n", + le16_to_cpu(fw_desc->header.size), data_size); + return -EIO; + } + + data_crc32 = hl_fw_compat_crc32(data_ptr, data_size); + + if (data_crc32 != le32_to_cpu(fw_desc->header.crc32)) { + dev_err(hdev->dev, + "CRC32 mismatch for dynamic FW descriptor (%x:%x)\n", + data_crc32, fw_desc->header.crc32); + return -EIO; + } + + /* find memory region to which to copy the image */ + addr = le64_to_cpu(fw_desc->img_addr); + region_id = hl_get_pci_memory_region(hdev, addr); + if ((region_id != PCI_REGION_SRAM) && + ((region_id != PCI_REGION_DRAM))) { + dev_err(hdev->dev, + "Invalid region to copy FW image address=%llx\n", addr); + return -EIO; + } + + region = &hdev->pci_mem_region[region_id]; + + /* store the region for the copy stage */ + fw_loader->dynamic_loader.image_region = region; + + /* + * here we know that the start address is valid, now make sure that the + * image is within region's bounds + */ + rc = hl_fw_dynamic_validate_memory_bound(hdev, addr, + fw_loader->dynamic_loader.fw_image_size, + region); + if (rc) { + dev_err(hdev->dev, + "invalid mem transfer request for FW image\n"); + return rc; + } + + return 0; +} + +static int hl_fw_dynamic_validate_response(struct hl_device *hdev, + struct fw_response *response, + struct pci_mem_region *region) +{ + u64 device_addr; + int rc; + + device_addr = region->region_base + response->ram_offset; + + /* + * validate that the descriptor is within region's bounds + * Note that as the start address was supplied according to the RAM + * type- testing only the end address is enough + */ + rc = hl_fw_dynamic_validate_memory_bound(hdev, device_addr, + sizeof(struct lkd_fw_comms_desc), + region); + return rc; +} + +/** + * hl_fw_dynamic_read_and_validate_descriptor - read and validate FW descriptor + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_dynamic_read_and_validate_descriptor(struct hl_device *hdev, + struct fw_load_mgr *fw_loader) +{ + struct lkd_fw_comms_desc *fw_desc; + struct pci_mem_region *region; + struct fw_response *response; + enum pci_region region_id; + void __iomem *src; + int rc; + + fw_desc = &fw_loader->dynamic_loader.comm_desc; + response = &fw_loader->dynamic_loader.response; + + region_id = (response->ram_type == COMMS_SRAM) ? + PCI_REGION_SRAM : PCI_REGION_DRAM; + + region = &hdev->pci_mem_region[region_id]; + + rc = hl_fw_dynamic_validate_response(hdev, response, region); + if (rc) { + dev_err(hdev->dev, + "invalid mem transfer request for FW descriptor\n"); + return rc; + } + + /* extract address copy the descriptor from */ + src = hdev->pcie_bar[region->bar_id] + region->offset_in_bar + + response->ram_offset; + memcpy_fromio(fw_desc, src, sizeof(struct lkd_fw_comms_desc)); + + return hl_fw_dynamic_validate_descriptor(hdev, fw_loader, fw_desc); +} + +/** + * hl_fw_dynamic_request_descriptor - handshake with CPU to get FW descriptor + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @next_image_size: size to allocate for next FW component + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_dynamic_request_descriptor(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + size_t next_image_size) +{ + int rc; + + rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_PREP_DESC, + next_image_size, true, + fw_loader->cpu_timeout); + if (rc) + return rc; + + return hl_fw_dynamic_read_and_validate_descriptor(hdev, fw_loader); +} + +/** + * hl_fw_dynamic_read_device_fw_version - read FW version to exposed properties + * + * @hdev: pointer to the habanalabs device structure + * @fwc: the firmware component + * @fw_version: fw component's version string + */ +static void hl_fw_dynamic_read_device_fw_version(struct hl_device *hdev, + enum hl_fw_component fwc, + const char *fw_version) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + char *preboot_ver, *boot_ver; + char btl_ver[32]; + + switch (fwc) { + case FW_COMP_BOOT_FIT: + strscpy(prop->uboot_ver, fw_version, VERSION_MAX_LEN); + boot_ver = extract_fw_ver_from_str(prop->uboot_ver); + if (boot_ver) { + dev_info(hdev->dev, "boot-fit version %s\n", boot_ver); + kfree(boot_ver); + } + + break; + case FW_COMP_PREBOOT: + strscpy(prop->preboot_ver, fw_version, VERSION_MAX_LEN); + preboot_ver = strnstr(prop->preboot_ver, "Preboot", + VERSION_MAX_LEN); + if (preboot_ver && preboot_ver != prop->preboot_ver) { + strscpy(btl_ver, prop->preboot_ver, + min((int) (preboot_ver - prop->preboot_ver), + 31)); + dev_info(hdev->dev, "%s\n", btl_ver); + } + + preboot_ver = extract_fw_ver_from_str(prop->preboot_ver); + if (preboot_ver) { + dev_info(hdev->dev, "preboot version %s\n", + preboot_ver); + kfree(preboot_ver); + } + + break; + default: + dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc); + return; + } +} + +/** + * hl_fw_dynamic_copy_image - copy image to memory allocated by the FW + * + * @hdev: pointer to the habanalabs device structure + * @fw: fw descriptor + * @fw_loader: managing structure for loading device's FW + */ +static int hl_fw_dynamic_copy_image(struct hl_device *hdev, + const struct firmware *fw, + struct fw_load_mgr *fw_loader) +{ + struct lkd_fw_comms_desc *fw_desc; + struct pci_mem_region *region; + void __iomem *dest; + u64 addr; + int rc; + + fw_desc = &fw_loader->dynamic_loader.comm_desc; + addr = le64_to_cpu(fw_desc->img_addr); + + /* find memory region to which to copy the image */ + region = fw_loader->dynamic_loader.image_region; + + dest = hdev->pcie_bar[region->bar_id] + region->offset_in_bar + + (addr - region->region_base); + + rc = hl_fw_copy_fw_to_device(hdev, fw, dest, + fw_loader->boot_fit_img.src_off, + fw_loader->boot_fit_img.copy_size); + + return rc; +} + +/** + * hl_fw_dynamic_copy_msg - copy msg to memory allocated by the FW + * + * @hdev: pointer to the habanalabs device structure + * @msg: message + * @fw_loader: managing structure for loading device's FW + */ +static int hl_fw_dynamic_copy_msg(struct hl_device *hdev, + struct lkd_msg_comms *msg, struct fw_load_mgr *fw_loader) +{ + struct lkd_fw_comms_desc *fw_desc; + struct pci_mem_region *region; + void __iomem *dest; + u64 addr; + int rc; + + fw_desc = &fw_loader->dynamic_loader.comm_desc; + addr = le64_to_cpu(fw_desc->img_addr); + + /* find memory region to which to copy the image */ + region = fw_loader->dynamic_loader.image_region; + + dest = hdev->pcie_bar[region->bar_id] + region->offset_in_bar + + (addr - region->region_base); + + rc = hl_fw_copy_msg_to_device(hdev, msg, dest, 0, 0); + + return rc; +} + +/** + * hl_fw_boot_fit_update_state - update internal data structures after boot-fit + * is loaded + * + * @hdev: pointer to the habanalabs device structure + * @cpu_boot_dev_sts0_reg: register holding CPU boot dev status 0 + * @cpu_boot_dev_sts1_reg: register holding CPU boot dev status 1 + * + * @return 0 on success, otherwise non-zero error code + */ +static void hl_fw_boot_fit_update_state(struct hl_device *hdev, + u32 cpu_boot_dev_sts0_reg, + u32 cpu_boot_dev_sts1_reg) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + + /* Clear reset status since we need to read it again from boot CPU */ + prop->hard_reset_done_by_fw = false; + + /* Read boot_cpu status bits */ + if (prop->fw_preboot_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_ENABLED) { + prop->fw_bootfit_cpu_boot_dev_sts0 = + RREG32(cpu_boot_dev_sts0_reg); + + if (prop->fw_bootfit_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_FW_HARD_RST_EN) prop->hard_reset_done_by_fw = true; - } else { - prop->fw_security_status_valid = 0; + + dev_dbg(hdev->dev, "Firmware boot CPU status0 %#x\n", + prop->fw_bootfit_cpu_boot_dev_sts0); } - dev_dbg(hdev->dev, "Firmware preboot security status %#x\n", - security_status); + if (prop->fw_cpu_boot_dev_sts1_valid) { + prop->fw_bootfit_cpu_boot_dev_sts1 = + RREG32(cpu_boot_dev_sts1_reg); - dev_dbg(hdev->dev, "Firmware preboot hard-reset is %s\n", + dev_dbg(hdev->dev, "Firmware boot CPU status1 %#x\n", + prop->fw_bootfit_cpu_boot_dev_sts1); + } + + dev_dbg(hdev->dev, "Firmware boot CPU hard-reset is %s\n", prop->hard_reset_done_by_fw ? "enabled" : "disabled"); +} + +static void hl_fw_dynamic_update_linux_interrupt_if(struct hl_device *hdev) +{ + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; + + /* Check whether all 3 interrupt interfaces are set, if not use a + * single interface + */ + if (!hdev->asic_prop.gic_interrupts_enable && + !(hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN)) { + dyn_regs->gic_host_halt_irq = dyn_regs->gic_host_irq_ctrl; + dyn_regs->gic_host_ints_irq = dyn_regs->gic_host_irq_ctrl; + + dev_warn(hdev->dev, + "Using a single interrupt interface towards cpucp"); + } +} +/** + * hl_fw_dynamic_load_image - load FW image using dynamic protocol + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @load_fwc: the FW component to be loaded + * @img_ld_timeout: image load timeout + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_dynamic_load_image(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + enum hl_fw_component load_fwc, + u32 img_ld_timeout) +{ + enum hl_fw_component cur_fwc; + const struct firmware *fw; + char *fw_name; + int rc = 0; + + /* + * when loading image we have one of 2 scenarios: + * 1. current FW component is preboot and we want to load boot-fit + * 2. current FW component is boot-fit and we want to load linux + */ + if (load_fwc == FW_COMP_BOOT_FIT) { + cur_fwc = FW_COMP_PREBOOT; + fw_name = fw_loader->boot_fit_img.image_name; + } else { + cur_fwc = FW_COMP_BOOT_FIT; + fw_name = fw_loader->linux_img.image_name; + } + + /* request FW in order to communicate to FW the size to be allocated */ + rc = hl_request_fw(hdev, &fw, fw_name); + if (rc) + return rc; + + /* store the image size for future validation */ + fw_loader->dynamic_loader.fw_image_size = fw->size; + + rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, fw->size); + if (rc) + goto release_fw; + + /* read preboot version */ + hl_fw_dynamic_read_device_fw_version(hdev, cur_fwc, + fw_loader->dynamic_loader.comm_desc.cur_fw_ver); + + + /* update state according to boot stage */ + if (cur_fwc == FW_COMP_BOOT_FIT) { + struct cpu_dyn_regs *dyn_regs; + + dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; + hl_fw_boot_fit_update_state(hdev, + le32_to_cpu(dyn_regs->cpu_boot_dev_sts0), + le32_to_cpu(dyn_regs->cpu_boot_dev_sts1)); + } + + /* copy boot fit to space allocated by FW */ + rc = hl_fw_dynamic_copy_image(hdev, fw, fw_loader); + if (rc) + goto release_fw; - dev_info(hdev->dev, "firmware-level security is %s\n", - prop->fw_security_disabled ? "disabled" : "enabled"); + rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_DATA_RDY, + 0, true, + fw_loader->cpu_timeout); + if (rc) + goto release_fw; + + rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_EXEC, + 0, false, + img_ld_timeout); +release_fw: + hl_release_firmware(fw); + return rc; +} + +static int hl_fw_dynamic_wait_for_boot_fit_active(struct hl_device *hdev, + struct fw_load_mgr *fw_loader) +{ + struct dynamic_fw_load_mgr *dyn_loader; + u32 status; + int rc; + + dyn_loader = &fw_loader->dynamic_loader; + + /* Make sure CPU boot-loader is running */ + rc = hl_poll_timeout( + hdev, + le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status), + status, + (status == CPU_BOOT_STATUS_NIC_FW_RDY) || + (status == CPU_BOOT_STATUS_READY_TO_BOOT), + FW_CPU_STATUS_POLL_INTERVAL_USEC, + dyn_loader->wait_for_bl_timeout); + if (rc) { + dev_err(hdev->dev, "failed to wait for boot\n"); + return rc; + } + + dev_dbg(hdev->dev, "uboot status = %d\n", status); return 0; } -int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, - u32 msg_to_cpu_reg, u32 cpu_msg_status_reg, - u32 cpu_security_boot_status_reg, u32 boot_err0_reg, - bool skip_bmc, u32 cpu_timeout, u32 boot_fit_timeout) +static int hl_fw_dynamic_wait_for_linux_active(struct hl_device *hdev, + struct fw_load_mgr *fw_loader) { - struct asic_fixed_properties *prop = &hdev->asic_prop; + struct dynamic_fw_load_mgr *dyn_loader; u32 status; int rc; + dyn_loader = &fw_loader->dynamic_loader; + + /* Make sure CPU boot-loader is running */ + + rc = hl_poll_timeout( + hdev, + le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status), + status, + (status == CPU_BOOT_STATUS_SRAM_AVAIL), + FW_CPU_STATUS_POLL_INTERVAL_USEC, + fw_loader->cpu_timeout); + if (rc) { + dev_err(hdev->dev, "failed to wait for Linux\n"); + return rc; + } + + dev_dbg(hdev->dev, "Boot status = %d\n", status); + return 0; +} + +/** + * hl_fw_linux_update_state - update internal data structures after Linux + * is loaded. + * Note: Linux initialization is comprised mainly + * of two stages - loading kernel (SRAM_AVAIL) + * & loading ARMCP. + * Therefore reading boot device status in any of + * these stages might result in different values. + * + * @hdev: pointer to the habanalabs device structure + * @cpu_boot_dev_sts0_reg: register holding CPU boot dev status 0 + * @cpu_boot_dev_sts1_reg: register holding CPU boot dev status 1 + * + * @return 0 on success, otherwise non-zero error code + */ +static void hl_fw_linux_update_state(struct hl_device *hdev, + u32 cpu_boot_dev_sts0_reg, + u32 cpu_boot_dev_sts1_reg) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + + hdev->fw_loader.linux_loaded = true; + + /* Clear reset status since we need to read again from app */ + prop->hard_reset_done_by_fw = false; + + /* Read FW application security bits */ + if (prop->fw_cpu_boot_dev_sts0_valid) { + prop->fw_app_cpu_boot_dev_sts0 = + RREG32(cpu_boot_dev_sts0_reg); + + if (prop->fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_FW_HARD_RST_EN) + prop->hard_reset_done_by_fw = true; + + if (prop->fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN) + prop->gic_interrupts_enable = false; + + dev_dbg(hdev->dev, + "Firmware application CPU status0 %#x\n", + prop->fw_app_cpu_boot_dev_sts0); + + dev_dbg(hdev->dev, "GIC controller is %s\n", + prop->gic_interrupts_enable ? + "enabled" : "disabled"); + } + + if (prop->fw_cpu_boot_dev_sts1_valid) { + prop->fw_app_cpu_boot_dev_sts1 = + RREG32(cpu_boot_dev_sts1_reg); + + dev_dbg(hdev->dev, + "Firmware application CPU status1 %#x\n", + prop->fw_app_cpu_boot_dev_sts1); + } + + dev_dbg(hdev->dev, "Firmware application CPU hard-reset is %s\n", + prop->hard_reset_done_by_fw ? "enabled" : "disabled"); + + dev_info(hdev->dev, "Successfully loaded firmware to device\n"); +} + +/** + * hl_fw_dynamic_report_reset_cause - send a COMMS message with the cause + * of the newly triggered hard reset + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * @reset_cause: enumerated cause for the recent hard reset + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_dynamic_report_reset_cause(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + enum comms_reset_cause reset_cause) +{ + struct lkd_msg_comms msg; + int rc; + + memset(&msg, 0, sizeof(msg)); + + /* create message to be sent */ + msg.header.type = HL_COMMS_RESET_CAUSE_TYPE; + msg.header.size = cpu_to_le16(sizeof(struct comms_msg_header)); + msg.header.magic = cpu_to_le32(HL_COMMS_MSG_MAGIC); + + msg.reset_cause = reset_cause; + + rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, + sizeof(struct lkd_msg_comms)); + if (rc) + return rc; + + /* copy message to space allocated by FW */ + rc = hl_fw_dynamic_copy_msg(hdev, &msg, fw_loader); + if (rc) + return rc; + + rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_DATA_RDY, + 0, true, + fw_loader->cpu_timeout); + if (rc) + return rc; + + rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_EXEC, + 0, true, + fw_loader->cpu_timeout); + if (rc) + return rc; + + return 0; +} + +/** + * hl_fw_dynamic_init_cpu - initialize the device CPU using dynamic protocol + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * + * @return 0 on success, otherwise non-zero error code + * + * brief: the dynamic protocol is master (LKD) slave (FW CPU) protocol. + * the communication is done using registers: + * - LKD command register + * - FW status register + * the protocol is race free. this goal is achieved by splitting the requests + * and response to known synchronization points between the LKD and the FW. + * each response to LKD request is known and bound to a predefined timeout. + * in case of timeout expiration without the desired status from FW- the + * protocol (and hence the boot) will fail. + */ +static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, + struct fw_load_mgr *fw_loader) +{ + struct cpu_dyn_regs *dyn_regs; + int rc; + + dev_info(hdev->dev, + "Loading firmware to device, may take some time...\n"); + + dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; + + rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_RST_STATE, + 0, true, + fw_loader->cpu_timeout); + if (rc) + goto protocol_err; + + if (hdev->curr_reset_cause) { + rc = hl_fw_dynamic_report_reset_cause(hdev, fw_loader, + hdev->curr_reset_cause); + if (rc) + goto protocol_err; + + /* Clear current reset cause */ + hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + } + + if (!(hdev->fw_components & FW_TYPE_BOOT_CPU)) { + rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, 0); + if (rc) + goto protocol_err; + + /* read preboot version */ + hl_fw_dynamic_read_device_fw_version(hdev, FW_COMP_PREBOOT, + fw_loader->dynamic_loader.comm_desc.cur_fw_ver); + return 0; + } + + /* load boot fit to FW */ + rc = hl_fw_dynamic_load_image(hdev, fw_loader, FW_COMP_BOOT_FIT, + fw_loader->boot_fit_timeout); + if (rc) { + dev_err(hdev->dev, "failed to load boot fit\n"); + goto protocol_err; + } + + rc = hl_fw_dynamic_wait_for_boot_fit_active(hdev, fw_loader); + if (rc) + goto protocol_err; + + /* Enable DRAM scrambling before Linux boot and after successful + * UBoot + */ + hdev->asic_funcs->init_cpu_scrambler_dram(hdev); + + if (!(hdev->fw_components & FW_TYPE_LINUX)) { + dev_info(hdev->dev, "Skip loading Linux F/W\n"); + return 0; + } + + if (fw_loader->skip_bmc) { + rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, + COMMS_SKIP_BMC, 0, + true, + fw_loader->cpu_timeout); + if (rc) { + dev_err(hdev->dev, "failed to load boot fit\n"); + goto protocol_err; + } + } + + /* load Linux image to FW */ + rc = hl_fw_dynamic_load_image(hdev, fw_loader, FW_COMP_LINUX, + fw_loader->cpu_timeout); + if (rc) { + dev_err(hdev->dev, "failed to load Linux\n"); + goto protocol_err; + } + + rc = hl_fw_dynamic_wait_for_linux_active(hdev, fw_loader); + if (rc) + goto protocol_err; + + hl_fw_linux_update_state(hdev, le32_to_cpu(dyn_regs->cpu_boot_dev_sts0), + le32_to_cpu(dyn_regs->cpu_boot_dev_sts1)); + + hl_fw_dynamic_update_linux_interrupt_if(hdev); + + return 0; + +protocol_err: + fw_read_errors(hdev, le32_to_cpu(dyn_regs->cpu_boot_err0), + le32_to_cpu(dyn_regs->cpu_boot_err1), + le32_to_cpu(dyn_regs->cpu_boot_dev_sts0), + le32_to_cpu(dyn_regs->cpu_boot_dev_sts1)); + return rc; +} + +/** + * hl_fw_static_init_cpu - initialize the device CPU using static protocol + * + * @hdev: pointer to the habanalabs device structure + * @fw_loader: managing structure for loading device's FW + * + * @return 0 on success, otherwise non-zero error code + */ +static int hl_fw_static_init_cpu(struct hl_device *hdev, + struct fw_load_mgr *fw_loader) +{ + u32 cpu_msg_status_reg, cpu_timeout, msg_to_cpu_reg, status; + u32 cpu_boot_dev_status0_reg, cpu_boot_dev_status1_reg; + struct static_fw_load_mgr *static_loader; + u32 cpu_boot_status_reg; + int rc; + if (!(hdev->fw_components & FW_TYPE_BOOT_CPU)) return 0; + /* init common loader parameters */ + cpu_timeout = fw_loader->cpu_timeout; + + /* init static loader parameters */ + static_loader = &fw_loader->static_loader; + cpu_msg_status_reg = static_loader->cpu_cmd_status_to_host_reg; + msg_to_cpu_reg = static_loader->kmd_msg_to_cpu_reg; + cpu_boot_dev_status0_reg = static_loader->cpu_boot_dev_status0_reg; + cpu_boot_dev_status1_reg = static_loader->cpu_boot_dev_status1_reg; + cpu_boot_status_reg = static_loader->cpu_boot_status_reg; + dev_info(hdev->dev, "Going to wait for device boot (up to %lds)\n", cpu_timeout / USEC_PER_SEC); @@ -925,8 +2364,8 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, cpu_boot_status_reg, status, status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT, - 10000, - boot_fit_timeout); + FW_CPU_STATUS_POLL_INTERVAL_USEC, + fw_loader->boot_fit_timeout); if (rc) { dev_dbg(hdev->dev, @@ -948,8 +2387,8 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, cpu_msg_status_reg, status, status == CPU_MSG_OK, - 10000, - boot_fit_timeout); + FW_CPU_STATUS_POLL_INTERVAL_USEC, + fw_loader->boot_fit_timeout); if (rc) { dev_err(hdev->dev, @@ -970,33 +2409,17 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, (status == CPU_BOOT_STATUS_NIC_FW_RDY) || (status == CPU_BOOT_STATUS_READY_TO_BOOT) || (status == CPU_BOOT_STATUS_SRAM_AVAIL), - 10000, + FW_CPU_STATUS_POLL_INTERVAL_USEC, cpu_timeout); dev_dbg(hdev->dev, "uboot status = %d\n", status); /* Read U-Boot version now in case we will later fail */ - hdev->asic_funcs->read_device_fw_version(hdev, FW_COMP_UBOOT); - - /* Clear reset status since we need to read it again from boot CPU */ - prop->hard_reset_done_by_fw = false; - - /* Read boot_cpu security bits */ - if (prop->fw_security_status_valid) { - prop->fw_boot_cpu_security_map = - RREG32(cpu_security_boot_status_reg); - - if (prop->fw_boot_cpu_security_map & - CPU_BOOT_DEV_STS0_FW_HARD_RST_EN) - prop->hard_reset_done_by_fw = true; - - dev_dbg(hdev->dev, - "Firmware boot CPU security status %#x\n", - prop->fw_boot_cpu_security_map); - } + hl_fw_static_read_device_fw_version(hdev, FW_COMP_BOOT_FIT); - dev_dbg(hdev->dev, "Firmware boot CPU hard-reset is %s\n", - prop->hard_reset_done_by_fw ? "enabled" : "disabled"); + /* update state according to boot stage */ + hl_fw_boot_fit_update_state(hdev, cpu_boot_dev_status0_reg, + cpu_boot_dev_status1_reg); if (rc) { detect_cpu_boot_status(hdev, status); @@ -1004,13 +2427,21 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, goto out; } + /* Enable DRAM scrambling before Linux boot and after successful + * UBoot + */ + hdev->asic_funcs->init_cpu_scrambler_dram(hdev); + if (!(hdev->fw_components & FW_TYPE_LINUX)) { dev_info(hdev->dev, "Skip loading Linux F/W\n"); + rc = 0; goto out; } - if (status == CPU_BOOT_STATUS_SRAM_AVAIL) + if (status == CPU_BOOT_STATUS_SRAM_AVAIL) { + rc = 0; goto out; + } dev_info(hdev->dev, "Loading firmware to device, may take some time...\n"); @@ -1019,7 +2450,7 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, if (rc) goto out; - if (skip_bmc) { + if (fw_loader->skip_bmc) { WREG32(msg_to_cpu_reg, KMD_MSG_SKIP_BMC); rc = hl_poll_timeout( @@ -1027,7 +2458,7 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, cpu_boot_status_reg, status, (status == CPU_BOOT_STATUS_BMC_WAITING_SKIPPED), - 10000, + FW_CPU_STATUS_POLL_INTERVAL_USEC, cpu_timeout); if (rc) { @@ -1047,7 +2478,7 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, cpu_boot_status_reg, status, (status == CPU_BOOT_STATUS_SRAM_AVAIL), - 10000, + FW_CPU_STATUS_POLL_INTERVAL_USEC, cpu_timeout); /* Clear message */ @@ -1066,36 +2497,43 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, goto out; } - rc = fw_read_errors(hdev, boot_err0_reg, cpu_security_boot_status_reg); + rc = fw_read_errors(hdev, fw_loader->static_loader.boot_err0_reg, + fw_loader->static_loader.boot_err1_reg, + cpu_boot_dev_status0_reg, + cpu_boot_dev_status1_reg); if (rc) return rc; - /* Clear reset status since we need to read again from app */ - prop->hard_reset_done_by_fw = false; - - /* Read FW application security bits */ - if (prop->fw_security_status_valid) { - prop->fw_app_security_map = - RREG32(cpu_security_boot_status_reg); - - if (prop->fw_app_security_map & - CPU_BOOT_DEV_STS0_FW_HARD_RST_EN) - prop->hard_reset_done_by_fw = true; - - dev_dbg(hdev->dev, - "Firmware application CPU security status %#x\n", - prop->fw_app_security_map); - } - - dev_dbg(hdev->dev, "Firmware application CPU hard-reset is %s\n", - prop->hard_reset_done_by_fw ? "enabled" : "disabled"); - - dev_info(hdev->dev, "Successfully loaded firmware to device\n"); + hl_fw_linux_update_state(hdev, cpu_boot_dev_status0_reg, + cpu_boot_dev_status1_reg); return 0; out: - fw_read_errors(hdev, boot_err0_reg, cpu_security_boot_status_reg); + fw_read_errors(hdev, fw_loader->static_loader.boot_err0_reg, + fw_loader->static_loader.boot_err1_reg, + cpu_boot_dev_status0_reg, + cpu_boot_dev_status1_reg); return rc; } + +/** + * hl_fw_init_cpu - initialize the device CPU + * + * @hdev: pointer to the habanalabs device structure + * + * @return 0 on success, otherwise non-zero error code + * + * perform necessary initializations for device's CPU. takes into account if + * init protocol is static or dynamic. + */ +int hl_fw_init_cpu(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct fw_load_mgr *fw_loader = &hdev->fw_loader; + + return prop->dynamic_fw_load ? + hl_fw_dynamic_init_cpu(hdev, fw_loader) : + hl_fw_static_init_cpu(hdev, fw_loader); +} diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 6579f8767abd..6b3cdd7e068a 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -48,6 +48,7 @@ #define HL_PENDING_RESET_LONG_SEC 60 #define HL_HARD_RESET_MAX_TIMEOUT 120 +#define HL_PLDM_HARD_RESET_MAX_TIMEOUT (HL_HARD_RESET_MAX_TIMEOUT * 3) #define HL_DEVICE_TIMEOUT_USEC 1000000 /* 1 s */ @@ -115,10 +116,18 @@ enum hl_mmu_page_table_location { * * - HL_RESET_HEARTBEAT * Set if reset is due to heartbeat + * + * - HL_RESET_TDR + * Set if reset is due to TDR + * + * - HL_RESET_DEVICE_RELEASE + * Set if reset is due to device release */ #define HL_RESET_HARD (1 << 0) #define HL_RESET_FROM_RESET_THREAD (1 << 1) #define HL_RESET_HEARTBEAT (1 << 2) +#define HL_RESET_TDR (1 << 3) +#define HL_RESET_DEVICE_RELEASE (1 << 4) #define HL_MAX_SOBS_PER_MONITOR 8 @@ -178,12 +187,14 @@ enum hl_pci_match_mode { /** * enum hl_fw_component - F/W components to read version through registers. - * @FW_COMP_UBOOT: u-boot. + * @FW_COMP_BOOT_FIT: boot fit. * @FW_COMP_PREBOOT: preboot. + * @FW_COMP_LINUX: linux. */ enum hl_fw_component { - FW_COMP_UBOOT, - FW_COMP_PREBOOT + FW_COMP_BOOT_FIT, + FW_COMP_PREBOOT, + FW_COMP_LINUX, }; /** @@ -420,12 +431,24 @@ struct hl_mmu_properties { * @cb_pool_cb_size: size of each CB in the CB pool. * @max_pending_cs: maximum of concurrent pending command submissions * @max_queues: maximum amount of queues in the system - * @fw_boot_cpu_security_map: bitmap representation of boot cpu security status - * reported by FW, bit description can be found in - * CPU_BOOT_DEV_STS* - * @fw_app_security_map: bitmap representation of application security status - * reported by FW, bit description can be found in - * CPU_BOOT_DEV_STS* + * @fw_preboot_cpu_boot_dev_sts0: bitmap representation of preboot cpu + * capabilities reported by FW, bit description + * can be found in CPU_BOOT_DEV_STS0 + * @fw_preboot_cpu_boot_dev_sts1: bitmap representation of preboot cpu + * capabilities reported by FW, bit description + * can be found in CPU_BOOT_DEV_STS1 + * @fw_bootfit_cpu_boot_dev_sts0: bitmap representation of boot cpu security + * status reported by FW, bit description can be + * found in CPU_BOOT_DEV_STS0 + * @fw_bootfit_cpu_boot_dev_sts1: bitmap representation of boot cpu security + * status reported by FW, bit description can be + * found in CPU_BOOT_DEV_STS1 + * @fw_app_cpu_boot_dev_sts0: bitmap representation of application security + * status reported by FW, bit description can be + * found in CPU_BOOT_DEV_STS0 + * @fw_app_cpu_boot_dev_sts1: bitmap representation of application security + * status reported by FW, bit description can be + * found in CPU_BOOT_DEV_STS1 * @collective_first_sob: first sync object available for collective use * @collective_first_mon: first monitor available for collective use * @sync_stream_first_sob: first sync object available for sync stream use @@ -438,14 +461,19 @@ struct hl_mmu_properties { * @user_interrupt_count: number of user interrupts. * @tpc_enabled_mask: which TPCs are enabled. * @completion_queues_count: number of completion queues. - * @fw_security_disabled: true if security measures are disabled in firmware, - * false otherwise - * @fw_security_status_valid: security status bits are valid and can be fetched - * from BOOT_DEV_STS0 + * @fw_security_enabled: true if security measures are enabled in firmware, + * false otherwise + * @fw_cpu_boot_dev_sts0_valid: status bits are valid and can be fetched from + * BOOT_DEV_STS0 + * @fw_cpu_boot_dev_sts1_valid: status bits are valid and can be fetched from + * BOOT_DEV_STS1 * @dram_supports_virtual_memory: is there an MMU towards the DRAM * @hard_reset_done_by_fw: true if firmware is handling hard reset flow * @num_functional_hbms: number of functional HBMs in each DCORE. * @iatu_done_by_fw: true if iATU configuration is being done by FW. + * @dynamic_fw_load: is dynamic FW load is supported. + * @gic_interrupts_enable: true if FW is not blocking GIC controller, + * false otherwise. */ struct asic_fixed_properties { struct hw_queue_properties *hw_queues_props; @@ -491,8 +519,12 @@ struct asic_fixed_properties { u32 cb_pool_cb_size; u32 max_pending_cs; u32 max_queues; - u32 fw_boot_cpu_security_map; - u32 fw_app_security_map; + u32 fw_preboot_cpu_boot_dev_sts0; + u32 fw_preboot_cpu_boot_dev_sts1; + u32 fw_bootfit_cpu_boot_dev_sts0; + u32 fw_bootfit_cpu_boot_dev_sts1; + u32 fw_app_cpu_boot_dev_sts0; + u32 fw_app_cpu_boot_dev_sts1; u16 collective_first_sob; u16 collective_first_mon; u16 sync_stream_first_sob; @@ -504,12 +536,15 @@ struct asic_fixed_properties { u16 user_interrupt_count; u8 tpc_enabled_mask; u8 completion_queues_count; - u8 fw_security_disabled; - u8 fw_security_status_valid; + u8 fw_security_enabled; + u8 fw_cpu_boot_dev_sts0_valid; + u8 fw_cpu_boot_dev_sts1_valid; u8 dram_supports_virtual_memory; u8 hard_reset_done_by_fw; u8 num_functional_hbms; u8 iatu_done_by_fw; + u8 dynamic_fw_load; + u8 gic_interrupts_enable; }; /** @@ -750,12 +785,19 @@ struct hl_user_pending_interrupt { * @kernel_address: holds the queue's kernel virtual address * @bus_address: holds the queue's DMA address * @ci: ci inside the queue + * @prev_eqe_index: the index of the previous event queue entry. The index of + * the current entry's index must be +1 of the previous one. + * @check_eqe_index: do we need to check the index of the current entry vs. the + * previous one. This is for backward compatibility with older + * firmwares */ struct hl_eq { struct hl_device *hdev; void *kernel_address; dma_addr_t bus_address; u32 ci; + u32 prev_eqe_index; + bool check_eqe_index; }; @@ -812,6 +854,132 @@ enum div_select_defs { DIV_SEL_DIVIDED_PLL = 3, }; +enum pci_region { + PCI_REGION_CFG, + PCI_REGION_SRAM, + PCI_REGION_DRAM, + PCI_REGION_SP_SRAM, + PCI_REGION_NUMBER, +}; + +/** + * struct pci_mem_region - describe memory region in a PCI bar + * @region_base: region base address + * @region_size: region size + * @bar_size: size of the BAR + * @offset_in_bar: region offset into the bar + * @bar_id: bar ID of the region + * @used: if used 1, otherwise 0 + */ +struct pci_mem_region { + u64 region_base; + u64 region_size; + u64 bar_size; + u32 offset_in_bar; + u8 bar_id; + u8 used; +}; + +/** + * struct static_fw_load_mgr - static FW load manager + * @preboot_version_max_off: max offset to preboot version + * @boot_fit_version_max_off: max offset to boot fit version + * @kmd_msg_to_cpu_reg: register address for KDM->CPU messages + * @cpu_cmd_status_to_host_reg: register address for CPU command status response + * @cpu_boot_status_reg: boot status register + * @cpu_boot_dev_status0_reg: boot device status register 0 + * @cpu_boot_dev_status1_reg: boot device status register 1 + * @boot_err0_reg: boot error register 0 + * @boot_err1_reg: boot error register 1 + * @preboot_version_offset_reg: SRAM offset to preboot version register + * @boot_fit_version_offset_reg: SRAM offset to boot fit version register + * @sram_offset_mask: mask for getting offset into the SRAM + * @cpu_reset_wait_msec: used when setting WFE via kmd_msg_to_cpu_reg + */ +struct static_fw_load_mgr { + u64 preboot_version_max_off; + u64 boot_fit_version_max_off; + u32 kmd_msg_to_cpu_reg; + u32 cpu_cmd_status_to_host_reg; + u32 cpu_boot_status_reg; + u32 cpu_boot_dev_status0_reg; + u32 cpu_boot_dev_status1_reg; + u32 boot_err0_reg; + u32 boot_err1_reg; + u32 preboot_version_offset_reg; + u32 boot_fit_version_offset_reg; + u32 sram_offset_mask; + u32 cpu_reset_wait_msec; +}; + +/** + * struct fw_response - FW response to LKD command + * @ram_offset: descriptor offset into the RAM + * @ram_type: RAM type containing the descriptor (SRAM/DRAM) + * @status: command status + */ +struct fw_response { + u32 ram_offset; + u8 ram_type; + u8 status; +}; + +/** + * struct dynamic_fw_load_mgr - dynamic FW load manager + * @response: FW to LKD response + * @comm_desc: the communication descriptor with FW + * @image_region: region to copy the FW image to + * @fw_image_size: size of FW image to load + * @wait_for_bl_timeout: timeout for waiting for boot loader to respond + */ +struct dynamic_fw_load_mgr { + struct fw_response response; + struct lkd_fw_comms_desc comm_desc; + struct pci_mem_region *image_region; + size_t fw_image_size; + u32 wait_for_bl_timeout; +}; + +/** + * struct fw_image_props - properties of FW image + * @image_name: name of the image + * @src_off: offset in src FW to copy from + * @copy_size: amount of bytes to copy (0 to copy the whole binary) + */ +struct fw_image_props { + char *image_name; + u32 src_off; + u32 copy_size; +}; + +/** + * struct fw_load_mgr - manager FW loading process + * @dynamic_loader: specific structure for dynamic load + * @static_loader: specific structure for static load + * @boot_fit_img: boot fit image properties + * @linux_img: linux image properties + * @cpu_timeout: CPU response timeout in usec + * @boot_fit_timeout: Boot fit load timeout in usec + * @skip_bmc: should BMC be skipped + * @sram_bar_id: SRAM bar ID + * @dram_bar_id: DRAM bar ID + * @linux_loaded: true if linux was loaded so far + */ +struct fw_load_mgr { + union { + struct dynamic_fw_load_mgr dynamic_loader; + struct static_fw_load_mgr static_loader; + }; + struct fw_image_props boot_fit_img; + struct fw_image_props linux_img; + u32 cpu_timeout; + u32 boot_fit_timeout; + u8 skip_bmc; + u8 sram_bar_id; + u8 dram_bar_id; + u8 linux_loaded; +}; + /** * struct hl_asic_funcs - ASIC specific functions that are can be called from * common code. @@ -901,8 +1069,6 @@ enum div_select_defs { * @ctx_fini: context dependent cleanup. * @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz * @get_queue_id_for_cq: Get the H/W queue id related to the given CQ index. - * @read_device_fw_version: read the device's firmware versions that are - * contained in registers * @load_firmware_to_device: load the firmware to the device's memory * @load_boot_fit_to_device: load boot fit to device's memory * @get_signal_cb_size: Get signal CB size. @@ -933,6 +1099,8 @@ enum div_select_defs { * @get_msi_info: Retrieve asic-specific MSI ID of the f/w async event * @map_pll_idx_to_fw_idx: convert driver specific per asic PLL index to * generic f/w compatible PLL Indexes + * @init_firmware_loader: initialize data for FW loader. + * @init_cpu_scrambler_dram: Enable CPU specific DRAM scrambling */ struct hl_asic_funcs { int (*early_init)(struct hl_device *hdev); @@ -1006,7 +1174,7 @@ struct hl_asic_funcs { int (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard, u32 flags); int (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard, - u32 asid, u64 va, u64 size); + u32 flags, u32 asid, u64 va, u64 size); int (*send_heartbeat)(struct hl_device *hdev); void (*set_clock_gating)(struct hl_device *hdev); void (*disable_clock_gating)(struct hl_device *hdev); @@ -1030,8 +1198,6 @@ struct hl_asic_funcs { void (*ctx_fini)(struct hl_ctx *ctx); int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); u32 (*get_queue_id_for_cq)(struct hl_device *hdev, u32 cq_idx); - int (*read_device_fw_version)(struct hl_device *hdev, - enum hl_fw_component fwc); int (*load_firmware_to_device)(struct hl_device *hdev); int (*load_boot_fit_to_device)(struct hl_device *hdev); u32 (*get_signal_cb_size)(struct hl_device *hdev); @@ -1056,8 +1222,10 @@ struct hl_asic_funcs { int (*hw_block_mmap)(struct hl_device *hdev, struct vm_area_struct *vma, u32 block_id, u32 block_size); void (*enable_events_from_fw)(struct hl_device *hdev); - void (*get_msi_info)(u32 *table); + void (*get_msi_info)(__le32 *table); int (*map_pll_idx_to_fw_idx)(u32 pll_idx); + void (*init_firmware_loader)(struct hl_device *hdev); + void (*init_cpu_scrambler_dram)(struct hl_device *hdev); }; @@ -1262,6 +1430,7 @@ struct hl_userptr { * @staged_sequence: the sequence of the staged submission this CS is part of, * relevant only if staged_cs is set. * @timeout_jiffies: cs timeout in jiffies. + * @submission_time_jiffies: submission time of the cs * @type: CS_TYPE_*. * @submitted: true if CS was submitted to H/W. * @completed: true if CS was completed by device. @@ -1274,6 +1443,8 @@ struct hl_userptr { * @staged_first: true if this is the first staged CS and we need to receive * timeout for this CS. * @staged_cs: true if this CS is part of a staged submission. + * @skip_reset_on_timeout: true if we shall not reset the device in case + * timeout occurs (debug scenario). */ struct hl_cs { u16 *jobs_in_queue_cnt; @@ -1291,6 +1462,7 @@ struct hl_cs { u64 sequence; u64 staged_sequence; u64 timeout_jiffies; + u64 submission_time_jiffies; enum hl_cs_type type; u8 submitted; u8 completed; @@ -1301,6 +1473,7 @@ struct hl_cs { u8 staged_last; u8 staged_first; u8 staged_cs; + u8 skip_reset_on_timeout; }; /** @@ -1922,7 +2095,7 @@ struct hl_mmu_funcs { * @kernel_queues: array of hl_hw_queue. * @cs_mirror_list: CS mirror list for TDR. * @cs_mirror_lock: protects cs_mirror_list. - * @kernel_cb_mgr: command buffer manager for creating/destroying/handling CGs. + * @kernel_cb_mgr: command buffer manager for creating/destroying/handling CBs. * @event_queue: event queue for IRQ from CPU-CP. * @dma_pool: DMA pool for small allocations. * @cpu_accessible_dma_mem: Host <-> CPU-CP shared memory CPU address. @@ -1954,6 +2127,8 @@ struct hl_mmu_funcs { * @aggregated_cs_counters: aggregated cs counters among all contexts * @mmu_priv: device-specific MMU data. * @mmu_func: device-related MMU functions. + * @fw_loader: FW loader manager. + * @pci_mem_region: array of memory regions in the PCI * @dram_used_mem: current DRAM memory consumption. * @timeout_jiffies: device CS timeout value. * @max_power: the max power of the device, as configured by the sysadmin. This @@ -1968,6 +2143,11 @@ struct hl_mmu_funcs { * the error will be ignored by the driver during * device initialization. Mainly used to debug and * workaround firmware bugs + * @last_successful_open_jif: timestamp (jiffies) of the last successful + * device open. + * @last_open_session_duration_jif: duration (jiffies) of the last device open + * session. + * @open_counter: number of successful device open operations. * @in_reset: is device in reset flow. * @curr_pll_profile: current PLL profile. * @card_type: Various ASICs have several card types. This indicates the card @@ -2007,6 +2187,8 @@ struct hl_mmu_funcs { * @collective_mon_idx: helper index for collective initialization * @supports_coresight: is CoreSight supported. * @supports_soft_reset: is soft reset supported. + * @allow_external_soft_reset: true if soft reset initiated by user or TDR is + * allowed. * @supports_cb_mapping: is mapping a CB to the device's MMU supported. * @needs_reset: true if reset_on_lockup is false and device should be reset * due to lockup. @@ -2015,6 +2197,14 @@ struct hl_mmu_funcs { * @device_fini_pending: true if device_fini was called and might be * waiting for the reset thread to finish * @supports_staged_submission: true if staged submissions are supported + * @curr_reset_cause: saves an enumerated reset cause when a hard reset is + * triggered, and cleared after it is shared with preboot. + * @skip_reset_on_timeout: Skip device reset if CS has timed out, wait for it to + * complete instead. + * @device_cpu_is_halted: Flag to indicate whether the device CPU was already + * halted. We can't halt it again because the COMMS + * protocol will throw an error. Relevant only for + * cases where Linux was not loaded to device CPU */ struct hl_device { struct pci_dev *pdev; @@ -2079,11 +2269,18 @@ struct hl_device { struct hl_mmu_priv mmu_priv; struct hl_mmu_funcs mmu_func[MMU_NUM_PGT_LOCATIONS]; + struct fw_load_mgr fw_loader; + + struct pci_mem_region pci_mem_region[PCI_REGION_NUMBER]; + atomic64_t dram_used_mem; u64 timeout_jiffies; u64 max_power; u64 clock_gating_mask; u64 boot_error_status_mask; + u64 last_successful_open_jif; + u64 last_open_session_duration_jif; + u64 open_counter; atomic_t in_reset; enum hl_pll_frequency curr_pll_profile; enum cpucp_card_types card_type; @@ -2116,11 +2313,15 @@ struct hl_device { u8 collective_mon_idx; u8 supports_coresight; u8 supports_soft_reset; + u8 allow_external_soft_reset; u8 supports_cb_mapping; u8 needs_reset; u8 process_kill_trial_cnt; u8 device_fini_pending; u8 supports_staged_submission; + u8 curr_reset_cause; + u8 skip_reset_on_timeout; + u8 device_cpu_is_halted; /* Parameters for bring-up */ u64 nic_ports_mask; @@ -2138,6 +2339,7 @@ struct hl_device { u8 rl_enable; u8 reset_on_preboot_fail; u8 reset_upon_device_release; + u8 reset_if_device_not_idle; }; @@ -2384,11 +2586,13 @@ void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, void *vaddr); int hl_fw_send_heartbeat(struct hl_device *hdev); int hl_fw_cpucp_info_get(struct hl_device *hdev, - u32 cpu_security_boot_status_reg, - u32 boot_err0_reg); + u32 sts_boot_dev_sts0_reg, + u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, + u32 boot_err1_reg); int hl_fw_cpucp_handshake(struct hl_device *hdev, - u32 cpu_security_boot_status_reg, - u32 boot_err0_reg); + u32 sts_boot_dev_sts0_reg, + u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, + u32 boot_err1_reg); int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size); int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev, struct hl_info_pci_counters *counters); @@ -2399,14 +2603,17 @@ int get_used_pll_index(struct hl_device *hdev, u32 input_pll_index, int hl_fw_cpucp_pll_info_get(struct hl_device *hdev, u32 pll_index, u16 *pll_freq_arr); int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power); -int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, - u32 msg_to_cpu_reg, u32 cpu_msg_status_reg, - u32 cpu_security_boot_status_reg, u32 boot_err0_reg, - bool skip_bmc, u32 cpu_timeout, u32 boot_fit_timeout); +void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev); +void hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev); +int hl_fw_init_cpu(struct hl_device *hdev); int hl_fw_read_preboot_status(struct hl_device *hdev, u32 cpu_boot_status_reg, - u32 cpu_security_boot_status_reg, u32 boot_err0_reg, - u32 timeout); - + u32 sts_boot_dev_sts0_reg, + u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, + u32 boot_err1_reg, u32 timeout); +int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev, + struct fw_load_mgr *fw_loader, + enum comms_cmd cmd, unsigned int size, + bool wait_ok, u32 timeout); int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], bool is_wc[3]); int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data); @@ -2415,6 +2622,7 @@ int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region, struct hl_inbound_pci_region *pci_region); int hl_pci_set_outbound_region(struct hl_device *hdev, struct hl_outbound_pci_region *pci_region); +enum pci_region hl_get_pci_memory_region(struct hl_device *hdev, u64 addr); int hl_pci_init(struct hl_device *hdev); void hl_pci_fini(struct hl_device *hdev); @@ -2443,6 +2651,8 @@ int hl_set_voltage(struct hl_device *hdev, int hl_set_current(struct hl_device *hdev, int sensor_index, u32 attr, long value); void hl_release_pending_user_interrupts(struct hl_device *hdev); +int hl_cs_signal_sob_wraparound_handler(struct hl_device *hdev, u32 q_idx, + struct hl_hw_sob **hw_sob, u32 count); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 64d1530db985..4194cda2d04c 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -29,7 +29,7 @@ static DEFINE_MUTEX(hl_devs_idr_lock); static int timeout_locked = 30; static int reset_on_lockup = 1; -static int memory_scrub = 1; +static int memory_scrub; static ulong boot_error_status_mask = ULONG_MAX; module_param(timeout_locked, int, 0444); @@ -42,7 +42,7 @@ MODULE_PARM_DESC(reset_on_lockup, module_param(memory_scrub, int, 0444); MODULE_PARM_DESC(memory_scrub, - "Scrub device memory in various states (0 = no, 1 = yes, default yes)"); + "Scrub device memory in various states (0 = no, 1 = yes, default no)"); module_param(boot_error_status_mask, ulong, 0444); MODULE_PARM_DESC(boot_error_status_mask, @@ -187,6 +187,9 @@ int hl_device_open(struct inode *inode, struct file *filp) hl_debugfs_add_file(hpriv); + hdev->open_counter++; + hdev->last_successful_open_jif = jiffies; + return 0; out_err: @@ -264,6 +267,7 @@ static void set_driver_behavior_per_device(struct hl_device *hdev) hdev->bmc_enable = 1; hdev->hard_reset_on_fw_events = 1; hdev->reset_on_preboot_fail = 1; + hdev->reset_if_device_not_idle = 1; hdev->reset_pcilink = 0; hdev->axi_drain = 0; @@ -308,10 +312,10 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev, } if (pdev) - hdev->asic_prop.fw_security_disabled = - !is_asic_secured(pdev->device); + hdev->asic_prop.fw_security_enabled = + is_asic_secured(hdev->asic_type); else - hdev->asic_prop.fw_security_disabled = true; + hdev->asic_prop.fw_security_enabled = false; /* Assign status description string */ strncpy(hdev->status[HL_DEVICE_STATUS_MALFUNCTION], @@ -325,11 +329,14 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev, hdev->reset_on_lockup = reset_on_lockup; hdev->memory_scrub = memory_scrub; hdev->boot_error_status_mask = boot_error_status_mask; + hdev->stop_on_err = true; hdev->pldm = 0; set_driver_behavior_per_device(hdev); + hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + if (timeout_locked) hdev->timeout_jiffies = msecs_to_jiffies(timeout_locked * 1000); else @@ -464,6 +471,7 @@ static int hl_pci_probe(struct pci_dev *pdev, return 0; disable_device: + pci_disable_pcie_error_reporting(pdev); pci_set_drvdata(pdev, NULL); destroy_hdev(hdev); @@ -572,7 +580,11 @@ static struct pci_driver hl_pci_driver = { .probe = hl_pci_probe, .remove = hl_pci_remove, .shutdown = hl_pci_remove, - .driver.pm = &hl_pm_ops, + .driver = { + .name = HL_NAME, + .pm = &hl_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, .err_handler = &hl_pci_err_handler, }; diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 33841c272eb6..f4dda7b4acdd 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -95,7 +95,7 @@ static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args) hw_ip.first_available_interrupt_id = prop->first_available_user_msix_interrupt; return copy_to_user(out, &hw_ip, - min((size_t)size, sizeof(hw_ip))) ? -EFAULT : 0; + min((size_t) size, sizeof(hw_ip))) ? -EFAULT : 0; } static int hw_events_info(struct hl_device *hdev, bool aggregate, @@ -460,6 +460,24 @@ static int power_info(struct hl_fpriv *hpriv, struct hl_info_args *args) min((size_t) max_size, sizeof(power_info))) ? -EFAULT : 0; } +static int open_stats_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + u32 max_size = args->return_size; + struct hl_open_stats_info open_stats_info = {0}; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + open_stats_info.last_open_period_ms = jiffies64_to_msecs( + hdev->last_open_session_duration_jif); + open_stats_info.open_counter = hdev->open_counter; + + return copy_to_user(out, &open_stats_info, + min((size_t) max_size, sizeof(open_stats_info))) ? -EFAULT : 0; +} + static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, struct device *dev) { @@ -543,6 +561,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_POWER: return power_info(hpriv, args); + case HL_INFO_OPEN_STATS: + return open_stats_info(hpriv, args); + default: dev_err(dev, "Invalid request %d\n", args->op); rc = -ENOTTY; diff --git a/drivers/misc/habanalabs/common/hw_queue.c b/drivers/misc/habanalabs/common/hw_queue.c index 173438461835..bcabfdbf1e01 100644 --- a/drivers/misc/habanalabs/common/hw_queue.c +++ b/drivers/misc/habanalabs/common/hw_queue.c @@ -410,19 +410,20 @@ static void hw_queue_schedule_job(struct hl_cs_job *job) ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); } -static void init_signal_cs(struct hl_device *hdev, +static int init_signal_cs(struct hl_device *hdev, struct hl_cs_job *job, struct hl_cs_compl *cs_cmpl) { struct hl_sync_stream_properties *prop; struct hl_hw_sob *hw_sob; u32 q_idx; + int rc = 0; q_idx = job->hw_queue_id; prop = &hdev->kernel_queues[q_idx].sync_stream_prop; hw_sob = &prop->hw_sob[prop->curr_sob_offset]; cs_cmpl->hw_sob = hw_sob; - cs_cmpl->sob_val = prop->next_sob_val++; + cs_cmpl->sob_val = prop->next_sob_val; dev_dbg(hdev->dev, "generate signal CB, sob_id: %d, sob val: 0x%x, q_idx: %d\n", @@ -434,24 +435,9 @@ static void init_signal_cs(struct hl_device *hdev, hdev->asic_funcs->gen_signal_cb(hdev, job->patched_cb, cs_cmpl->hw_sob->sob_id, 0, true); - kref_get(&hw_sob->kref); + rc = hl_cs_signal_sob_wraparound_handler(hdev, q_idx, &hw_sob, 1); - /* check for wraparound */ - if (prop->next_sob_val == HL_MAX_SOB_VAL) { - /* - * Decrement as we reached the max value. - * The release function won't be called here as we've - * just incremented the refcount. - */ - kref_put(&hw_sob->kref, hl_sob_reset_error); - prop->next_sob_val = 1; - /* only two SOBs are currently in use */ - prop->curr_sob_offset = - (prop->curr_sob_offset + 1) % HL_RSVD_SOBS; - - dev_dbg(hdev->dev, "switched to SOB %d, q_idx: %d\n", - prop->curr_sob_offset, q_idx); - } + return rc; } static void init_wait_cs(struct hl_device *hdev, struct hl_cs *cs, @@ -504,22 +490,25 @@ static void init_wait_cs(struct hl_device *hdev, struct hl_cs *cs, * * H/W queues spinlock should be taken before calling this function */ -static void init_signal_wait_cs(struct hl_cs *cs) +static int init_signal_wait_cs(struct hl_cs *cs) { struct hl_ctx *ctx = cs->ctx; struct hl_device *hdev = ctx->hdev; struct hl_cs_job *job; struct hl_cs_compl *cs_cmpl = container_of(cs->fence, struct hl_cs_compl, base_fence); + int rc = 0; /* There is only one job in a signal/wait CS */ job = list_first_entry(&cs->job_list, struct hl_cs_job, cs_node); if (cs->type & CS_TYPE_SIGNAL) - init_signal_cs(hdev, job, cs_cmpl); + rc = init_signal_cs(hdev, job, cs_cmpl); else if (cs->type & CS_TYPE_WAIT) init_wait_cs(hdev, cs, job, cs_cmpl); + + return rc; } /* @@ -590,11 +579,16 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) } } - if ((cs->type == CS_TYPE_SIGNAL) || (cs->type == CS_TYPE_WAIT)) - init_signal_wait_cs(cs); - else if (cs->type == CS_TYPE_COLLECTIVE_WAIT) + if ((cs->type == CS_TYPE_SIGNAL) || (cs->type == CS_TYPE_WAIT)) { + rc = init_signal_wait_cs(cs); + if (rc) { + dev_err(hdev->dev, "Failed to submit signal cs\n"); + goto unroll_cq_resv; + } + } else if (cs->type == CS_TYPE_COLLECTIVE_WAIT) hdev->asic_funcs->collective_wait_init_cs(cs); + spin_lock(&hdev->cs_mirror_lock); /* Verify staged CS exists and add to the staged list */ diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 27129868c711..39b14a933393 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -207,17 +207,33 @@ irqreturn_t hl_irq_handler_eq(int irq, void *arg) struct hl_eq_entry *eq_entry; struct hl_eq_entry *eq_base; struct hl_eqe_work *handle_eqe_work; + bool entry_ready; + u32 cur_eqe; + u16 cur_eqe_index; eq_base = eq->kernel_address; while (1) { - bool entry_ready = - ((le32_to_cpu(eq_base[eq->ci].hdr.ctl) & - EQ_CTL_READY_MASK) >> EQ_CTL_READY_SHIFT); + cur_eqe = le32_to_cpu(eq_base[eq->ci].hdr.ctl); + entry_ready = !!FIELD_GET(EQ_CTL_READY_MASK, cur_eqe); if (!entry_ready) break; + cur_eqe_index = FIELD_GET(EQ_CTL_INDEX_MASK, cur_eqe); + if ((hdev->event_queue.check_eqe_index) && + (((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK) + != cur_eqe_index)) { + dev_dbg(hdev->dev, + "EQE 0x%x in queue is ready but index does not match %d!=%d", + eq_base[eq->ci].hdr.ctl, + ((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK), + cur_eqe_index); + break; + } + + eq->prev_eqe_index++; + eq_entry = &eq_base[eq->ci]; /* @@ -341,6 +357,7 @@ int hl_eq_init(struct hl_device *hdev, struct hl_eq *q) q->hdev = hdev; q->kernel_address = p; q->ci = 0; + q->prev_eqe_index = 0; return 0; } @@ -365,6 +382,7 @@ void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q) void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q) { q->ci = 0; + q->prev_eqe_index = 0; /* * It's not enough to just reset the PI/CI because the H/W may have diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 2938cbbafbbc..af339ce1ab4f 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -570,8 +570,10 @@ static u64 get_va_block(struct hl_device *hdev, if ((is_align_pow_2 && (hint_addr & (va_block_align - 1))) || (!is_align_pow_2 && do_div(tmp_hint_addr, va_range->page_size))) { - dev_info(hdev->dev, "Hint address 0x%llx will be ignored\n", - hint_addr); + + dev_dbg(hdev->dev, + "Hint address 0x%llx will be ignored because it is not aligned\n", + hint_addr); hint_addr = 0; } @@ -1117,7 +1119,8 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, goto map_err; } - rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, false, *vm_type); + rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, false, + *vm_type, ctx->asid, ret_vaddr, phys_pg_pack->total_size); mutex_unlock(&ctx->mmu_lock); @@ -1261,8 +1264,9 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, * at the loop end rather than for each iteration */ if (!ctx_free) - rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, true, - *vm_type); + rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, true, + *vm_type, ctx->asid, vaddr, + phys_pg_pack->total_size); mutex_unlock(&ctx->mmu_lock); @@ -1369,12 +1373,7 @@ int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) /* Driver only allows mapping of a complete HW block */ block_size = vma->vm_end - vma->vm_start; -#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK - if (!access_ok(VERIFY_WRITE, - (void __user *) (uintptr_t) vma->vm_start, block_size)) { -#else if (!access_ok((void __user *) (uintptr_t) vma->vm_start, block_size)) { -#endif dev_err(hdev->dev, "user pointer is invalid - 0x%lx\n", vma->vm_start); @@ -1608,7 +1607,8 @@ static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size, if (rc != npages) { dev_err(hdev->dev, - "Failed to map host memory, user ptr probably wrong\n"); + "Failed (%d) to pin host memory with user ptr 0x%llx, size 0x%llx, npages %d\n", + rc, addr, size, npages); if (rc < 0) goto destroy_pages; npages = rc; diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c index b37189956b14..792d25b79ea6 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu.c +++ b/drivers/misc/habanalabs/common/mmu/mmu.c @@ -501,12 +501,20 @@ static void hl_mmu_pa_page_with_offset(struct hl_ctx *ctx, u64 virt_addr, if ((hops->range_type == HL_VA_RANGE_TYPE_DRAM) && !is_power_of_2(prop->dram_page_size)) { - u32 bit; + unsigned long dram_page_size = prop->dram_page_size; u64 page_offset_mask; u64 phys_addr_mask; + u32 bit; - bit = __ffs64((u64)prop->dram_page_size); - page_offset_mask = ((1ull << bit) - 1); + /* + * find last set bit in page_size to cover all bits of page + * offset. note that 1 has to be added to bit index. + * note that the internal ulong variable is used to avoid + * alignment issue. + */ + bit = find_last_bit(&dram_page_size, + sizeof(dram_page_size) * BITS_PER_BYTE) + 1; + page_offset_mask = (BIT_ULL(bit) - 1); phys_addr_mask = ~page_offset_mask; *phys_addr = (tmp_phys_addr & phys_addr_mask) | (virt_addr & page_offset_mask); diff --git a/drivers/misc/habanalabs/common/pci/pci.c b/drivers/misc/habanalabs/common/pci/pci.c index e941b7eef346..d5bedf5ba011 100644 --- a/drivers/misc/habanalabs/common/pci/pci.c +++ b/drivers/misc/habanalabs/common/pci/pci.c @@ -10,7 +10,7 @@ #include <linux/pci.h> -#define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 10) +#define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 100) #define IATU_REGION_CTRL_REGION_EN_MASK BIT(31) #define IATU_REGION_CTRL_MATCH_MODE_MASK BIT(30) @@ -360,6 +360,32 @@ int hl_pci_set_outbound_region(struct hl_device *hdev, } /** + * hl_get_pci_memory_region() - get PCI region for given address + * @hdev: Pointer to hl_device structure. + * @addr: device address + * + * @return region index on success, otherwise PCI_REGION_NUMBER (invalid + * region index) + */ +enum pci_region hl_get_pci_memory_region(struct hl_device *hdev, u64 addr) +{ + int i; + + for (i = 0 ; i < PCI_REGION_NUMBER ; i++) { + struct pci_mem_region *region = &hdev->pci_mem_region[i]; + + if (!region->used) + continue; + + if ((addr >= region->region_base) && + (addr < region->region_base + region->region_size)) + return i; + } + + return PCI_REGION_NUMBER; +} + +/** * hl_pci_init() - PCI initialization code. * @hdev: Pointer to hl_device structure. * @@ -395,6 +421,12 @@ int hl_pci_init(struct hl_device *hdev) goto unmap_pci_bars; } + /* Driver must sleep in order for FW to finish the iATU configuration */ + if (hdev->asic_prop.iatu_done_by_fw) { + usleep_range(2000, 3000); + hdev->asic_funcs->set_dma_mask_from_fw(hdev); + } + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(hdev->dma_mask)); if (rc) { diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index c9f649b31e3a..db72df282ef8 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -208,7 +208,7 @@ static ssize_t soft_reset_store(struct device *dev, goto out; } - if (!hdev->supports_soft_reset) { + if (!hdev->allow_external_soft_reset) { dev_err(hdev->dev, "Device does not support soft-reset\n"); goto out; } diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 9e4a6bb3acd1..aa8a0ca5aca2 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -78,6 +78,7 @@ #define GAUDI_PLDM_TPC_KERNEL_WAIT_USEC (HL_DEVICE_TIMEOUT_USEC * 30) #define GAUDI_BOOT_FIT_REQ_TIMEOUT_USEC 1000000 /* 1s */ #define GAUDI_MSG_TO_CPU_TIMEOUT_USEC 4000000 /* 4s */ +#define GAUDI_WAIT_FOR_BL_TIMEOUT_USEC 15000000 /* 15s */ #define GAUDI_QMAN0_FENCE_VAL 0x72E91AB9 @@ -409,7 +410,7 @@ static inline void set_default_power_values(struct hl_device *hdev) } } -static int gaudi_get_fixed_properties(struct hl_device *hdev) +static int gaudi_set_fixed_properties(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; u32 num_sync_stream_queues = 0; @@ -545,8 +546,10 @@ static int gaudi_get_fixed_properties(struct hl_device *hdev) for (i = 0 ; i < HL_MAX_DCORES ; i++) prop->first_available_cq[i] = USHRT_MAX; - prop->fw_security_status_valid = false; + prop->fw_cpu_boot_dev_sts0_valid = false; + prop->fw_cpu_boot_dev_sts1_valid = false; prop->hard_reset_done_by_fw = false; + prop->gic_interrupts_enable = true; return 0; } @@ -577,6 +580,9 @@ static u64 gaudi_set_hbm_bar_base(struct hl_device *hdev, u64 addr) if ((gaudi) && (gaudi->hbm_bar_cur_addr == addr)) return old_addr; + if (hdev->asic_prop.iatu_done_by_fw) + return U64_MAX; + /* Inbound Region 2 - Bar 4 - Point to HBM */ pci_region.mode = PCI_BAR_MATCH_MODE; pci_region.bar = HBM_BAR_ID; @@ -599,10 +605,8 @@ static int gaudi_init_iatu(struct hl_device *hdev) struct hl_outbound_pci_region outbound_region; int rc; - if (hdev->asic_prop.iatu_done_by_fw) { - hdev->asic_funcs->set_dma_mask_from_fw(hdev); + if (hdev->asic_prop.iatu_done_by_fw) return 0; - } /* Inbound Region 0 - Bar 0 - Point to SRAM + CFG */ inbound_region.mode = PCI_BAR_MATCH_MODE; @@ -651,9 +655,9 @@ static int gaudi_early_init(struct hl_device *hdev) u32 fw_boot_status; int rc; - rc = gaudi_get_fixed_properties(hdev); + rc = gaudi_set_fixed_properties(hdev); if (rc) { - dev_err(hdev->dev, "Failed to get fixed properties\n"); + dev_err(hdev->dev, "Failed setting fixed properties\n"); return rc; } @@ -683,8 +687,14 @@ static int gaudi_early_init(struct hl_device *hdev) prop->dram_pci_bar_size = pci_resource_len(pdev, HBM_BAR_ID); /* If FW security is enabled at this point it means no access to ELBI */ - if (!hdev->asic_prop.fw_security_disabled) { + if (hdev->asic_prop.fw_security_enabled) { hdev->asic_prop.iatu_done_by_fw = true; + + /* + * GIC-security-bit can ONLY be set by CPUCP, so in this stage + * decision can only be taken based on PCI ID security. + */ + hdev->asic_prop.gic_interrupts_enable = false; goto pci_init; } @@ -707,8 +717,10 @@ pci_init: * version to determine whether we run with a security-enabled firmware */ rc = hl_fw_read_preboot_status(hdev, mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS, - mmCPU_BOOT_DEV_STS0, mmCPU_BOOT_ERR0, - GAUDI_BOOT_FIT_REQ_TIMEOUT_USEC); + mmCPU_BOOT_DEV_STS0, + mmCPU_BOOT_DEV_STS1, mmCPU_BOOT_ERR0, + mmCPU_BOOT_ERR1, + GAUDI_BOOT_FIT_REQ_TIMEOUT_USEC); if (rc) { if (hdev->reset_on_preboot_fail) hdev->asic_funcs->hw_fini(hdev, true); @@ -751,7 +763,14 @@ static int gaudi_fetch_psoc_frequency(struct hl_device *hdev) u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS], freq; int rc; - if (hdev->asic_prop.fw_security_disabled) { + if (hdev->asic_prop.fw_security_enabled) { + rc = hl_fw_cpucp_pll_info_get(hdev, HL_GAUDI_CPU_PLL, pll_freq_arr); + + if (rc) + return rc; + + freq = pll_freq_arr[2]; + } else { /* Backward compatibility */ div_fctr = RREG32(mmPSOC_CPU_PLL_DIV_FACTOR_2); div_sel = RREG32(mmPSOC_CPU_PLL_DIV_SEL_2); @@ -779,13 +798,6 @@ static int gaudi_fetch_psoc_frequency(struct hl_device *hdev) div_sel); freq = 0; } - } else { - rc = hl_fw_cpucp_pll_info_get(hdev, HL_GAUDI_CPU_PLL, pll_freq_arr); - - if (rc) - return rc; - - freq = pll_freq_arr[2]; } prop->psoc_timestamp_frequency = freq; @@ -988,9 +1000,27 @@ static void gaudi_sob_group_reset_error(struct kref *ref) hw_sob_group->base_sob_id); } +static void gaudi_collective_mstr_sob_mask_set(struct gaudi_device *gaudi) +{ + struct gaudi_collective_properties *prop; + int i; + + prop = &gaudi->collective_props; + + memset(prop->mstr_sob_mask, 0, sizeof(prop->mstr_sob_mask)); + + for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++) + if (gaudi->hw_cap_initialized & BIT(HW_CAP_NIC_SHIFT + i)) + prop->mstr_sob_mask[i / HL_MAX_SOBS_PER_MONITOR] |= + BIT(i % HL_MAX_SOBS_PER_MONITOR); + /* Set collective engine bit */ + prop->mstr_sob_mask[i / HL_MAX_SOBS_PER_MONITOR] |= + BIT(i % HL_MAX_SOBS_PER_MONITOR); +} + static int gaudi_collective_init(struct hl_device *hdev) { - u32 i, master_monitor_sobs, sob_id, reserved_sobs_per_group; + u32 i, sob_id, reserved_sobs_per_group; struct gaudi_collective_properties *prop; struct gaudi_device *gaudi; @@ -1016,22 +1046,7 @@ static int gaudi_collective_init(struct hl_device *hdev) gaudi_collective_map_sobs(hdev, i); } - prop->mstr_sob_mask[0] = 0; - master_monitor_sobs = HL_MAX_SOBS_PER_MONITOR; - for (i = 0 ; i < master_monitor_sobs ; i++) - if (gaudi->hw_cap_initialized & BIT(HW_CAP_NIC_SHIFT + i)) - prop->mstr_sob_mask[0] |= BIT(i); - - prop->mstr_sob_mask[1] = 0; - master_monitor_sobs = - NIC_NUMBER_OF_ENGINES - HL_MAX_SOBS_PER_MONITOR; - for (i = 0 ; i < master_monitor_sobs; i++) { - if (gaudi->hw_cap_initialized & BIT(HW_CAP_NIC_SHIFT + i)) - prop->mstr_sob_mask[1] |= BIT(i); - } - - /* Set collective engine bit */ - prop->mstr_sob_mask[1] |= BIT(i); + gaudi_collective_mstr_sob_mask_set(gaudi); return 0; } @@ -1513,7 +1528,7 @@ static int gaudi_alloc_cpu_accessible_dma_mem(struct hl_device *hdev) hdev->cpu_pci_msb_addr = GAUDI_CPU_PCI_MSB_ADDR(hdev->cpu_accessible_dma_address); - if (hdev->asic_prop.fw_security_disabled) + if (!hdev->asic_prop.fw_security_enabled) GAUDI_PCI_TO_CPU_ADDR(hdev->cpu_accessible_dma_address); free_dma_mem_arr: @@ -1590,6 +1605,48 @@ free_internal_qmans_pq_mem: return rc; } +static void gaudi_set_pci_memory_regions(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct pci_mem_region *region; + + /* CFG */ + region = &hdev->pci_mem_region[PCI_REGION_CFG]; + region->region_base = CFG_BASE; + region->region_size = CFG_SIZE; + region->offset_in_bar = CFG_BASE - SPI_FLASH_BASE_ADDR; + region->bar_size = CFG_BAR_SIZE; + region->bar_id = CFG_BAR_ID; + region->used = 1; + + /* SRAM */ + region = &hdev->pci_mem_region[PCI_REGION_SRAM]; + region->region_base = SRAM_BASE_ADDR; + region->region_size = SRAM_SIZE; + region->offset_in_bar = 0; + region->bar_size = SRAM_BAR_SIZE; + region->bar_id = SRAM_BAR_ID; + region->used = 1; + + /* DRAM */ + region = &hdev->pci_mem_region[PCI_REGION_DRAM]; + region->region_base = DRAM_PHYS_BASE; + region->region_size = hdev->asic_prop.dram_size; + region->offset_in_bar = 0; + region->bar_size = prop->dram_pci_bar_size; + region->bar_id = HBM_BAR_ID; + region->used = 1; + + /* SP SRAM */ + region = &hdev->pci_mem_region[PCI_REGION_SP_SRAM]; + region->region_base = PSOC_SCRATCHPAD_ADDR; + region->region_size = PSOC_SCRATCHPAD_SIZE; + region->offset_in_bar = PSOC_SCRATCHPAD_ADDR - SPI_FLASH_BASE_ADDR; + region->bar_size = CFG_BAR_SIZE; + region->bar_id = CFG_BAR_ID; + region->used = 1; +} + static int gaudi_sw_init(struct hl_device *hdev) { struct gaudi_device *gaudi; @@ -1664,12 +1721,14 @@ static int gaudi_sw_init(struct hl_device *hdev) hdev->supports_coresight = true; hdev->supports_staged_submission = true; + gaudi_set_pci_memory_regions(hdev); + return 0; free_cpu_accessible_dma_pool: gen_pool_destroy(hdev->cpu_accessible_dma_pool); free_cpu_dma_mem: - if (hdev->asic_prop.fw_security_disabled) + if (!hdev->asic_prop.fw_security_enabled) GAUDI_CPU_TO_PCI_ADDR(hdev->cpu_accessible_dma_address, hdev->cpu_pci_msb_addr); hdev->asic_funcs->asic_dma_free_coherent(hdev, @@ -1691,7 +1750,7 @@ static int gaudi_sw_fini(struct hl_device *hdev) gen_pool_destroy(hdev->cpu_accessible_dma_pool); - if (hdev->asic_prop.fw_security_disabled) + if (!hdev->asic_prop.fw_security_enabled) GAUDI_CPU_TO_PCI_ADDR(hdev->cpu_accessible_dma_address, hdev->cpu_pci_msb_addr); @@ -1879,12 +1938,11 @@ static void gaudi_init_scrambler_sram(struct hl_device *hdev) { struct gaudi_device *gaudi = hdev->asic_specific; - if (!hdev->asic_prop.fw_security_disabled) + if (hdev->asic_prop.fw_security_enabled) return; - if (hdev->asic_prop.fw_security_status_valid && - (hdev->asic_prop.fw_app_security_map & - CPU_BOOT_DEV_STS0_SRAM_SCR_EN)) + if (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_SRAM_SCR_EN) return; if (gaudi->hw_cap_initialized & HW_CAP_SRAM_SCRAMBLER) @@ -1951,12 +2009,11 @@ static void gaudi_init_scrambler_hbm(struct hl_device *hdev) { struct gaudi_device *gaudi = hdev->asic_specific; - if (!hdev->asic_prop.fw_security_disabled) + if (hdev->asic_prop.fw_security_enabled) return; - if (hdev->asic_prop.fw_security_status_valid && - (hdev->asic_prop.fw_boot_cpu_security_map & - CPU_BOOT_DEV_STS0_DRAM_SCR_EN)) + if (hdev->asic_prop.fw_bootfit_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_DRAM_SCR_EN) return; if (gaudi->hw_cap_initialized & HW_CAP_HBM_SCRAMBLER) @@ -2021,12 +2078,11 @@ static void gaudi_init_scrambler_hbm(struct hl_device *hdev) static void gaudi_init_e2e(struct hl_device *hdev) { - if (!hdev->asic_prop.fw_security_disabled) + if (hdev->asic_prop.fw_security_enabled) return; - if (hdev->asic_prop.fw_security_status_valid && - (hdev->asic_prop.fw_boot_cpu_security_map & - CPU_BOOT_DEV_STS0_E2E_CRED_EN)) + if (hdev->asic_prop.fw_bootfit_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_E2E_CRED_EN) return; WREG32(mmSIF_RTR_CTRL_0_E2E_HBM_WR_SIZE, 247 >> 3); @@ -2396,12 +2452,11 @@ static void gaudi_init_hbm_cred(struct hl_device *hdev) { uint32_t hbm0_wr, hbm1_wr, hbm0_rd, hbm1_rd; - if (!hdev->asic_prop.fw_security_disabled) + if (hdev->asic_prop.fw_security_enabled) return; - if (hdev->asic_prop.fw_security_status_valid && - (hdev->asic_prop.fw_boot_cpu_security_map & - CPU_BOOT_DEV_STS0_HBM_CRED_EN)) + if (hdev->asic_prop.fw_bootfit_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_HBM_CRED_EN) return; hbm0_wr = 0x33333333; @@ -2487,10 +2542,12 @@ static void gaudi_init_golden_registers(struct hl_device *hdev) static void gaudi_init_pci_dma_qman(struct hl_device *hdev, int dma_id, int qman_id, dma_addr_t qman_pq_addr) { + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; u32 mtr_base_en_lo, mtr_base_en_hi, mtr_base_ws_lo, mtr_base_ws_hi; u32 so_base_en_lo, so_base_en_hi, so_base_ws_lo, so_base_ws_hi; u32 q_off, dma_qm_offset; - u32 dma_qm_err_cfg; + u32 dma_qm_err_cfg, irq_handler_offset; dma_qm_offset = dma_id * DMA_QMAN_OFFSET; @@ -2539,20 +2596,23 @@ static void gaudi_init_pci_dma_qman(struct hl_device *hdev, int dma_id, /* The following configuration is needed only once per QMAN */ if (qman_id == 0) { + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_dma_qm_irq_ctrl); + /* Configure RAZWI IRQ */ dma_qm_err_cfg = PCI_DMA_QMAN_GLBL_ERR_CFG_MSG_EN_MASK; - if (hdev->stop_on_err) { + if (hdev->stop_on_err) dma_qm_err_cfg |= PCI_DMA_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK; - } WREG32(mmDMA0_QM_GLBL_ERR_CFG + dma_qm_offset, dma_qm_err_cfg); + WREG32(mmDMA0_QM_GLBL_ERR_ADDR_LO + dma_qm_offset, - lower_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + lower_32_bits(CFG_BASE + irq_handler_offset)); WREG32(mmDMA0_QM_GLBL_ERR_ADDR_HI + dma_qm_offset, - upper_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + upper_32_bits(CFG_BASE + irq_handler_offset)); + WREG32(mmDMA0_QM_GLBL_ERR_WDATA + dma_qm_offset, gaudi_irq_map_table[GAUDI_EVENT_DMA0_QM].cpu_id + dma_id); @@ -2573,8 +2633,11 @@ static void gaudi_init_pci_dma_qman(struct hl_device *hdev, int dma_id, static void gaudi_init_dma_core(struct hl_device *hdev, int dma_id) { - u32 dma_offset = dma_id * DMA_CORE_OFFSET; + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; u32 dma_err_cfg = 1 << DMA0_CORE_ERR_CFG_ERR_MSG_EN_SHIFT; + u32 dma_offset = dma_id * DMA_CORE_OFFSET; + u32 irq_handler_offset; /* Set to maximum possible according to physical size */ WREG32(mmDMA0_CORE_RD_MAX_OUTSTAND + dma_offset, 0); @@ -2588,10 +2651,16 @@ static void gaudi_init_dma_core(struct hl_device *hdev, int dma_id) dma_err_cfg |= 1 << DMA0_CORE_ERR_CFG_STOP_ON_ERR_SHIFT; WREG32(mmDMA0_CORE_ERR_CFG + dma_offset, dma_err_cfg); + + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_dma_core_irq_ctrl); + WREG32(mmDMA0_CORE_ERRMSG_ADDR_LO + dma_offset, - lower_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + lower_32_bits(CFG_BASE + irq_handler_offset)); WREG32(mmDMA0_CORE_ERRMSG_ADDR_HI + dma_offset, - upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + upper_32_bits(CFG_BASE + irq_handler_offset)); + WREG32(mmDMA0_CORE_ERRMSG_WDATA + dma_offset, gaudi_irq_map_table[GAUDI_EVENT_DMA0_CORE].cpu_id + dma_id); WREG32(mmDMA0_CORE_PROT + dma_offset, @@ -2654,10 +2723,12 @@ static void gaudi_init_pci_dma_qmans(struct hl_device *hdev) static void gaudi_init_hbm_dma_qman(struct hl_device *hdev, int dma_id, int qman_id, u64 qman_base_addr) { + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; u32 mtr_base_en_lo, mtr_base_en_hi, mtr_base_ws_lo, mtr_base_ws_hi; u32 so_base_en_lo, so_base_en_hi, so_base_ws_lo, so_base_ws_hi; + u32 dma_qm_err_cfg, irq_handler_offset; u32 q_off, dma_qm_offset; - u32 dma_qm_err_cfg; dma_qm_offset = dma_id * DMA_QMAN_OFFSET; @@ -2697,6 +2768,10 @@ static void gaudi_init_hbm_dma_qman(struct hl_device *hdev, int dma_id, WREG32(mmDMA0_QM_CP_LDMA_DST_BASE_LO_OFFSET_0 + q_off, QMAN_CPDMA_DST_OFFSET); } else { + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_dma_qm_irq_ctrl); + WREG32(mmDMA0_QM_CP_LDMA_TSIZE_OFFSET_0 + q_off, QMAN_LDMA_SIZE_OFFSET); WREG32(mmDMA0_QM_CP_LDMA_SRC_BASE_LO_OFFSET_0 + q_off, @@ -2706,18 +2781,17 @@ static void gaudi_init_hbm_dma_qman(struct hl_device *hdev, int dma_id, /* Configure RAZWI IRQ */ dma_qm_err_cfg = HBM_DMA_QMAN_GLBL_ERR_CFG_MSG_EN_MASK; - if (hdev->stop_on_err) { + if (hdev->stop_on_err) dma_qm_err_cfg |= HBM_DMA_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK; - } + WREG32(mmDMA0_QM_GLBL_ERR_CFG + dma_qm_offset, dma_qm_err_cfg); WREG32(mmDMA0_QM_GLBL_ERR_ADDR_LO + dma_qm_offset, - lower_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + lower_32_bits(CFG_BASE + irq_handler_offset)); WREG32(mmDMA0_QM_GLBL_ERR_ADDR_HI + dma_qm_offset, - upper_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + upper_32_bits(CFG_BASE + irq_handler_offset)); + WREG32(mmDMA0_QM_GLBL_ERR_WDATA + dma_qm_offset, gaudi_irq_map_table[GAUDI_EVENT_DMA0_QM].cpu_id + dma_id); @@ -2792,8 +2866,11 @@ static void gaudi_init_hbm_dma_qmans(struct hl_device *hdev) static void gaudi_init_mme_qman(struct hl_device *hdev, u32 mme_offset, int qman_id, u64 qman_base_addr) { + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; u32 mtr_base_lo, mtr_base_hi; u32 so_base_lo, so_base_hi; + u32 irq_handler_offset; u32 q_off, mme_id; u32 mme_qm_err_cfg; @@ -2825,6 +2902,10 @@ static void gaudi_init_mme_qman(struct hl_device *hdev, u32 mme_offset, WREG32(mmMME0_QM_CP_LDMA_DST_BASE_LO_OFFSET_0 + q_off, QMAN_CPDMA_DST_OFFSET); } else { + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_mme_qm_irq_ctrl); + WREG32(mmMME0_QM_CP_LDMA_TSIZE_OFFSET_0 + q_off, QMAN_LDMA_SIZE_OFFSET); WREG32(mmMME0_QM_CP_LDMA_SRC_BASE_LO_OFFSET_0 + q_off, @@ -2834,20 +2915,20 @@ static void gaudi_init_mme_qman(struct hl_device *hdev, u32 mme_offset, /* Configure RAZWI IRQ */ mme_id = mme_offset / - (mmMME1_QM_GLBL_CFG0 - mmMME0_QM_GLBL_CFG0); + (mmMME1_QM_GLBL_CFG0 - mmMME0_QM_GLBL_CFG0) / 2; mme_qm_err_cfg = MME_QMAN_GLBL_ERR_CFG_MSG_EN_MASK; - if (hdev->stop_on_err) { + if (hdev->stop_on_err) mme_qm_err_cfg |= MME_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK; - } + WREG32(mmMME0_QM_GLBL_ERR_CFG + mme_offset, mme_qm_err_cfg); + WREG32(mmMME0_QM_GLBL_ERR_ADDR_LO + mme_offset, - lower_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + lower_32_bits(CFG_BASE + irq_handler_offset)); WREG32(mmMME0_QM_GLBL_ERR_ADDR_HI + mme_offset, - upper_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + upper_32_bits(CFG_BASE + irq_handler_offset)); + WREG32(mmMME0_QM_GLBL_ERR_WDATA + mme_offset, gaudi_irq_map_table[GAUDI_EVENT_MME0_QM].cpu_id + mme_id); @@ -2912,10 +2993,12 @@ static void gaudi_init_mme_qmans(struct hl_device *hdev) static void gaudi_init_tpc_qman(struct hl_device *hdev, u32 tpc_offset, int qman_id, u64 qman_base_addr) { + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; u32 mtr_base_en_lo, mtr_base_en_hi, mtr_base_ws_lo, mtr_base_ws_hi; u32 so_base_en_lo, so_base_en_hi, so_base_ws_lo, so_base_ws_hi; + u32 tpc_qm_err_cfg, irq_handler_offset; u32 q_off, tpc_id; - u32 tpc_qm_err_cfg; mtr_base_en_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_E_N_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0); @@ -2956,6 +3039,10 @@ static void gaudi_init_tpc_qman(struct hl_device *hdev, u32 tpc_offset, WREG32(mmTPC0_QM_CP_LDMA_DST_BASE_LO_OFFSET_0 + q_off, QMAN_CPDMA_DST_OFFSET); } else { + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_tpc_qm_irq_ctrl); + WREG32(mmTPC0_QM_CP_LDMA_TSIZE_OFFSET_0 + q_off, QMAN_LDMA_SIZE_OFFSET); WREG32(mmTPC0_QM_CP_LDMA_SRC_BASE_LO_OFFSET_0 + q_off, @@ -2965,18 +3052,17 @@ static void gaudi_init_tpc_qman(struct hl_device *hdev, u32 tpc_offset, /* Configure RAZWI IRQ */ tpc_qm_err_cfg = TPC_QMAN_GLBL_ERR_CFG_MSG_EN_MASK; - if (hdev->stop_on_err) { + if (hdev->stop_on_err) tpc_qm_err_cfg |= TPC_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK; - } WREG32(mmTPC0_QM_GLBL_ERR_CFG + tpc_offset, tpc_qm_err_cfg); + WREG32(mmTPC0_QM_GLBL_ERR_ADDR_LO + tpc_offset, - lower_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + lower_32_bits(CFG_BASE + irq_handler_offset)); WREG32(mmTPC0_QM_GLBL_ERR_ADDR_HI + tpc_offset, - upper_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + upper_32_bits(CFG_BASE + irq_handler_offset)); + WREG32(mmTPC0_QM_GLBL_ERR_WDATA + tpc_offset, gaudi_irq_map_table[GAUDI_EVENT_TPC0_QM].cpu_id + tpc_id); @@ -3059,10 +3145,12 @@ static void gaudi_init_tpc_qmans(struct hl_device *hdev) static void gaudi_init_nic_qman(struct hl_device *hdev, u32 nic_offset, int qman_id, u64 qman_base_addr, int nic_id) { + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; u32 mtr_base_en_lo, mtr_base_en_hi, mtr_base_ws_lo, mtr_base_ws_hi; u32 so_base_en_lo, so_base_en_hi, so_base_ws_lo, so_base_ws_hi; + u32 nic_qm_err_cfg, irq_handler_offset; u32 q_off; - u32 nic_qm_err_cfg; mtr_base_en_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_E_N_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0); @@ -3109,20 +3197,23 @@ static void gaudi_init_nic_qman(struct hl_device *hdev, u32 nic_offset, WREG32(mmNIC0_QM0_CP_MSG_BASE3_ADDR_HI_0 + q_off, so_base_ws_hi); if (qman_id == 0) { + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_nic_qm_irq_ctrl); + /* Configure RAZWI IRQ */ nic_qm_err_cfg = NIC_QMAN_GLBL_ERR_CFG_MSG_EN_MASK; - if (hdev->stop_on_err) { + if (hdev->stop_on_err) nic_qm_err_cfg |= NIC_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK; - } WREG32(mmNIC0_QM0_GLBL_ERR_CFG + nic_offset, nic_qm_err_cfg); + WREG32(mmNIC0_QM0_GLBL_ERR_ADDR_LO + nic_offset, - lower_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + lower_32_bits(CFG_BASE + irq_handler_offset)); WREG32(mmNIC0_QM0_GLBL_ERR_ADDR_HI + nic_offset, - upper_32_bits(CFG_BASE + - mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR)); + upper_32_bits(CFG_BASE + irq_handler_offset)); + WREG32(mmNIC0_QM0_GLBL_ERR_WDATA + nic_offset, gaudi_irq_map_table[GAUDI_EVENT_NIC0_QM0].cpu_id + nic_id); @@ -3475,7 +3566,7 @@ static void gaudi_set_clock_gating(struct hl_device *hdev) if (hdev->in_debug) return; - if (!hdev->asic_prop.fw_security_disabled) + if (hdev->asic_prop.fw_security_enabled) return; for (i = GAUDI_PCI_DMA_1, qman_offset = 0 ; i < GAUDI_HBM_DMA_1 ; i++) { @@ -3535,7 +3626,7 @@ static void gaudi_disable_clock_gating(struct hl_device *hdev) u32 qman_offset; int i; - if (!hdev->asic_prop.fw_security_disabled) + if (hdev->asic_prop.fw_security_enabled) return; for (i = 0, qman_offset = 0 ; i < DMA_NUMBER_OF_CHANNELS ; i++) { @@ -3674,9 +3765,6 @@ static int gaudi_load_firmware_to_device(struct hl_device *hdev) { void __iomem *dst; - /* HBM scrambler must be initialized before pushing F/W to HBM */ - gaudi_init_scrambler_hbm(hdev); - dst = hdev->pcie_bar[HBM_BAR_ID] + LINUX_FW_OFFSET; return hl_fw_load_fw_to_device(hdev, GAUDI_LINUX_FW_FILE, dst, 0, 0); @@ -3691,42 +3779,71 @@ static int gaudi_load_boot_fit_to_device(struct hl_device *hdev) return hl_fw_load_fw_to_device(hdev, GAUDI_BOOT_FIT_FILE, dst, 0, 0); } -static int gaudi_read_device_fw_version(struct hl_device *hdev, - enum hl_fw_component fwc) +static void gaudi_init_dynamic_firmware_loader(struct hl_device *hdev) { - const char *name; - u32 ver_off; - char *dest; + struct dynamic_fw_load_mgr *dynamic_loader; + struct cpu_dyn_regs *dyn_regs; - switch (fwc) { - case FW_COMP_UBOOT: - ver_off = RREG32(mmUBOOT_VER_OFFSET); - dest = hdev->asic_prop.uboot_ver; - name = "U-Boot"; - break; - case FW_COMP_PREBOOT: - ver_off = RREG32(mmPREBOOT_VER_OFFSET); - dest = hdev->asic_prop.preboot_ver; - name = "Preboot"; - break; - default: - dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc); - return -EIO; - } + dynamic_loader = &hdev->fw_loader.dynamic_loader; - ver_off &= ~((u32)SRAM_BASE_ADDR); + /* + * here we update initial values for few specific dynamic regs (as + * before reading the first descriptor from FW those value has to be + * hard-coded) in later stages of the protocol those values will be + * updated automatically by reading the FW descriptor so data there + * will always be up-to-date + */ + dyn_regs = &dynamic_loader->comm_desc.cpu_dyn_regs; + dyn_regs->kmd_msg_to_cpu = + cpu_to_le32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU); + dyn_regs->cpu_cmd_status_to_host = + cpu_to_le32(mmCPU_CMD_STATUS_TO_HOST); - if (ver_off < SRAM_SIZE - VERSION_MAX_LEN) { - memcpy_fromio(dest, hdev->pcie_bar[SRAM_BAR_ID] + ver_off, - VERSION_MAX_LEN); - } else { - dev_err(hdev->dev, "%s version offset (0x%x) is above SRAM\n", - name, ver_off); - strcpy(dest, "unavailable"); - return -EIO; - } + dynamic_loader->wait_for_bl_timeout = GAUDI_WAIT_FOR_BL_TIMEOUT_USEC; +} - return 0; +static void gaudi_init_static_firmware_loader(struct hl_device *hdev) +{ + struct static_fw_load_mgr *static_loader; + + static_loader = &hdev->fw_loader.static_loader; + + static_loader->preboot_version_max_off = SRAM_SIZE - VERSION_MAX_LEN; + static_loader->boot_fit_version_max_off = SRAM_SIZE - VERSION_MAX_LEN; + static_loader->kmd_msg_to_cpu_reg = mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU; + static_loader->cpu_cmd_status_to_host_reg = mmCPU_CMD_STATUS_TO_HOST; + static_loader->cpu_boot_status_reg = mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS; + static_loader->cpu_boot_dev_status0_reg = mmCPU_BOOT_DEV_STS0; + static_loader->cpu_boot_dev_status1_reg = mmCPU_BOOT_DEV_STS1; + static_loader->boot_err0_reg = mmCPU_BOOT_ERR0; + static_loader->boot_err1_reg = mmCPU_BOOT_ERR1; + static_loader->preboot_version_offset_reg = mmPREBOOT_VER_OFFSET; + static_loader->boot_fit_version_offset_reg = mmUBOOT_VER_OFFSET; + static_loader->sram_offset_mask = ~(lower_32_bits(SRAM_BASE_ADDR)); + static_loader->cpu_reset_wait_msec = hdev->pldm ? + GAUDI_PLDM_RESET_WAIT_MSEC : + GAUDI_CPU_RESET_WAIT_MSEC; +} + +static void gaudi_init_firmware_loader(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct fw_load_mgr *fw_loader = &hdev->fw_loader; + + /* fill common fields */ + fw_loader->linux_loaded = false; + fw_loader->boot_fit_img.image_name = GAUDI_BOOT_FIT_FILE; + fw_loader->linux_img.image_name = GAUDI_LINUX_FW_FILE; + fw_loader->cpu_timeout = GAUDI_CPU_TIMEOUT_USEC; + fw_loader->boot_fit_timeout = GAUDI_BOOT_FIT_REQ_TIMEOUT_USEC; + fw_loader->skip_bmc = !hdev->bmc_enable; + fw_loader->sram_bar_id = SRAM_BAR_ID; + fw_loader->dram_bar_id = HBM_BAR_ID; + + if (prop->dynamic_fw_load) + gaudi_init_dynamic_firmware_loader(hdev); + else + gaudi_init_static_firmware_loader(hdev); } static int gaudi_init_cpu(struct hl_device *hdev) @@ -3744,15 +3861,10 @@ static int gaudi_init_cpu(struct hl_device *hdev) * The device CPU works with 40 bits addresses. * This register sets the extension to 50 bits. */ - if (hdev->asic_prop.fw_security_disabled) + if (!hdev->asic_prop.fw_security_enabled) WREG32(mmCPU_IF_CPU_MSB_ADDR, hdev->cpu_pci_msb_addr); - rc = hl_fw_init_cpu(hdev, mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS, - mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, - mmCPU_CMD_STATUS_TO_HOST, - mmCPU_BOOT_DEV_STS0, mmCPU_BOOT_ERR0, - !hdev->bmc_enable, GAUDI_CPU_TIMEOUT_USEC, - GAUDI_BOOT_FIT_REQ_TIMEOUT_USEC); + rc = hl_fw_init_cpu(hdev); if (rc) return rc; @@ -3764,10 +3876,12 @@ static int gaudi_init_cpu(struct hl_device *hdev) static int gaudi_init_cpu_queues(struct hl_device *hdev, u32 cpu_timeout) { - struct gaudi_device *gaudi = hdev->asic_specific; + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; struct asic_fixed_properties *prop = &hdev->asic_prop; + struct gaudi_device *gaudi = hdev->asic_specific; + u32 status, irq_handler_offset; struct hl_eq *eq; - u32 status; struct hl_hw_queue *cpu_pq = &hdev->kernel_queues[GAUDI_QUEUE_ID_CPU_PQ]; int err; @@ -3806,7 +3920,12 @@ static int gaudi_init_cpu_queues(struct hl_device *hdev, u32 cpu_timeout) WREG32(mmCPU_IF_QUEUE_INIT, PQ_INIT_STATUS_READY_FOR_CP_SINGLE_MSI); - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, GAUDI_EVENT_PI_UPDATE); + irq_handler_offset = prop->gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_host_pi_upd_irq); + + WREG32(irq_handler_offset, + gaudi_irq_map_table[GAUDI_EVENT_PI_UPDATE].cpu_id); err = hl_poll_timeout( hdev, @@ -3823,8 +3942,10 @@ static int gaudi_init_cpu_queues(struct hl_device *hdev, u32 cpu_timeout) } /* update FW application security bits */ - if (prop->fw_security_status_valid) - prop->fw_app_security_map = RREG32(mmCPU_BOOT_DEV_STS0); + if (prop->fw_cpu_boot_dev_sts0_valid) + prop->fw_app_cpu_boot_dev_sts0 = RREG32(mmCPU_BOOT_DEV_STS0); + if (prop->fw_cpu_boot_dev_sts1_valid) + prop->fw_app_cpu_boot_dev_sts1 = RREG32(mmCPU_BOOT_DEV_STS1); gaudi->hw_cap_initialized |= HW_CAP_CPU_Q; return 0; @@ -3835,7 +3956,7 @@ static void gaudi_pre_hw_init(struct hl_device *hdev) /* Perform read from the device to make sure device is up */ RREG32(mmHW_STATE); - if (hdev->asic_prop.fw_security_disabled) { + if (!hdev->asic_prop.fw_security_enabled) { /* Set the access through PCI bars (Linux driver only) as * secured */ @@ -3860,13 +3981,27 @@ static void gaudi_pre_hw_init(struct hl_device *hdev) static int gaudi_hw_init(struct hl_device *hdev) { + struct gaudi_device *gaudi = hdev->asic_specific; int rc; gaudi_pre_hw_init(hdev); - gaudi_init_pci_dma_qmans(hdev); + /* If iATU is done by FW, the HBM bar ALWAYS points to DRAM_PHYS_BASE. + * So we set it here and if anyone tries to move it later to + * a different address, there will be an error + */ + if (hdev->asic_prop.iatu_done_by_fw) + gaudi->hbm_bar_cur_addr = DRAM_PHYS_BASE; - gaudi_init_hbm_dma_qmans(hdev); + /* + * Before pushing u-boot/linux to device, need to set the hbm bar to + * base address of dram + */ + if (gaudi_set_hbm_bar_base(hdev, DRAM_PHYS_BASE) == U64_MAX) { + dev_err(hdev->dev, + "failed to map HBM bar to DRAM base address\n"); + return -EIO; + } rc = gaudi_init_cpu(hdev); if (rc) { @@ -3895,6 +4030,10 @@ static int gaudi_hw_init(struct hl_device *hdev) gaudi_init_security(hdev); + gaudi_init_pci_dma_qmans(hdev); + + gaudi_init_hbm_dma_qmans(hdev); + gaudi_init_mme_qmans(hdev); gaudi_init_tpc_qmans(hdev); @@ -3934,8 +4073,11 @@ disable_queues: static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset) { + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; + u32 status, reset_timeout_ms, cpu_timeout_ms, irq_handler_offset; struct gaudi_device *gaudi = hdev->asic_specific; - u32 status, reset_timeout_ms, cpu_timeout_ms; + bool driver_performs_reset; if (!hard_reset) { dev_err(hdev->dev, "GAUDI doesn't support soft-reset\n"); @@ -3950,26 +4092,35 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset) cpu_timeout_ms = GAUDI_CPU_RESET_WAIT_MSEC; } + driver_performs_reset = !!(!hdev->asic_prop.fw_security_enabled && + !hdev->asic_prop.hard_reset_done_by_fw); + /* Set device to handle FLR by H/W as we will put the device CPU to * halt mode */ - if (hdev->asic_prop.fw_security_disabled && - !hdev->asic_prop.hard_reset_done_by_fw) + if (driver_performs_reset) WREG32(mmPCIE_AUX_FLR_CTRL, (PCIE_AUX_FLR_CTRL_HW_CTRL_MASK | PCIE_AUX_FLR_CTRL_INT_MASK_MASK)); - /* I don't know what is the state of the CPU so make sure it is - * stopped in any means necessary + /* If linux is loaded in the device CPU we need to communicate with it + * via the GIC. Otherwise, we need to use COMMS or the MSG_TO_CPU + * registers in case of old F/Ws */ - if (hdev->asic_prop.hard_reset_done_by_fw) - WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_RST_DEV); - else - WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_GOTO_WFE); + if (hdev->fw_loader.linux_loaded) { + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_host_halt_irq); - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, GAUDI_EVENT_HALT_MACHINE); + WREG32(irq_handler_offset, + gaudi_irq_map_table[GAUDI_EVENT_HALT_MACHINE].cpu_id); + } else { + if (hdev->asic_prop.hard_reset_done_by_fw) + hl_fw_ask_hard_reset_without_linux(hdev); + else + hl_fw_ask_halt_machine_without_linux(hdev); + } - if (hdev->asic_prop.fw_security_disabled && - !hdev->asic_prop.hard_reset_done_by_fw) { + if (driver_performs_reset) { /* Configure the reset registers. Must be done as early as * possible in case we fail during H/W initialization @@ -4003,8 +4154,7 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset) WREG32(mmPREBOOT_PCIE_EN, LKD_HARD_RESET_MAGIC); /* Restart BTL/BLR upon hard-reset */ - if (hdev->asic_prop.fw_security_disabled) - WREG32(mmPSOC_GLOBAL_CONF_BOOT_SEQ_RE_START, 1); + WREG32(mmPSOC_GLOBAL_CONF_BOOT_SEQ_RE_START, 1); WREG32(mmPSOC_GLOBAL_CONF_SW_ALL_RST, 1 << PSOC_GLOBAL_CONF_SW_ALL_RST_IND_SHIFT); @@ -4041,6 +4191,8 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset) HW_CAP_CLK_GATE); memset(gaudi->events_stat, 0, sizeof(gaudi->events_stat)); + + hdev->device_cpu_is_halted = false; } } @@ -4078,10 +4230,12 @@ static int gaudi_cb_mmap(struct hl_device *hdev, struct vm_area_struct *vma, static void gaudi_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi) { + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; + u32 db_reg_offset, db_value, dma_qm_offset, q_off, irq_handler_offset; struct gaudi_device *gaudi = hdev->asic_specific; - u32 db_reg_offset, db_value, dma_qm_offset, q_off; - int dma_id; bool invalid_queue = false; + int dma_id; switch (hw_queue_id) { case GAUDI_QUEUE_ID_DMA_0_0...GAUDI_QUEUE_ID_DMA_0_3: @@ -4307,164 +4461,84 @@ static void gaudi_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi) db_reg_offset = mmTPC7_QM_PQ_PI_3; break; - case GAUDI_QUEUE_ID_NIC_0_0: - db_reg_offset = mmNIC0_QM0_PQ_PI_0; - break; - - case GAUDI_QUEUE_ID_NIC_0_1: - db_reg_offset = mmNIC0_QM0_PQ_PI_1; - break; - - case GAUDI_QUEUE_ID_NIC_0_2: - db_reg_offset = mmNIC0_QM0_PQ_PI_2; - break; - - case GAUDI_QUEUE_ID_NIC_0_3: - db_reg_offset = mmNIC0_QM0_PQ_PI_3; - break; - - case GAUDI_QUEUE_ID_NIC_1_0: - db_reg_offset = mmNIC0_QM1_PQ_PI_0; - break; - - case GAUDI_QUEUE_ID_NIC_1_1: - db_reg_offset = mmNIC0_QM1_PQ_PI_1; - break; - - case GAUDI_QUEUE_ID_NIC_1_2: - db_reg_offset = mmNIC0_QM1_PQ_PI_2; - break; - - case GAUDI_QUEUE_ID_NIC_1_3: - db_reg_offset = mmNIC0_QM1_PQ_PI_3; - break; - - case GAUDI_QUEUE_ID_NIC_2_0: - db_reg_offset = mmNIC1_QM0_PQ_PI_0; - break; - - case GAUDI_QUEUE_ID_NIC_2_1: - db_reg_offset = mmNIC1_QM0_PQ_PI_1; - break; - - case GAUDI_QUEUE_ID_NIC_2_2: - db_reg_offset = mmNIC1_QM0_PQ_PI_2; - break; - - case GAUDI_QUEUE_ID_NIC_2_3: - db_reg_offset = mmNIC1_QM0_PQ_PI_3; - break; - - case GAUDI_QUEUE_ID_NIC_3_0: - db_reg_offset = mmNIC1_QM1_PQ_PI_0; - break; - - case GAUDI_QUEUE_ID_NIC_3_1: - db_reg_offset = mmNIC1_QM1_PQ_PI_1; - break; - - case GAUDI_QUEUE_ID_NIC_3_2: - db_reg_offset = mmNIC1_QM1_PQ_PI_2; - break; - - case GAUDI_QUEUE_ID_NIC_3_3: - db_reg_offset = mmNIC1_QM1_PQ_PI_3; - break; - - case GAUDI_QUEUE_ID_NIC_4_0: - db_reg_offset = mmNIC2_QM0_PQ_PI_0; - break; - - case GAUDI_QUEUE_ID_NIC_4_1: - db_reg_offset = mmNIC2_QM0_PQ_PI_1; - break; - - case GAUDI_QUEUE_ID_NIC_4_2: - db_reg_offset = mmNIC2_QM0_PQ_PI_2; - break; - - case GAUDI_QUEUE_ID_NIC_4_3: - db_reg_offset = mmNIC2_QM0_PQ_PI_3; - break; - - case GAUDI_QUEUE_ID_NIC_5_0: - db_reg_offset = mmNIC2_QM1_PQ_PI_0; - break; + case GAUDI_QUEUE_ID_NIC_0_0...GAUDI_QUEUE_ID_NIC_0_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC0)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_5_1: - db_reg_offset = mmNIC2_QM1_PQ_PI_1; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC0_QM0_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_5_2: - db_reg_offset = mmNIC2_QM1_PQ_PI_2; - break; + case GAUDI_QUEUE_ID_NIC_1_0...GAUDI_QUEUE_ID_NIC_1_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC1)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_5_3: - db_reg_offset = mmNIC2_QM1_PQ_PI_3; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC0_QM1_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_6_0: - db_reg_offset = mmNIC3_QM0_PQ_PI_0; - break; + case GAUDI_QUEUE_ID_NIC_2_0...GAUDI_QUEUE_ID_NIC_2_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC2)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_6_1: - db_reg_offset = mmNIC3_QM0_PQ_PI_1; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC1_QM0_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_6_2: - db_reg_offset = mmNIC3_QM0_PQ_PI_2; - break; + case GAUDI_QUEUE_ID_NIC_3_0...GAUDI_QUEUE_ID_NIC_3_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC3)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_6_3: - db_reg_offset = mmNIC3_QM0_PQ_PI_3; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC1_QM1_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_7_0: - db_reg_offset = mmNIC3_QM1_PQ_PI_0; - break; + case GAUDI_QUEUE_ID_NIC_4_0...GAUDI_QUEUE_ID_NIC_4_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC4)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_7_1: - db_reg_offset = mmNIC3_QM1_PQ_PI_1; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC2_QM0_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_7_2: - db_reg_offset = mmNIC3_QM1_PQ_PI_2; - break; + case GAUDI_QUEUE_ID_NIC_5_0...GAUDI_QUEUE_ID_NIC_5_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC5)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_7_3: - db_reg_offset = mmNIC3_QM1_PQ_PI_3; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC2_QM1_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_8_0: - db_reg_offset = mmNIC4_QM0_PQ_PI_0; - break; + case GAUDI_QUEUE_ID_NIC_6_0...GAUDI_QUEUE_ID_NIC_6_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC6)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_8_1: - db_reg_offset = mmNIC4_QM0_PQ_PI_1; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC3_QM0_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_8_2: - db_reg_offset = mmNIC4_QM0_PQ_PI_2; - break; + case GAUDI_QUEUE_ID_NIC_7_0...GAUDI_QUEUE_ID_NIC_7_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC7)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_8_3: - db_reg_offset = mmNIC4_QM0_PQ_PI_3; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC3_QM1_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_9_0: - db_reg_offset = mmNIC4_QM1_PQ_PI_0; - break; + case GAUDI_QUEUE_ID_NIC_8_0...GAUDI_QUEUE_ID_NIC_8_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC8)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_9_1: - db_reg_offset = mmNIC4_QM1_PQ_PI_1; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC4_QM0_PQ_PI_0 + q_off; break; - case GAUDI_QUEUE_ID_NIC_9_2: - db_reg_offset = mmNIC4_QM1_PQ_PI_2; - break; + case GAUDI_QUEUE_ID_NIC_9_0...GAUDI_QUEUE_ID_NIC_9_3: + if (!(gaudi->hw_cap_initialized & HW_CAP_NIC9)) + invalid_queue = true; - case GAUDI_QUEUE_ID_NIC_9_3: - db_reg_offset = mmNIC4_QM1_PQ_PI_3; + q_off = ((hw_queue_id - 1) & 0x3) * 4; + db_reg_offset = mmNIC4_QM1_PQ_PI_0 + q_off; break; default: @@ -4486,8 +4560,13 @@ static void gaudi_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi) if (hw_queue_id == GAUDI_QUEUE_ID_CPU_PQ) { /* make sure device CPU will read latest data from host */ mb(); - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, - GAUDI_EVENT_PI_UPDATE); + + irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_host_pi_upd_irq); + + WREG32(irq_handler_offset, + gaudi_irq_map_table[GAUDI_EVENT_PI_UPDATE].cpu_id); } } @@ -4934,6 +5013,7 @@ already_pinned: return 0; unpin_memory: + list_del(&userptr->job_node); hl_unpin_host_memory(hdev, userptr); free_userptr: kfree(userptr); @@ -6513,7 +6593,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) gaudi_mmu_prepare_reg(hdev, mmMME2_ACC_WBC, asid); gaudi_mmu_prepare_reg(hdev, mmMME3_ACC_WBC, asid); - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC0) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC0) { gaudi_mmu_prepare_reg(hdev, mmNIC0_QM0_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC0_QM0_GLBL_NON_SECURE_PROPS_1, @@ -6526,7 +6606,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC1) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC1) { gaudi_mmu_prepare_reg(hdev, mmNIC0_QM1_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC0_QM1_GLBL_NON_SECURE_PROPS_1, @@ -6539,7 +6619,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC2) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC2) { gaudi_mmu_prepare_reg(hdev, mmNIC1_QM0_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC1_QM0_GLBL_NON_SECURE_PROPS_1, @@ -6552,7 +6632,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC3) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC3) { gaudi_mmu_prepare_reg(hdev, mmNIC1_QM1_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC1_QM1_GLBL_NON_SECURE_PROPS_1, @@ -6565,7 +6645,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC4) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC4) { gaudi_mmu_prepare_reg(hdev, mmNIC2_QM0_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC2_QM0_GLBL_NON_SECURE_PROPS_1, @@ -6578,7 +6658,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC5) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC5) { gaudi_mmu_prepare_reg(hdev, mmNIC2_QM1_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC2_QM1_GLBL_NON_SECURE_PROPS_1, @@ -6591,7 +6671,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC6) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC6) { gaudi_mmu_prepare_reg(hdev, mmNIC3_QM0_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC3_QM0_GLBL_NON_SECURE_PROPS_1, @@ -6604,7 +6684,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC7) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC7) { gaudi_mmu_prepare_reg(hdev, mmNIC3_QM1_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC3_QM1_GLBL_NON_SECURE_PROPS_1, @@ -6617,7 +6697,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC8) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC8) { gaudi_mmu_prepare_reg(hdev, mmNIC4_QM0_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC4_QM0_GLBL_NON_SECURE_PROPS_1, @@ -6630,7 +6710,7 @@ static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid) asid); } - if (hdev->nic_ports_mask & GAUDI_NIC_MASK_NIC9) { + if (gaudi->hw_cap_initialized & HW_CAP_NIC9) { gaudi_mmu_prepare_reg(hdev, mmNIC4_QM1_GLBL_NON_SECURE_PROPS_0, asid); gaudi_mmu_prepare_reg(hdev, mmNIC4_QM1_GLBL_NON_SECURE_PROPS_1, @@ -7044,14 +7124,158 @@ enable_clk_gate: return rc; } +/* + * gaudi_queue_idx_dec - decrement queue index (pi/ci) and handle wrap + * + * @idx: the current pi/ci value + * @q_len: the queue length (power of 2) + * + * @return the cyclically decremented index + */ +static inline u32 gaudi_queue_idx_dec(u32 idx, u32 q_len) +{ + u32 mask = q_len - 1; + + /* + * modular decrement is equivalent to adding (queue_size -1) + * later we take LSBs to make sure the value is in the + * range [0, queue_len - 1] + */ + return (idx + q_len - 1) & mask; +} + +/** + * gaudi_print_sw_config_stream_data - print SW config stream data + * + * @hdev: pointer to the habanalabs device structure + * @stream: the QMAN's stream + * @qman_base: base address of QMAN registers block + */ +static void gaudi_print_sw_config_stream_data(struct hl_device *hdev, u32 stream, + u64 qman_base) +{ + u64 cq_ptr_lo, cq_ptr_hi, cq_tsize, cq_ptr; + u32 cq_ptr_lo_off, size; + + cq_ptr_lo_off = mmTPC0_QM_CQ_PTR_LO_1 - mmTPC0_QM_CQ_PTR_LO_0; + + cq_ptr_lo = qman_base + (mmTPC0_QM_CQ_PTR_LO_0 - mmTPC0_QM_BASE) + + stream * cq_ptr_lo_off; + cq_ptr_hi = cq_ptr_lo + + (mmTPC0_QM_CQ_PTR_HI_0 - mmTPC0_QM_CQ_PTR_LO_0); + cq_tsize = cq_ptr_lo + + (mmTPC0_QM_CQ_TSIZE_0 - mmTPC0_QM_CQ_PTR_LO_0); + + cq_ptr = (((u64) RREG32(cq_ptr_hi)) << 32) | RREG32(cq_ptr_lo); + size = RREG32(cq_tsize); + dev_info(hdev->dev, "stop on err: stream: %u, addr: %#llx, size: %x\n", + stream, cq_ptr, size); +} + +/** + * gaudi_print_last_pqes_on_err - print last PQEs on error + * + * @hdev: pointer to the habanalabs device structure + * @qid_base: first QID of the QMAN (out of 4 streams) + * @stream: the QMAN's stream + * @qman_base: base address of QMAN registers block + * @pr_sw_conf: if true print the SW config stream data (CQ PTR and SIZE) + */ +static void gaudi_print_last_pqes_on_err(struct hl_device *hdev, u32 qid_base, + u32 stream, u64 qman_base, + bool pr_sw_conf) +{ + u32 ci, qm_ci_stream_off, queue_len; + struct hl_hw_queue *q; + u64 pq_ci; + int i; + + q = &hdev->kernel_queues[qid_base + stream]; + + qm_ci_stream_off = mmTPC0_QM_PQ_CI_1 - mmTPC0_QM_PQ_CI_0; + pq_ci = qman_base + (mmTPC0_QM_PQ_CI_0 - mmTPC0_QM_BASE) + + stream * qm_ci_stream_off; + + queue_len = (q->queue_type == QUEUE_TYPE_INT) ? + q->int_queue_len : HL_QUEUE_LENGTH; + + hdev->asic_funcs->hw_queues_lock(hdev); + + if (pr_sw_conf) + gaudi_print_sw_config_stream_data(hdev, stream, qman_base); + + ci = RREG32(pq_ci); + + /* we should start printing form ci -1 */ + ci = gaudi_queue_idx_dec(ci, queue_len); + + for (i = 0; i < PQ_FETCHER_CACHE_SIZE; i++) { + struct hl_bd *bd; + u64 addr; + u32 len; + + bd = q->kernel_address; + bd += ci; + + len = le32_to_cpu(bd->len); + /* len 0 means uninitialized entry- break */ + if (!len) + break; + + addr = le64_to_cpu(bd->ptr); + + dev_info(hdev->dev, "stop on err PQE(stream %u): ci: %u, addr: %#llx, size: %x\n", + stream, ci, addr, len); + + /* get previous ci, wrap if needed */ + ci = gaudi_queue_idx_dec(ci, queue_len); + } + + hdev->asic_funcs->hw_queues_unlock(hdev); +} + +/** + * print_qman_data_on_err - extract QMAN data on error + * + * @hdev: pointer to the habanalabs device structure + * @qid_base: first QID of the QMAN (out of 4 streams) + * @stream: the QMAN's stream + * @qman_base: base address of QMAN registers block + * + * This function attempt to exatract as much data as possible on QMAN error. + * On upper CP print the SW config stream data and last 8 PQEs. + * On lower CP print SW config data and last PQEs of ALL 4 upper CPs + */ +static void print_qman_data_on_err(struct hl_device *hdev, u32 qid_base, + u32 stream, u64 qman_base) +{ + u32 i; + + if (stream != QMAN_STREAMS) { + gaudi_print_last_pqes_on_err(hdev, qid_base, stream, qman_base, + true); + return; + } + + gaudi_print_sw_config_stream_data(hdev, stream, qman_base); + + for (i = 0; i < QMAN_STREAMS; i++) + gaudi_print_last_pqes_on_err(hdev, qid_base, i, qman_base, + false); +} + static void gaudi_handle_qman_err_generic(struct hl_device *hdev, const char *qm_name, - u64 glbl_sts_addr, - u64 arb_err_addr) + u64 qman_base, + u32 qid_base) { u32 i, j, glbl_sts_val, arb_err_val, glbl_sts_clr_val; + u64 glbl_sts_addr, arb_err_addr; char reg_desc[32]; + glbl_sts_addr = qman_base + (mmTPC0_QM_GLBL_STS1_0 - mmTPC0_QM_BASE); + arb_err_addr = qman_base + (mmTPC0_QM_ARB_ERR_CAUSE - mmTPC0_QM_BASE); + /* Iterate through all stream GLBL_STS1 registers + Lower CP */ for (i = 0 ; i < QMAN_STREAMS + 1 ; i++) { glbl_sts_clr_val = 0; @@ -7078,6 +7302,8 @@ static void gaudi_handle_qman_err_generic(struct hl_device *hdev, /* Write 1 clear errors */ if (!hdev->stop_on_err) WREG32(glbl_sts_addr + 4 * i, glbl_sts_clr_val); + else + print_qman_data_on_err(hdev, qid_base, i, qman_base); } arb_err_val = RREG32(arb_err_addr); @@ -7222,90 +7448,88 @@ static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, static void gaudi_handle_qman_err(struct hl_device *hdev, u16 event_type) { - u64 glbl_sts_addr, arb_err_addr; - u8 index; + u64 qman_base; char desc[32]; + u32 qid_base; + u8 index; switch (event_type) { case GAUDI_EVENT_TPC0_QM ... GAUDI_EVENT_TPC7_QM: index = event_type - GAUDI_EVENT_TPC0_QM; - glbl_sts_addr = - mmTPC0_QM_GLBL_STS1_0 + index * TPC_QMAN_OFFSET; - arb_err_addr = - mmTPC0_QM_ARB_ERR_CAUSE + index * TPC_QMAN_OFFSET; + qid_base = GAUDI_QUEUE_ID_TPC_0_0 + index * QMAN_STREAMS; + qman_base = mmTPC0_QM_BASE + index * TPC_QMAN_OFFSET; snprintf(desc, ARRAY_SIZE(desc), "%s%d", "TPC_QM", index); break; case GAUDI_EVENT_MME0_QM ... GAUDI_EVENT_MME2_QM: index = event_type - GAUDI_EVENT_MME0_QM; - glbl_sts_addr = - mmMME0_QM_GLBL_STS1_0 + index * MME_QMAN_OFFSET; - arb_err_addr = - mmMME0_QM_ARB_ERR_CAUSE + index * MME_QMAN_OFFSET; + qid_base = GAUDI_QUEUE_ID_MME_0_0 + index * QMAN_STREAMS; + qman_base = mmMME0_QM_BASE + index * MME_QMAN_OFFSET; snprintf(desc, ARRAY_SIZE(desc), "%s%d", "MME_QM", index); break; case GAUDI_EVENT_DMA0_QM ... GAUDI_EVENT_DMA7_QM: index = event_type - GAUDI_EVENT_DMA0_QM; - glbl_sts_addr = - mmDMA0_QM_GLBL_STS1_0 + index * DMA_QMAN_OFFSET; - arb_err_addr = - mmDMA0_QM_ARB_ERR_CAUSE + index * DMA_QMAN_OFFSET; + qid_base = GAUDI_QUEUE_ID_DMA_0_0 + index * QMAN_STREAMS; + /* skip GAUDI_QUEUE_ID_CPU_PQ if necessary */ + if (index > 1) + qid_base++; + qman_base = mmDMA0_QM_BASE + index * DMA_QMAN_OFFSET; snprintf(desc, ARRAY_SIZE(desc), "%s%d", "DMA_QM", index); break; case GAUDI_EVENT_NIC0_QM0: - glbl_sts_addr = mmNIC0_QM0_GLBL_STS1_0; - arb_err_addr = mmNIC0_QM0_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_0_0; + qman_base = mmNIC0_QM0_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC0_QM0"); break; case GAUDI_EVENT_NIC0_QM1: - glbl_sts_addr = mmNIC0_QM1_GLBL_STS1_0; - arb_err_addr = mmNIC0_QM1_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_1_0; + qman_base = mmNIC0_QM1_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC0_QM1"); break; case GAUDI_EVENT_NIC1_QM0: - glbl_sts_addr = mmNIC1_QM0_GLBL_STS1_0; - arb_err_addr = mmNIC1_QM0_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_2_0; + qman_base = mmNIC1_QM0_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC1_QM0"); break; case GAUDI_EVENT_NIC1_QM1: - glbl_sts_addr = mmNIC1_QM1_GLBL_STS1_0; - arb_err_addr = mmNIC1_QM1_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_3_0; + qman_base = mmNIC1_QM1_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC1_QM1"); break; case GAUDI_EVENT_NIC2_QM0: - glbl_sts_addr = mmNIC2_QM0_GLBL_STS1_0; - arb_err_addr = mmNIC2_QM0_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_4_0; + qman_base = mmNIC2_QM0_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC2_QM0"); break; case GAUDI_EVENT_NIC2_QM1: - glbl_sts_addr = mmNIC2_QM1_GLBL_STS1_0; - arb_err_addr = mmNIC2_QM1_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_5_0; + qman_base = mmNIC2_QM1_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC2_QM1"); break; case GAUDI_EVENT_NIC3_QM0: - glbl_sts_addr = mmNIC3_QM0_GLBL_STS1_0; - arb_err_addr = mmNIC3_QM0_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_6_0; + qman_base = mmNIC3_QM0_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC3_QM0"); break; case GAUDI_EVENT_NIC3_QM1: - glbl_sts_addr = mmNIC3_QM1_GLBL_STS1_0; - arb_err_addr = mmNIC3_QM1_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_7_0; + qman_base = mmNIC3_QM1_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC3_QM1"); break; case GAUDI_EVENT_NIC4_QM0: - glbl_sts_addr = mmNIC4_QM0_GLBL_STS1_0; - arb_err_addr = mmNIC4_QM0_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_8_0; + qman_base = mmNIC4_QM0_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC4_QM0"); break; case GAUDI_EVENT_NIC4_QM1: - glbl_sts_addr = mmNIC4_QM1_GLBL_STS1_0; - arb_err_addr = mmNIC4_QM1_ARB_ERR_CAUSE; + qid_base = GAUDI_QUEUE_ID_NIC_9_0; + qman_base = mmNIC4_QM1_BASE; snprintf(desc, ARRAY_SIZE(desc), "NIC4_QM1"); break; default: return; } - gaudi_handle_qman_err_generic(hdev, desc, glbl_sts_addr, arb_err_addr); + gaudi_handle_qman_err_generic(hdev, desc, qman_base, qid_base); } static void gaudi_print_irq_info(struct hl_device *hdev, u16 event_type, @@ -7332,6 +7556,16 @@ static void gaudi_print_out_of_sync_info(struct hl_device *hdev, sync_err->pi, sync_err->ci, q->pi, atomic_read(&q->ci)); } +static void gaudi_print_fw_alive_info(struct hl_device *hdev, + struct hl_eq_fw_alive *fw_alive) +{ + dev_err(hdev->dev, + "FW alive report: severity=%s, process_id=%u, thread_id=%u, uptime=%llu seconds\n", + (fw_alive->severity == FW_ALIVE_SEVERITY_MINOR) ? + "Minor" : "Critical", fw_alive->process_id, + fw_alive->thread_id, fw_alive->uptime_seconds); +} + static int gaudi_soft_reset_late_init(struct hl_device *hdev) { struct gaudi_device *gaudi = hdev->asic_specific; @@ -7346,11 +7580,10 @@ static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device, struct hl_eq_hbm_ecc_data *hbm_ecc_data) { u32 base, val, val2, wr_par, rd_par, ca_par, derr, serr, type, ch; - int err = 0; + int rc = 0; - if (hdev->asic_prop.fw_security_status_valid && - (hdev->asic_prop.fw_app_security_map & - CPU_BOOT_DEV_STS0_HBM_ECC_EN)) { + if (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_HBM_ECC_EN) { if (!hbm_ecc_data) { dev_err(hdev->dev, "No FW ECC data"); return 0; @@ -7379,13 +7612,10 @@ static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device, device, ch, hbm_ecc_data->first_addr, type, hbm_ecc_data->sec_cont_cnt, hbm_ecc_data->sec_cnt, hbm_ecc_data->dec_cnt); - - err = 1; - return 0; } - if (!hdev->asic_prop.fw_security_disabled) { + if (hdev->asic_prop.fw_security_enabled) { dev_info(hdev->dev, "Cannot access MC regs for ECC data while security is enabled\n"); return 0; } @@ -7395,7 +7625,7 @@ static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device, val = RREG32_MASK(base + ch * 0x1000 + 0x06C, 0x0000FFFF); val = (val & 0xFF) | ((val >> 8) & 0xFF); if (val) { - err = 1; + rc = -EIO; dev_err(hdev->dev, "HBM%d pc%d interrupts info: WR_PAR=%d, RD_PAR=%d, CA_PAR=%d, SERR=%d, DERR=%d\n", device, ch * 2, val & 0x1, (val >> 1) & 0x1, @@ -7415,7 +7645,7 @@ static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device, val = RREG32_MASK(base + ch * 0x1000 + 0x07C, 0x0000FFFF); val = (val & 0xFF) | ((val >> 8) & 0xFF); if (val) { - err = 1; + rc = -EIO; dev_err(hdev->dev, "HBM%d pc%d interrupts info: WR_PAR=%d, RD_PAR=%d, CA_PAR=%d, SERR=%d, DERR=%d\n", device, ch * 2 + 1, val & 0x1, (val >> 1) & 0x1, @@ -7444,7 +7674,7 @@ static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device, val = RREG32(base + 0x8F30); val2 = RREG32(base + 0x8F34); if (val | val2) { - err = 1; + rc = -EIO; dev_err(hdev->dev, "HBM %d MC SRAM SERR info: Reg 0x8F30=0x%x, Reg 0x8F34=0x%x\n", device, val, val2); @@ -7452,13 +7682,13 @@ static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device, val = RREG32(base + 0x8F40); val2 = RREG32(base + 0x8F44); if (val | val2) { - err = 1; + rc = -EIO; dev_err(hdev->dev, "HBM %d MC SRAM DERR info: Reg 0x8F40=0x%x, Reg 0x8F44=0x%x\n", device, val, val2); } - return err; + return rc; } static int gaudi_hbm_event_to_dev(u16 hbm_event_type) @@ -7604,6 +7834,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_DMA_IF0_DERR ... GAUDI_EVENT_DMA_IF3_DERR: case GAUDI_EVENT_HBM_0_DERR ... GAUDI_EVENT_HBM_3_DERR: case GAUDI_EVENT_MMU_DERR: + case GAUDI_EVENT_NIC0_CS_DBG_DERR ... GAUDI_EVENT_NIC4_CS_DBG_DERR: gaudi_print_irq_info(hdev, event_type, true); gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data); goto reset_device; @@ -7786,6 +8017,11 @@ static void gaudi_handle_eqe(struct hl_device *hdev, gaudi_print_out_of_sync_info(hdev, &eq_entry->pkt_sync_err); goto reset_device; + case GAUDI_EVENT_FW_ALIVE_S: + gaudi_print_irq_info(hdev, event_type, false); + gaudi_print_fw_alive_info(hdev, &eq_entry->fw_alive); + goto reset_device; + default: dev_err(hdev->dev, "Received invalid H/W interrupt %d\n", event_type); @@ -7856,52 +8092,13 @@ static int gaudi_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, } static int gaudi_mmu_invalidate_cache_range(struct hl_device *hdev, - bool is_hard, u32 asid, u64 va, u64 size) + bool is_hard, u32 flags, + u32 asid, u64 va, u64 size) { - struct gaudi_device *gaudi = hdev->asic_specific; - u32 status, timeout_usec; - u32 inv_data; - u32 pi; - int rc; - - if (!(gaudi->hw_cap_initialized & HW_CAP_MMU) || - hdev->hard_reset_pending) - return 0; - - if (hdev->pldm) - timeout_usec = GAUDI_PLDM_MMU_TIMEOUT_USEC; - else - timeout_usec = MMU_CONFIG_TIMEOUT_USEC; - - /* - * TODO: currently invalidate entire L0 & L1 as in regular hard - * invalidation. Need to apply invalidation of specific cache - * lines with mask of ASID & VA & size. - * Note that L1 with be flushed entirely in any case. + /* Treat as invalidate all because there is no range invalidation + * in Gaudi */ - - /* L0 & L1 invalidation */ - inv_data = RREG32(mmSTLB_CACHE_INV); - /* PI is 8 bit */ - pi = ((inv_data & STLB_CACHE_INV_PRODUCER_INDEX_MASK) + 1) & 0xFF; - WREG32(mmSTLB_CACHE_INV, - (inv_data & STLB_CACHE_INV_INDEX_MASK_MASK) | pi); - - rc = hl_poll_timeout( - hdev, - mmSTLB_INV_CONSUMER_INDEX, - status, - status == pi, - 1000, - timeout_usec); - - if (rc) { - dev_err_ratelimited(hdev->dev, - "MMU cache invalidation timeout\n"); - hl_device_reset(hdev, HL_RESET_HARD); - } - - return rc; + return hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags); } static int gaudi_mmu_update_asid_hop0_addr(struct hl_device *hdev, @@ -7956,7 +8153,9 @@ static int gaudi_cpucp_info_get(struct hl_device *hdev) if (!(gaudi->hw_cap_initialized & HW_CAP_CPU_Q)) return 0; - rc = hl_fw_cpucp_handshake(hdev, mmCPU_BOOT_DEV_STS0, mmCPU_BOOT_ERR0); + rc = hl_fw_cpucp_handshake(hdev, mmCPU_BOOT_DEV_STS0, + mmCPU_BOOT_DEV_STS1, mmCPU_BOOT_ERR0, + mmCPU_BOOT_ERR1); if (rc) return rc; @@ -8077,7 +8276,7 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, for (i = 0 ; i < (NIC_NUMBER_OF_ENGINES / 2) ; i++) { offset = i * NIC_MACRO_QMAN_OFFSET; port = 2 * i; - if (hdev->nic_ports_mask & BIT(port)) { + if (gaudi->hw_cap_initialized & BIT(HW_CAP_NIC_SHIFT + port)) { qm_glbl_sts0 = RREG32(mmNIC0_QM0_GLBL_STS0 + offset); qm_cgm_sts = RREG32(mmNIC0_QM0_CGM_STS + offset); is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_cgm_sts); @@ -8092,7 +8291,7 @@ static bool gaudi_is_device_idle(struct hl_device *hdev, u64 *mask_arr, } port = 2 * i + 1; - if (hdev->nic_ports_mask & BIT(port)) { + if (gaudi->hw_cap_initialized & BIT(HW_CAP_NIC_SHIFT + port)) { qm_glbl_sts0 = RREG32(mmNIC0_QM1_GLBL_STS0 + offset); qm_cgm_sts = RREG32(mmNIC0_QM1_CGM_STS + offset); is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_cgm_sts); @@ -8306,8 +8505,10 @@ static int gaudi_internal_cb_pool_init(struct hl_device *hdev, HL_VA_RANGE_TYPE_HOST, HOST_SPACE_INTERNAL_CB_SZ, HL_MMU_VA_ALIGNMENT_NOT_NEEDED); - if (!hdev->internal_cb_va_base) + if (!hdev->internal_cb_va_base) { + rc = -ENOMEM; goto destroy_internal_cb_pool; + } mutex_lock(&ctx->mmu_lock); rc = hl_mmu_map_contiguous(ctx, hdev->internal_cb_va_base, @@ -8749,7 +8950,14 @@ static int gaudi_block_mmap(struct hl_device *hdev, static void gaudi_enable_events_from_fw(struct hl_device *hdev) { - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, GAUDI_EVENT_INTS_REGISTER); + struct cpu_dyn_regs *dyn_regs = + &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; + u32 irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : + le32_to_cpu(dyn_regs->gic_host_ints_irq); + + WREG32(irq_handler_offset, + gaudi_irq_map_table[GAUDI_EVENT_INTS_REGISTER].cpu_id); } static int gaudi_map_pll_idx_to_fw_idx(u32 pll_idx) @@ -8834,7 +9042,6 @@ static const struct hl_asic_funcs gaudi_funcs = { .ctx_fini = gaudi_ctx_fini, .get_clk_rate = gaudi_get_clk_rate, .get_queue_id_for_cq = gaudi_get_queue_id_for_cq, - .read_device_fw_version = gaudi_read_device_fw_version, .load_firmware_to_device = gaudi_load_firmware_to_device, .load_boot_fit_to_device = gaudi_load_boot_fit_to_device, .get_signal_cb_size = gaudi_get_signal_cb_size, @@ -8853,7 +9060,9 @@ static const struct hl_asic_funcs gaudi_funcs = { .get_hw_block_id = gaudi_get_hw_block_id, .hw_block_mmap = gaudi_block_mmap, .enable_events_from_fw = gaudi_enable_events_from_fw, - .map_pll_idx_to_fw_idx = gaudi_map_pll_idx_to_fw_idx + .map_pll_idx_to_fw_idx = gaudi_map_pll_idx_to_fw_idx, + .init_firmware_loader = gaudi_init_firmware_loader, + .init_cpu_scrambler_dram = gaudi_init_scrambler_hbm }; /** diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index 5929be81ec23..957bf3720f70 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -82,6 +82,7 @@ QMAN_STREAMS) #define QMAN_STREAMS 4 +#define PQ_FETCHER_CACHE_SIZE 8 #define DMA_QMAN_OFFSET (mmDMA1_QM_BASE - mmDMA0_QM_BASE) #define TPC_QMAN_OFFSET (mmTPC1_QM_BASE - mmTPC0_QM_BASE) diff --git a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c index 6e56fa1c6c69..c2a27ed1c4d1 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c @@ -424,7 +424,7 @@ static int gaudi_config_stm(struct hl_device *hdev, if (frequency == 0) frequency = input->frequency; WREG32(base_reg + 0xE8C, frequency); - WREG32(base_reg + 0xE90, 0x7FF); + WREG32(base_reg + 0xE90, 0x1F00); /* SW-2176 - SW WA for HW bug */ if ((CFG_BASE + base_reg) >= mmDMA_CH_0_CS_STM_BASE && @@ -434,7 +434,7 @@ static int gaudi_config_stm(struct hl_device *hdev, WREG32(base_reg + 0xE6C, 0x0); } - WREG32(base_reg + 0xE80, 0x27 | (input->id << 16)); + WREG32(base_reg + 0xE80, 0x23 | (input->id << 16)); } else { WREG32(base_reg + 0xE80, 4); WREG32(base_reg + 0xD64, 0); @@ -634,7 +634,7 @@ static int gaudi_config_etr(struct hl_device *hdev, WREG32(mmPSOC_ETR_BUFWM, 0x3FFC); WREG32(mmPSOC_ETR_RSZ, input->buffer_size); WREG32(mmPSOC_ETR_MODE, input->sink_mode); - if (hdev->asic_prop.fw_security_disabled) { + if (!hdev->asic_prop.fw_security_enabled) { /* make ETR not privileged */ val = FIELD_PREP( PSOC_ETR_AXICTL_PROTCTRLBIT0_MASK, 0); diff --git a/drivers/misc/habanalabs/gaudi/gaudi_security.c b/drivers/misc/habanalabs/gaudi/gaudi_security.c index 9a706c5980ef..0d3240f1f7d7 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_security.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_security.c @@ -1448,7 +1448,7 @@ static void gaudi_init_dma_protection_bits(struct hl_device *hdev) u32 pb_addr, mask; u8 word_offset; - if (hdev->asic_prop.fw_security_disabled) { + if (!hdev->asic_prop.fw_security_enabled) { gaudi_pb_set_block(hdev, mmDMA_IF_E_S_BASE); gaudi_pb_set_block(hdev, mmDMA_IF_E_S_DOWN_CH0_BASE); gaudi_pb_set_block(hdev, mmDMA_IF_E_S_DOWN_CH1_BASE); @@ -9135,7 +9135,7 @@ static void gaudi_init_tpc_protection_bits(struct hl_device *hdev) u32 pb_addr, mask; u8 word_offset; - if (hdev->asic_prop.fw_security_disabled) { + if (!hdev->asic_prop.fw_security_enabled) { gaudi_pb_set_block(hdev, mmTPC0_E2E_CRED_BASE); gaudi_pb_set_block(hdev, mmTPC1_E2E_CRED_BASE); gaudi_pb_set_block(hdev, mmTPC2_E2E_CRED_BASE); @@ -12818,7 +12818,7 @@ static void gaudi_init_protection_bits(struct hl_device *hdev) * secured */ - if (hdev->asic_prop.fw_security_disabled) { + if (!hdev->asic_prop.fw_security_enabled) { gaudi_pb_set_block(hdev, mmIF_E_PLL_BASE); gaudi_pb_set_block(hdev, mmMESH_W_PLL_BASE); gaudi_pb_set_block(hdev, mmSRAM_W_PLL_BASE); @@ -13023,7 +13023,7 @@ void gaudi_init_security(struct hl_device *hdev) * property configuration of MME SBAB and ACC to be non-privileged and * non-secured */ - if (hdev->asic_prop.fw_security_disabled) { + if (!hdev->asic_prop.fw_security_enabled) { WREG32(mmMME0_SBAB_PROT, 0x2); WREG32(mmMME0_ACC_PROT, 0x2); WREG32(mmMME1_SBAB_PROT, 0x2); @@ -13032,11 +13032,12 @@ void gaudi_init_security(struct hl_device *hdev) WREG32(mmMME2_ACC_PROT, 0x2); WREG32(mmMME3_SBAB_PROT, 0x2); WREG32(mmMME3_ACC_PROT, 0x2); - } - /* On RAZWI, 0 will be returned from RR and 0xBABA0BAD from PB */ - if (hdev->asic_prop.fw_security_disabled) + /* + * On RAZWI, 0 will be returned from RR and 0xBABA0BAD from PB + */ WREG32(0xC01B28, 0x1); + } gaudi_init_range_registers_lbw(hdev); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index e0ad2a269779..755e08cf2ecc 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -87,6 +87,7 @@ #define GOYA_PLDM_QMAN0_TIMEOUT_USEC (HL_DEVICE_TIMEOUT_USEC * 30) #define GOYA_BOOT_FIT_REQ_TIMEOUT_USEC 1000000 /* 1s */ #define GOYA_MSG_TO_CPU_TIMEOUT_USEC 4000000 /* 4s */ +#define GOYA_WAIT_FOR_BL_TIMEOUT_USEC 15000000 /* 15s */ #define GOYA_QMAN0_FENCE_VAL 0xD169B243 @@ -354,7 +355,7 @@ static int goya_mmu_set_dram_default_page(struct hl_device *hdev); static int goya_mmu_add_mappings_for_device_cpu(struct hl_device *hdev); static void goya_mmu_prepare(struct hl_device *hdev, u32 asid); -int goya_get_fixed_properties(struct hl_device *hdev) +int goya_set_fixed_properties(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; int i; @@ -460,8 +461,10 @@ int goya_get_fixed_properties(struct hl_device *hdev) for (i = 0 ; i < HL_MAX_DCORES ; i++) prop->first_available_cq[i] = USHRT_MAX; - prop->fw_security_status_valid = false; + prop->fw_cpu_boot_dev_sts0_valid = false; + prop->fw_cpu_boot_dev_sts1_valid = false; prop->hard_reset_done_by_fw = false; + prop->gic_interrupts_enable = true; return 0; } @@ -531,10 +534,8 @@ static int goya_init_iatu(struct hl_device *hdev) struct hl_outbound_pci_region outbound_region; int rc; - if (hdev->asic_prop.iatu_done_by_fw) { - hdev->asic_funcs->set_dma_mask_from_fw(hdev); + if (hdev->asic_prop.iatu_done_by_fw) return 0; - } /* Inbound Region 0 - Bar 0 - Point to SRAM and CFG */ inbound_region.mode = PCI_BAR_MATCH_MODE; @@ -586,7 +587,7 @@ static int goya_early_init(struct hl_device *hdev) u32 fw_boot_status, val; int rc; - rc = goya_get_fixed_properties(hdev); + rc = goya_set_fixed_properties(hdev); if (rc) { dev_err(hdev->dev, "Failed to get fixed properties\n"); return rc; @@ -618,7 +619,7 @@ static int goya_early_init(struct hl_device *hdev) prop->dram_pci_bar_size = pci_resource_len(pdev, DDR_BAR_ID); /* If FW security is enabled at this point it means no access to ELBI */ - if (!hdev->asic_prop.fw_security_disabled) { + if (hdev->asic_prop.fw_security_enabled) { hdev->asic_prop.iatu_done_by_fw = true; goto pci_init; } @@ -642,8 +643,10 @@ pci_init: * version to determine whether we run with a security-enabled firmware */ rc = hl_fw_read_preboot_status(hdev, mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS, - mmCPU_BOOT_DEV_STS0, mmCPU_BOOT_ERR0, - GOYA_BOOT_FIT_REQ_TIMEOUT_USEC); + mmCPU_BOOT_DEV_STS0, + mmCPU_BOOT_DEV_STS1, mmCPU_BOOT_ERR0, + mmCPU_BOOT_ERR1, + GOYA_BOOT_FIT_REQ_TIMEOUT_USEC); if (rc) { if (hdev->reset_on_preboot_fail) hdev->asic_funcs->hw_fini(hdev, true); @@ -723,7 +726,15 @@ static void goya_fetch_psoc_frequency(struct hl_device *hdev) u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS], freq; int rc; - if (hdev->asic_prop.fw_security_disabled) { + if (hdev->asic_prop.fw_security_enabled) { + rc = hl_fw_cpucp_pll_info_get(hdev, HL_GOYA_PCI_PLL, + pll_freq_arr); + + if (rc) + return; + + freq = pll_freq_arr[1]; + } else { div_fctr = RREG32(mmPSOC_PCI_PLL_DIV_FACTOR_1); div_sel = RREG32(mmPSOC_PCI_PLL_DIV_SEL_1); nr = RREG32(mmPSOC_PCI_PLL_NR); @@ -750,14 +761,6 @@ static void goya_fetch_psoc_frequency(struct hl_device *hdev) div_sel); freq = 0; } - } else { - rc = hl_fw_cpucp_pll_info_get(hdev, HL_GOYA_PCI_PLL, - pll_freq_arr); - - if (rc) - return; - - freq = pll_freq_arr[1]; } prop->psoc_timestamp_frequency = freq; @@ -849,6 +852,39 @@ void goya_late_fini(struct hl_device *hdev) hdev->hl_chip_info->info = NULL; } +static void goya_set_pci_memory_regions(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct pci_mem_region *region; + + /* CFG */ + region = &hdev->pci_mem_region[PCI_REGION_CFG]; + region->region_base = CFG_BASE; + region->region_size = CFG_SIZE; + region->offset_in_bar = CFG_BASE - SRAM_BASE_ADDR; + region->bar_size = CFG_BAR_SIZE; + region->bar_id = SRAM_CFG_BAR_ID; + region->used = 1; + + /* SRAM */ + region = &hdev->pci_mem_region[PCI_REGION_SRAM]; + region->region_base = SRAM_BASE_ADDR; + region->region_size = SRAM_SIZE; + region->offset_in_bar = 0; + region->bar_size = CFG_BAR_SIZE; + region->bar_id = SRAM_CFG_BAR_ID; + region->used = 1; + + /* DRAM */ + region = &hdev->pci_mem_region[PCI_REGION_DRAM]; + region->region_base = DRAM_PHYS_BASE; + region->region_size = hdev->asic_prop.dram_size; + region->offset_in_bar = 0; + region->bar_size = prop->dram_pci_bar_size; + region->bar_id = DDR_BAR_ID; + region->used = 1; +} + /* * goya_sw_init - Goya software initialization code * @@ -918,6 +954,9 @@ static int goya_sw_init(struct hl_device *hdev) spin_lock_init(&goya->hw_queues_lock); hdev->supports_coresight = true; hdev->supports_soft_reset = true; + hdev->allow_external_soft_reset = true; + + goya_set_pci_memory_regions(hdev); return 0; @@ -1263,8 +1302,11 @@ int goya_init_cpu_queues(struct hl_device *hdev) } /* update FW application security bits */ - if (prop->fw_security_status_valid) - prop->fw_app_security_map = RREG32(mmCPU_BOOT_DEV_STS0); + if (prop->fw_cpu_boot_dev_sts0_valid) + prop->fw_app_cpu_boot_dev_sts0 = RREG32(mmCPU_BOOT_DEV_STS0); + + if (prop->fw_cpu_boot_dev_sts1_valid) + prop->fw_app_cpu_boot_dev_sts1 = RREG32(mmCPU_BOOT_DEV_STS1); goya->hw_cap_initialized |= HW_CAP_CPU_Q; return 0; @@ -2402,47 +2444,67 @@ static int goya_load_boot_fit_to_device(struct hl_device *hdev) return hl_fw_load_fw_to_device(hdev, GOYA_BOOT_FIT_FILE, dst, 0, 0); } -/* - * FW component passes an offset from SRAM_BASE_ADDR in SCRATCHPAD_xx. - * The version string should be located by that offset. - */ -static int goya_read_device_fw_version(struct hl_device *hdev, - enum hl_fw_component fwc) -{ - const char *name; - u32 ver_off; - char *dest; - - switch (fwc) { - case FW_COMP_UBOOT: - ver_off = RREG32(mmUBOOT_VER_OFFSET); - dest = hdev->asic_prop.uboot_ver; - name = "U-Boot"; - break; - case FW_COMP_PREBOOT: - ver_off = RREG32(mmPREBOOT_VER_OFFSET); - dest = hdev->asic_prop.preboot_ver; - name = "Preboot"; - break; - default: - dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc); - return -EIO; - } +static void goya_init_dynamic_firmware_loader(struct hl_device *hdev) +{ + struct dynamic_fw_load_mgr *dynamic_loader; + struct cpu_dyn_regs *dyn_regs; - ver_off &= ~((u32)SRAM_BASE_ADDR); + dynamic_loader = &hdev->fw_loader.dynamic_loader; - if (ver_off < SRAM_SIZE - VERSION_MAX_LEN) { - memcpy_fromio(dest, hdev->pcie_bar[SRAM_CFG_BAR_ID] + ver_off, - VERSION_MAX_LEN); - } else { - dev_err(hdev->dev, "%s version offset (0x%x) is above SRAM\n", - name, ver_off); - strcpy(dest, "unavailable"); + /* + * here we update initial values for few specific dynamic regs (as + * before reading the first descriptor from FW those value has to be + * hard-coded) in later stages of the protocol those values will be + * updated automatically by reading the FW descriptor so data there + * will always be up-to-date + */ + dyn_regs = &dynamic_loader->comm_desc.cpu_dyn_regs; + dyn_regs->kmd_msg_to_cpu = + cpu_to_le32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU); + dyn_regs->cpu_cmd_status_to_host = + cpu_to_le32(mmCPU_CMD_STATUS_TO_HOST); - return -EIO; - } + dynamic_loader->wait_for_bl_timeout = GOYA_WAIT_FOR_BL_TIMEOUT_USEC; +} - return 0; +static void goya_init_static_firmware_loader(struct hl_device *hdev) +{ + struct static_fw_load_mgr *static_loader; + + static_loader = &hdev->fw_loader.static_loader; + + static_loader->preboot_version_max_off = SRAM_SIZE - VERSION_MAX_LEN; + static_loader->boot_fit_version_max_off = SRAM_SIZE - VERSION_MAX_LEN; + static_loader->kmd_msg_to_cpu_reg = mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU; + static_loader->cpu_cmd_status_to_host_reg = mmCPU_CMD_STATUS_TO_HOST; + static_loader->cpu_boot_status_reg = mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS; + static_loader->cpu_boot_dev_status0_reg = mmCPU_BOOT_DEV_STS0; + static_loader->cpu_boot_dev_status1_reg = mmCPU_BOOT_DEV_STS1; + static_loader->boot_err0_reg = mmCPU_BOOT_ERR0; + static_loader->boot_err1_reg = mmCPU_BOOT_ERR1; + static_loader->preboot_version_offset_reg = mmPREBOOT_VER_OFFSET; + static_loader->boot_fit_version_offset_reg = mmUBOOT_VER_OFFSET; + static_loader->sram_offset_mask = ~(lower_32_bits(SRAM_BASE_ADDR)); +} + +static void goya_init_firmware_loader(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct fw_load_mgr *fw_loader = &hdev->fw_loader; + + /* fill common fields */ + fw_loader->boot_fit_img.image_name = GOYA_BOOT_FIT_FILE; + fw_loader->linux_img.image_name = GOYA_LINUX_FW_FILE; + fw_loader->cpu_timeout = GOYA_CPU_TIMEOUT_USEC; + fw_loader->boot_fit_timeout = GOYA_BOOT_FIT_REQ_TIMEOUT_USEC; + fw_loader->skip_bmc = false; + fw_loader->sram_bar_id = SRAM_CFG_BAR_ID; + fw_loader->dram_bar_id = DDR_BAR_ID; + + if (prop->dynamic_fw_load) + goya_init_dynamic_firmware_loader(hdev); + else + goya_init_static_firmware_loader(hdev); } static int goya_init_cpu(struct hl_device *hdev) @@ -2466,12 +2528,7 @@ static int goya_init_cpu(struct hl_device *hdev) return -EIO; } - rc = hl_fw_init_cpu(hdev, mmPSOC_GLOBAL_CONF_CPU_BOOT_STATUS, - mmPSOC_GLOBAL_CONF_UBOOT_MAGIC, - mmCPU_CMD_STATUS_TO_HOST, - mmCPU_BOOT_DEV_STS0, mmCPU_BOOT_ERR0, - false, GOYA_CPU_TIMEOUT_USEC, - GOYA_BOOT_FIT_REQ_TIMEOUT_USEC); + rc = hl_fw_init_cpu(hdev); if (rc) return rc; @@ -2881,7 +2938,7 @@ void *goya_get_int_queue_base(struct hl_device *hdev, u32 queue_id, *dma_handle = hdev->asic_prop.sram_base_address; - base = (void *) hdev->pcie_bar[SRAM_CFG_BAR_ID]; + base = (__force void *) hdev->pcie_bar[SRAM_CFG_BAR_ID]; switch (queue_id) { case GOYA_QUEUE_ID_MME: @@ -3270,6 +3327,7 @@ already_pinned: return 0; unpin_memory: + list_del(&userptr->job_node); hl_unpin_host_memory(hdev, userptr); free_userptr: kfree(userptr); @@ -5169,54 +5227,13 @@ static int goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, } static int goya_mmu_invalidate_cache_range(struct hl_device *hdev, - bool is_hard, u32 asid, u64 va, u64 size) + bool is_hard, u32 flags, + u32 asid, u64 va, u64 size) { - struct goya_device *goya = hdev->asic_specific; - u32 status, timeout_usec, inv_data, pi; - int rc; - - if (!(goya->hw_cap_initialized & HW_CAP_MMU) || - hdev->hard_reset_pending) - return 0; - - /* no need in L1 only invalidation in Goya */ - if (!is_hard) - return 0; - - if (hdev->pldm) - timeout_usec = GOYA_PLDM_MMU_TIMEOUT_USEC; - else - timeout_usec = MMU_CONFIG_TIMEOUT_USEC; - - /* - * TODO: currently invalidate entire L0 & L1 as in regular hard - * invalidation. Need to apply invalidation of specific cache lines with - * mask of ASID & VA & size. - * Note that L1 with be flushed entirely in any case. + /* Treat as invalidate all because there is no range invalidation + * in Goya */ - - /* L0 & L1 invalidation */ - inv_data = RREG32(mmSTLB_CACHE_INV); - /* PI is 8 bit */ - pi = ((inv_data & STLB_CACHE_INV_PRODUCER_INDEX_MASK) + 1) & 0xFF; - WREG32(mmSTLB_CACHE_INV, - (inv_data & STLB_CACHE_INV_INDEX_MASK_MASK) | pi); - - rc = hl_poll_timeout( - hdev, - mmSTLB_INV_CONSUMER_INDEX, - status, - status == pi, - 1000, - timeout_usec); - - if (rc) { - dev_err_ratelimited(hdev->dev, - "MMU cache invalidation timeout\n"); - hl_device_reset(hdev, HL_RESET_HARD); - } - - return rc; + return hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags); } int goya_send_heartbeat(struct hl_device *hdev) @@ -5239,7 +5256,9 @@ int goya_cpucp_info_get(struct hl_device *hdev) if (!(goya->hw_cap_initialized & HW_CAP_CPU_Q)) return 0; - rc = hl_fw_cpucp_handshake(hdev, mmCPU_BOOT_DEV_STS0, mmCPU_BOOT_ERR0); + rc = hl_fw_cpucp_handshake(hdev, mmCPU_BOOT_DEV_STS0, + mmCPU_BOOT_DEV_STS1, mmCPU_BOOT_ERR0, + mmCPU_BOOT_ERR1); if (rc) return rc; @@ -5385,6 +5404,11 @@ static int goya_get_eeprom_data(struct hl_device *hdev, void *data, return hl_fw_get_eeprom_data(hdev, data, max_size); } +static void goya_cpu_init_scrambler_dram(struct hl_device *hdev) +{ + +} + static int goya_ctx_init(struct hl_ctx *ctx) { if (ctx->asid != HL_KERNEL_ASID_ID) @@ -5565,7 +5589,6 @@ static const struct hl_asic_funcs goya_funcs = { .ctx_fini = goya_ctx_fini, .get_clk_rate = goya_get_clk_rate, .get_queue_id_for_cq = goya_get_queue_id_for_cq, - .read_device_fw_version = goya_read_device_fw_version, .load_firmware_to_device = goya_load_firmware_to_device, .load_boot_fit_to_device = goya_load_boot_fit_to_device, .get_signal_cb_size = goya_get_signal_cb_size, @@ -5584,7 +5607,9 @@ static const struct hl_asic_funcs goya_funcs = { .get_hw_block_id = goya_get_hw_block_id, .hw_block_mmap = goya_block_mmap, .enable_events_from_fw = goya_enable_events_from_fw, - .map_pll_idx_to_fw_idx = goya_map_pll_idx_to_fw_idx + .map_pll_idx_to_fw_idx = goya_map_pll_idx_to_fw_idx, + .init_firmware_loader = goya_init_firmware_loader, + .init_cpu_scrambler_dram = goya_cpu_init_scrambler_dram }; /* diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index ef8c6c8b5c8d..0b05da614729 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -168,7 +168,7 @@ struct goya_device { u8 device_cpu_mmu_mappings_done; }; -int goya_get_fixed_properties(struct hl_device *hdev); +int goya_set_fixed_properties(struct hl_device *hdev); int goya_mmu_init(struct hl_device *hdev); void goya_init_dma_qmans(struct hl_device *hdev); void goya_init_mme_qmans(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/misc/habanalabs/goya/goya_coresight.c index 6b7445cca580..c55c100fdd24 100644 --- a/drivers/misc/habanalabs/goya/goya_coresight.c +++ b/drivers/misc/habanalabs/goya/goya_coresight.c @@ -434,7 +434,7 @@ static int goya_config_etr(struct hl_device *hdev, WREG32(mmPSOC_ETR_BUFWM, 0x3FFC); WREG32(mmPSOC_ETR_RSZ, input->buffer_size); WREG32(mmPSOC_ETR_MODE, input->sink_mode); - if (hdev->asic_prop.fw_security_disabled) { + if (!hdev->asic_prop.fw_security_enabled) { /* make ETR not privileged */ val = FIELD_PREP(PSOC_ETR_AXICTL_PROTCTRLBIT0_MASK, 0); /* make ETR non-secured (inverted logic) */ diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index 27cd0ba99aa3..80b1d5a9d9f1 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -84,6 +84,20 @@ struct hl_eq_sm_sei_data { __u8 pad[3]; }; +enum hl_fw_alive_severity { + FW_ALIVE_SEVERITY_MINOR, + FW_ALIVE_SEVERITY_CRITICAL +}; + +struct hl_eq_fw_alive { + __le64 uptime_seconds; + __le32 process_id; + __le32 thread_id; + /* enum hl_fw_alive_severity */ + __u8 severity; + __u8 pad[7]; +}; + struct hl_eq_entry { struct hl_eq_header hdr; union { @@ -91,6 +105,7 @@ struct hl_eq_entry { struct hl_eq_hbm_ecc_data hbm_ecc_data; struct hl_eq_sm_sei_data sm_sei_data; struct cpucp_pkt_sync_err pkt_sync_err; + struct hl_eq_fw_alive fw_alive; __le64 data[7]; }; }; @@ -103,11 +118,16 @@ struct hl_eq_entry { #define EQ_CTL_EVENT_TYPE_SHIFT 16 #define EQ_CTL_EVENT_TYPE_MASK 0x03FF0000 +#define EQ_CTL_INDEX_SHIFT 0 +#define EQ_CTL_INDEX_MASK 0x0000FFFF + enum pq_init_status { PQ_INIT_STATUS_NA = 0, PQ_INIT_STATUS_READY_FOR_CP, PQ_INIT_STATUS_READY_FOR_HOST, - PQ_INIT_STATUS_READY_FOR_CP_SINGLE_MSI + PQ_INIT_STATUS_READY_FOR_CP_SINGLE_MSI, + PQ_INIT_STATUS_LEN_NOT_POWER_OF_TWO_ERR, + PQ_INIT_STATUS_ILLEGAL_Q_ADDR_ERR }; /* @@ -384,6 +404,20 @@ enum cpucp_packet_id { #define CPUCP_PKT_RES_PLL_OUT3_SHIFT 48 #define CPUCP_PKT_RES_PLL_OUT3_MASK 0xFFFF000000000000ull +#define CPUCP_PKT_VAL_PFC_IN1_SHIFT 0 +#define CPUCP_PKT_VAL_PFC_IN1_MASK 0x0000000000000001ull +#define CPUCP_PKT_VAL_PFC_IN2_SHIFT 1 +#define CPUCP_PKT_VAL_PFC_IN2_MASK 0x000000000000001Eull + +#define CPUCP_PKT_VAL_LPBK_IN1_SHIFT 0 +#define CPUCP_PKT_VAL_LPBK_IN1_MASK 0x0000000000000001ull +#define CPUCP_PKT_VAL_LPBK_IN2_SHIFT 1 +#define CPUCP_PKT_VAL_LPBK_IN2_MASK 0x000000000000001Eull + +/* heartbeat status bits */ +#define CPUCP_PKT_HB_STATUS_EQ_FAULT_SHIFT 0 +#define CPUCP_PKT_HB_STATUS_EQ_FAULT_MASK 0x00000001 + struct cpucp_packet { union { __le64 value; /* For SET packets */ @@ -425,6 +459,12 @@ struct cpucp_packet { /* For get CpuCP info/EEPROM data/NIC info */ __le32 data_max_size; + + /* + * For any general status bitmask. Shall be used whenever the + * result cannot be used to hold general purpose data. + */ + __le32 status_mask; }; __le32 reserved; @@ -629,6 +669,8 @@ struct cpucp_security_info { * @card_name: card name that will be displayed in HWMON subsystem on the host * @sec_info: security information * @pll_map: Bit map of supported PLLs for current ASIC version. + * @mme_binning_mask: MME binning mask, + * (0 = functional, 1 = binned) */ struct cpucp_info { struct cpucp_sensor sensors[CPUCP_MAX_SENSORS]; @@ -651,6 +693,7 @@ struct cpucp_info { struct cpucp_security_info sec_info; __le32 reserved6; __u8 pll_map[PLL_MAP_LEN]; + __le64 mme_binning_mask; }; struct cpucp_mac_addr { diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index e0a259e0495c..fa8a5ad2d438 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -8,7 +8,7 @@ #ifndef HL_BOOT_IF_H #define HL_BOOT_IF_H -#define LKD_HARD_RESET_MAGIC 0xED7BD694 +#define LKD_HARD_RESET_MAGIC 0xED7BD694 /* deprecated - do not use */ #define HL_POWER9_HOST_MAGIC 0x1DA30009 #define BOOT_FIT_SRAM_OFFSET 0x200000 @@ -99,6 +99,7 @@ #define CPU_BOOT_ERR0_PLL_FAIL (1 << 12) #define CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL (1 << 13) #define CPU_BOOT_ERR0_ENABLED (1 << 31) +#define CPU_BOOT_ERR1_ENABLED (1 << 31) /* * BOOT DEVICE STATUS bits in BOOT_DEVICE_STS registers @@ -190,6 +191,24 @@ * PLLs. * Initialized in: linux * + * CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN GIC access permission only from + * previleged entity. FW sets this status + * bit for host. If this bit is set then + * GIC can not be accessed from host. + * Initialized in: linux + * + * CPU_BOOT_DEV_STS0_EQ_INDEX_EN Event Queue (EQ) index is a running + * index for each new event sent to host. + * This is used as a method in host to + * identify that the waiting event in + * queue is actually a new event which + * was not served before. + * Initialized in: linux + * + * CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN Use multiple scratchpad interfaces to + * prevent IRQs overriding each other. + * Initialized in: linux + * * CPU_BOOT_DEV_STS0_ENABLED Device status register enabled. * This is a main indication that the * running FW populates the device status @@ -218,7 +237,11 @@ #define CPU_BOOT_DEV_STS0_FW_LD_COM_EN (1 << 16) #define CPU_BOOT_DEV_STS0_FW_IATU_CONF_EN (1 << 17) #define CPU_BOOT_DEV_STS0_DYN_PLL_EN (1 << 19) +#define CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN (1 << 20) +#define CPU_BOOT_DEV_STS0_EQ_INDEX_EN (1 << 21) +#define CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN (1 << 22) #define CPU_BOOT_DEV_STS0_ENABLED (1 << 31) +#define CPU_BOOT_DEV_STS1_ENABLED (1 << 31) enum cpu_boot_status { CPU_BOOT_STATUS_NA = 0, /* Default value after reset of chip */ @@ -264,46 +287,98 @@ enum cpu_msg_status { /* communication registers mapping - consider ABI when changing */ struct cpu_dyn_regs { - uint32_t cpu_pq_base_addr_low; - uint32_t cpu_pq_base_addr_high; - uint32_t cpu_pq_length; - uint32_t cpu_pq_init_status; - uint32_t cpu_eq_base_addr_low; - uint32_t cpu_eq_base_addr_high; - uint32_t cpu_eq_length; - uint32_t cpu_eq_ci; - uint32_t cpu_cq_base_addr_low; - uint32_t cpu_cq_base_addr_high; - uint32_t cpu_cq_length; - uint32_t cpu_pf_pq_pi; - uint32_t cpu_boot_dev_sts0; - uint32_t cpu_boot_dev_sts1; - uint32_t cpu_boot_err0; - uint32_t cpu_boot_err1; - uint32_t cpu_boot_status; - uint32_t fw_upd_sts; - uint32_t fw_upd_cmd; - uint32_t fw_upd_pending_sts; - uint32_t fuse_ver_offset; - uint32_t preboot_ver_offset; - uint32_t uboot_ver_offset; - uint32_t hw_state; - uint32_t kmd_msg_to_cpu; - uint32_t cpu_cmd_status_to_host; - uint32_t reserved1[32]; /* reserve for future use */ + __le32 cpu_pq_base_addr_low; + __le32 cpu_pq_base_addr_high; + __le32 cpu_pq_length; + __le32 cpu_pq_init_status; + __le32 cpu_eq_base_addr_low; + __le32 cpu_eq_base_addr_high; + __le32 cpu_eq_length; + __le32 cpu_eq_ci; + __le32 cpu_cq_base_addr_low; + __le32 cpu_cq_base_addr_high; + __le32 cpu_cq_length; + __le32 cpu_pf_pq_pi; + __le32 cpu_boot_dev_sts0; + __le32 cpu_boot_dev_sts1; + __le32 cpu_boot_err0; + __le32 cpu_boot_err1; + __le32 cpu_boot_status; + __le32 fw_upd_sts; + __le32 fw_upd_cmd; + __le32 fw_upd_pending_sts; + __le32 fuse_ver_offset; + __le32 preboot_ver_offset; + __le32 uboot_ver_offset; + __le32 hw_state; + __le32 kmd_msg_to_cpu; + __le32 cpu_cmd_status_to_host; + union { + __le32 gic_host_irq_ctrl; + __le32 gic_host_pi_upd_irq; + }; + __le32 gic_tpc_qm_irq_ctrl; + __le32 gic_mme_qm_irq_ctrl; + __le32 gic_dma_qm_irq_ctrl; + __le32 gic_nic_qm_irq_ctrl; + __le32 gic_dma_core_irq_ctrl; + __le32 gic_host_halt_irq; + __le32 gic_host_ints_irq; + __le32 reserved1[24]; /* reserve for future use */ }; +/* TODO: remove the desc magic after the code is updated to use message */ /* HCDM - Habana Communications Descriptor Magic */ #define HL_COMMS_DESC_MAGIC 0x4843444D #define HL_COMMS_DESC_VER 1 +/* HCMv - Habana Communications Message + header version */ +#define HL_COMMS_MSG_MAGIC_VALUE 0x48434D00 +#define HL_COMMS_MSG_MAGIC_MASK 0xFFFFFF00 +#define HL_COMMS_MSG_MAGIC_VER_MASK 0xFF + +#define HL_COMMS_MSG_MAGIC_VER(ver) (HL_COMMS_MSG_MAGIC_VALUE | \ + ((ver) & HL_COMMS_MSG_MAGIC_VER_MASK)) +#define HL_COMMS_MSG_MAGIC_V0 HL_COMMS_DESC_MAGIC +#define HL_COMMS_MSG_MAGIC_V1 HL_COMMS_MSG_MAGIC_VER(1) + +#define HL_COMMS_MSG_MAGIC HL_COMMS_MSG_MAGIC_V1 + +#define HL_COMMS_MSG_MAGIC_VALIDATE_MAGIC(magic) \ + (((magic) & HL_COMMS_MSG_MAGIC_MASK) == \ + HL_COMMS_MSG_MAGIC_VALUE) + +#define HL_COMMS_MSG_MAGIC_VALIDATE_VERSION(magic, ver) \ + (((magic) & HL_COMMS_MSG_MAGIC_VER_MASK) >= \ + ((ver) & HL_COMMS_MSG_MAGIC_VER_MASK)) + +#define HL_COMMS_MSG_MAGIC_VALIDATE(magic, ver) \ + (HL_COMMS_MSG_MAGIC_VALIDATE_MAGIC((magic)) && \ + HL_COMMS_MSG_MAGIC_VALIDATE_VERSION((magic), (ver))) + +enum comms_msg_type { + HL_COMMS_DESC_TYPE = 0, + HL_COMMS_RESET_CAUSE_TYPE = 1, +}; + +/* TODO: remove this struct after the code is updated to use message */ /* this is the comms descriptor header - meta data */ struct comms_desc_header { - uint32_t magic; /* magic for validation */ - uint32_t crc32; /* CRC32 of the descriptor w/o header */ - uint16_t size; /* size of the descriptor w/o header */ - uint8_t version; /* descriptor version */ - uint8_t reserved[5]; /* pad to 64 bit */ + __le32 magic; /* magic for validation */ + __le32 crc32; /* CRC32 of the descriptor w/o header */ + __le16 size; /* size of the descriptor w/o header */ + __u8 version; /* descriptor version */ + __u8 reserved[5]; /* pad to 64 bit */ +}; + +/* this is the comms message header - meta data */ +struct comms_msg_header { + __le32 magic; /* magic for validation */ + __le32 crc32; /* CRC32 of the message w/o header */ + __le16 size; /* size of the message w/o header */ + __u8 version; /* message payload version */ + __u8 type; /* message type */ + __u8 reserved[4]; /* pad to 64 bit */ }; /* this is the main FW descriptor - consider ABI when changing */ @@ -314,7 +389,36 @@ struct lkd_fw_comms_desc { char cur_fw_ver[VERSION_MAX_LEN]; /* can be used for 1 more version w/o ABI change */ char reserved0[VERSION_MAX_LEN]; - uint64_t img_addr; /* address for next FW component load */ + __le64 img_addr; /* address for next FW component load */ +}; + +enum comms_reset_cause { + HL_RESET_CAUSE_UNKNOWN = 0, + HL_RESET_CAUSE_HEARTBEAT = 1, + HL_RESET_CAUSE_TDR = 2, +}; + +/* TODO: remove define after struct name is aligned on all projects */ +#define lkd_msg_comms lkd_fw_comms_msg + +/* this is the comms message descriptor */ +struct lkd_fw_comms_msg { + struct comms_msg_header header; + /* union for future expantions of new messages */ + union { + struct { + struct cpu_dyn_regs cpu_dyn_regs; + char fuse_ver[VERSION_MAX_LEN]; + char cur_fw_ver[VERSION_MAX_LEN]; + /* can be used for 1 more version w/o ABI change */ + char reserved0[VERSION_MAX_LEN]; + /* address for next FW component load */ + __le64 img_addr; + }; + struct { + __u8 reset_cause; + }; + }; }; /* @@ -386,11 +490,11 @@ enum comms_cmd { struct comms_command { union { /* bit fields are only for FW use */ struct { - unsigned int size :25; /* 32MB max. */ - unsigned int reserved :2; + u32 size :25; /* 32MB max. */ + u32 reserved :2; enum comms_cmd cmd :5; /* 32 commands */ }; - unsigned int val; + __le32 val; }; }; @@ -449,11 +553,11 @@ enum comms_ram_types { struct comms_status { union { /* bit fields are only for FW use */ struct { - unsigned int offset :26; - unsigned int ram_type :2; + u32 offset :26; + enum comms_ram_types ram_type :2; enum comms_sts status :4; /* 16 statuses */ }; - unsigned int val; + __le32 val; }; }; diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h b/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h index e8651abf84f2..d966bd4dfea6 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_async_events.h @@ -252,10 +252,11 @@ enum gaudi_async_event_id { GAUDI_EVENT_HBM3_SPI_0 = 407, GAUDI_EVENT_HBM3_SPI_1 = 408, GAUDI_EVENT_PSOC_GPIO_U16_0 = 421, - GAUDI_EVENT_PI_UPDATE = 484, - GAUDI_EVENT_HALT_MACHINE = 485, - GAUDI_EVENT_INTS_REGISTER = 486, - GAUDI_EVENT_SOFT_RESET = 487, + GAUDI_EVENT_NIC0_CS_DBG_DERR = 483, + GAUDI_EVENT_NIC1_CS_DBG_DERR = 487, + GAUDI_EVENT_NIC2_CS_DBG_DERR = 491, + GAUDI_EVENT_NIC3_CS_DBG_DERR = 495, + GAUDI_EVENT_NIC4_CS_DBG_DERR = 499, GAUDI_EVENT_RAZWI_OR_ADC = 548, GAUDI_EVENT_TPC0_QM = 572, GAUDI_EVENT_TPC1_QM = 573, @@ -303,6 +304,11 @@ enum gaudi_async_event_id { GAUDI_EVENT_NIC3_QP1 = 619, GAUDI_EVENT_NIC4_QP0 = 620, GAUDI_EVENT_NIC4_QP1 = 621, + GAUDI_EVENT_PI_UPDATE = 635, + GAUDI_EVENT_HALT_MACHINE = 636, + GAUDI_EVENT_INTS_REGISTER = 637, + GAUDI_EVENT_SOFT_RESET = 638, + GAUDI_EVENT_FW_ALIVE_S = 645, GAUDI_EVENT_DEV_RESET_REQ = 646, GAUDI_EVENT_PKT_QUEUE_OUT_SYNC = 647, GAUDI_EVENT_FIX_POWER_ENV_S = 658, diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h b/drivers/misc/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h index 3dc79c131805..479b6b038254 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_async_ids_map_extended.h @@ -507,23 +507,28 @@ static struct gaudi_async_events_ids_map gaudi_irq_map_table[] = { { .fc_id = 480, .cpu_id = 329, .valid = 0, .name = "" }, { .fc_id = 481, .cpu_id = 330, .valid = 0, .name = "" }, { .fc_id = 482, .cpu_id = 331, .valid = 0, .name = "" }, - { .fc_id = 483, .cpu_id = 332, .valid = 0, .name = "" }, - { .fc_id = 484, .cpu_id = 333, .valid = 1, .name = "PI_UPDATE" }, - { .fc_id = 485, .cpu_id = 334, .valid = 1, .name = "HALT_MACHINE" }, - { .fc_id = 486, .cpu_id = 335, .valid = 1, .name = "INTS_REGISTER" }, - { .fc_id = 487, .cpu_id = 336, .valid = 1, .name = "SOFT_RESET" }, + { .fc_id = 483, .cpu_id = 332, .valid = 1, + .name = "NIC0_CS_DBG_DERR" }, + { .fc_id = 484, .cpu_id = 333, .valid = 0, .name = "" }, + { .fc_id = 485, .cpu_id = 334, .valid = 0, .name = "" }, + { .fc_id = 486, .cpu_id = 335, .valid = 0, .name = "" }, + { .fc_id = 487, .cpu_id = 336, .valid = 1, + .name = "NIC1_CS_DBG_DERR" }, { .fc_id = 488, .cpu_id = 337, .valid = 0, .name = "" }, { .fc_id = 489, .cpu_id = 338, .valid = 0, .name = "" }, { .fc_id = 490, .cpu_id = 339, .valid = 0, .name = "" }, - { .fc_id = 491, .cpu_id = 340, .valid = 0, .name = "" }, + { .fc_id = 491, .cpu_id = 340, .valid = 1, + .name = "NIC2_CS_DBG_DERR" }, { .fc_id = 492, .cpu_id = 341, .valid = 0, .name = "" }, { .fc_id = 493, .cpu_id = 342, .valid = 0, .name = "" }, { .fc_id = 494, .cpu_id = 343, .valid = 0, .name = "" }, - { .fc_id = 495, .cpu_id = 344, .valid = 0, .name = "" }, + { .fc_id = 495, .cpu_id = 344, .valid = 1, + .name = "NIC3_CS_DBG_DERR" }, { .fc_id = 496, .cpu_id = 345, .valid = 0, .name = "" }, { .fc_id = 497, .cpu_id = 346, .valid = 0, .name = "" }, { .fc_id = 498, .cpu_id = 347, .valid = 0, .name = "" }, - { .fc_id = 499, .cpu_id = 348, .valid = 0, .name = "" }, + { .fc_id = 499, .cpu_id = 348, .valid = 1, + .name = "NIC4_CS_DBG_DERR" }, { .fc_id = 500, .cpu_id = 349, .valid = 0, .name = "" }, { .fc_id = 501, .cpu_id = 350, .valid = 0, .name = "" }, { .fc_id = 502, .cpu_id = 351, .valid = 0, .name = "" }, @@ -659,17 +664,17 @@ static struct gaudi_async_events_ids_map gaudi_irq_map_table[] = { { .fc_id = 632, .cpu_id = 481, .valid = 0, .name = "" }, { .fc_id = 633, .cpu_id = 482, .valid = 0, .name = "" }, { .fc_id = 634, .cpu_id = 483, .valid = 0, .name = "" }, - { .fc_id = 635, .cpu_id = 484, .valid = 0, .name = "" }, - { .fc_id = 636, .cpu_id = 485, .valid = 0, .name = "" }, - { .fc_id = 637, .cpu_id = 486, .valid = 0, .name = "" }, - { .fc_id = 638, .cpu_id = 487, .valid = 0, .name = "" }, + { .fc_id = 635, .cpu_id = 484, .valid = 1, .name = "PI_UPDATE" }, + { .fc_id = 636, .cpu_id = 485, .valid = 1, .name = "HALT_MACHINE" }, + { .fc_id = 637, .cpu_id = 486, .valid = 1, .name = "INTS_REGISTER" }, + { .fc_id = 638, .cpu_id = 487, .valid = 1, .name = "SOFT_RESET" }, { .fc_id = 639, .cpu_id = 488, .valid = 0, .name = "" }, { .fc_id = 640, .cpu_id = 489, .valid = 0, .name = "" }, { .fc_id = 641, .cpu_id = 490, .valid = 0, .name = "" }, { .fc_id = 642, .cpu_id = 491, .valid = 0, .name = "" }, { .fc_id = 643, .cpu_id = 492, .valid = 0, .name = "" }, { .fc_id = 644, .cpu_id = 493, .valid = 0, .name = "" }, - { .fc_id = 645, .cpu_id = 494, .valid = 0, .name = "" }, + { .fc_id = 645, .cpu_id = 494, .valid = 1, .name = "FW_ALIVE_S" }, { .fc_id = 646, .cpu_id = 495, .valid = 1, .name = "DEV_RESET_REQ" }, { .fc_id = 647, .cpu_id = 496, .valid = 1, .name = "PKT_QUEUE_OUT_SYNC" }, diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h b/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h index a9f51f9f9e92..34ca4fe50d91 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_fw_if.h @@ -20,6 +20,9 @@ #define UBOOT_FW_OFFSET 0x100000 /* 1MB in SRAM */ #define LINUX_FW_OFFSET 0x800000 /* 8MB in HBM */ +/* HBM thermal delta in [Deg] added to composite (CTemp) */ +#define HBM_TEMP_ADJUST_COEFF 6 + enum gaudi_nic_axi_error { RXB, RXE, @@ -27,6 +30,7 @@ enum gaudi_nic_axi_error { TXE, QPC_RESP, NON_AXI_ERR, + TMR, }; /* @@ -42,6 +46,48 @@ struct eq_nic_sei_event { __u8 pad[6]; }; +/* + * struct gaudi_nic_status - describes the status of a NIC port. + * @port: NIC port index. + * @bad_format_cnt: e.g. CRC. + * @responder_out_of_sequence_psn_cnt: e.g NAK. + * @high_ber_reinit_cnt: link reinit due to high BER. + * @correctable_err_cnt: e.g. bit-flip. + * @uncorrectable_err_cnt: e.g. MAC errors. + * @retraining_cnt: re-training counter. + * @up: is port up. + * @pcs_link: has PCS link. + * @phy_ready: is PHY ready. + * @auto_neg: is Autoneg enabled. + * @timeout_retransmission_cnt: timeout retransmission events + * @high_ber_cnt: high ber events + */ +struct gaudi_nic_status { + __u32 port; + __u32 bad_format_cnt; + __u32 responder_out_of_sequence_psn_cnt; + __u32 high_ber_reinit; + __u32 correctable_err_cnt; + __u32 uncorrectable_err_cnt; + __u32 retraining_cnt; + __u8 up; + __u8 pcs_link; + __u8 phy_ready; + __u8 auto_neg; + __u32 timeout_retransmission_cnt; + __u32 high_ber_cnt; +}; + +struct gaudi_flops_2_data { + union { + struct { + __u32 spsram_init_done : 1; + __u32 reserved : 31; + }; + __u32 data; + }; +}; + #define GAUDI_PLL_FREQ_LOW 200000000 /* 200 MHz */ #endif /* GAUDI_FW_IF_H */ diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h b/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h index b53aeda9a982..9aea7e996654 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h @@ -66,7 +66,8 @@ #define PCI_DMA_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK (\ (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_PQF_STOP_ON_ERR_MASK, 0xF)) | \ (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_CQF_STOP_ON_ERR_MASK, 0xF)) | \ - (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0xF))) + (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0xF)) | \ + (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_ARB_STOP_ON_ERR_MASK, 0x1))) #define HBM_DMA_QMAN_GLBL_ERR_CFG_MSG_EN_MASK (\ (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_PQF_ERR_MSG_EN_MASK, 0xF)) | \ @@ -76,7 +77,8 @@ #define HBM_DMA_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK (\ (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_PQF_STOP_ON_ERR_MASK, 0xF)) | \ (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_CQF_STOP_ON_ERR_MASK, 0x1F)) | \ - (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0x1F))) + (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0x1F)) | \ + (FIELD_PREP(DMA0_QM_GLBL_ERR_CFG_ARB_STOP_ON_ERR_MASK, 0x1))) #define TPC_QMAN_GLBL_ERR_CFG_MSG_EN_MASK (\ (FIELD_PREP(TPC0_QM_GLBL_ERR_CFG_PQF_ERR_MSG_EN_MASK, 0xF)) | \ @@ -86,7 +88,8 @@ #define TPC_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK (\ (FIELD_PREP(TPC0_QM_GLBL_ERR_CFG_PQF_STOP_ON_ERR_MASK, 0xF)) | \ (FIELD_PREP(TPC0_QM_GLBL_ERR_CFG_CQF_STOP_ON_ERR_MASK, 0x1F)) | \ - (FIELD_PREP(TPC0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0x1F))) + (FIELD_PREP(TPC0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0x1F)) | \ + (FIELD_PREP(TPC0_QM_GLBL_ERR_CFG_ARB_STOP_ON_ERR_MASK, 0x1))) #define MME_QMAN_GLBL_ERR_CFG_MSG_EN_MASK (\ (FIELD_PREP(MME0_QM_GLBL_ERR_CFG_PQF_ERR_MSG_EN_MASK, 0xF)) | \ @@ -96,7 +99,8 @@ #define MME_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK (\ (FIELD_PREP(MME0_QM_GLBL_ERR_CFG_PQF_STOP_ON_ERR_MASK, 0xF)) | \ (FIELD_PREP(MME0_QM_GLBL_ERR_CFG_CQF_STOP_ON_ERR_MASK, 0x1F)) | \ - (FIELD_PREP(MME0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0x1F))) + (FIELD_PREP(MME0_QM_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0x1F)) | \ + (FIELD_PREP(MME0_QM_GLBL_ERR_CFG_ARB_STOP_ON_ERR_MASK, 0x1))) #define NIC_QMAN_GLBL_ERR_CFG_MSG_EN_MASK (\ (FIELD_PREP(NIC0_QM0_GLBL_ERR_CFG_PQF_ERR_MSG_EN_MASK, 0xF)) | \ @@ -106,7 +110,8 @@ #define NIC_QMAN_GLBL_ERR_CFG_STOP_ON_ERR_EN_MASK (\ (FIELD_PREP(NIC0_QM0_GLBL_ERR_CFG_PQF_STOP_ON_ERR_MASK, 0xF)) | \ (FIELD_PREP(NIC0_QM0_GLBL_ERR_CFG_CQF_STOP_ON_ERR_MASK, 0xF)) | \ - (FIELD_PREP(NIC0_QM0_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0xF))) + (FIELD_PREP(NIC0_QM0_GLBL_ERR_CFG_CP_STOP_ON_ERR_MASK, 0xF)) | \ + (FIELD_PREP(NIC0_QM0_GLBL_ERR_CFG_ARB_STOP_ON_ERR_MASK, 0x1))) #define QMAN_CGM1_PWR_GATE_EN (FIELD_PREP(DMA0_QM_CGM_CFG1_MASK_TH_MASK, 0xA)) diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h b/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h index 137afedf5f15..d95d4162ae2c 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_reg_map.h @@ -12,6 +12,16 @@ * PSOC scratch-pad registers */ #define mmHW_STATE mmPSOC_GLOBAL_CONF_SCRATCHPAD_0 +/* TODO: remove mmGIC_HOST_IRQ_CTRL_POLL_REG */ +#define mmGIC_HOST_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_1 +#define mmGIC_HOST_PI_UPD_IRQ_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_1 +#define mmGIC_TPC_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_2 +#define mmGIC_MME_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_3 +#define mmGIC_DMA_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_4 +#define mmGIC_NIC_QM_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_5 +#define mmGIC_DMA_CR_IRQ_CTRL_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_6 +#define mmGIC_HOST_HALT_IRQ_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_7 +#define mmGIC_HOST_INTS_IRQ_POLL_REG mmPSOC_GLOBAL_CONF_SCRATCHPAD_8 #define mmCPU_BOOT_DEV_STS0 mmPSOC_GLOBAL_CONF_SCRATCHPAD_20 #define mmCPU_BOOT_DEV_STS1 mmPSOC_GLOBAL_CONF_SCRATCHPAD_21 #define mmFUSE_VER_OFFSET mmPSOC_GLOBAL_CONF_SCRATCHPAD_22 diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index fea3ae9d8686..8d00df9243c4 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -693,6 +693,8 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) { int bar; unsigned long off; + u8 pci_rev_id; + int rc; /* map the memory mapped i/o registers */ hw->mmio_vaddr = pci_iomap(pdev, 1, 0); @@ -702,7 +704,13 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) } /* map the adapter shared memory region */ - if (pdev->subsystem_device == 0x00E4) { + rc = pci_read_config_byte(pdev, PCI_REVISION_ID, &pci_rev_id); + if (rc != 0) { + dev_err(&pdev->dev, "Error reading PCI rev id: %d\n", rc); + goto out; + } + + if (pci_rev_id >= PCI_REV_ID_NECHES) { bar = 5; /* Last 8k is reserved for CCBs */ off = pci_resource_len(pdev, bar) - 0x2000; diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index f69ff645cac9..d57c34680b09 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h @@ -10,6 +10,9 @@ #define ILO_NAME "hpilo" +/* iLO ASIC PCI revision id */ +#define PCI_REV_ID_NECHES 7 + /* max number of open channel control blocks per device, hw limited to 32 */ #define MAX_CCB 24 /* min number of open channel control blocks per device, hw limited to 32 */ diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 4edad6c445d3..dc8a06c06c63 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -111,7 +111,7 @@ static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) result = ibmasm_init_remote_input_dev(sp); if (result) { dev_err(sp->dev, "Failed to initialize remote queue\n"); - goto error_send_message; + goto error_init_remote; } result = ibmasm_send_driver_vpd(sp); @@ -131,8 +131,9 @@ static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return 0; error_send_message: - disable_sp_interrupts(sp->base_address); ibmasm_free_remote_input_dev(sp); +error_init_remote: + disable_sp_interrupts(sp->base_address); free_irq(sp->irq, (void *)sp); error_request_irq: iounmap(sp->base_address); diff --git a/drivers/misc/ibmasm/remote.h b/drivers/misc/ibmasm/remote.h index 8d364462aeea..ec4e78ec5a67 100644 --- a/drivers/misc/ibmasm/remote.h +++ b/drivers/misc/ibmasm/remote.h @@ -43,7 +43,7 @@ #define REMOTE_BUTTON_MIDDLE 0x02 #define REMOTE_BUTTON_RIGHT 0x04 -/* size of keysym/keycode translation matricies */ +/* size of keysym/keycode translation matrices */ #define XLATE_SIZE 256 struct mouse_input { diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index a164896dc6d4..88c218a9f8b3 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -161,6 +161,9 @@ void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) if (*p == 0) val = 0x87654321; *p = val; + + if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) + pr_err("XFAIL: arch has CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS\n"); } void lkdtm_SOFTLOCKUP(void) @@ -300,8 +303,10 @@ void lkdtm_CORRUPT_LIST_ADD(void) if (target[0] == NULL && target[1] == NULL) pr_err("Overwrite did not happen, but no BUG?!\n"); - else + else { pr_err("list_add() corruption not detected!\n"); + pr_expected_config(CONFIG_DEBUG_LIST); + } } void lkdtm_CORRUPT_LIST_DEL(void) @@ -325,8 +330,10 @@ void lkdtm_CORRUPT_LIST_DEL(void) if (target[0] == NULL && target[1] == NULL) pr_err("Overwrite did not happen, but no BUG?!\n"); - else + else { pr_err("list_del() corruption not detected!\n"); + pr_expected_config(CONFIG_DEBUG_LIST); + } } /* Test that VMAP_STACK is actually allocating with a leading guard page */ diff --git a/drivers/misc/lkdtm/cfi.c b/drivers/misc/lkdtm/cfi.c index e73ebdbfa806..c9aeddef1044 100644 --- a/drivers/misc/lkdtm/cfi.c +++ b/drivers/misc/lkdtm/cfi.c @@ -38,5 +38,6 @@ void lkdtm_CFI_FORWARD_PROTO(void) func = (void *)lkdtm_increment_int; func(&called_count); - pr_info("Fail: survived mismatched prototype function call!\n"); + pr_err("FAIL: survived mismatched prototype function call!\n"); + pr_expected_config(CONFIG_CFI_CLANG); } diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 8024b6a5cc7f..9dda87c6b54a 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -26,6 +26,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/debugfs.h> +#include <linux/init.h> #define DEFAULT_COUNT 10 @@ -120,11 +121,14 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(FORTIFY_OBJECT), CRASHTYPE(FORTIFY_SUBOBJECT), - CRASHTYPE(OVERWRITE_ALLOCATION), + CRASHTYPE(SLAB_LINEAR_OVERFLOW), + CRASHTYPE(VMALLOC_LINEAR_OVERFLOW), CRASHTYPE(WRITE_AFTER_FREE), CRASHTYPE(READ_AFTER_FREE), CRASHTYPE(WRITE_BUDDY_AFTER_FREE), CRASHTYPE(READ_BUDDY_AFTER_FREE), + CRASHTYPE(SLAB_INIT_ON_ALLOC), + CRASHTYPE(BUDDY_INIT_ON_ALLOC), CRASHTYPE(SLAB_FREE_DOUBLE), CRASHTYPE(SLAB_FREE_CROSS), CRASHTYPE(SLAB_FREE_PAGE), @@ -177,9 +181,7 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(STACKLEAK_ERASING), CRASHTYPE(CFI_FORWARD_PROTO), CRASHTYPE(FORTIFIED_STRSCPY), -#ifdef CONFIG_X86_32 CRASHTYPE(DOUBLE_FAULT), -#endif #ifdef CONFIG_PPC_BOOK3S_64 CRASHTYPE(PPC_SLB_MULTIHIT), #endif @@ -399,6 +401,56 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf, return count; } +#ifndef MODULE +/* + * To avoid needing to export parse_args(), just don't use this code + * when LKDTM is built as a module. + */ +struct check_cmdline_args { + const char *param; + int value; +}; + +static int lkdtm_parse_one(char *param, char *val, + const char *unused, void *arg) +{ + struct check_cmdline_args *args = arg; + + /* short circuit if we already found a value. */ + if (args->value != -ESRCH) + return 0; + if (strncmp(param, args->param, strlen(args->param)) == 0) { + bool bool_result; + int ret; + + ret = kstrtobool(val, &bool_result); + if (ret == 0) + args->value = bool_result; + } + return 0; +} + +int lkdtm_check_bool_cmdline(const char *param) +{ + char *command_line; + struct check_cmdline_args args = { + .param = param, + .value = -ESRCH, + }; + + command_line = kstrdup(saved_command_line, GFP_KERNEL); + if (!command_line) + return -ENOMEM; + + parse_args("Setting sysctl args", command_line, + NULL, 0, -1, -1, &args, lkdtm_parse_one); + + kfree(command_line); + + return args.value; +} +#endif + static struct dentry *lkdtm_debugfs_root; static int __init lkdtm_module_init(void) diff --git a/drivers/misc/lkdtm/fortify.c b/drivers/misc/lkdtm/fortify.c index faf29cf04baa..0f51d31b57ca 100644 --- a/drivers/misc/lkdtm/fortify.c +++ b/drivers/misc/lkdtm/fortify.c @@ -76,7 +76,8 @@ void lkdtm_FORTIFIED_STRSCPY(void) */ strscpy(dst, src, strlen(src)); - pr_warn("FAIL: No overflow in above strscpy()\n"); + pr_err("FAIL: strscpy() overflow not detected!\n"); + pr_expected_config(CONFIG_FORTIFY_SOURCE); kfree(src); } diff --git a/drivers/misc/lkdtm/heap.c b/drivers/misc/lkdtm/heap.c index 1323bc16f113..3d9aae5821a0 100644 --- a/drivers/misc/lkdtm/heap.c +++ b/drivers/misc/lkdtm/heap.c @@ -5,6 +5,7 @@ */ #include "lkdtm.h" #include <linux/slab.h> +#include <linux/vmalloc.h> #include <linux/sched.h> static struct kmem_cache *double_free_cache; @@ -12,17 +13,36 @@ static struct kmem_cache *a_cache; static struct kmem_cache *b_cache; /* + * If there aren't guard pages, it's likely that a consecutive allocation will + * let us overflow into the second allocation without overwriting something real. + */ +void lkdtm_VMALLOC_LINEAR_OVERFLOW(void) +{ + char *one, *two; + + one = vzalloc(PAGE_SIZE); + two = vzalloc(PAGE_SIZE); + + pr_info("Attempting vmalloc linear overflow ...\n"); + memset(one, 0xAA, PAGE_SIZE + 1); + + vfree(two); + vfree(one); +} + +/* * This tries to stay within the next largest power-of-2 kmalloc cache * to avoid actually overwriting anything important if it's not detected * correctly. */ -void lkdtm_OVERWRITE_ALLOCATION(void) +void lkdtm_SLAB_LINEAR_OVERFLOW(void) { size_t len = 1020; u32 *data = kmalloc(len, GFP_KERNEL); if (!data) return; + pr_info("Attempting slab linear overflow ...\n"); data[1024 / sizeof(u32)] = 0x12345678; kfree(data); } @@ -89,9 +109,10 @@ void lkdtm_READ_AFTER_FREE(void) if (saw != *val) { /* Good! Poisoning happened, so declare a win. */ pr_info("Memory correctly poisoned (%x)\n", saw); - BUG(); + } else { + pr_err("FAIL: Memory was not poisoned!\n"); + pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); } - pr_info("Memory was not poisoned\n"); kfree(val); } @@ -145,13 +166,79 @@ void lkdtm_READ_BUDDY_AFTER_FREE(void) if (saw != *val) { /* Good! Poisoning happened, so declare a win. */ pr_info("Memory correctly poisoned (%x)\n", saw); - BUG(); + } else { + pr_err("FAIL: Buddy page was not poisoned!\n"); + pr_expected_config_param(CONFIG_INIT_ON_FREE_DEFAULT_ON, "init_on_free"); + } + + kfree(val); +} + +void lkdtm_SLAB_INIT_ON_ALLOC(void) +{ + u8 *first; + u8 *val; + + first = kmalloc(512, GFP_KERNEL); + if (!first) { + pr_info("Unable to allocate 512 bytes the first time.\n"); + return; + } + + memset(first, 0xAB, 512); + kfree(first); + + val = kmalloc(512, GFP_KERNEL); + if (!val) { + pr_info("Unable to allocate 512 bytes the second time.\n"); + return; + } + if (val != first) { + pr_warn("Reallocation missed clobbered memory.\n"); } - pr_info("Buddy page was not poisoned\n"); + if (memchr(val, 0xAB, 512) == NULL) { + pr_info("Memory appears initialized (%x, no earlier values)\n", *val); + } else { + pr_err("FAIL: Slab was not initialized\n"); + pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc"); + } kfree(val); } +void lkdtm_BUDDY_INIT_ON_ALLOC(void) +{ + u8 *first; + u8 *val; + + first = (u8 *)__get_free_page(GFP_KERNEL); + if (!first) { + pr_info("Unable to allocate first free page\n"); + return; + } + + memset(first, 0xAB, PAGE_SIZE); + free_page((unsigned long)first); + + val = (u8 *)__get_free_page(GFP_KERNEL); + if (!val) { + pr_info("Unable to allocate second free page\n"); + return; + } + + if (val != first) { + pr_warn("Reallocation missed clobbered memory.\n"); + } + + if (memchr(val, 0xAB, PAGE_SIZE) == NULL) { + pr_info("Memory appears initialized (%x, no earlier values)\n", *val); + } else { + pr_err("FAIL: Slab was not initialized\n"); + pr_expected_config_param(CONFIG_INIT_ON_ALLOC_DEFAULT_ON, "init_on_alloc"); + } + free_page((unsigned long)val); +} + void lkdtm_SLAB_FREE_DOUBLE(void) { int *val; diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index 99f90d3e5e9c..6a30b60519f3 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -6,6 +6,47 @@ #include <linux/kernel.h> +#define pr_expected_config(kconfig) \ +{ \ + if (IS_ENABLED(kconfig)) \ + pr_err("Unexpected! This kernel was built with " #kconfig "=y\n"); \ + else \ + pr_warn("This is probably expected, since this kernel was built *without* " #kconfig "=y\n"); \ +} + +#ifndef MODULE +int lkdtm_check_bool_cmdline(const char *param); +#define pr_expected_config_param(kconfig, param) \ +{ \ + if (IS_ENABLED(kconfig)) { \ + switch (lkdtm_check_bool_cmdline(param)) { \ + case 0: \ + pr_warn("This is probably expected, since this kernel was built with " #kconfig "=y but booted with '" param "=N'\n"); \ + break; \ + case 1: \ + pr_err("Unexpected! This kernel was built with " #kconfig "=y and booted with '" param "=Y'\n"); \ + break; \ + default: \ + pr_err("Unexpected! This kernel was built with " #kconfig "=y (and booted without '" param "' specified)\n"); \ + } \ + } else { \ + switch (lkdtm_check_bool_cmdline(param)) { \ + case 0: \ + pr_warn("This is probably expected, as kernel was built *without* " #kconfig "=y and booted with '" param "=N'\n"); \ + break; \ + case 1: \ + pr_err("Unexpected! This kernel was built *without* " #kconfig "=y but booted with '" param "=Y'\n"); \ + break; \ + default: \ + pr_err("This is probably expected, since this kernel was built *without* " #kconfig "=y (and booted without '" param "' specified)\n"); \ + break; \ + } \ + } \ +} +#else +#define pr_expected_config_param(kconfig, param) pr_expected_config(kconfig) +#endif + /* bugs.c */ void __init lkdtm_bugs_init(int *recur_param); void lkdtm_PANIC(void); @@ -39,11 +80,14 @@ void lkdtm_FORTIFY_SUBOBJECT(void); /* heap.c */ void __init lkdtm_heap_init(void); void __exit lkdtm_heap_exit(void); -void lkdtm_OVERWRITE_ALLOCATION(void); +void lkdtm_VMALLOC_LINEAR_OVERFLOW(void); +void lkdtm_SLAB_LINEAR_OVERFLOW(void); void lkdtm_WRITE_AFTER_FREE(void); void lkdtm_READ_AFTER_FREE(void); void lkdtm_WRITE_BUDDY_AFTER_FREE(void); void lkdtm_READ_BUDDY_AFTER_FREE(void); +void lkdtm_SLAB_INIT_ON_ALLOC(void); +void lkdtm_BUDDY_INIT_ON_ALLOC(void); void lkdtm_SLAB_FREE_DOUBLE(void); void lkdtm_SLAB_FREE_CROSS(void); void lkdtm_SLAB_FREE_PAGE(void); diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/stackleak.c index d1a5c0705be3..00db21ff115e 100644 --- a/drivers/misc/lkdtm/stackleak.c +++ b/drivers/misc/lkdtm/stackleak.c @@ -74,8 +74,8 @@ void lkdtm_STACKLEAK_ERASING(void) end: if (test_failed) { - pr_err("FAIL: the thread stack is NOT properly erased\n"); - dump_stack(); + pr_err("FAIL: the thread stack is NOT properly erased!\n"); + pr_expected_config(CONFIG_GCC_PLUGIN_STACKLEAK); } else { pr_info("OK: the rest of the thread stack is properly erased\n"); } diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c index 15d220ef35a5..9161ce7ed47a 100644 --- a/drivers/misc/lkdtm/usercopy.c +++ b/drivers/misc/lkdtm/usercopy.c @@ -173,6 +173,8 @@ static void do_usercopy_heap_size(bool to_user) goto free_user; } } + pr_err("FAIL: bad usercopy not detected!\n"); + pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy"); free_user: vm_munmap(user_addr, PAGE_SIZE); @@ -248,6 +250,8 @@ static void do_usercopy_heap_whitelist(bool to_user) goto free_user; } } + pr_err("FAIL: bad usercopy not detected!\n"); + pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy"); free_user: vm_munmap(user_alloc, PAGE_SIZE); @@ -319,7 +323,8 @@ void lkdtm_USERCOPY_KERNEL(void) pr_warn("copy_to_user failed, but lacked Oops\n"); goto free_user; } - pr_err("FAIL: survived bad copy_to_user()\n"); + pr_err("FAIL: bad copy_to_user() not detected!\n"); + pr_expected_config_param(CONFIG_HARDENED_USERCOPY, "hardened_usercopy"); free_user: vm_munmap(user_addr, PAGE_SIZE); diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index d8e760b11ae3..67844089db21 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -498,7 +498,7 @@ static struct mei_fixup { }; /** - * mei_cldev_fixup - run fixup handlers + * mei_cl_bus_dev_fixup - run fixup handlers * * @cldev: me client device */ diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 2cc370adb238..96f4e59c32a5 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -326,7 +326,7 @@ void mei_io_cb_free(struct mei_cl_cb *cb) } /** - * mei_tx_cb_queue - queue tx callback + * mei_tx_cb_enqueue - queue tx callback * * Locking: called under "dev->device_lock" lock * @@ -1726,12 +1726,15 @@ nortpm: return rets; } -static inline u8 mei_ext_hdr_set_vtag(struct mei_ext_hdr *ext, u8 vtag) +static inline u8 mei_ext_hdr_set_vtag(void *ext, u8 vtag) { - ext->type = MEI_EXT_HDR_VTAG; - ext->ext_payload[0] = vtag; - ext->length = mei_data2slots(sizeof(*ext)); - return ext->length; + struct mei_ext_hdr_vtag *vtag_hdr = ext; + + vtag_hdr->hdr.type = MEI_EXT_HDR_VTAG; + vtag_hdr->hdr.length = mei_data2slots(sizeof(*vtag_hdr)); + vtag_hdr->vtag = vtag; + vtag_hdr->reserved = 0; + return vtag_hdr->hdr.length; } /** @@ -1745,7 +1748,6 @@ static struct mei_msg_hdr *mei_msg_hdr_init(const struct mei_cl_cb *cb) { size_t hdr_len; struct mei_ext_meta_hdr *meta; - struct mei_ext_hdr *ext; struct mei_msg_hdr *mei_hdr; bool is_ext, is_vtag; @@ -1764,7 +1766,7 @@ static struct mei_msg_hdr *mei_msg_hdr_init(const struct mei_cl_cb *cb) hdr_len += sizeof(*meta); if (is_vtag) - hdr_len += sizeof(*ext); + hdr_len += sizeof(struct mei_ext_hdr_vtag); setup_hdr: mei_hdr = kzalloc(hdr_len, GFP_KERNEL); @@ -2250,7 +2252,7 @@ static void mei_cl_dma_free(struct mei_cl *cl) } /** - * mei_cl_alloc_and_map - send client dma map request + * mei_cl_dma_alloc_and_map - send client dma map request * * @cl: host client * @fp: pointer to file structure @@ -2349,7 +2351,7 @@ out: } /** - * mei_cl_unmap_and_free - send client dma unmap request + * mei_cl_dma_unmap - send client dma unmap request * * @cl: host client * @fp: pointer to file structure diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index d0277c7fed10..99b5c1ecc444 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -853,7 +853,7 @@ out: } /** - * mei_hbm_cl_flow_control_res - flow control response from me + * mei_hbm_cl_tx_flow_ctrl_creds_res - flow control response from me * * @dev: the device structure * @fctrl: flow control response bus message diff --git a/drivers/misc/mei/hdcp/Kconfig b/drivers/misc/mei/hdcp/Kconfig index 95b2d6d37f10..54e1c9526909 100644 --- a/drivers/misc/mei/hdcp/Kconfig +++ b/drivers/misc/mei/hdcp/Kconfig @@ -1,4 +1,3 @@ - # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2019, Intel Corporation. All rights reserved. # diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index cda0829ac589..d3a6c0728645 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1380,7 +1380,7 @@ static bool mei_me_fw_type_nm(const struct pci_dev *pdev) .quirk_probe = mei_me_fw_type_nm /** - * mei_me_fw_sku_sps_4() - check for sps 4.0 sku + * mei_me_fw_type_sps_4() - check for sps 4.0 sku * * Read ME FW Status register to check for SPS Firmware. * The SPS FW is only signaled in the PCI function 0. @@ -1405,7 +1405,7 @@ static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev) .quirk_probe = mei_me_fw_type_sps_4 /** - * mei_me_fw_sku_sps() - check for sps sku + * mei_me_fw_type_sps() - check for sps sku * * Read ME FW Status register to check for SPS Firmware. * The SPS FW is only signaled in pci function 0 diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index b10606550613..dfd60c916da0 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -235,9 +235,8 @@ enum mei_ext_hdr_type { struct mei_ext_hdr { u8 type; u8 length; - u8 ext_payload[2]; - u8 hdr[]; -}; + u8 data[]; +} __packed; /** * struct mei_ext_meta_hdr - extend header meta data @@ -250,8 +249,21 @@ struct mei_ext_meta_hdr { u8 count; u8 size; u8 reserved[2]; - struct mei_ext_hdr hdrs[]; -}; + u8 hdrs[]; +} __packed; + +/** + * struct mei_ext_hdr_vtag - extend header for vtag + * + * @hdr: standard extend header + * @vtag: virtual tag + * @reserved: reserved + */ +struct mei_ext_hdr_vtag { + struct mei_ext_hdr hdr; + u8 vtag; + u8 reserved; +} __packed; /* * Extended header iterator functions @@ -266,7 +278,7 @@ struct mei_ext_meta_hdr { */ static inline struct mei_ext_hdr *mei_ext_begin(struct mei_ext_meta_hdr *meta) { - return meta->hdrs; + return (struct mei_ext_hdr *)meta->hdrs; } /** @@ -284,7 +296,7 @@ static inline bool mei_ext_last(struct mei_ext_meta_hdr *meta, } /** - *mei_ext_next - following extended header on the TLV list + * mei_ext_next - following extended header on the TLV list * * @ext: current extend header * @@ -295,7 +307,7 @@ static inline bool mei_ext_last(struct mei_ext_meta_hdr *meta, */ static inline struct mei_ext_hdr *mei_ext_next(struct mei_ext_hdr *ext) { - return (struct mei_ext_hdr *)(ext->hdr + (ext->length * 4)); + return (struct mei_ext_hdr *)((u8 *)ext + (ext->length * 4)); } /** diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index aab3ebfa9fc4..a67f4f2d33a9 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -123,13 +123,13 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl, if (mei_hdr->extended) { struct mei_ext_hdr *ext; - struct mei_ext_hdr *vtag = NULL; + struct mei_ext_hdr_vtag *vtag_hdr = NULL; ext = mei_ext_begin(meta); do { switch (ext->type) { case MEI_EXT_HDR_VTAG: - vtag = ext; + vtag_hdr = (struct mei_ext_hdr_vtag *)ext; break; case MEI_EXT_HDR_NONE: fallthrough; @@ -141,20 +141,20 @@ static int mei_cl_irq_read_msg(struct mei_cl *cl, ext = mei_ext_next(ext); } while (!mei_ext_last(meta, ext)); - if (!vtag) { + if (!vtag_hdr) { cl_dbg(dev, cl, "vtag not found in extended header.\n"); cb->status = -EPROTO; goto discard; } - cl_dbg(dev, cl, "vtag: %d\n", vtag->ext_payload[0]); - if (cb->vtag && cb->vtag != vtag->ext_payload[0]) { + cl_dbg(dev, cl, "vtag: %d\n", vtag_hdr->vtag); + if (cb->vtag && cb->vtag != vtag_hdr->vtag) { cl_err(dev, cl, "mismatched tag: %d != %d\n", - cb->vtag, vtag->ext_payload[0]); + cb->vtag, vtag_hdr->vtag); cb->status = -EPROTO; goto discard; } - cb->vtag = vtag->ext_payload[0]; + cb->vtag = vtag_hdr->vtag; } if (!mei_cl_is_connected(cl)) { @@ -331,7 +331,6 @@ int mei_irq_read_handler(struct mei_device *dev, struct mei_ext_meta_hdr *meta_hdr = NULL; struct mei_cl *cl; int ret; - u32 ext_meta_hdr_u32; u32 hdr_size_left; u32 hdr_size_ext; int i; @@ -367,14 +366,12 @@ int mei_irq_read_handler(struct mei_device *dev, if (mei_hdr->extended) { if (!dev->rd_msg_hdr[1]) { - ext_meta_hdr_u32 = mei_read_hdr(dev); - dev->rd_msg_hdr[1] = ext_meta_hdr_u32; + dev->rd_msg_hdr[1] = mei_read_hdr(dev); dev->rd_msg_hdr_count++; (*slots)--; - dev_dbg(dev->dev, "extended header is %08x\n", - ext_meta_hdr_u32); + dev_dbg(dev->dev, "extended header is %08x\n", dev->rd_msg_hdr[1]); } - meta_hdr = ((struct mei_ext_meta_hdr *)dev->rd_msg_hdr + 1); + meta_hdr = ((struct mei_ext_meta_hdr *)&dev->rd_msg_hdr[1]); if (check_add_overflow((u32)sizeof(*meta_hdr), mei_slots2data(meta_hdr->size), &hdr_size_ext)) { diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 28937b6e7e0c..786f7c8f7f61 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -50,8 +50,6 @@ static int mei_open(struct inode *inode, struct file *file) int err; dev = container_of(inode->i_cdev, struct mei_device, cdev); - if (!dev) - return -ENODEV; mutex_lock(&dev->device_lock); @@ -1104,7 +1102,7 @@ static ssize_t dev_state_show(struct device *device, static DEVICE_ATTR_RO(dev_state); /** - * dev_set_devstate: set to new device state and notify sysfs file. + * mei_set_devstate: set to new device state and notify sysfs file. * * @dev: mei_device * @state: new device state diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 4bf26ce61044..aec0483b8e72 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -156,7 +156,7 @@ end: } /** - * mei_txe_remove - Device Shutdown Routine + * mei_txe_shutdown- Device Shutdown Routine * * @pdev: PCI device structure * diff --git a/drivers/misc/pvpanic/pvpanic-mmio.c b/drivers/misc/pvpanic/pvpanic-mmio.c index 4c0841776087..be4016084979 100644 --- a/drivers/misc/pvpanic/pvpanic-mmio.c +++ b/drivers/misc/pvpanic/pvpanic-mmio.c @@ -93,7 +93,7 @@ static int pvpanic_mmio_probe(struct platform_device *pdev) return -EINVAL; } - pi = kmalloc(sizeof(*pi), GFP_ATOMIC); + pi = devm_kmalloc(dev, sizeof(*pi), GFP_KERNEL); if (!pi) return -ENOMEM; @@ -104,19 +104,7 @@ static int pvpanic_mmio_probe(struct platform_device *pdev) pi->capability &= ioread8(base); pi->events = pi->capability; - dev_set_drvdata(dev, pi); - - return pvpanic_probe(pi); -} - -static int pvpanic_mmio_remove(struct platform_device *pdev) -{ - struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev); - - pvpanic_remove(pi); - kfree(pi); - - return 0; + return devm_pvpanic_probe(dev, pi); } static const struct of_device_id pvpanic_mmio_match[] = { @@ -139,6 +127,5 @@ static struct platform_driver pvpanic_mmio_driver = { .dev_groups = pvpanic_mmio_dev_groups, }, .probe = pvpanic_mmio_probe, - .remove = pvpanic_mmio_remove, }; module_platform_driver(pvpanic_mmio_driver); diff --git a/drivers/misc/pvpanic/pvpanic-pci.c b/drivers/misc/pvpanic/pvpanic-pci.c index 9ecc4e8559d5..a43c401017ae 100644 --- a/drivers/misc/pvpanic/pvpanic-pci.c +++ b/drivers/misc/pvpanic/pvpanic-pci.c @@ -73,20 +73,19 @@ ATTRIBUTE_GROUPS(pvpanic_pci_dev); static int pvpanic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - struct device *dev = &pdev->dev; struct pvpanic_instance *pi; void __iomem *base; int ret; - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret < 0) return ret; - base = pci_iomap(pdev, 0, 0); + base = pcim_iomap(pdev, 0, 0); if (!base) return -ENOMEM; - pi = kmalloc(sizeof(*pi), GFP_ATOMIC); + pi = devm_kmalloc(&pdev->dev, sizeof(*pi), GFP_KERNEL); if (!pi) return -ENOMEM; @@ -97,26 +96,13 @@ static int pvpanic_pci_probe(struct pci_dev *pdev, pi->capability &= ioread8(base); pi->events = pi->capability; - dev_set_drvdata(dev, pi); - - return pvpanic_probe(pi); -} - -static void pvpanic_pci_remove(struct pci_dev *pdev) -{ - struct pvpanic_instance *pi = dev_get_drvdata(&pdev->dev); - - pvpanic_remove(pi); - iounmap(pi->base); - kfree(pi); - pci_disable_device(pdev); + return devm_pvpanic_probe(&pdev->dev, pi); } static struct pci_driver pvpanic_pci_driver = { .name = "pvpanic-pci", .id_table = pvpanic_pci_id_tbl, .probe = pvpanic_pci_probe, - .remove = pvpanic_pci_remove, .driver = { .dev_groups = pvpanic_pci_dev_groups, }, diff --git a/drivers/misc/pvpanic/pvpanic.c b/drivers/misc/pvpanic/pvpanic.c index 793ea0c01193..02b807c788c9 100644 --- a/drivers/misc/pvpanic/pvpanic.c +++ b/drivers/misc/pvpanic/pvpanic.c @@ -61,25 +61,10 @@ static struct notifier_block pvpanic_panic_nb = { .priority = 1, /* let this called before broken drm_fb_helper */ }; -int pvpanic_probe(struct pvpanic_instance *pi) -{ - if (!pi || !pi->base) - return -EINVAL; - - spin_lock(&pvpanic_lock); - list_add(&pi->list, &pvpanic_list); - spin_unlock(&pvpanic_lock); - - return 0; -} -EXPORT_SYMBOL_GPL(pvpanic_probe); - -void pvpanic_remove(struct pvpanic_instance *pi) +static void pvpanic_remove(void *param) { struct pvpanic_instance *pi_cur, *pi_next; - - if (!pi) - return; + struct pvpanic_instance *pi = param; spin_lock(&pvpanic_lock); list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) { @@ -90,7 +75,19 @@ void pvpanic_remove(struct pvpanic_instance *pi) } spin_unlock(&pvpanic_lock); } -EXPORT_SYMBOL_GPL(pvpanic_remove); + +int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi) +{ + if (!pi || !pi->base) + return -EINVAL; + + spin_lock(&pvpanic_lock); + list_add(&pi->list, &pvpanic_list); + spin_unlock(&pvpanic_lock); + + return devm_add_action_or_reset(dev, pvpanic_remove, pi); +} +EXPORT_SYMBOL_GPL(devm_pvpanic_probe); static int pvpanic_init(void) { diff --git a/drivers/misc/pvpanic/pvpanic.h b/drivers/misc/pvpanic/pvpanic.h index 1afccc2e9fec..493545951754 100644 --- a/drivers/misc/pvpanic/pvpanic.h +++ b/drivers/misc/pvpanic/pvpanic.h @@ -15,7 +15,6 @@ struct pvpanic_instance { struct list_head list; }; -int pvpanic_probe(struct pvpanic_instance *pi); -void pvpanic_remove(struct pvpanic_instance *pi); +int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi); #endif /* PVPANIC_H_ */ diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index bae18ef03dcb..488eeb2811ae 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -387,15 +387,22 @@ static void uacce_release(struct device *dev) static unsigned int uacce_enable_sva(struct device *parent, unsigned int flags) { + int ret; + if (!(flags & UACCE_DEV_SVA)) return flags; flags &= ~UACCE_DEV_SVA; - if (iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_IOPF)) + ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_IOPF); + if (ret) { + dev_err(parent, "failed to enable IOPF feature! ret = %pe\n", ERR_PTR(ret)); return flags; + } - if (iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA)) { + ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); + if (ret) { + dev_err(parent, "failed to enable SVA feature! ret = %pe\n", ERR_PTR(ret)); iommu_dev_disable_feature(parent, IOMMU_DEV_FEAT_IOPF); return flags; } diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c index 26ff49fdf0f7..c0b5e339d5a1 100644 --- a/drivers/misc/vmw_vmci/vmci_context.c +++ b/drivers/misc/vmw_vmci/vmci_context.c @@ -107,7 +107,7 @@ struct vmci_ctx *vmci_ctx_create(u32 cid, u32 priv_flags, context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) { pr_warn("Failed to allocate memory for VMCI context\n"); - error = -EINVAL; + error = -ENOMEM; goto err_out; } diff --git a/drivers/misc/xilinx_sdfec.c b/drivers/misc/xilinx_sdfec.c index 23c8448a9c3b..d6e3c650bd11 100644 --- a/drivers/misc/xilinx_sdfec.c +++ b/drivers/misc/xilinx_sdfec.c @@ -1013,9 +1013,6 @@ static __poll_t xsdfec_poll(struct file *file, poll_table *wait) xsdfec = container_of(file->private_data, struct xsdfec_dev, miscdev); - if (!xsdfec) - return EPOLLNVAL | EPOLLHUP; - poll_wait(file, &xsdfec->waitq, wait); /* XSDFEC ISR detected an error */ diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index 01ea0d6f8819..50bd4e3b0af9 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -404,7 +404,7 @@ err_out_free_dev: return err; } -static int vsw_port_remove(struct vio_dev *vdev) +static void vsw_port_remove(struct vio_dev *vdev) { struct vnet_port *port = dev_get_drvdata(&vdev->dev); unsigned long flags; @@ -430,8 +430,6 @@ static int vsw_port_remove(struct vio_dev *vdev) free_netdev(port->dev); } - - return 0; } static void vsw_cleanup(void) diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 96b883f965f6..58ee89223951 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -510,7 +510,7 @@ err_out_put_mdesc: return err; } -static int vnet_port_remove(struct vio_dev *vdev) +static void vnet_port_remove(struct vio_dev *vdev) { struct vnet_port *port = dev_get_drvdata(&vdev->dev); @@ -533,7 +533,6 @@ static int vnet_port_remove(struct vio_dev *vdev) kfree(port); } - return 0; } static const struct vio_device_id vnet_port_match[] = { diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 62d363a399d3..b3bc30a04ed7 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -180,6 +180,7 @@ static const char * const nvmem_type_str[] = { [NVMEM_TYPE_EEPROM] = "EEPROM", [NVMEM_TYPE_OTP] = "OTP", [NVMEM_TYPE_BATTERY_BACKED] = "Battery backed", + [NVMEM_TYPE_FRAM] = "FRAM", }; #ifdef CONFIG_DEBUG_LOCK_ALLOC @@ -359,6 +360,9 @@ static int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, if (!config->base_dev) return -EINVAL; + if (config->type == NVMEM_TYPE_FRAM) + bin_attr_nvmem_eeprom_compat.attr.name = "fram"; + nvmem->eeprom = bin_attr_nvmem_eeprom_compat; nvmem->eeprom.attr.mode = nvmem_bin_attr_get_umode(nvmem); nvmem->eeprom.size = nvmem->size; @@ -686,15 +690,17 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) continue; if (len < 2 * sizeof(u32)) { dev_err(dev, "nvmem: invalid reg on %pOF\n", child); + of_node_put(child); return -EINVAL; } cell = kzalloc(sizeof(*cell), GFP_KERNEL); - if (!cell) + if (!cell) { + of_node_put(child); return -ENOMEM; + } cell->nvmem = nvmem; - cell->np = of_node_get(child); cell->offset = be32_to_cpup(addr++); cell->bytes = be32_to_cpup(addr); cell->name = kasprintf(GFP_KERNEL, "%pOFn", child); @@ -715,11 +721,12 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) cell->name, nvmem->stride); /* Cells already added will be freed later. */ kfree_const(cell->name); - of_node_put(cell->np); kfree(cell); + of_node_put(child); return -EINVAL; } + cell->np = of_node_get(child); nvmem_cell_add(cell); } @@ -1608,9 +1615,9 @@ int nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val) } EXPORT_SYMBOL_GPL(nvmem_cell_read_u64); -static void *nvmem_cell_read_variable_common(struct device *dev, - const char *cell_id, - size_t max_len, size_t *len) +static const void *nvmem_cell_read_variable_common(struct device *dev, + const char *cell_id, + size_t max_len, size_t *len) { struct nvmem_cell *cell; int nbits; @@ -1654,7 +1661,7 @@ int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id, u32 *val) { size_t len; - u8 *buf; + const u8 *buf; int i; buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len); @@ -1685,7 +1692,7 @@ int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id, u64 *val) { size_t len; - u8 *buf; + const u8 *buf; int i; buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len); diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index d6d3f24685a8..81fbad5e939d 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -122,6 +122,7 @@ static const struct qfprom_soc_compatible_data sc7280_qfprom = { .keepout = sc7280_qfprom_keepout, .nkeepout = ARRAY_SIZE(sc7280_qfprom_keepout) }; + /** * qfprom_disable_fuse_blowing() - Undo enabling of fuse blowing. * @priv: Our driver data. @@ -195,9 +196,9 @@ static int qfprom_enable_fuse_blowing(const struct qfprom_priv *priv, } /* - * Hardware requires 1.8V min for fuse blowing; this may be - * a rail shared do don't specify a max--regulator constraints - * will handle. + * Hardware requires a minimum voltage for fuse blowing. + * This may be a shared rail so don't specify a maximum. + * Regulator constraints will cap to the actual maximum. */ ret = regulator_set_voltage(priv->vcc, qfprom_blow_uV, INT_MAX); if (ret) { @@ -399,7 +400,7 @@ static int qfprom_probe(struct platform_device *pdev) if (major_version == 7 && minor_version == 8) priv->soc_data = &qfprom_7_8_data; - if (major_version == 7 && minor_version == 15) + else if (major_version == 7 && minor_version == 15) priv->soc_data = &qfprom_7_15_data; priv->vcc = devm_regulator_get(&pdev->dev, "vcc"); diff --git a/drivers/nvmem/sprd-efuse.c b/drivers/nvmem/sprd-efuse.c index 59523245db8a..4f1fcbfec394 100644 --- a/drivers/nvmem/sprd-efuse.c +++ b/drivers/nvmem/sprd-efuse.c @@ -234,7 +234,7 @@ static int sprd_efuse_raw_prog(struct sprd_efuse *efuse, u32 blk, bool doub, status = readl(efuse->base + SPRD_EFUSE_ERR_FLAG); if (status) { dev_err(efuse->dev, - "write error status %d of block %d\n", ret, blk); + "write error status %u of block %d\n", status, blk); writel(SPRD_EFUSE_ERR_CLR_MASK, efuse->base + SPRD_EFUSE_ERR_CLR); diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index e26ef1bbf198..275b9155e473 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -142,6 +142,7 @@ static int sunxi_sid_probe(struct platform_device *pdev) nvmem_cfg->dev = dev; nvmem_cfg->name = "sunxi-sid"; + nvmem_cfg->type = NVMEM_TYPE_OTP; nvmem_cfg->read_only = true; nvmem_cfg->size = cfg->size; nvmem_cfg->word_size = 1; diff --git a/drivers/parport/probe.c b/drivers/parport/probe.c index 7e6d713fa5ac..5d1b9aacb130 100644 --- a/drivers/parport/probe.c +++ b/drivers/parport/probe.c @@ -8,8 +8,8 @@ #include <linux/module.h> #include <linux/parport.h> -#include <linux/ctype.h> #include <linux/string.h> +#include <linux/string_helpers.h> #include <linux/slab.h> #include <linux/uaccess.h> @@ -74,11 +74,7 @@ static void parse_data(struct parport *port, int device, char *str) u = sep + strlen (sep) - 1; while (u >= p && *u == ' ') *u-- = '\0'; - u = p; - while (*u) { - *u = toupper(*u); - u++; - } + string_upper(p, p); if (!strcmp(p, "MFG") || !strcmp(p, "MANUFACTURER")) { kfree(info->mfr); info->mfr = kstrdup(sep, GFP_KERNEL); @@ -90,8 +86,7 @@ static void parse_data(struct parport *port, int device, char *str) kfree(info->class_name); info->class_name = kstrdup(sep, GFP_KERNEL); - for (u = sep; *u; u++) - *u = toupper(*u); + string_upper(sep, sep); for (i = 0; classes[i].token; i++) { if (!strcmp(classes[i].token, sep)) { info->class = i; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 54c1f2f0985f..7dd35f1b9cc5 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -61,6 +61,15 @@ config USB_LGM_PHY interface to interact with USB GEN-II and USB 3.x PHY that is part of the Intel network SOC. +config PHY_CAN_TRANSCEIVER + tristate "CAN transceiver PHY" + select GENERIC_PHY + help + This option enables support for CAN transceivers as a PHY. This + driver provides function for putting the transceivers in various + functional modes using gpios and sets the attribute max link + rate, for CAN drivers. + source "drivers/phy/allwinner/Kconfig" source "drivers/phy/amlogic/Kconfig" source "drivers/phy/broadcom/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index adac1b1a39d1..01e9efffc726 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o +obj-$(CONFIG_PHY_CAN_TRANSCEIVER) += phy-can-transceiver.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c index eb10ffa13a62..b1adaecc26f8 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb3.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c @@ -215,10 +215,8 @@ static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev) return err; usb3->dmp = devm_ioremap_resource(dev, &res); - if (IS_ERR(usb3->dmp)) { - dev_err(dev, "Failed to map DMP regs\n"); + if (IS_ERR(usb3->dmp)) return PTR_ERR(usb3->dmp); - } usb3->phy = devm_phy_create(dev, NULL, &ops); if (IS_ERR(usb3->phy)) { diff --git a/drivers/phy/marvell/phy-mmp3-hsic.c b/drivers/phy/marvell/phy-mmp3-hsic.c index 47c1e8894939..7cccf01848d8 100644 --- a/drivers/phy/marvell/phy-mmp3-hsic.c +++ b/drivers/phy/marvell/phy-mmp3-hsic.c @@ -47,10 +47,8 @@ static int mmp3_hsic_phy_probe(struct platform_device *pdev) resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(dev, resource); - if (IS_ERR(base)) { - dev_err(dev, "failed to remap PHY regs\n"); + if (IS_ERR(base)) return PTR_ERR(base); - } phy = devm_phy_create(dev, NULL, &mmp3_hsic_phy_ops); if (IS_ERR(phy)) { diff --git a/drivers/phy/mediatek/phy-mtk-hdmi.c b/drivers/phy/mediatek/phy-mtk-hdmi.c index 8313bd517e4c..8ad8f717ef43 100644 --- a/drivers/phy/mediatek/phy-mtk-hdmi.c +++ b/drivers/phy/mediatek/phy-mtk-hdmi.c @@ -119,9 +119,7 @@ static int mtk_hdmi_phy_probe(struct platform_device *pdev) mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); hdmi_phy->regs = devm_ioremap_resource(dev, mem); if (IS_ERR(hdmi_phy->regs)) { - ret = PTR_ERR(hdmi_phy->regs); - dev_err(dev, "Failed to get memory resource: %d\n", ret); - return ret; + return PTR_ERR(hdmi_phy->regs); } ref_clk = devm_clk_get(dev, "pll_ref"); diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c index c51114d8e437..01cf31633019 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c @@ -151,9 +151,7 @@ static int mtk_mipi_tx_probe(struct platform_device *pdev) mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mipi_tx->regs = devm_ioremap_resource(dev, mem); if (IS_ERR(mipi_tx->regs)) { - ret = PTR_ERR(mipi_tx->regs); - dev_err(dev, "Failed to get memory resource: %d\n", ret); - return ret; + return PTR_ERR(mipi_tx->regs); } ref_clk = devm_clk_get(dev, NULL); diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c new file mode 100644 index 000000000000..c2cb93b4df71 --- /dev/null +++ b/drivers/phy/phy-can-transceiver.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-can-transceiver.c - phy driver for CAN transceivers + * + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com + * + */ +#include<linux/phy/phy.h> +#include<linux/platform_device.h> +#include<linux/module.h> +#include<linux/gpio.h> +#include<linux/gpio/consumer.h> + +struct can_transceiver_data { + u32 flags; +#define CAN_TRANSCEIVER_STB_PRESENT BIT(0) +#define CAN_TRANSCEIVER_EN_PRESENT BIT(1) +}; + +struct can_transceiver_phy { + struct phy *generic_phy; + struct gpio_desc *standby_gpio; + struct gpio_desc *enable_gpio; +}; + +/* Power on function */ +static int can_transceiver_phy_power_on(struct phy *phy) +{ + struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + + if (can_transceiver_phy->standby_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 0); + if (can_transceiver_phy->enable_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 1); + + return 0; +} + +/* Power off function */ +static int can_transceiver_phy_power_off(struct phy *phy) +{ + struct can_transceiver_phy *can_transceiver_phy = phy_get_drvdata(phy); + + if (can_transceiver_phy->standby_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->standby_gpio, 1); + if (can_transceiver_phy->enable_gpio) + gpiod_set_value_cansleep(can_transceiver_phy->enable_gpio, 0); + + return 0; +} + +static const struct phy_ops can_transceiver_phy_ops = { + .power_on = can_transceiver_phy_power_on, + .power_off = can_transceiver_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct can_transceiver_data tcan1042_drvdata = { + .flags = CAN_TRANSCEIVER_STB_PRESENT, +}; + +static const struct can_transceiver_data tcan1043_drvdata = { + .flags = CAN_TRANSCEIVER_STB_PRESENT | CAN_TRANSCEIVER_EN_PRESENT, +}; + +static const struct of_device_id can_transceiver_phy_ids[] = { + { + .compatible = "ti,tcan1042", + .data = &tcan1042_drvdata + }, + { + .compatible = "ti,tcan1043", + .data = &tcan1043_drvdata + }, + { } +}; +MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); + +static int can_transceiver_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct can_transceiver_phy *can_transceiver_phy; + const struct can_transceiver_data *drvdata; + const struct of_device_id *match; + struct phy *phy; + struct gpio_desc *standby_gpio; + struct gpio_desc *enable_gpio; + u32 max_bitrate = 0; + + can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), GFP_KERNEL); + if (!can_transceiver_phy) + return -ENOMEM; + + match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node); + drvdata = match->data; + + phy = devm_phy_create(dev, dev->of_node, + &can_transceiver_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create can transceiver phy\n"); + return PTR_ERR(phy); + } + + device_property_read_u32(dev, "max-bitrate", &max_bitrate); + if (!max_bitrate) + dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n"); + phy->attrs.max_link_rate = max_bitrate; + + can_transceiver_phy->generic_phy = phy; + + if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) { + standby_gpio = devm_gpiod_get(dev, "standby", GPIOD_OUT_HIGH); + if (IS_ERR(standby_gpio)) + return PTR_ERR(standby_gpio); + can_transceiver_phy->standby_gpio = standby_gpio; + } + + if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) { + enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(enable_gpio)) + return PTR_ERR(enable_gpio); + can_transceiver_phy->enable_gpio = enable_gpio; + } + + phy_set_drvdata(can_transceiver_phy->generic_phy, can_transceiver_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver can_transceiver_phy_driver = { + .probe = can_transceiver_phy_probe, + .driver = { + .name = "can-transceiver-phy", + .of_match_table = can_transceiver_phy_ids, + }, +}; + +module_platform_driver(can_transceiver_phy_driver); + +MODULE_AUTHOR("Faiz Abbas <faiz_abbas@ti.com>"); +MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>"); +MODULE_DESCRIPTION("CAN TRANSCEIVER PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index 77fe65367ce5..288c9c67aa74 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -15,7 +15,7 @@ /* * Minimum D-PHY timings based on MIPI D-PHY specification. Derived * from the valid ranges specified in Section 6.9, Table 14, Page 41 - * of the D-PHY specification (v2.1). + * of the D-PHY specification (v1.2). */ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, unsigned int bpp, diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index ccb575b13777..91e28d6ce450 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -697,16 +697,18 @@ struct phy *phy_get(struct device *dev, const char *string) struct phy *phy; struct device_link *link; - if (string == NULL) { - dev_WARN(dev, "missing string\n"); - return ERR_PTR(-EINVAL); - } - if (dev->of_node) { - index = of_property_match_string(dev->of_node, "phy-names", - string); + if (string) + index = of_property_match_string(dev->of_node, "phy-names", + string); + else + index = 0; phy = _of_phy_get(dev->of_node, index); } else { + if (string == NULL) { + dev_WARN(dev, "missing string\n"); + return ERR_PTR(-EINVAL); + } phy = phy_find(dev, string); } if (IS_ERR(phy)) diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index f4cd590fbde7..d0f4546648f0 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -961,7 +961,8 @@ static void xgene_phy_sata_cfg_lanes(struct xgene_phy_ctx *ctx) serdes_wr(ctx, lane, RXTX_REG1, val); /* Latch VTT value based on the termination to ground and - enable TX FIFO */ + * enable TX FIFO + */ serdes_rd(ctx, lane, RXTX_REG2, &val); val = RXTX_REG2_VTT_ENA_SET(val, 0x1); val = RXTX_REG2_VTT_SEL_SET(val, 0x1); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 7877f70cf86f..cfe359488f5c 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -35,6 +35,7 @@ #define PLL_READY_GATE_EN BIT(3) /* QPHY_PCS_STATUS bit */ #define PHYSTATUS BIT(6) +#define PHYSTATUS_4_20 BIT(7) /* QPHY_PCS_READY_STATUS & QPHY_COM_PCS_READY_STATUS bit */ #define PCS_READY BIT(0) @@ -143,6 +144,13 @@ static const unsigned int msm8996_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_READY_STATUS] = 0x168, }; +static const unsigned int ipq_pciephy_gen3_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, +}; + static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_COM_SW_RESET] = 0x400, [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, @@ -614,6 +622,113 @@ static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08), }; +static const struct qmp_phy_init_tbl ipq6018_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE2_MODE0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE1_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_PLL_SSC_STEP_SIZE2_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE0, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE1, 0x29), + QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE0, 0x09), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE1, 0x09), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE1, 0x28), + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_PLL_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE0, 0x68), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE1, 0x53), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_PLL_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE0, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE1, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_PLL_CLK_SELECT, 0x32), + QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_PLL_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV_MODE1, 0x08), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX0_RES_CODE_LANE_OFFSET_TX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_TX0_LANE_MODE_1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_TX0_RCV_DETECT_LVL_2, 0x12), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_SO_GAIN, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_RX0_UCDR_PI_CONTROLS, 0x70), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2, 0x61), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x73), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_RX0_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_RX0_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_RX0_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_LOW, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH, 0x01), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH2, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH3, 0xd3), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_00_HIGH4, 0x40), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_LOW, 0x01), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_01_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_RX0_RX_MODE_10_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_RX0_DFE_EN_TIMER, 0x04), +}; + +static const struct qmp_phy_init_tbl ipq6018_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(PCS_COM_FLL_CNTRL1, 0x01), + QMP_PHY_INIT_CFG(PCS_COM_REFGEN_REQ_CONFIG1, 0x0d), + QMP_PHY_INIT_CFG(PCS_COM_G12S1_TXDEEMPH_M3P5DB, 0x10), + QMP_PHY_INIT_CFG(PCS_COM_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(PCS_COM_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(PCS_COM_RX_DCC_CAL_CONFIG, 0x01), + QMP_PHY_INIT_CFG(PCS_COM_EQ_CONFIG5, 0x01), + QMP_PHY_INIT_CFG(PCS_PCIE_POWER_STATE_CONFIG2, 0x0d), + QMP_PHY_INIT_CFG(PCS_PCIE_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(PCS_PCIE_EQ_CONFIG1, 0x11), + QMP_PHY_INIT_CFG(PCS_PCIE_PRESET_P10_PRE, 0x00), + QMP_PHY_INIT_CFG(PCS_PCIE_PRESET_P10_POST, 0x58), +}; + static const struct qmp_phy_init_tbl ipq8074_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18), QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), @@ -2110,6 +2225,101 @@ static const struct qmp_phy_init_tbl sdx55_usb3_uniphy_rx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), }; +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x50), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0xfb), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1, 0xfb), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MISC1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTERNAL_DIG_CORECLK_DIV, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MODE, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_DC_LEVEL_CTRL, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x56), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x22), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_2, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_LANE_MODE_3, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_VMODE_CTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_20_TX_PI_QEC_CTRL, 0x00), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_AUX_DATA_TCOARSE_TFINE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_3, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_DAC_ENABLE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_DAC_ENABLE2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_VGA_CAL_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x27), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B1, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B2, 0x5a), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B4, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B0, 0xbd), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B1, 0xf9), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B3, 0xce), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE2_B4, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B0, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B3, 0xcf), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_RX_MODE_RATE3_B4, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_PHPRE_CTRL, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_20_RX_MARG_COARSE_CTRL2, 0x12), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG2, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG4, 0x16), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_EQ_CONFIG5, 0x02), +}; + +static const struct qmp_phy_init_tbl sdx55_qmp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_EQ_CONFIG1, 0x17), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G3_RXEQEVAL_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_RXEQEVAL_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG2, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG5, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_LANE1_INSIG_SW_CTRL2, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2, 0x00), +}; + static const struct qmp_phy_init_tbl sm8350_ufsphy_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0xd9), QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x11), @@ -2411,6 +2621,8 @@ struct qmp_phy_cfg { unsigned int start_ctrl; unsigned int pwrdn_ctrl; unsigned int mask_com_pcs_ready; + /* bit offset of PHYSTATUS in QPHY_PCS_STATUS register */ + unsigned int phy_status; /* true, if PHY has a separate PHY_COM control block */ bool has_phy_com_ctrl; @@ -2624,6 +2836,7 @@ static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, }; static const struct qmp_phy_cfg msm8996_pciephy_cfg = { @@ -2649,6 +2862,7 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = { .start_ctrl = PCS_START | PLL_READY_GATE_EN, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, .mask_com_pcs_ready = PCS_READY, + .phy_status = PHYSTATUS, .has_phy_com_ctrl = true, .has_lane_rst = true, @@ -2678,6 +2892,7 @@ static const struct qmp_phy_cfg msm8996_ufs_cfg = { .start_ctrl = SERDES_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .no_pcs_sw_reset = true, }; @@ -2704,6 +2919,7 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, }; static const char * const ipq8074_pciephy_clk_l[] = { @@ -2736,6 +2952,37 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_phy_com_ctrl = false, + .has_lane_rst = false, + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg ipq6018_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 1, + + .serdes_tbl = ipq6018_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq6018_pcie_serdes_tbl), + .tx_tbl = ipq6018_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(ipq6018_pcie_tx_tbl), + .rx_tbl = ipq6018_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq6018_pcie_rx_tbl), + .pcs_tbl = ipq6018_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq6018_pcie_pcs_tbl), + .clk_list = ipq8074_pciephy_clk_l, + .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = NULL, + .num_vregs = 0, + .regs = ipq_pciephy_gen3_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, .has_phy_com_ctrl = false, .has_lane_rst = false, @@ -2768,6 +3015,7 @@ static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { .start_ctrl = PCS_START | SERDES_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = 995, /* us */ @@ -2796,6 +3044,7 @@ static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = { .start_ctrl = PCS_START | SERDES_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = 995, /* us */ @@ -2834,6 +3083,7 @@ static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = { .start_ctrl = PCS_START | SERDES_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = 995, /* us */ @@ -2872,6 +3122,7 @@ static const struct qmp_phy_cfg sm8250_qmp_gen3x2_pciephy_cfg = { .start_ctrl = PCS_START | SERDES_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, .is_dual_lane_phy = true, .has_pwrdn_delay = true, @@ -2901,6 +3152,7 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -2932,6 +3184,7 @@ static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3003,6 +3256,7 @@ static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3029,6 +3283,7 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { .start_ctrl = SERDES_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .is_dual_lane_phy = true, .no_pcs_sw_reset = true, @@ -3056,6 +3311,7 @@ static const struct qmp_phy_cfg msm8998_pciephy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, }; static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { @@ -3080,6 +3336,7 @@ static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .is_dual_lane_phy = true, }; @@ -3104,6 +3361,7 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { .start_ctrl = SERDES_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .is_dual_lane_phy = true, }; @@ -3130,6 +3388,8 @@ static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3161,6 +3421,7 @@ static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3189,6 +3450,7 @@ static const struct qmp_phy_cfg sm8250_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3220,6 +3482,7 @@ static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3288,12 +3551,45 @@ static const struct qmp_phy_cfg sdx55_usb3_uniphy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, }; +static const struct qmp_phy_cfg sdx55_qmp_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 2, + + .serdes_tbl = sdx55_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_serdes_tbl), + .tx_tbl = sdx55_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_tx_tbl), + .rx_tbl = sdx55_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_rx_tbl), + .pcs_tbl = sdx55_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_pcs_tbl), + .pcs_misc_tbl = sdx55_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sdx55_qmp_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS_4_20, + + .is_dual_lane_phy = true, + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + static const struct qmp_phy_cfg sm8350_ufsphy_cfg = { .type = PHY_TYPE_UFS, .nlanes = 2, @@ -3314,6 +3610,7 @@ static const struct qmp_phy_cfg sm8350_ufsphy_cfg = { .start_ctrl = SERDES_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .is_dual_lane_phy = true, }; @@ -3340,6 +3637,7 @@ static const struct qmp_phy_cfg sm8350_usb3phy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3371,6 +3669,7 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { .start_ctrl = SERDES_START | PCS_START, .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, .has_pwrdn_delay = true, .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, @@ -3993,10 +4292,8 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) } ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); - if (ret) { - dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret); + if (ret) goto err_rst; - } if (cfg->has_phy_dp_com_ctrl) { qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, @@ -4238,7 +4535,7 @@ static int qcom_qmp_phy_power_on(struct phy *phy) ready = PCS_READY; } else { status = pcs + cfg->regs[QPHY_PCS_STATUS]; - mask = PHYSTATUS; + mask = cfg->phy_status; ready = 0; } @@ -4430,10 +4727,8 @@ static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev) } ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); - if (ret) { - dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret); + if (ret) return ret; - } ret = clk_prepare_enable(qphy->pipe_clk); if (ret) { @@ -4928,6 +5223,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,ipq8074-qmp-pcie-phy", .data = &ipq8074_pciephy_cfg, }, { + .compatible = "qcom,ipq6018-qmp-pcie-phy", + .data = &ipq6018_pciephy_cfg, + }, { .compatible = "qcom,sc7180-qmp-usb3-phy", .data = &sc7180_usb3phy_cfg, }, { @@ -4991,6 +5289,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,sm8250-qmp-modem-pcie-phy", .data = &sm8250_qmp_gen3x2_pciephy_cfg, }, { + .compatible = "qcom,sdx55-qmp-pcie-phy", + .data = &sdx55_qmp_pciephy_cfg, + }, { .compatible = "qcom,sdx55-qmp-usb3-uni-phy", .data = &sdx55_usb3_uniphy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 67bd2dd0d8c5..6592b58b13f6 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -6,6 +6,138 @@ #ifndef QCOM_PHY_QMP_H_ #define QCOM_PHY_QMP_H_ +/* QMP V2 PHY for PCIE gen3 ports - QSERDES PLL registers */ + +#define QSERDES_PLL_BG_TIMER 0x00c +#define QSERDES_PLL_SSC_PER1 0x01c +#define QSERDES_PLL_SSC_PER2 0x020 +#define QSERDES_PLL_SSC_STEP_SIZE1_MODE0 0x024 +#define QSERDES_PLL_SSC_STEP_SIZE2_MODE0 0x028 +#define QSERDES_PLL_SSC_STEP_SIZE1_MODE1 0x02c +#define QSERDES_PLL_SSC_STEP_SIZE2_MODE1 0x030 +#define QSERDES_PLL_BIAS_EN_CLKBUFLR_EN 0x03c +#define QSERDES_PLL_CLK_ENABLE1 0x040 +#define QSERDES_PLL_SYS_CLK_CTRL 0x044 +#define QSERDES_PLL_SYSCLK_BUF_ENABLE 0x048 +#define QSERDES_PLL_PLL_IVCO 0x050 +#define QSERDES_PLL_LOCK_CMP1_MODE0 0x054 +#define QSERDES_PLL_LOCK_CMP2_MODE0 0x058 +#define QSERDES_PLL_LOCK_CMP1_MODE1 0x060 +#define QSERDES_PLL_LOCK_CMP2_MODE1 0x064 +#define QSERDES_PLL_BG_TRIM 0x074 +#define QSERDES_PLL_CLK_EP_DIV_MODE0 0x078 +#define QSERDES_PLL_CLK_EP_DIV_MODE1 0x07c +#define QSERDES_PLL_CP_CTRL_MODE0 0x080 +#define QSERDES_PLL_CP_CTRL_MODE1 0x084 +#define QSERDES_PLL_PLL_RCTRL_MODE0 0x088 +#define QSERDES_PLL_PLL_RCTRL_MODE1 0x08C +#define QSERDES_PLL_PLL_CCTRL_MODE0 0x090 +#define QSERDES_PLL_PLL_CCTRL_MODE1 0x094 +#define QSERDES_PLL_BIAS_EN_CTRL_BY_PSM 0x0a4 +#define QSERDES_PLL_SYSCLK_EN_SEL 0x0a8 +#define QSERDES_PLL_RESETSM_CNTRL 0x0b0 +#define QSERDES_PLL_LOCK_CMP_EN 0x0c4 +#define QSERDES_PLL_DEC_START_MODE0 0x0cc +#define QSERDES_PLL_DEC_START_MODE1 0x0d0 +#define QSERDES_PLL_DIV_FRAC_START1_MODE0 0x0d8 +#define QSERDES_PLL_DIV_FRAC_START2_MODE0 0x0dc +#define QSERDES_PLL_DIV_FRAC_START3_MODE0 0x0e0 +#define QSERDES_PLL_DIV_FRAC_START1_MODE1 0x0e4 +#define QSERDES_PLL_DIV_FRAC_START2_MODE1 0x0e8 +#define QSERDES_PLL_DIV_FRAC_START3_MODE1 0x0eC +#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE0 0x100 +#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE0 0x104 +#define QSERDES_PLL_INTEGLOOP_GAIN0_MODE1 0x108 +#define QSERDES_PLL_INTEGLOOP_GAIN1_MODE1 0x10c +#define QSERDES_PLL_VCO_TUNE_MAP 0x120 +#define QSERDES_PLL_VCO_TUNE1_MODE0 0x124 +#define QSERDES_PLL_VCO_TUNE2_MODE0 0x128 +#define QSERDES_PLL_VCO_TUNE1_MODE1 0x12c +#define QSERDES_PLL_VCO_TUNE2_MODE1 0x130 +#define QSERDES_PLL_VCO_TUNE_TIMER1 0x13c +#define QSERDES_PLL_VCO_TUNE_TIMER2 0x140 +#define QSERDES_PLL_CLK_SELECT 0x16c +#define QSERDES_PLL_HSCLK_SEL 0x170 +#define QSERDES_PLL_CORECLK_DIV 0x17c +#define QSERDES_PLL_CORE_CLK_EN 0x184 +#define QSERDES_PLL_CMN_CONFIG 0x18c +#define QSERDES_PLL_SVS_MODE_CLK_SEL 0x194 +#define QSERDES_PLL_CORECLK_DIV_MODE1 0x1b4 + +/* QMP V2 PHY for PCIE gen3 ports - QSERDES TX registers */ + +#define QSERDES_TX0_RES_CODE_LANE_OFFSET_TX 0x03c +#define QSERDES_TX0_HIGHZ_DRVR_EN 0x058 +#define QSERDES_TX0_LANE_MODE_1 0x084 +#define QSERDES_TX0_RCV_DETECT_LVL_2 0x09c + +/* QMP V2 PHY for PCIE gen3 ports - QSERDES RX registers */ + +#define QSERDES_RX0_UCDR_FO_GAIN 0x008 +#define QSERDES_RX0_UCDR_SO_GAIN 0x014 +#define QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE 0x034 +#define QSERDES_RX0_UCDR_PI_CONTROLS 0x044 +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL2 0x0ec +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL3 0x0f0 +#define QSERDES_RX0_RX_EQU_ADAPTOR_CNTRL4 0x0f4 +#define QSERDES_RX0_RX_IDAC_TSETTLE_LOW 0x0f8 +#define QSERDES_RX0_RX_IDAC_TSETTLE_HIGH 0x0fc +#define QSERDES_RX0_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 +#define QSERDES_RX0_RX_OFFSET_ADAPTOR_CNTRL2 0x114 +#define QSERDES_RX0_SIGDET_ENABLES 0x118 +#define QSERDES_RX0_SIGDET_CNTRL 0x11c +#define QSERDES_RX0_SIGDET_DEGLITCH_CNTRL 0x124 +#define QSERDES_RX0_RX_MODE_00_LOW 0x170 +#define QSERDES_RX0_RX_MODE_00_HIGH 0x174 +#define QSERDES_RX0_RX_MODE_00_HIGH2 0x178 +#define QSERDES_RX0_RX_MODE_00_HIGH3 0x17c +#define QSERDES_RX0_RX_MODE_00_HIGH4 0x180 +#define QSERDES_RX0_RX_MODE_01_LOW 0x184 +#define QSERDES_RX0_RX_MODE_01_HIGH 0x188 +#define QSERDES_RX0_RX_MODE_01_HIGH2 0x18c +#define QSERDES_RX0_RX_MODE_01_HIGH3 0x190 +#define QSERDES_RX0_RX_MODE_01_HIGH4 0x194 +#define QSERDES_RX0_RX_MODE_10_LOW 0x198 +#define QSERDES_RX0_RX_MODE_10_HIGH 0x19c +#define QSERDES_RX0_RX_MODE_10_HIGH2 0x1a0 +#define QSERDES_RX0_RX_MODE_10_HIGH3 0x1a4 +#define QSERDES_RX0_RX_MODE_10_HIGH4 0x1a8 +#define QSERDES_RX0_DFE_EN_TIMER 0x1b4 + +/* QMP V2 PHY for PCIE gen3 ports - PCS registers */ + +#define PCS_COM_FLL_CNTRL1 0x098 +#define PCS_COM_FLL_CNTRL2 0x09c +#define PCS_COM_FLL_CNT_VAL_L 0x0a0 +#define PCS_COM_FLL_CNT_VAL_H_TOL 0x0a4 +#define PCS_COM_FLL_MAN_CODE 0x0a8 +#define PCS_COM_REFGEN_REQ_CONFIG1 0x0dc +#define PCS_COM_G12S1_TXDEEMPH_M3P5DB 0x16c +#define PCS_COM_RX_SIGDET_LVL 0x188 +#define PCS_COM_P2U3_WAKEUP_DLY_TIME_AUXCLK_L 0x1a4 +#define PCS_COM_P2U3_WAKEUP_DLY_TIME_AUXCLK_H 0x1a8 +#define PCS_COM_RX_DCC_CAL_CONFIG 0x1d8 +#define PCS_COM_EQ_CONFIG5 0x1ec + +/* QMP V2 PHY for PCIE gen3 ports - PCS Misc registers */ + +#define PCS_PCIE_POWER_STATE_CONFIG2 0x40c +#define PCS_PCIE_POWER_STATE_CONFIG4 0x414 +#define PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x41c +#define PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L 0x440 +#define PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_H 0x444 +#define PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L 0x448 +#define PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_H 0x44c +#define PCS_PCIE_OSC_DTCT_CONFIG2 0x45c +#define PCS_PCIE_OSC_DTCT_MODE2_CONFIG2 0x478 +#define PCS_PCIE_OSC_DTCT_MODE2_CONFIG4 0x480 +#define PCS_PCIE_OSC_DTCT_MODE2_CONFIG5 0x484 +#define PCS_PCIE_OSC_DTCT_ACTIONS 0x490 +#define PCS_PCIE_EQ_CONFIG1 0x4a0 +#define PCS_PCIE_EQ_CONFIG2 0x4a4 +#define PCS_PCIE_PRESET_P10_PRE 0x4bc +#define PCS_PCIE_PRESET_P10_POST 0x4e0 + /* Only for QMP V2 PHY - QSERDES COM registers */ #define QSERDES_COM_BG_TIMER 0x00c #define QSERDES_COM_SSC_EN_CENTER 0x010 @@ -420,6 +552,7 @@ #define QSERDES_V4_COM_SYSCLK_EN_SEL 0x094 #define QSERDES_V4_COM_RESETSM_CNTRL 0x09c #define QSERDES_V4_COM_LOCK_CMP_EN 0x0a4 +#define QSERDES_V4_COM_LOCK_CMP_CFG 0x0a8 #define QSERDES_V4_COM_LOCK_CMP1_MODE0 0x0ac #define QSERDES_V4_COM_LOCK_CMP2_MODE0 0x0b0 #define QSERDES_V4_COM_LOCK_CMP1_MODE1 0x0b4 @@ -434,6 +567,8 @@ #define QSERDES_V4_COM_DIV_FRAC_START3_MODE1 0x0e0 #define QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0 0x0ec #define QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0 0x0f0 +#define QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1 0x0f4 +#define QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1 0x0f8 #define QSERDES_V4_COM_VCO_TUNE_CTRL 0x108 #define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c #define QSERDES_V4_COM_VCO_TUNE1_MODE0 0x110 @@ -451,11 +586,15 @@ #define QSERDES_V4_COM_C_READY_STATUS 0x178 #define QSERDES_V4_COM_CMN_CONFIG 0x17c #define QSERDES_V4_COM_SVS_MODE_CLK_SEL 0x184 +#define QSERDES_V4_COM_CMN_MISC1 0x19c +#define QSERDES_V4_COM_INTERNAL_DIG_CORECLK_DIV 0x1a0 +#define QSERDES_V4_COM_CMN_MODE 0x1a4 +#define QSERDES_V4_COM_VCO_DC_LEVEL_CTRL 0x1a8 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 -#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8 +#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL 0x1bc /* Only for QMP V4 PHY - TX registers */ #define QSERDES_V4_TX_CLKBUF_ENABLE 0x08 @@ -485,6 +624,13 @@ #define QSERDES_V4_TX_VMODE_CTRL1 0xe8 #define QSERDES_V4_TX_PI_QEC_CTRL 0x104 +/* Only for QMP V4_20 PHY - TX registers */ +#define QSERDES_V4_20_TX_LANE_MODE_1 0x88 +#define QSERDES_V4_20_TX_LANE_MODE_2 0x8c +#define QSERDES_V4_20_TX_LANE_MODE_3 0x90 +#define QSERDES_V4_20_TX_VMODE_CTRL1 0xc4 +#define QSERDES_V4_20_TX_PI_QEC_CTRL 0xe0 + /* Only for QMP V4 PHY - RX registers */ #define QSERDES_V4_RX_UCDR_FO_GAIN 0x008 #define QSERDES_V4_RX_UCDR_SO_GAIN 0x014 @@ -551,6 +697,33 @@ #define QSERDES_V4_DP_PHY_AUX_INTERRUPT_STATUS 0x0d8 #define QSERDES_V4_DP_PHY_STATUS 0x0dc +/* Only for QMP V4_20 PHY - RX registers */ +#define QSERDES_V4_20_RX_FO_GAIN_RATE2 0x008 +#define QSERDES_V4_20_RX_UCDR_PI_CONTROLS 0x058 +#define QSERDES_V4_20_RX_AUX_DATA_TCOARSE_TFINE 0x0ac +#define QSERDES_V4_20_RX_DFE_3 0x110 +#define QSERDES_V4_20_RX_DFE_DAC_ENABLE1 0x134 +#define QSERDES_V4_20_RX_DFE_DAC_ENABLE2 0x138 +#define QSERDES_V4_20_RX_VGA_CAL_CNTRL2 0x150 +#define QSERDES_V4_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x178 +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B1 0x1c8 +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B2 0x1cc +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B3 0x1d0 +#define QSERDES_V4_20_RX_RX_MODE_RATE_0_1_B4 0x1d4 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B0 0x1d8 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B1 0x1dc +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B2 0x1e0 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B3 0x1e4 +#define QSERDES_V4_20_RX_RX_MODE_RATE2_B4 0x1e8 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B0 0x1ec +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B1 0x1f0 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B2 0x1f4 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B3 0x1f8 +#define QSERDES_V4_20_RX_RX_MODE_RATE3_B4 0x1fc +#define QSERDES_V4_20_RX_PHPRE_CTRL 0x200 +#define QSERDES_V4_20_RX_DFE_CTLE_POST_CAL_OFFSET 0x20c +#define QSERDES_V4_20_RX_MARG_COARSE_CTRL2 0x23c + /* Only for QMP V4 PHY - UFS PCS registers */ #define QPHY_V4_PCS_UFS_PHY_START 0x000 #define QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL 0x004 @@ -836,6 +1009,12 @@ #define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x354 #define QPHY_V4_PCS_USB3_TEST_CONTROL 0x358 +/* Only for QMP V4_20 PHY - USB/PCIe PCS registers */ +#define QPHY_V4_20_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V4_20_PCS_EQ_CONFIG2 0x1d8 +#define QPHY_V4_20_PCS_EQ_CONFIG4 0x1e0 +#define QPHY_V4_20_PCS_EQ_CONFIG5 0x1e4 + /* Only for QMP V4 PHY - UNI has 0x300 offset for PCS_USB3 regs */ #define QPHY_V4_PCS_USB3_UNI_LFPS_DET_HIGH_COUNT_VAL 0x618 #define QPHY_V4_PCS_USB3_UNI_RXEQTRAINING_DFE_TIME_S2 0x638 @@ -861,6 +1040,14 @@ #define QPHY_V4_PCS_PCIE_PRESET_P10_PRE 0xbc #define QPHY_V4_PCS_PCIE_PRESET_P10_POST 0xe0 +#define QPHY_V4_20_PCS_PCIE_EQ_CONFIG1 0x0a0 +#define QPHY_V4_20_PCS_PCIE_G3_RXEQEVAL_TIME 0x0f0 +#define QPHY_V4_20_PCS_PCIE_G4_RXEQEVAL_TIME 0x0f4 +#define QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG2 0x0fc +#define QPHY_V4_20_PCS_PCIE_G4_EQ_CONFIG5 0x108 +#define QPHY_V4_20_PCS_LANE1_INSIG_SW_CTRL2 0x824 +#define QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2 0x828 + /* Only for QMP V5 PHY - QSERDES COM registers */ #define QSERDES_V5_COM_PLL_IVCO 0x058 #define QSERDES_V5_COM_CP_CTRL_MODE0 0x074 diff --git a/drivers/phy/ralink/Kconfig b/drivers/phy/ralink/Kconfig index ecc309ba9fee..c2373b30b8a6 100644 --- a/drivers/phy/ralink/Kconfig +++ b/drivers/phy/ralink/Kconfig @@ -4,7 +4,7 @@ # config PHY_MT7621_PCI tristate "MediaTek MT7621 PCI PHY Driver" - depends on RALINK && OF + depends on (RALINK && OF) || COMPILE_TEST select GENERIC_PHY select REGMAP_MMIO help diff --git a/drivers/phy/ralink/phy-mt7621-pci.c b/drivers/phy/ralink/phy-mt7621-pci.c index 2a9465f4bb3a..5e6530f545b5 100644 --- a/drivers/phy/ralink/phy-mt7621-pci.c +++ b/drivers/phy/ralink/phy-mt7621-pci.c @@ -5,6 +5,7 @@ */ #include <dt-bindings/phy/phy.h> +#include <linux/clk.h> #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/module.h> @@ -14,8 +15,6 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/sys_soc.h> -#include <mt7621.h> -#include <ralink_regs.h> #define RG_PE1_PIPE_REG 0x02c #define RG_PE1_PIPE_RST BIT(12) @@ -62,8 +61,6 @@ #define RG_PE1_FRC_MSTCKDIV BIT(5) -#define XTAL_MASK GENMASK(8, 6) - #define MAX_PHYS 2 /** @@ -71,6 +68,7 @@ * @dev: pointer to device * @regmap: kernel regmap pointer * @phy: pointer to the kernel PHY device + * @sys_clk: pointer to the system XTAL clock * @port_base: base register * @has_dual_port: if the phy has dual ports. * @bypass_pipe_rst: mark if 'mt7621_bypass_pipe_rst' @@ -80,6 +78,7 @@ struct mt7621_pci_phy { struct device *dev; struct regmap *regmap; struct phy *phy; + struct clk *sys_clk; void __iomem *port_base; bool has_dual_port; bool bypass_pipe_rst; @@ -116,12 +115,14 @@ static void mt7621_bypass_pipe_rst(struct mt7621_pci_phy *phy) } } -static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) +static int mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) { struct device *dev = phy->dev; - u32 xtal_mode; + unsigned long clk_rate; - xtal_mode = FIELD_GET(XTAL_MASK, rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG0)); + clk_rate = clk_get_rate(phy->sys_clk); + if (!clk_rate) + return -EINVAL; /* Set PCIe Port PHY to disable SSC */ /* Debug Xtal Type */ @@ -139,13 +140,13 @@ static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) RG_PE1_PHY_EN, RG_PE1_FRC_PHY_EN); } - if (xtal_mode <= 5 && xtal_mode >= 3) { /* 40MHz Xtal */ + if (clk_rate == 40000000) { /* 40MHz Xtal */ /* Set Pre-divider ratio (for host mode) */ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x01)); dev_dbg(dev, "Xtal is 40MHz\n"); - } else if (xtal_mode >= 6) { /* 25MHz Xal */ + } else if (clk_rate == 25000000) { /* 25MHz Xal */ mt7621_phy_rmw(phy, RG_PE1_H_PLL_REG, RG_PE1_H_PLL_PREDIV, FIELD_PREP(RG_PE1_H_PLL_PREDIV, 0x00)); @@ -196,13 +197,15 @@ static void mt7621_set_phy_for_ssc(struct mt7621_pci_phy *phy) mt7621_phy_rmw(phy, RG_PE1_H_PLL_BR_REG, RG_PE1_H_PLL_BR, FIELD_PREP(RG_PE1_H_PLL_BR, 0x00)); - if (xtal_mode <= 5 && xtal_mode >= 3) { /* 40MHz Xtal */ + if (clk_rate == 40000000) { /* 40MHz Xtal */ /* set force mode enable of da_pe1_mstckdiv */ mt7621_phy_rmw(phy, RG_PE1_MSTCKDIV_REG, RG_PE1_MSTCKDIV | RG_PE1_FRC_MSTCKDIV, FIELD_PREP(RG_PE1_MSTCKDIV, 0x01) | RG_PE1_FRC_MSTCKDIV); } + + return 0; } static int mt7621_pci_phy_init(struct phy *phy) @@ -212,9 +215,7 @@ static int mt7621_pci_phy_init(struct phy *phy) if (mphy->bypass_pipe_rst) mt7621_bypass_pipe_rst(mphy); - mt7621_set_phy_for_ssc(mphy); - - return 0; + return mt7621_set_phy_for_ssc(mphy); } static int mt7621_pci_phy_power_on(struct phy *phy) @@ -272,8 +273,8 @@ static struct phy *mt7621_pcie_phy_of_xlate(struct device *dev, mt7621_phy->has_dual_port = args->args[0]; - dev_info(dev, "PHY for 0x%08x (dual port = %d)\n", - (unsigned int)mt7621_phy->port_base, mt7621_phy->has_dual_port); + dev_dbg(dev, "PHY for 0x%px (dual port = %d)\n", + mt7621_phy->port_base, mt7621_phy->has_dual_port); return mt7621_phy->phy; } @@ -324,6 +325,12 @@ static int mt7621_pci_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->phy); } + phy->sys_clk = devm_clk_get(dev, NULL); + if (IS_ERR(phy->sys_clk)) { + dev_err(dev, "failed to get phy clock\n"); + return PTR_ERR(phy->sys_clk); + } + phy_set_drvdata(phy->phy, phy); provider = devm_of_phy_provider_register(dev, mt7621_pcie_phy_of_xlate); diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig index 159285f42e5c..e812adad7242 100644 --- a/drivers/phy/rockchip/Kconfig +++ b/drivers/phy/rockchip/Kconfig @@ -48,6 +48,15 @@ config PHY_ROCKCHIP_INNO_USB2 help Support for Rockchip USB2.0 PHY with Innosilicon IP block. +config PHY_ROCKCHIP_INNO_CSIDPHY + tristate "Rockchip Innosilicon MIPI CSI PHY driver" + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Enable this to support the Rockchip MIPI CSI PHY with + Innosilicon IP block. + config PHY_ROCKCHIP_INNO_DSIDPHY tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile index c3cfc7f0af5c..f0eec212b2aa 100644 --- a/drivers/phy/rockchip/Makefile +++ b/drivers/phy/rockchip/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0) += phy-rockchip-dphy-rx0.o obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o +obj-$(CONFIG_PHY_ROCKCHIP_INNO_CSIDPHY) += phy-rockchip-inno-csidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY) += phy-rockchip-inno-dsidphy.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c new file mode 100644 index 000000000000..ca13a604ab4f --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip MIPI RX Innosilicon DPHY driver + * + * Copyright (C) 2021 Fuzhou Rockchip Electronics Co., Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +/* GRF */ +#define RK1808_GRF_PD_VI_CON_OFFSET 0x0430 + +#define RK3326_GRF_PD_VI_CON_OFFSET 0x0430 + +#define RK3368_GRF_SOC_CON6_OFFSET 0x0418 + +/* PHY */ +#define CSIDPHY_CTRL_LANE_ENABLE 0x00 +#define CSIDPHY_CTRL_LANE_ENABLE_CK BIT(6) +#define CSIDPHY_CTRL_LANE_ENABLE_MASK GENMASK(5, 2) +#define CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED BIT(0) + +/* not present on all variants */ +#define CSIDPHY_CTRL_PWRCTL 0x04 +#define CSIDPHY_CTRL_PWRCTL_UNDEFINED GENMASK(7, 5) +#define CSIDPHY_CTRL_PWRCTL_SYNCRST BIT(2) +#define CSIDPHY_CTRL_PWRCTL_LDO_PD BIT(1) +#define CSIDPHY_CTRL_PWRCTL_PLL_PD BIT(0) + +#define CSIDPHY_CTRL_DIG_RST 0x80 +#define CSIDPHY_CTRL_DIG_RST_UNDEFINED 0x1e +#define CSIDPHY_CTRL_DIG_RST_RESET BIT(0) + +/* offset after ths_settle_offset */ +#define CSIDPHY_CLK_THS_SETTLE 0 +#define CSIDPHY_LANE_THS_SETTLE(n) (((n) + 1) * 0x80) +#define CSIDPHY_THS_SETTLE_MASK GENMASK(6, 0) + +/* offset after calib_offset */ +#define CSIDPHY_CLK_CALIB_EN 0 +#define CSIDPHY_LANE_CALIB_EN(n) (((n) + 1) * 0x80) +#define CSIDPHY_CALIB_EN BIT(7) + +/* Configure the count time of the THS-SETTLE by protocol. */ +#define RK1808_CSIDPHY_CLK_WR_THS_SETTLE 0x160 +#define RK3326_CSIDPHY_CLK_WR_THS_SETTLE 0x100 +#define RK3368_CSIDPHY_CLK_WR_THS_SETTLE 0x100 + +/* Calibration reception enable */ +#define RK1808_CSIDPHY_CLK_CALIB_EN 0x168 + +/* + * The higher 16-bit of this register is used for write protection + * only if BIT(x + 16) set to 1 the BIT(x) can be written. + */ +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define HZ_TO_MHZ(freq) div_u64(freq, 1000 * 1000) + +enum dphy_reg_id { + /* rk1808 & rk3326 */ + GRF_DPHY_CSIPHY_FORCERXMODE, + GRF_DPHY_CSIPHY_CLKLANE_EN, + GRF_DPHY_CSIPHY_DATALANE_EN, +}; + +struct dphy_reg { + u32 offset; + u32 mask; + u32 shift; +}; + +#define PHY_REG(_offset, _width, _shift) \ + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, } + +static const struct dphy_reg rk1808_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 0), + [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 1, 8), + [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 4), +}; + +static const struct dphy_reg rk3326_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 0), + [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 1, 8), + [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3326_GRF_PD_VI_CON_OFFSET, 4, 4), +}; + +static const struct dphy_reg rk3368_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3368_GRF_SOC_CON6_OFFSET, 4, 8), +}; + +struct hsfreq_range { + u32 range_h; + u8 cfg_bit; +}; + +struct dphy_drv_data { + int pwrctl_offset; + int ths_settle_offset; + int calib_offset; + const struct hsfreq_range *hsfreq_ranges; + int num_hsfreq_ranges; + const struct dphy_reg *grf_regs; +}; + +struct rockchip_inno_csidphy { + struct device *dev; + void __iomem *phy_base; + struct clk *pclk; + struct regmap *grf; + struct reset_control *rst; + const struct dphy_drv_data *drv_data; + struct phy_configure_opts_mipi_dphy config; + u8 hsfreq; +}; + +static inline void write_grf_reg(struct rockchip_inno_csidphy *priv, + int index, u8 value) +{ + const struct dphy_drv_data *drv_data = priv->drv_data; + const struct dphy_reg *reg = &drv_data->grf_regs[index]; + + if (reg->offset) + regmap_write(priv->grf, reg->offset, + HIWORD_UPDATE(value, reg->mask, reg->shift)); +} + +/* These tables must be sorted by .range_h ascending. */ +static const struct hsfreq_range rk1808_mipidphy_hsfreq_ranges[] = { + { 109, 0x02}, { 149, 0x03}, { 199, 0x06}, { 249, 0x06}, + { 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e}, + { 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e}, + {1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37}, + {2199, 0x3c}, {2399, 0x41}, {2499, 0x46} +}; + +static const struct hsfreq_range rk3326_mipidphy_hsfreq_ranges[] = { + { 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03}, + { 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07}, + { 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b}, + {1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e} +}; + +static const struct hsfreq_range rk3368_mipidphy_hsfreq_ranges[] = { + { 109, 0x00}, { 149, 0x01}, { 199, 0x02}, { 249, 0x03}, + { 299, 0x04}, { 399, 0x05}, { 499, 0x06}, { 599, 0x07}, + { 699, 0x08}, { 799, 0x09}, { 899, 0x0a}, {1099, 0x0b}, + {1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e} +}; + +static void rockchip_inno_csidphy_ths_settle(struct rockchip_inno_csidphy *priv, + int hsfreq, int offset) +{ + const struct dphy_drv_data *drv_data = priv->drv_data; + u32 val; + + val = readl(priv->phy_base + drv_data->ths_settle_offset + offset); + val &= ~CSIDPHY_THS_SETTLE_MASK; + val |= hsfreq; + writel(val, priv->phy_base + drv_data->ths_settle_offset + offset); +} + +static int rockchip_inno_csidphy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + const struct dphy_drv_data *drv_data = priv->drv_data; + struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy; + unsigned int hsfreq = 0; + unsigned int i; + u64 data_rate_mbps; + int ret; + + /* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */ + ret = phy_mipi_dphy_config_validate(config); + if (ret) + return ret; + + data_rate_mbps = HZ_TO_MHZ(config->hs_clk_rate); + + dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n", + config->lanes, data_rate_mbps); + for (i = 0; i < drv_data->num_hsfreq_ranges; i++) { + if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) { + hsfreq = drv_data->hsfreq_ranges[i].cfg_bit; + break; + } + } + if (!hsfreq) + return -EINVAL; + + priv->hsfreq = hsfreq; + priv->config = *config; + return 0; +} + +static int rockchip_inno_csidphy_power_on(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + const struct dphy_drv_data *drv_data = priv->drv_data; + u64 data_rate_mbps = HZ_TO_MHZ(priv->config.hs_clk_rate); + u32 val; + int ret, i; + + ret = clk_enable(priv->pclk); + if (ret < 0) + return ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret < 0) { + clk_disable(priv->pclk); + return ret; + } + + /* phy start */ + if (drv_data->pwrctl_offset >= 0) + writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED | + CSIDPHY_CTRL_PWRCTL_SYNCRST, + priv->phy_base + drv_data->pwrctl_offset); + + /* set data lane num and enable clock lane */ + val = FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_MASK, GENMASK(priv->config.lanes - 1, 0)) | + FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_CK, 1) | + FIELD_PREP(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED, 1); + writel(val, priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE); + + /* Reset dphy analog part */ + if (drv_data->pwrctl_offset >= 0) + writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED, + priv->phy_base + drv_data->pwrctl_offset); + usleep_range(500, 1000); + + /* Reset dphy digital part */ + writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED, + priv->phy_base + CSIDPHY_CTRL_DIG_RST); + writel(CSIDPHY_CTRL_DIG_RST_UNDEFINED + CSIDPHY_CTRL_DIG_RST_RESET, + priv->phy_base + CSIDPHY_CTRL_DIG_RST); + + /* not into receive mode/wait stopstate */ + write_grf_reg(priv, GRF_DPHY_CSIPHY_FORCERXMODE, 0x0); + + /* enable calibration */ + if (data_rate_mbps > 1500 && drv_data->calib_offset >= 0) { + writel(CSIDPHY_CALIB_EN, + priv->phy_base + drv_data->calib_offset + + CSIDPHY_CLK_CALIB_EN); + for (i = 0; i < priv->config.lanes; i++) + writel(CSIDPHY_CALIB_EN, + priv->phy_base + drv_data->calib_offset + + CSIDPHY_LANE_CALIB_EN(i)); + } + + rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq, + CSIDPHY_CLK_THS_SETTLE); + for (i = 0; i < priv->config.lanes; i++) + rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq, + CSIDPHY_LANE_THS_SETTLE(i)); + + write_grf_reg(priv, GRF_DPHY_CSIPHY_CLKLANE_EN, 0x1); + write_grf_reg(priv, GRF_DPHY_CSIPHY_DATALANE_EN, + GENMASK(priv->config.lanes - 1, 0)); + + return 0; +} + +static int rockchip_inno_csidphy_power_off(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + const struct dphy_drv_data *drv_data = priv->drv_data; + + /* disable all lanes */ + writel(CSIDPHY_CTRL_LANE_ENABLE_UNDEFINED, + priv->phy_base + CSIDPHY_CTRL_LANE_ENABLE); + + /* disable pll and ldo */ + if (drv_data->pwrctl_offset >= 0) + writel(CSIDPHY_CTRL_PWRCTL_UNDEFINED | + CSIDPHY_CTRL_PWRCTL_LDO_PD | + CSIDPHY_CTRL_PWRCTL_PLL_PD, + priv->phy_base + drv_data->pwrctl_offset); + usleep_range(500, 1000); + + pm_runtime_put(priv->dev); + clk_disable(priv->pclk); + + return 0; +} + +static int rockchip_inno_csidphy_init(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + + return clk_prepare(priv->pclk); +} + +static int rockchip_inno_csidphy_exit(struct phy *phy) +{ + struct rockchip_inno_csidphy *priv = phy_get_drvdata(phy); + + clk_unprepare(priv->pclk); + + return 0; +} + +static const struct phy_ops rockchip_inno_csidphy_ops = { + .power_on = rockchip_inno_csidphy_power_on, + .power_off = rockchip_inno_csidphy_power_off, + .init = rockchip_inno_csidphy_init, + .exit = rockchip_inno_csidphy_exit, + .configure = rockchip_inno_csidphy_configure, + .owner = THIS_MODULE, +}; + +static const struct dphy_drv_data rk1808_mipidphy_drv_data = { + .pwrctl_offset = -1, + .ths_settle_offset = RK1808_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = RK1808_CSIDPHY_CLK_CALIB_EN, + .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges), + .grf_regs = rk1808_grf_dphy_regs, +}; + +static const struct dphy_drv_data rk3326_mipidphy_drv_data = { + .pwrctl_offset = CSIDPHY_CTRL_PWRCTL, + .ths_settle_offset = RK3326_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = -1, + .hsfreq_ranges = rk3326_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3326_mipidphy_hsfreq_ranges), + .grf_regs = rk3326_grf_dphy_regs, +}; + +static const struct dphy_drv_data rk3368_mipidphy_drv_data = { + .pwrctl_offset = CSIDPHY_CTRL_PWRCTL, + .ths_settle_offset = RK3368_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = -1, + .hsfreq_ranges = rk3368_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk3368_mipidphy_hsfreq_ranges), + .grf_regs = rk3368_grf_dphy_regs, +}; + +static const struct of_device_id rockchip_inno_csidphy_match_id[] = { + { + .compatible = "rockchip,px30-csi-dphy", + .data = &rk3326_mipidphy_drv_data, + }, + { + .compatible = "rockchip,rk1808-csi-dphy", + .data = &rk1808_mipidphy_drv_data, + }, + { + .compatible = "rockchip,rk3326-csi-dphy", + .data = &rk3326_mipidphy_drv_data, + }, + { + .compatible = "rockchip,rk3368-csi-dphy", + .data = &rk3368_mipidphy_drv_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, rockchip_inno_csidphy_match_id); + +static int rockchip_inno_csidphy_probe(struct platform_device *pdev) +{ + struct rockchip_inno_csidphy *priv; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct phy *phy; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + platform_set_drvdata(pdev, priv); + + priv->drv_data = of_device_get_match_data(dev); + if (!priv->drv_data) { + dev_err(dev, "Can't find device data\n"); + return -ENODEV; + } + + priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(priv->grf)) { + dev_err(dev, "Can't find GRF syscon\n"); + return PTR_ERR(priv->grf); + } + + priv->phy_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->phy_base)) + return PTR_ERR(priv->phy_base); + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + dev_err(dev, "failed to get pclk\n"); + return PTR_ERR(priv->pclk); + } + + priv->rst = devm_reset_control_get(dev, "apb"); + if (IS_ERR(priv->rst)) { + dev_err(dev, "failed to get system reset control\n"); + return PTR_ERR(priv->rst); + } + + phy = devm_phy_create(dev, NULL, &rockchip_inno_csidphy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register phy provider\n"); + return PTR_ERR(phy_provider); + } + + pm_runtime_enable(dev); + + return 0; +} + +static int rockchip_inno_csidphy_remove(struct platform_device *pdev) +{ + struct rockchip_inno_csidphy *priv = platform_get_drvdata(pdev); + + pm_runtime_disable(priv->dev); + + return 0; +} + +static struct platform_driver rockchip_inno_csidphy_driver = { + .driver = { + .name = "rockchip-inno-csidphy", + .of_match_table = rockchip_inno_csidphy_match_id, + }, + .probe = rockchip_inno_csidphy_probe, + .remove = rockchip_inno_csidphy_remove, +}; + +module_platform_driver(rockchip_inno_csidphy_driver); +MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); +MODULE_DESCRIPTION("Rockchip MIPI Innosilicon CSI-DPHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c index a37f3f342642..80acca4e9e14 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c @@ -620,7 +620,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw, unsigned long parent_rate) { struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw); - const struct pre_pll_config *cfg = pre_pll_cfg_table; + const struct pre_pll_config *cfg; unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate); u32 v; int ret; @@ -774,7 +774,7 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw, unsigned long parent_rate) { struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw); - const struct pre_pll_config *cfg = pre_pll_cfg_table; + const struct pre_pll_config *cfg; unsigned long tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate); u32 val; int ret; diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 46ebdb1460a3..beacac1dd253 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1256,6 +1256,49 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = { + { + .reg = 0x100, + .num_ports = 2, + .clkout_ctl = { 0x108, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x3020, 2, 2, 0, 1 }, + .bvalid_det_st = { 0x3024, 2, 2, 0, 1 }, + .bvalid_det_clr = { 0x3028, 2, 2, 0, 1 }, + .ls_det_en = { 0x3020, 0, 0, 0, 1 }, + .ls_det_st = { 0x3024, 0, 0, 0, 1 }, + .ls_det_clr = { 0x3028, 0, 0, 0, 1 }, + .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, + .utmi_ls = { 0x0120, 5, 4, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0104, 8, 0, 0, 0x1d1 }, + .ls_det_en = { 0x3020, 1, 1, 0, 1 }, + .ls_det_st = { 0x3024, 1, 1, 0, 1 }, + .ls_det_clr = { 0x3028, 1, 1, 0, 1 }, + .utmi_ls = { 0x0120, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x0120, 19, 19, 0, 1 } + } + }, + .chg_det = { + .opmode = { 0x0100, 3, 0, 5, 1 }, + .cp_det = { 0x0120, 24, 24, 0, 1 }, + .dcp_det = { 0x0120, 23, 23, 0, 1 }, + .dp_det = { 0x0120, 25, 25, 0, 1 }, + .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, + .idp_src_en = { 0x0108, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { { .reg = 0x100, @@ -1425,6 +1468,7 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { static const struct of_device_id rockchip_usb2phy_dt_match[] = { { .compatible = "rockchip,px30-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, + { .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs }, { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c index e4adab375c73..6bdbd1f214dd 100644 --- a/drivers/phy/socionext/phy-uniphier-pcie.c +++ b/drivers/phy/socionext/phy-uniphier-pcie.c @@ -24,11 +24,13 @@ #define PORT_SEL_1 FIELD_PREP(PORT_SEL_MASK, 1) #define PCL_PHY_TEST_I 0x2000 -#define PCL_PHY_TEST_O 0x2004 #define TESTI_DAT_MASK GENMASK(13, 6) #define TESTI_ADR_MASK GENMASK(5, 1) #define TESTI_WR_EN BIT(0) +#define PCL_PHY_TEST_O 0x2004 +#define TESTO_DAT_MASK GENMASK(7, 0) + #define PCL_PHY_RESET 0x200c #define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */ #define PCL_PHY_RESET_N BIT(0) /* =1:deasssert */ @@ -77,11 +79,12 @@ static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv, val = FIELD_PREP(TESTI_DAT_MASK, 1); val |= FIELD_PREP(TESTI_ADR_MASK, reg); uniphier_pciephy_testio_write(priv, val); - val = readl(priv->base + PCL_PHY_TEST_O); + val = readl(priv->base + PCL_PHY_TEST_O) & TESTO_DAT_MASK; /* update value */ - val &= ~FIELD_PREP(TESTI_DAT_MASK, mask); - val = FIELD_PREP(TESTI_DAT_MASK, mask & param); + val &= ~mask; + val |= mask & param; + val = FIELD_PREP(TESTI_DAT_MASK, val); val |= FIELD_PREP(TESTI_ADR_MASK, reg); uniphier_pciephy_testio_write(priv, val); uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN); diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index c184f4e34584..3e491dfb2525 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -57,6 +57,7 @@ struct pll_params { struct stm32_usbphyc_phy { struct phy *phy; struct stm32_usbphyc *usbphyc; + struct regulator *vbus; u32 index; bool active; }; @@ -291,9 +292,31 @@ static int stm32_usbphyc_phy_exit(struct phy *phy) return stm32_usbphyc_pll_disable(usbphyc); } +static int stm32_usbphyc_phy_power_on(struct phy *phy) +{ + struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); + + if (usbphyc_phy->vbus) + return regulator_enable(usbphyc_phy->vbus); + + return 0; +} + +static int stm32_usbphyc_phy_power_off(struct phy *phy) +{ + struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); + + if (usbphyc_phy->vbus) + return regulator_disable(usbphyc_phy->vbus); + + return 0; +} + static const struct phy_ops stm32_usbphyc_phy_ops = { .init = stm32_usbphyc_phy_init, .exit = stm32_usbphyc_phy_exit, + .power_on = stm32_usbphyc_phy_power_on, + .power_off = stm32_usbphyc_phy_power_off, .owner = THIS_MODULE, }; @@ -519,6 +542,14 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) usbphyc->phys[port]->index = index; usbphyc->phys[port]->active = false; + usbphyc->phys[port]->vbus = devm_regulator_get_optional(&phy->dev, "vbus"); + if (IS_ERR(usbphyc->phys[port]->vbus)) { + ret = PTR_ERR(usbphyc->phys[port]->vbus); + if (ret == -EPROBE_DEFER) + goto put_child; + usbphyc->phys[port]->vbus = NULL; + } + port++; } diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index 57adc08a89b2..9fe6ea6fdae5 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -242,19 +242,28 @@ static int dm816x_usb_phy_probe(struct platform_device *pdev) pm_runtime_enable(phy->dev); generic_phy = devm_phy_create(phy->dev, NULL, &ops); - if (IS_ERR(generic_phy)) - return PTR_ERR(generic_phy); + if (IS_ERR(generic_phy)) { + error = PTR_ERR(generic_phy); + goto clk_unprepare; + } phy_set_drvdata(generic_phy, phy); phy_provider = devm_of_phy_provider_register(phy->dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); + if (IS_ERR(phy_provider)) { + error = PTR_ERR(phy_provider); + goto clk_unprepare; + } usb_add_phy_dev(&phy->phy); return 0; + +clk_unprepare: + pm_runtime_disable(phy->dev); + clk_unprepare(phy->refclk); + return error; } static int dm816x_usb_phy_remove(struct platform_device *pdev) diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c index 812e5409d359..5771e2486a3b 100644 --- a/drivers/phy/ti/phy-twl4030-usb.c +++ b/drivers/phy/ti/phy-twl4030-usb.c @@ -544,8 +544,8 @@ static int twl4030_usb_ldo_init(struct twl4030_usb *twl) return 0; } -static ssize_t twl4030_usb_vbus_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct twl4030_usb *twl = dev_get_drvdata(dev); int ret = -EINVAL; @@ -557,7 +557,7 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev, return ret; } -static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); +static DEVICE_ATTR_RO(vbus); static irqreturn_t twl4030_usb_irq(int irq, void *_twl) { diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index d40ed8621571..9610a9f08ff4 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -369,6 +369,7 @@ err_out: dev->card_link = NULL; return NULL; } +EXPORT_SYMBOL(pnp_request_card_device); /** * pnp_release_card_device - call this when the driver no longer needs the device @@ -382,6 +383,7 @@ void pnp_release_card_device(struct pnp_dev *dev) device_release_driver(&dev->dev); drv->link.remove = &card_remove_first; } +EXPORT_SYMBOL(pnp_release_card_device); /* * suspend/resume callbacks @@ -439,6 +441,7 @@ int pnp_register_card_driver(struct pnp_card_driver *drv) } return 0; } +EXPORT_SYMBOL(pnp_register_card_driver); /** * pnp_unregister_card_driver - unregisters a PnP card driver from the PnP Layer @@ -451,8 +454,4 @@ void pnp_unregister_card_driver(struct pnp_card_driver *drv) mutex_unlock(&pnp_lock); pnp_unregister_driver(&drv->link); } - -EXPORT_SYMBOL(pnp_request_card_device); -EXPORT_SYMBOL(pnp_release_card_device); -EXPORT_SYMBOL(pnp_register_card_driver); EXPORT_SYMBOL(pnp_unregister_card_driver); diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 93a30a8f88d1..c29d590c5e4f 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -68,6 +68,7 @@ int pnp_device_attach(struct pnp_dev *pnp_dev) mutex_unlock(&pnp_lock); return 0; } +EXPORT_SYMBOL(pnp_device_attach); void pnp_device_detach(struct pnp_dev *pnp_dev) { @@ -76,6 +77,7 @@ void pnp_device_detach(struct pnp_dev *pnp_dev) pnp_dev->status = PNP_READY; mutex_unlock(&pnp_lock); } +EXPORT_SYMBOL(pnp_device_detach); static int pnp_device_probe(struct device *dev) { @@ -271,11 +273,13 @@ int pnp_register_driver(struct pnp_driver *drv) return driver_register(&drv->driver); } +EXPORT_SYMBOL(pnp_register_driver); void pnp_unregister_driver(struct pnp_driver *drv) { driver_unregister(&drv->driver); } +EXPORT_SYMBOL(pnp_unregister_driver); /** * pnp_add_id - adds an EISA id to the specified device @@ -310,8 +314,3 @@ struct pnp_id *pnp_add_id(struct pnp_dev *dev, const char *id) return dev_id; } - -EXPORT_SYMBOL(pnp_register_driver); -EXPORT_SYMBOL(pnp_unregister_driver); -EXPORT_SYMBOL(pnp_device_attach); -EXPORT_SYMBOL(pnp_device_detach); diff --git a/drivers/pnp/isapnp/compat.c b/drivers/pnp/isapnp/compat.c index 035e95092489..d60d9e377da5 100644 --- a/drivers/pnp/isapnp/compat.c +++ b/drivers/pnp/isapnp/compat.c @@ -63,5 +63,4 @@ struct pnp_dev *pnp_find_dev(struct pnp_card *card, unsigned short vendor, } return NULL; } - EXPORT_SYMBOL(pnp_find_dev); diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 144055593ec8..1765d6e60a8a 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -350,6 +350,7 @@ int pnp_start_dev(struct pnp_dev *dev) dev_info(&dev->dev, "activated\n"); return 0; } +EXPORT_SYMBOL(pnp_start_dev); /** * pnp_stop_dev - low-level disable of the PnP device @@ -371,6 +372,7 @@ int pnp_stop_dev(struct pnp_dev *dev) dev_info(&dev->dev, "disabled\n"); return 0; } +EXPORT_SYMBOL(pnp_stop_dev); /** * pnp_activate_dev - activates a PnP device for use @@ -396,6 +398,7 @@ int pnp_activate_dev(struct pnp_dev *dev) dev->active = 1; return 0; } +EXPORT_SYMBOL(pnp_activate_dev); /** * pnp_disable_dev - disables device @@ -423,8 +426,4 @@ int pnp_disable_dev(struct pnp_dev *dev) return 0; } - -EXPORT_SYMBOL(pnp_start_dev); -EXPORT_SYMBOL(pnp_stop_dev); -EXPORT_SYMBOL(pnp_activate_dev); EXPORT_SYMBOL(pnp_disable_dev); diff --git a/drivers/pnp/support.c b/drivers/pnp/support.c index e4f53d31191d..a6073db10ec6 100644 --- a/drivers/pnp/support.c +++ b/drivers/pnp/support.c @@ -30,7 +30,6 @@ int pnp_is_active(struct pnp_dev *dev) else return 1; } - EXPORT_SYMBOL(pnp_is_active); /* diff --git a/drivers/siox/siox-bus-gpio.c b/drivers/siox/siox-bus-gpio.c index 46b4cda36bac..aeefeb725524 100644 --- a/drivers/siox/siox-bus-gpio.c +++ b/drivers/siox/siox-bus-gpio.c @@ -102,29 +102,29 @@ static int siox_gpio_probe(struct platform_device *pdev) ddata->din = devm_gpiod_get(dev, "din", GPIOD_IN); if (IS_ERR(ddata->din)) { - ret = PTR_ERR(ddata->din); - dev_err(dev, "Failed to get %s GPIO: %d\n", "din", ret); + ret = dev_err_probe(dev, PTR_ERR(ddata->din), + "Failed to get din GPIO\n"); goto err; } ddata->dout = devm_gpiod_get(dev, "dout", GPIOD_OUT_LOW); if (IS_ERR(ddata->dout)) { - ret = PTR_ERR(ddata->dout); - dev_err(dev, "Failed to get %s GPIO: %d\n", "dout", ret); + ret = dev_err_probe(dev, PTR_ERR(ddata->dout), + "Failed to get dout GPIO\n"); goto err; } ddata->dclk = devm_gpiod_get(dev, "dclk", GPIOD_OUT_LOW); if (IS_ERR(ddata->dclk)) { - ret = PTR_ERR(ddata->dclk); - dev_err(dev, "Failed to get %s GPIO: %d\n", "dclk", ret); + ret = dev_err_probe(dev, PTR_ERR(ddata->dclk), + "Failed to get dclk GPIO\n"); goto err; } ddata->dld = devm_gpiod_get(dev, "dld", GPIOD_OUT_LOW); if (IS_ERR(ddata->dld)) { - ret = PTR_ERR(ddata->dld); - dev_err(dev, "Failed to get %s GPIO: %d\n", "dld", ret); + ret = dev_err_probe(dev, PTR_ERR(ddata->dld), + "Failed to get dld GPIO\n"); goto err; } @@ -134,7 +134,8 @@ static int siox_gpio_probe(struct platform_device *pdev) ret = siox_master_register(smaster); if (ret) { - dev_err(dev, "Failed to register siox master: %d\n", ret); + dev_err_probe(dev, ret, + "Failed to register siox master\n"); err: siox_master_put(smaster); } diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index 016e74230bb7..2b7795233282 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -25,6 +25,7 @@ config SOUNDWIRE_INTEL tristate "Intel SoundWire Master driver" select SOUNDWIRE_CADENCE select SOUNDWIRE_GENERIC_ALLOCATION + select AUXILIARY_BUS depends on ACPI && SND_SOC help SoundWire Intel Master driver. diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 5d5b0bd59ae3..3e6d4addac2f 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -394,13 +394,13 @@ sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) } static int -sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) +sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val) { struct sdw_msg msg; int ret; ret = sdw_fill_msg(&msg, slave, addr, count, - slave->dev_num, SDW_MSG_FLAG_WRITE, val); + slave->dev_num, SDW_MSG_FLAG_WRITE, (u8 *)val); if (ret < 0) return ret; @@ -550,9 +550,9 @@ EXPORT_SYMBOL(sdw_nread); * @slave: SDW Slave * @addr: Register address * @count: length - * @val: Buffer for values to be read + * @val: Buffer for values to be written */ -int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val) +int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, const u8 *val) { int ret; @@ -836,26 +836,6 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, mutex_unlock(&bus->bus_lock); } -static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave) -{ - enum sdw_clk_stop_mode mode; - - /* - * Query for clock stop mode if Slave implements - * ops->get_clk_stop_mode, else read from property. - */ - if (slave->ops && slave->ops->get_clk_stop_mode) { - mode = slave->ops->get_clk_stop_mode(slave); - } else { - if (slave->prop.clk_stop_mode1) - mode = SDW_CLK_STOP_MODE1; - else - mode = SDW_CLK_STOP_MODE0; - } - - return mode; -} - static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, enum sdw_clk_stop_mode mode, enum sdw_clk_stop_type type) @@ -864,11 +844,8 @@ static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, if (slave->ops && slave->ops->clk_stop) { ret = slave->ops->clk_stop(slave, mode, type); - if (ret < 0) { - dev_err(&slave->dev, - "Clk Stop type =%d failed: %d\n", type, ret); + if (ret < 0) return ret; - } } return 0; @@ -895,7 +872,8 @@ static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, } else { ret = sdw_read_no_pm(slave, SDW_SCP_SYSTEMCTRL); if (ret < 0) { - dev_err(&slave->dev, "SDW_SCP_SYSTEMCTRL read failed:%d\n", ret); + if (ret != -ENODATA) + dev_err(&slave->dev, "SDW_SCP_SYSTEMCTRL read failed:%d\n", ret); return ret; } val = ret; @@ -904,9 +882,8 @@ static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, ret = sdw_write_no_pm(slave, SDW_SCP_SYSTEMCTRL, val); - if (ret < 0) - dev_err(&slave->dev, - "Clock Stop prepare failed for slave: %d", ret); + if (ret < 0 && ret != -ENODATA) + dev_err(&slave->dev, "SDW_SCP_SYSTEMCTRL write failed:%d\n", ret); return ret; } @@ -924,7 +901,7 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num) } val &= SDW_SCP_STAT_CLK_STP_NF; if (!val) { - dev_dbg(bus->dev, "clock stop prep/de-prep done slave:%d", + dev_dbg(bus->dev, "clock stop prep/de-prep done slave:%d\n", dev_num); return 0; } @@ -933,7 +910,7 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num) retry--; } while (retry); - dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d", + dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d\n", dev_num); return -ETIMEDOUT; @@ -948,7 +925,6 @@ static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num) */ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) { - enum sdw_clk_stop_mode slave_mode; bool simple_clk_stop = true; struct sdw_slave *slave; bool is_slave = false; @@ -958,6 +934,9 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) * In order to save on transition time, prepare * each Slave and then wait for all Slave(s) to be * prepared for clock stop. + * If one of the Slave devices has lost sync and + * replies with Command Ignored/-ENODATA, we continue + * the loop */ list_for_each_entry(slave, &bus->slaves, node) { if (!slave->dev_num) @@ -970,36 +949,45 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) /* Identify if Slave(s) are available on Bus */ is_slave = true; - slave_mode = sdw_get_clk_stop_mode(slave); - slave->curr_clk_stop_mode = slave_mode; - - ret = sdw_slave_clk_stop_callback(slave, slave_mode, + ret = sdw_slave_clk_stop_callback(slave, + SDW_CLK_STOP_MODE0, SDW_CLK_PRE_PREPARE); - if (ret < 0) { - dev_err(&slave->dev, - "pre-prepare failed:%d", ret); - return ret; - } - - ret = sdw_slave_clk_stop_prepare(slave, - slave_mode, true); - if (ret < 0) { - dev_err(&slave->dev, - "pre-prepare failed:%d", ret); + if (ret < 0 && ret != -ENODATA) { + dev_err(&slave->dev, "clock stop pre-prepare cb failed:%d\n", ret); return ret; } - if (slave_mode == SDW_CLK_STOP_MODE1) + /* Only prepare a Slave device if needed */ + if (!slave->prop.simple_clk_stop_capable) { simple_clk_stop = false; + + ret = sdw_slave_clk_stop_prepare(slave, + SDW_CLK_STOP_MODE0, + true); + if (ret < 0 && ret != -ENODATA) { + dev_err(&slave->dev, "clock stop prepare failed:%d\n", ret); + return ret; + } + } } /* Skip remaining clock stop preparation if no Slave is attached */ if (!is_slave) - return ret; + return 0; + /* + * Don't wait for all Slaves to be ready if they follow the simple + * state machine + */ if (!simple_clk_stop) { ret = sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); + /* + * if there are no Slave devices present and the reply is + * Command_Ignored/-ENODATA, we don't need to continue with the + * flow and can just return here. The error code is not modified + * and its handling left as an exercise for the caller. + */ if (ret < 0) return ret; } @@ -1013,21 +1001,17 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) slave->status != SDW_SLAVE_ALERT) continue; - slave_mode = slave->curr_clk_stop_mode; - - if (slave_mode == SDW_CLK_STOP_MODE1) { - ret = sdw_slave_clk_stop_callback(slave, - slave_mode, - SDW_CLK_POST_PREPARE); + ret = sdw_slave_clk_stop_callback(slave, + SDW_CLK_STOP_MODE0, + SDW_CLK_POST_PREPARE); - if (ret < 0) { - dev_err(&slave->dev, - "post-prepare failed:%d", ret); - } + if (ret < 0 && ret != -ENODATA) { + dev_err(&slave->dev, "clock stop post-prepare cb failed:%d\n", ret); + return ret; } } - return ret; + return 0; } EXPORT_SYMBOL(sdw_bus_prep_clk_stop); @@ -1050,12 +1034,8 @@ int sdw_bus_clk_stop(struct sdw_bus *bus) ret = sdw_bwrite_no_pm(bus, SDW_BROADCAST_DEV_NUM, SDW_SCP_CTRL, SDW_SCP_CTRL_CLK_STP_NOW); if (ret < 0) { - if (ret == -ENODATA) - dev_dbg(bus->dev, - "ClockStopNow Broadcast msg ignored %d", ret); - else - dev_err(bus->dev, - "ClockStopNow Broadcast msg failed %d", ret); + if (ret != -ENODATA) + dev_err(bus->dev, "ClockStopNow Broadcast msg failed %d\n", ret); return ret; } @@ -1074,7 +1054,6 @@ EXPORT_SYMBOL(sdw_bus_clk_stop); */ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) { - enum sdw_clk_stop_mode mode; bool simple_clk_stop = true; struct sdw_slave *slave; bool is_slave = false; @@ -1096,33 +1075,36 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) /* Identify if Slave(s) are available on Bus */ is_slave = true; - mode = slave->curr_clk_stop_mode; - - if (mode == SDW_CLK_STOP_MODE1) { - simple_clk_stop = false; - continue; - } - - ret = sdw_slave_clk_stop_callback(slave, mode, + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0, SDW_CLK_PRE_DEPREPARE); if (ret < 0) - dev_warn(&slave->dev, - "clk stop deprep failed:%d", ret); + dev_warn(&slave->dev, "clock stop pre-deprepare cb failed:%d\n", ret); - ret = sdw_slave_clk_stop_prepare(slave, mode, - false); + /* Only de-prepare a Slave device if needed */ + if (!slave->prop.simple_clk_stop_capable) { + simple_clk_stop = false; - if (ret < 0) - dev_warn(&slave->dev, - "clk stop deprep failed:%d", ret); + ret = sdw_slave_clk_stop_prepare(slave, SDW_CLK_STOP_MODE0, + false); + + if (ret < 0) + dev_warn(&slave->dev, "clock stop deprepare failed:%d\n", ret); + } } /* Skip remaining clock stop de-preparation if no Slave is attached */ if (!is_slave) return 0; - if (!simple_clk_stop) - sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); + /* + * Don't wait for all Slaves to be ready if they follow the simple + * state machine + */ + if (!simple_clk_stop) { + ret = sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); + if (ret < 0) + dev_warn(&slave->dev, "clock stop deprepare wait failed:%d\n", ret); + } list_for_each_entry(slave, &bus->slaves, node) { if (!slave->dev_num) @@ -1132,9 +1114,10 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) slave->status != SDW_SLAVE_ALERT) continue; - mode = slave->curr_clk_stop_mode; - sdw_slave_clk_stop_callback(slave, mode, - SDW_CLK_POST_DEPREPARE); + ret = sdw_slave_clk_stop_callback(slave, SDW_CLK_STOP_MODE0, + SDW_CLK_POST_DEPREPARE); + if (ret < 0) + dev_warn(&slave->dev, "clock stop post-deprepare cb failed:%d\n", ret); } return 0; diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 192dac10f0c2..25950422b085 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1428,20 +1428,6 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake) } } - /* - * This CMD_ACCEPT should be used when there are no devices - * attached on the link when entering clock stop mode. If this is - * not set and there is a broadcast write then the command ignored - * will be treated as a failure - */ - if (!slave_present) - cdns_updatel(cdns, CDNS_MCP_CONTROL, - CDNS_MCP_CONTROL_CMD_ACCEPT, - CDNS_MCP_CONTROL_CMD_ACCEPT); - else - cdns_updatel(cdns, CDNS_MCP_CONTROL, - CDNS_MCP_CONTROL_CMD_ACCEPT, 0); - /* commit changes */ ret = cdns_config_update(cdns); if (ret < 0) { @@ -1508,11 +1494,8 @@ int sdw_cdns_clock_restart(struct sdw_cdns *cdns, bool bus_reset) cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_BLOCK_WAKEUP, 0); - /* - * clear CMD_ACCEPT so that the command ignored - * will be treated as a failure during a broadcast write - */ - cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, 0); + cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, + CDNS_MCP_CONTROL_CMD_ACCEPT); if (!bus_reset) { diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index 4d1aab5b5ec2..0e7f8b35bb21 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -180,9 +180,6 @@ enum sdw_command_response cdns_xfer_msg_defer(struct sdw_bus *bus, struct sdw_msg *msg, struct sdw_defer *defer); -enum sdw_command_response -cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num); - int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params); int cdns_set_sdw_stream(struct snd_soc_dai *dai, diff --git a/drivers/soundwire/dmi-quirks.c b/drivers/soundwire/dmi-quirks.c index 82061c1d9835..5db0a2443a1d 100644 --- a/drivers/soundwire/dmi-quirks.c +++ b/drivers/soundwire/dmi-quirks.c @@ -80,7 +80,7 @@ u64 sdw_dmi_override_adr(struct sdw_bus *bus, u64 addr) /* check if any address remap quirk applies */ dmi_id = dmi_first_match(adr_remap_quirk_table); if (dmi_id) { - struct adr_remap *map = dmi_id->driver_data; + struct adr_remap *map; for (map = dmi_id->driver_data; map->adr; map++) { if (map->adr == addr) { diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c index 84d129587084..f7c66083a4dd 100644 --- a/drivers/soundwire/generic_bandwidth_allocation.c +++ b/drivers/soundwire/generic_bandwidth_allocation.c @@ -382,12 +382,18 @@ static int sdw_compute_bus_params(struct sdw_bus *bus) */ } - if (i == clk_values) + if (i == clk_values) { + dev_err(bus->dev, "%s: could not find clock value for bandwidth %d\n", + __func__, bus->params.bandwidth); return -EINVAL; + } ret = sdw_select_row_col(bus, curr_dr_freq); - if (ret < 0) + if (ret < 0) { + dev_err(bus->dev, "%s: could not find frame configuration for bus dr_freq %d\n", + __func__, curr_dr_freq); return -EINVAL; + } bus->params.curr_dr_freq = curr_dr_freq; return 0; @@ -404,10 +410,8 @@ int sdw_compute_params(struct sdw_bus *bus) /* Computes clock frequency, frame shape and frame frequency */ ret = sdw_compute_bus_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Compute bus params failed: %d\n", ret); + if (ret < 0) return ret; - } /* Compute transport and port params */ ret = sdw_compute_port_params(bus); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index fd95f94630b1..c11e3d8cd308 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -11,7 +11,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/platform_device.h> +#include <linux/auxiliary_bus.h> #include <sound/pcm_params.h> #include <linux/pm_runtime.h> #include <sound/soc.h> @@ -1327,11 +1327,14 @@ static int intel_init(struct sdw_intel *sdw) } /* - * probe and init + * probe and init (aux_dev_id argument is required by function prototype but not used) */ -static int intel_master_probe(struct platform_device *pdev) +static int intel_link_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *aux_dev_id) + { - struct device *dev = &pdev->dev; + struct device *dev = &auxdev->dev; + struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev); struct sdw_intel *sdw; struct sdw_cdns *cdns; struct sdw_bus *bus; @@ -1344,14 +1347,14 @@ static int intel_master_probe(struct platform_device *pdev) cdns = &sdw->cdns; bus = &cdns->bus; - sdw->instance = pdev->id; - sdw->link_res = dev_get_platdata(dev); + sdw->instance = auxdev->id; + sdw->link_res = &ldev->link_res; cdns->dev = dev; cdns->registers = sdw->link_res->registers; cdns->instance = sdw->instance; cdns->msg_count = 0; - bus->link_id = pdev->id; + bus->link_id = auxdev->id; sdw_cdns_probe(cdns); @@ -1384,10 +1387,10 @@ static int intel_master_probe(struct platform_device *pdev) return 0; } -int intel_master_startup(struct platform_device *pdev) +int intel_link_startup(struct auxiliary_device *auxdev) { struct sdw_cdns_stream_config config; - struct device *dev = &pdev->dev; + struct device *dev = &auxdev->dev; struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; @@ -1524,9 +1527,9 @@ err_init: return ret; } -static int intel_master_remove(struct platform_device *pdev) +static void intel_link_remove(struct auxiliary_device *auxdev) { - struct device *dev = &pdev->dev; + struct device *dev = &auxdev->dev; struct sdw_cdns *cdns = dev_get_drvdata(dev); struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; @@ -1542,19 +1545,17 @@ static int intel_master_remove(struct platform_device *pdev) snd_soc_unregister_component(dev); } sdw_bus_master_delete(bus); - - return 0; } -int intel_master_process_wakeen_event(struct platform_device *pdev) +int intel_link_process_wakeen_event(struct auxiliary_device *auxdev) { - struct device *dev = &pdev->dev; + struct device *dev = &auxdev->dev; struct sdw_intel *sdw; struct sdw_bus *bus; void __iomem *shim; u16 wake_sts; - sdw = platform_get_drvdata(pdev); + sdw = dev_get_drvdata(dev); bus = &sdw->cdns.bus; if (bus->prop.hw_disabled) { @@ -1976,17 +1977,22 @@ static const struct dev_pm_ops intel_pm = { SET_RUNTIME_PM_OPS(intel_suspend_runtime, intel_resume_runtime, NULL) }; -static struct platform_driver sdw_intel_drv = { - .probe = intel_master_probe, - .remove = intel_master_remove, +static const struct auxiliary_device_id intel_link_id_table[] = { + { .name = "soundwire_intel.link" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, intel_link_id_table); + +static struct auxiliary_driver sdw_intel_drv = { + .probe = intel_link_probe, + .remove = intel_link_remove, .driver = { - .name = "intel-sdw", + /* auxiliary_driver_register() sets .name to be the modname */ .pm = &intel_pm, - } + }, + .id_table = intel_link_id_table }; - -module_platform_driver(sdw_intel_drv); +module_auxiliary_driver(sdw_intel_drv); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS("platform:intel-sdw"); -MODULE_DESCRIPTION("Intel Soundwire Master Driver"); +MODULE_DESCRIPTION("Intel Soundwire Link Driver"); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 06bac8ba14e9..0b47b148da3f 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -7,7 +7,6 @@ /** * struct sdw_intel_link_res - Soundwire Intel link resource structure, * typically populated by the controller driver. - * @pdev: platform_device * @mmio_base: mmio base of SoundWire registers * @registers: Link IO registers base * @shim: Audio shim pointer @@ -23,7 +22,6 @@ * @list: used to walk-through all masters exposed by the same controller */ struct sdw_intel_link_res { - struct platform_device *pdev; void __iomem *mmio_base; /* not strictly needed, useful for debug */ void __iomem *registers; void __iomem *shim; @@ -48,7 +46,15 @@ struct sdw_intel { #endif }; -int intel_master_startup(struct platform_device *pdev); -int intel_master_process_wakeen_event(struct platform_device *pdev); +int intel_link_startup(struct auxiliary_device *auxdev); +int intel_link_process_wakeen_event(struct auxiliary_device *auxdev); + +struct sdw_intel_link_dev { + struct auxiliary_device auxdev; + struct sdw_intel_link_res link_res; +}; + +#define auxiliary_dev_to_sdw_intel_link_dev(auxiliary_dev) \ + container_of(auxiliary_dev, struct sdw_intel_link_dev, auxdev) #endif /* __SDW_INTEL_LOCAL_H */ diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 30ce95ec2d70..9e283bef53d2 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -12,7 +12,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/platform_device.h> +#include <linux/auxiliary_bus.h> #include <linux/pm_runtime.h> #include <linux/soundwire/sdw_intel.h> #include "cadence_master.h" @@ -24,28 +24,108 @@ #define SDW_LINK_BASE 0x30000 #define SDW_LINK_SIZE 0x10000 +static void intel_link_dev_release(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev); + + kfree(ldev); +} + +/* alloc, init and add link devices */ +static struct sdw_intel_link_dev *intel_link_dev_register(struct sdw_intel_res *res, + struct sdw_intel_ctx *ctx, + struct fwnode_handle *fwnode, + const char *name, + int link_id) +{ + struct sdw_intel_link_dev *ldev; + struct sdw_intel_link_res *link; + struct auxiliary_device *auxdev; + int ret; + + ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); + if (!ldev) + return ERR_PTR(-ENOMEM); + + auxdev = &ldev->auxdev; + auxdev->name = name; + auxdev->dev.parent = res->parent; + auxdev->dev.fwnode = fwnode; + auxdev->dev.release = intel_link_dev_release; + + /* we don't use an IDA since we already have a link ID */ + auxdev->id = link_id; + + /* + * keep a handle on the allocated memory, to be used in all other functions. + * Since the same pattern is used to skip links that are not enabled, there is + * no need to check if ctx->ldev[i] is NULL later on. + */ + ctx->ldev[link_id] = ldev; + + /* Add link information used in the driver probe */ + link = &ldev->link_res; + link->mmio_base = res->mmio_base; + link->registers = res->mmio_base + SDW_LINK_BASE + + (SDW_LINK_SIZE * link_id); + link->shim = res->mmio_base + SDW_SHIM_BASE; + link->alh = res->mmio_base + SDW_ALH_BASE; + + link->ops = res->ops; + link->dev = res->dev; + + link->clock_stop_quirks = res->clock_stop_quirks; + link->shim_lock = &ctx->shim_lock; + link->shim_mask = &ctx->shim_mask; + link->link_mask = ctx->link_mask; + + /* now follow the two-step init/add sequence */ + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + dev_err(res->parent, "failed to initialize link dev %s link_id %d\n", + name, link_id); + kfree(ldev); + return ERR_PTR(ret); + } + + ret = auxiliary_device_add(&ldev->auxdev); + if (ret < 0) { + dev_err(res->parent, "failed to add link dev %s link_id %d\n", + ldev->auxdev.name, link_id); + /* ldev will be freed with the put_device() and .release sequence */ + auxiliary_device_uninit(&ldev->auxdev); + return ERR_PTR(ret); + } + + return ldev; +} + +static void intel_link_dev_unregister(struct sdw_intel_link_dev *ldev) +{ + auxiliary_device_delete(&ldev->auxdev); + auxiliary_device_uninit(&ldev->auxdev); +} + static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx) { - struct sdw_intel_link_res *link = ctx->links; + struct sdw_intel_link_dev *ldev; u32 link_mask; int i; - if (!link) - return 0; - link_mask = ctx->link_mask; - for (i = 0; i < ctx->count; i++, link++) { + for (i = 0; i < ctx->count; i++) { if (!(link_mask & BIT(i))) continue; - if (link->pdev) { - pm_runtime_disable(&link->pdev->dev); - platform_device_unregister(link->pdev); - } + ldev = ctx->ldev[i]; - if (!link->clock_stop_quirks) - pm_runtime_put_noidle(link->dev); + pm_runtime_disable(&ldev->auxdev.dev); + if (!ldev->link_res.clock_stop_quirks) + pm_runtime_put_noidle(ldev->link_res.dev); + + intel_link_dev_unregister(ldev); } return 0; @@ -91,9 +171,8 @@ EXPORT_SYMBOL_NS(sdw_intel_thread, SOUNDWIRE_INTEL_INIT); static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { - struct platform_device_info pdevinfo; - struct platform_device *pdev; struct sdw_intel_link_res *link; + struct sdw_intel_link_dev *ldev; struct sdw_intel_ctx *ctx; struct acpi_device *adev; struct sdw_slave *slave; @@ -116,67 +195,60 @@ static struct sdw_intel_ctx count = res->count; dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); - ctx = devm_kzalloc(&adev->dev, sizeof(*ctx), GFP_KERNEL); + /* + * we need to alloc/free memory manually and can't use devm: + * this routine may be called from a workqueue, and not from + * the parent .probe. + * If devm_ was used, the memory might never be freed on errors. + */ + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; ctx->count = count; - ctx->links = devm_kcalloc(&adev->dev, ctx->count, - sizeof(*ctx->links), GFP_KERNEL); - if (!ctx->links) + + /* + * allocate the array of pointers. The link-specific data is allocated + * as part of the first loop below and released with the auxiliary_device_uninit(). + * If some links are disabled, the link pointer will remain NULL. Given that the + * number of links is small, this is simpler than using a list to keep track of links. + */ + ctx->ldev = kcalloc(ctx->count, sizeof(*ctx->ldev), GFP_KERNEL); + if (!ctx->ldev) { + kfree(ctx); return NULL; + } - ctx->count = count; ctx->mmio_base = res->mmio_base; ctx->link_mask = res->link_mask; ctx->handle = res->handle; mutex_init(&ctx->shim_lock); - link = ctx->links; link_mask = ctx->link_mask; INIT_LIST_HEAD(&ctx->link_list); - /* Create SDW Master devices */ - for (i = 0; i < count; i++, link++) { - if (!(link_mask & BIT(i))) { - dev_dbg(&adev->dev, - "Link %d masked, will not be enabled\n", i); + for (i = 0; i < count; i++) { + if (!(link_mask & BIT(i))) continue; - } - link->mmio_base = res->mmio_base; - link->registers = res->mmio_base + SDW_LINK_BASE - + (SDW_LINK_SIZE * i); - link->shim = res->mmio_base + SDW_SHIM_BASE; - link->alh = res->mmio_base + SDW_ALH_BASE; - - link->ops = res->ops; - link->dev = res->dev; - - link->clock_stop_quirks = res->clock_stop_quirks; - link->shim_lock = &ctx->shim_lock; - link->shim_mask = &ctx->shim_mask; - link->link_mask = link_mask; - - memset(&pdevinfo, 0, sizeof(pdevinfo)); - - pdevinfo.parent = res->parent; - pdevinfo.name = "intel-sdw"; - pdevinfo.id = i; - pdevinfo.fwnode = acpi_fwnode_handle(adev); - pdevinfo.data = link; - pdevinfo.size_data = sizeof(*link); - - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) { - dev_err(&adev->dev, - "platform device creation failed: %ld\n", - PTR_ERR(pdev)); + /* + * init and add a device for each link + * + * The name of the device will be soundwire_intel.link.[i], + * with the "soundwire_intel" module prefix automatically added + * by the auxiliary bus core. + */ + ldev = intel_link_dev_register(res, + ctx, + acpi_fwnode_handle(adev), + "link", + i); + if (IS_ERR(ldev)) goto err; - } - link->pdev = pdev; - link->cdns = platform_get_drvdata(pdev); + + link = &ldev->link_res; + link->cdns = dev_get_drvdata(&ldev->auxdev.dev); if (!link->cdns) { dev_err(&adev->dev, "failed to get link->cdns\n"); @@ -194,8 +266,7 @@ static struct sdw_intel_ctx num_slaves++; } - ctx->ids = devm_kcalloc(&adev->dev, num_slaves, - sizeof(*ctx->ids), GFP_KERNEL); + ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL); if (!ctx->ids) goto err; @@ -213,8 +284,14 @@ static struct sdw_intel_ctx return ctx; err: - ctx->count = i; - sdw_intel_cleanup(ctx); + while (i--) { + if (!(link_mask & BIT(i))) + continue; + ldev = ctx->ldev[i]; + intel_link_dev_unregister(ldev); + } + kfree(ctx->ldev); + kfree(ctx); return NULL; } @@ -222,7 +299,7 @@ static int sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) { struct acpi_device *adev; - struct sdw_intel_link_res *link; + struct sdw_intel_link_dev *ldev; u32 caps; u32 link_mask; int i; @@ -241,27 +318,28 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) return -EINVAL; } - if (!ctx->links) + if (!ctx->ldev) return -EINVAL; - link = ctx->links; link_mask = ctx->link_mask; /* Startup SDW Master devices */ - for (i = 0; i < ctx->count; i++, link++) { + for (i = 0; i < ctx->count; i++) { if (!(link_mask & BIT(i))) continue; - intel_master_startup(link->pdev); + ldev = ctx->ldev[i]; + + intel_link_startup(&ldev->auxdev); - if (!link->clock_stop_quirks) { + if (!ldev->link_res.clock_stop_quirks) { /* * we need to prevent the parent PCI device * from entering pm_runtime suspend, so that * power rails to the SoundWire IP are not * turned off. */ - pm_runtime_get_noresume(link->dev); + pm_runtime_get_noresume(ldev->link_res.dev); } } @@ -272,8 +350,8 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) * sdw_intel_probe() - SoundWire Intel probe routine * @res: resource data * - * This registers a platform device for each Master handled by the controller, - * and SoundWire Master and Slave devices will be created by the platform + * This registers an auxiliary device for each Master handled by the controller, + * and SoundWire Master and Slave devices will be created by the auxiliary * device probe. All the information necessary is stored in the context, and * the res argument pointer can be freed after this step. * This function will be called after sdw_intel_acpi_scan() by SOF probe. @@ -306,27 +384,31 @@ EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT); void sdw_intel_exit(struct sdw_intel_ctx *ctx) { sdw_intel_cleanup(ctx); + kfree(ctx->ids); + kfree(ctx->ldev); + kfree(ctx); } EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT); void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx) { - struct sdw_intel_link_res *link; + struct sdw_intel_link_dev *ldev; u32 link_mask; int i; - if (!ctx->links) + if (!ctx->ldev) return; - link = ctx->links; link_mask = ctx->link_mask; /* Startup SDW Master devices */ - for (i = 0; i < ctx->count; i++, link++) { + for (i = 0; i < ctx->count; i++) { if (!(link_mask & BIT(i))) continue; - intel_master_process_wakeen_event(link->pdev); + ldev = ctx->ldev[i]; + + intel_link_process_wakeen_event(&ldev->auxdev); } } EXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT); diff --git a/drivers/soundwire/slave.c b/drivers/soundwire/slave.c index 0eed38a79c6d..669d7573320b 100644 --- a/drivers/soundwire/slave.c +++ b/drivers/soundwire/slave.c @@ -39,12 +39,12 @@ int sdw_slave_add(struct sdw_bus *bus, if (id->unique_id == SDW_IGNORED_UNIQUE_ID) { /* name shall be sdw:link:mfg:part:class */ - dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x", + dev_set_name(&slave->dev, "sdw:%01x:%04x:%04x:%02x", bus->link_id, id->mfg_id, id->part_id, id->class_id); } else { /* name shall be sdw:link:mfg:part:class:unique */ - dev_set_name(&slave->dev, "sdw:%x:%x:%x:%x:%x", + dev_set_name(&slave->dev, "sdw:%01x:%04x:%04x:%02x:%01x", bus->link_id, id->mfg_id, id->part_id, id->class_id, id->unique_id); } diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 1eaedaaba094..1a18308f4ef4 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -422,7 +422,6 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, struct completion *port_ready; struct sdw_dpn_prop *dpn_prop; struct sdw_prepare_ch prep_ch; - unsigned int time_left; bool intr = false; int ret = 0, val; u32 addr; @@ -479,15 +478,15 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus, /* Wait for completion on port ready */ port_ready = &s_rt->slave->port_ready[prep_ch.num]; - time_left = wait_for_completion_timeout(port_ready, - msecs_to_jiffies(dpn_prop->ch_prep_timeout)); + wait_for_completion_timeout(port_ready, + msecs_to_jiffies(dpn_prop->ch_prep_timeout)); val = sdw_read(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num)); - val &= p_rt->ch_mask; - if (!time_left || val) { + if ((val < 0) || (val & p_rt->ch_mask)) { + ret = (val < 0) ? val : -ETIMEDOUT; dev_err(&s_rt->slave->dev, - "Chn prep failed for port:%d\n", prep_ch.num); - return -ETIMEDOUT; + "Chn prep failed for port %d: %d\n", prep_ch.num, ret); + return ret; } } diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 0a3a71e14df4..0c9b291ef307 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -668,7 +668,7 @@ free_port: * * Return: status of removal */ -static int vcc_remove(struct vio_dev *vdev) +static void vcc_remove(struct vio_dev *vdev) { struct vcc_port *port = dev_get_drvdata(&vdev->dev); @@ -703,8 +703,6 @@ static int vcc_remove(struct vio_dev *vdev) kfree(port->domain); kfree(port); } - - return 0; } static const struct vio_device_id vcc_match[] = { diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 5531f3afeb21..2e16c5338e5b 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -18,7 +18,7 @@ config UIO_CIF depends on PCI help Driver for Hilscher CIF DeviceNet and Profibus cards. This - driver requires a userspace component called cif that handles + driver requires a userspace component called cif that handles all of the heavy lifting and can be found at: <http://www.osadl.org/projects/downloads/UIO/user/> diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c index 32357f8a92b5..64eafd59e6e7 100644 --- a/drivers/uio/uio_aec.c +++ b/drivers/uio/uio_aec.c @@ -133,7 +133,7 @@ static void remove(struct pci_dev *pdev) uio_unregister_device(info); pci_release_regions(pdev); pci_disable_device(pdev); - iounmap(info->priv); + pci_iounmap(pdev, info->priv); } static struct pci_driver pci_driver = { diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c index 3bb0b0075467..e03f9b532a96 100644 --- a/drivers/uio/uio_pci_generic.c +++ b/drivers/uio/uio_pci_generic.c @@ -72,7 +72,9 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct uio_pci_generic_dev *gdev; + struct uio_mem *uiomem; int err; + int i; err = pcim_enable_device(pdev); if (err) { @@ -101,6 +103,36 @@ static int probe(struct pci_dev *pdev, "no support for interrupts?\n"); } + uiomem = &gdev->info.mem[0]; + for (i = 0; i < MAX_UIO_MAPS; ++i) { + struct resource *r = &pdev->resource[i]; + + if (r->flags != (IORESOURCE_SIZEALIGN | IORESOURCE_MEM)) + continue; + + if (uiomem >= &gdev->info.mem[MAX_UIO_MAPS]) { + dev_warn( + &pdev->dev, + "device has more than " __stringify( + MAX_UIO_MAPS) " I/O memory resources.\n"); + break; + } + + uiomem->memtype = UIO_MEM_PHYS; + uiomem->addr = r->start & PAGE_MASK; + uiomem->offs = r->start & ~PAGE_MASK; + uiomem->size = + (uiomem->offs + resource_size(r) + PAGE_SIZE - 1) & + PAGE_MASK; + uiomem->name = r->name; + ++uiomem; + } + + while (uiomem < &gdev->info.mem[MAX_UIO_MAPS]) { + uiomem->size = 0; + ++uiomem; + } + return devm_uio_register_device(&pdev->dev, &gdev->info); } diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5e1ad0c92bfc..d33c5cd684c0 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -2209,7 +2209,6 @@ config FB_SIMPLE config FB_SSD1307 tristate "Solomon SSD1307 framebuffer support" depends on FB && I2C - depends on OF depends on GPIOLIB || COMPILE_TEST select FB_SYS_FOPS select FB_SYS_FILLRECT diff --git a/drivers/virt/nitro_enclaves/ne_pci_dev.c b/drivers/virt/nitro_enclaves/ne_pci_dev.c index b9c1de41e300..143207e9b969 100644 --- a/drivers/virt/nitro_enclaves/ne_pci_dev.c +++ b/drivers/virt/nitro_enclaves/ne_pci_dev.c @@ -480,6 +480,8 @@ static int ne_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto free_ne_pci_dev; } + pci_set_master(pdev); + rc = pci_request_regions_exclusive(pdev, "nitro_enclaves"); if (rc < 0) { dev_err(&pdev->dev, "Error in pci request regions [rc=%d]\n", rc); diff --git a/drivers/visorbus/visorchipset.c b/drivers/visorbus/visorchipset.c index cb1eb7e05f87..5668cad86e37 100644 --- a/drivers/visorbus/visorchipset.c +++ b/drivers/visorbus/visorchipset.c @@ -1561,7 +1561,7 @@ schedule_out: static int visorchipset_init(struct acpi_device *acpi_device) { - int err = -ENODEV; + int err = -ENOMEM; struct visorchannel *controlvm_channel; chipset_dev = kzalloc(sizeof(*chipset_dev), GFP_KERNEL); @@ -1584,8 +1584,10 @@ static int visorchipset_init(struct acpi_device *acpi_device) "controlvm", sizeof(struct visor_controlvm_channel), VISOR_CONTROLVM_CHANNEL_VERSIONID, - VISOR_CHANNEL_SIGNATURE)) + VISOR_CHANNEL_SIGNATURE)) { + err = -ENODEV; goto error_delete_groups; + } /* if booting in a crash kernel */ if (is_kdump_kernel()) INIT_DELAYED_WORK(&chipset_dev->periodic_controlvm_work, diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index b471779c3e2c..6c962e88501c 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * ds2482.c - provides i2c to w1-master bridge(s) * Copyright (C) 2005 Ben Gardner <bgardner@wabtec.com> * @@ -19,7 +19,7 @@ #include <linux/w1.h> -/** +/* * Allow the active pullup to be disabled, default is enabled. * * Note from the DS2482 datasheet: @@ -39,7 +39,7 @@ static int extra_config; module_param(extra_config, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8=1WS"); -/** +/* * The DS2482 registers - there are 3 registers that are addressed by a read * pointer. The read pointer is set by the last command executed. * @@ -62,7 +62,7 @@ MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8 #define DS2482_PTR_CODE_CHANNEL 0xD2 /* DS2482-800 only */ #define DS2482_PTR_CODE_CONFIG 0xC3 -/** +/* * Configure Register bit definitions * The top 4 bits always read 0. * To write, the top nibble must be the 1's compl. of the low nibble. @@ -73,7 +73,7 @@ MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8 #define DS2482_REG_CFG_APU 0x01 /* active pull-up */ -/** +/* * Write and verify codes for the CHANNEL_SELECT command (DS2482-800 only). * To set the channel, write the value at the index of the channel. * Read and compare against the corresponding value to verify the change. @@ -84,7 +84,7 @@ static const u8 ds2482_chan_rd[8] = { 0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87 }; -/** +/* * Status Register bit definitions (read only) */ #define DS2482_REG_STS_DIR 0x80 @@ -124,9 +124,9 @@ struct ds2482_data { /** - * Helper to calculate values for configuration register - * @param conf the raw config value - * @return the value w/ complements that can be written to register + * ds2482_calculate_config - Helper to calculate values for configuration register + * @conf: the raw config value + * Return: the value w/ complements that can be written to register */ static inline u8 ds2482_calculate_config(u8 conf) { @@ -140,10 +140,10 @@ static inline u8 ds2482_calculate_config(u8 conf) /** - * Sets the read pointer. - * @param pdev The ds2482 client pointer - * @param read_ptr see DS2482_PTR_CODE_xxx above - * @return -1 on failure, 0 on success + * ds2482_select_register - Sets the read pointer. + * @pdev: The ds2482 client pointer + * @read_ptr: see DS2482_PTR_CODE_xxx above + * Return: -1 on failure, 0 on success */ static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr) { @@ -159,12 +159,12 @@ static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr) } /** - * Sends a command without a parameter - * @param pdev The ds2482 client pointer - * @param cmd DS2482_CMD_RESET, + * ds2482_send_cmd - Sends a command without a parameter + * @pdev: The ds2482 client pointer + * @cmd: DS2482_CMD_RESET, * DS2482_CMD_1WIRE_RESET, * DS2482_CMD_1WIRE_READ_BYTE - * @return -1 on failure, 0 on success + * Return: -1 on failure, 0 on success */ static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd) { @@ -176,14 +176,14 @@ static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd) } /** - * Sends a command with a parameter - * @param pdev The ds2482 client pointer - * @param cmd DS2482_CMD_WRITE_CONFIG, + * ds2482_send_cmd_data - Sends a command with a parameter + * @pdev: The ds2482 client pointer + * @cmd: DS2482_CMD_WRITE_CONFIG, * DS2482_CMD_1WIRE_SINGLE_BIT, * DS2482_CMD_1WIRE_WRITE_BYTE, * DS2482_CMD_1WIRE_TRIPLET - * @param byte The data to send - * @return -1 on failure, 0 on success + * @byte: The data to send + * Return: -1 on failure, 0 on success */ static inline int ds2482_send_cmd_data(struct ds2482_data *pdev, u8 cmd, u8 byte) @@ -205,10 +205,10 @@ static inline int ds2482_send_cmd_data(struct ds2482_data *pdev, #define DS2482_WAIT_IDLE_TIMEOUT 100 /** - * Waits until the 1-wire interface is idle (not busy) + * ds2482_wait_1wire_idle - Waits until the 1-wire interface is idle (not busy) * - * @param pdev Pointer to the device structure - * @return the last value read from status or -1 (failure) + * @pdev: Pointer to the device structure + * Return: the last value read from status or -1 (failure) */ static int ds2482_wait_1wire_idle(struct ds2482_data *pdev) { @@ -230,12 +230,12 @@ static int ds2482_wait_1wire_idle(struct ds2482_data *pdev) } /** - * Selects a w1 channel. + * ds2482_set_channel - Selects a w1 channel. * The 1-wire interface must be idle before calling this function. * - * @param pdev The ds2482 client pointer - * @param channel 0-7 - * @return -1 (failure) or 0 (success) + * @pdev: The ds2482 client pointer + * @channel: 0-7 + * Return: -1 (failure) or 0 (success) */ static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel) { @@ -254,11 +254,11 @@ static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel) /** - * Performs the touch-bit function, which writes a 0 or 1 and reads the level. + * ds2482_w1_touch_bit - Performs the touch-bit function, which writes a 0 or 1 and reads the level. * - * @param data The ds2482 channel pointer - * @param bit The level to write: 0 or non-zero - * @return The level read: 0 or 1 + * @data: The ds2482 channel pointer + * @bit: The level to write: 0 or non-zero + * Return: The level read: 0 or 1 */ static u8 ds2482_w1_touch_bit(void *data, u8 bit) { @@ -284,13 +284,13 @@ static u8 ds2482_w1_touch_bit(void *data, u8 bit) } /** - * Performs the triplet function, which reads two bits and writes a bit. + * ds2482_w1_triplet - Performs the triplet function, which reads two bits and writes a bit. * The bit written is determined by the two reads: * 00 => dbit, 01 => 0, 10 => 1 * - * @param data The ds2482 channel pointer - * @param dbit The direction to choose if both branches are valid - * @return b0=read1 b1=read2 b3=bit written + * @data: The ds2482 channel pointer + * @dbit: The direction to choose if both branches are valid + * Return: b0=read1 b1=read2 b3=bit written */ static u8 ds2482_w1_triplet(void *data, u8 dbit) { @@ -317,10 +317,10 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit) } /** - * Performs the write byte function. + * ds2482_w1_write_byte - Performs the write byte function. * - * @param data The ds2482 channel pointer - * @param byte The value to write + * @data: The ds2482 channel pointer + * @byte: The value to write */ static void ds2482_w1_write_byte(void *data, u8 byte) { @@ -341,10 +341,10 @@ static void ds2482_w1_write_byte(void *data, u8 byte) } /** - * Performs the read byte function. + * ds2482_w1_read_byte - Performs the read byte function. * - * @param data The ds2482 channel pointer - * @return The value read + * @data: The ds2482 channel pointer + * Return: The value read */ static u8 ds2482_w1_read_byte(void *data) { @@ -378,10 +378,10 @@ static u8 ds2482_w1_read_byte(void *data) /** - * Sends a reset on the 1-wire interface + * ds2482_w1_reset_bus - Sends a reset on the 1-wire interface * - * @param data The ds2482 channel pointer - * @return 0=Device present, 1=No device present or error + * @data: The ds2482 channel pointer + * Return: 0=Device present, 1=No device present or error */ static u8 ds2482_w1_reset_bus(void *data) { @@ -541,7 +541,7 @@ static int ds2482_remove(struct i2c_client *client) return 0; } -/** +/* * Driver data (common to all clients) */ static const struct i2c_device_id ds2482_id[] = { diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c index 5cfb0ae23e91..ca64f99c8f3d 100644 --- a/drivers/w1/slaves/w1_ds2438.c +++ b/drivers/w1/slaves/w1_ds2438.c @@ -49,6 +49,15 @@ #define DS2438_CURRENT_MSB 0x06 #define DS2438_THRESHOLD 0x07 +/* Page #1 definitions */ +#define DS2438_ETM_0 0x00 +#define DS2438_ETM_1 0x01 +#define DS2438_ETM_2 0x02 +#define DS2438_ETM_3 0x03 +#define DS2438_ICA 0x04 +#define DS2438_OFFSET_LSB 0x05 +#define DS2438_OFFSET_MSB 0x06 + static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf) { unsigned int retries = W1_DS2438_RETRIES; @@ -62,13 +71,13 @@ static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf) if (w1_reset_select_slave(sl)) continue; w1_buf[0] = W1_DS2438_RECALL_MEMORY; - w1_buf[1] = 0x00; + w1_buf[1] = (u8)pageno; w1_write_block(sl->master, w1_buf, 2); if (w1_reset_select_slave(sl)) continue; w1_buf[0] = W1_DS2438_READ_SCRATCH; - w1_buf[1] = 0x00; + w1_buf[1] = (u8)pageno; w1_write_block(sl->master, w1_buf, 2); count = w1_read_block(sl->master, buf, DS2438_PAGE_SIZE + 1); @@ -154,11 +163,11 @@ static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) if ((status & mask) == value) return 0; /* already set as requested */ - else { - /* changing bit */ - status ^= mask; - perform_write = 1; - } + + /* changing bit */ + status ^= mask; + perform_write = 1; + break; } @@ -184,6 +193,34 @@ static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) return -1; } +static int w1_ds2438_change_offset_register(struct w1_slave *sl, u8 *value) +{ + unsigned int retries = W1_DS2438_RETRIES; + u8 w1_buf[9]; + u8 w1_page1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; + + if (w1_ds2438_get_page(sl, 1, w1_page1_buf) == 0) { + memcpy(&w1_buf[2], w1_page1_buf, DS2438_PAGE_SIZE - 1); /* last register reserved */ + w1_buf[7] = value[0]; /* change only offset register */ + w1_buf[8] = value[1]; + while (retries--) { + if (w1_reset_select_slave(sl)) + continue; + w1_buf[0] = W1_DS2438_WRITE_SCRATCH; + w1_buf[1] = 0x01; /* write to page 1 */ + w1_write_block(sl->master, w1_buf, 9); + + if (w1_reset_select_slave(sl)) + continue; + w1_buf[0] = W1_DS2438_COPY_SCRATCH; + w1_buf[1] = 0x01; + w1_write_block(sl->master, w1_buf, 2); + return 0; + } + } + return -1; +} + static int w1_ds2438_get_voltage(struct w1_slave *sl, int adc_input, uint16_t *voltage) { @@ -287,9 +324,9 @@ static ssize_t iad_read(struct file *filp, struct kobject *kobj, if (!buf) return -EINVAL; - if (w1_ds2438_get_current(sl, &voltage) == 0) { + if (w1_ds2438_get_current(sl, &voltage) == 0) ret = snprintf(buf, count, "%i\n", voltage); - } else + else ret = -EIO; return ret; @@ -325,6 +362,55 @@ static ssize_t page0_read(struct file *filp, struct kobject *kobj, return ret; } +static ssize_t page1_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int ret; + u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; + + if (off != 0) + return 0; + if (!buf) + return -EINVAL; + + mutex_lock(&sl->master->bus_mutex); + + /* Read no more than page1 size */ + if (count > DS2438_PAGE_SIZE) + count = DS2438_PAGE_SIZE; + + if (w1_ds2438_get_page(sl, 1, w1_buf) == 0) { + memcpy(buf, &w1_buf, count); + ret = count; + } else + ret = -EIO; + + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + +static ssize_t offset_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int ret; + + mutex_lock(&sl->master->bus_mutex); + + if (w1_ds2438_change_offset_register(sl, buf) == 0) + ret = count; + else + ret = -EIO; + + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + static ssize_t temperature_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -338,9 +424,9 @@ static ssize_t temperature_read(struct file *filp, struct kobject *kobj, if (!buf) return -EINVAL; - if (w1_ds2438_get_temperature(sl, &temp) == 0) { + if (w1_ds2438_get_temperature(sl, &temp) == 0) ret = snprintf(buf, count, "%i\n", temp); - } else + else ret = -EIO; return ret; @@ -359,9 +445,9 @@ static ssize_t vad_read(struct file *filp, struct kobject *kobj, if (!buf) return -EINVAL; - if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) { + if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) ret = snprintf(buf, count, "%u\n", voltage); - } else + else ret = -EIO; return ret; @@ -380,16 +466,18 @@ static ssize_t vdd_read(struct file *filp, struct kobject *kobj, if (!buf) return -EINVAL; - if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) { + if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) ret = snprintf(buf, count, "%u\n", voltage); - } else + else ret = -EIO; return ret; } -static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, iad_read, iad_write, 0); +static BIN_ATTR_RW(iad, 0); static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE); +static BIN_ATTR_RO(page1, DS2438_PAGE_SIZE); +static BIN_ATTR_WO(offset, 2); static BIN_ATTR_RO(temperature, 0/* real length varies */); static BIN_ATTR_RO(vad, 0/* real length varies */); static BIN_ATTR_RO(vdd, 0/* real length varies */); @@ -397,6 +485,8 @@ static BIN_ATTR_RO(vdd, 0/* real length varies */); static struct bin_attribute *w1_ds2438_bin_attrs[] = { &bin_attr_iad, &bin_attr_page0, + &bin_attr_page1, + &bin_attr_offset, &bin_attr_temperature, &bin_attr_vad, &bin_attr_vdd, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 9d08a1c9c445..ca70c5f03206 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -834,7 +834,7 @@ static int check_family_data(struct w1_slave *sl) } /** - * support_bulk_read() - check if slave support bulk read + * bulk_read_support() - check if slave support bulk read * @sl: device to check the ability * * Return: true if bulk read is supported, false if not or error @@ -2056,7 +2056,6 @@ static ssize_t w1_seq_show(struct device *device, { struct w1_slave *sl = dev_to_w1_slave(device); ssize_t c = PAGE_SIZE; - int rv; int i; u8 ack; u64 rn; @@ -2084,7 +2083,7 @@ static ssize_t w1_seq_show(struct device *device, goto error; w1_write_8(sl->master, W1_42_COND_READ); - rv = w1_read_block(sl->master, (u8 *)&rn, 8); + w1_read_block(sl->master, (u8 *)&rn, 8); reg_num = (struct w1_reg_num *) &rn; if (reg_num->family == W1_42_FINISHED_BYTE) break; diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c index e023d7d90d66..c8ac36135e28 100644 --- a/drivers/watchdog/mei_wdt.c +++ b/drivers/watchdog/mei_wdt.c @@ -121,7 +121,7 @@ struct mei_mc_hdr { }; /** - * struct mei_wdt_start_request watchdog start/ping + * struct mei_wdt_start_request - watchdog start/ping * * @hdr: Management Control Command Header * @timeout: timeout value @@ -134,7 +134,7 @@ struct mei_wdt_start_request { } __packed; /** - * struct mei_wdt_start_response watchdog start/ping response + * struct mei_wdt_start_response - watchdog start/ping response * * @hdr: Management Control Command Header * @status: operation status |