diff options
Diffstat (limited to 'drivers')
835 files changed, 17638 insertions, 8886 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 391ac0503dc0..76d0f9de767b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1786,6 +1786,21 @@ nothing_to_do: return 1; } +static bool ata_check_nblocks(struct scsi_cmnd *scmd, u32 n_blocks) +{ + struct request *rq = scmd->request; + u32 req_blocks; + + if (!blk_rq_is_passthrough(rq)) + return true; + + req_blocks = blk_rq_bytes(rq) / scmd->device->sector_size; + if (n_blocks > req_blocks) + return false; + + return true; +} + /** * ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one * @qc: Storage for translated ATA taskfile @@ -1830,6 +1845,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) scsi_10_lba_len(cdb, &block, &n_block); if (cdb[1] & (1 << 3)) tf_flags |= ATA_TFLAG_FUA; + if (!ata_check_nblocks(scmd, n_block)) + goto invalid_fld; break; case READ_6: case WRITE_6: @@ -1844,6 +1861,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) */ if (!n_block) n_block = 256; + if (!ata_check_nblocks(scmd, n_block)) + goto invalid_fld; break; case READ_16: case WRITE_16: @@ -1854,6 +1873,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc) scsi_16_lba_len(cdb, &block, &n_block); if (cdb[1] & (1 << 3)) tf_flags |= ATA_TFLAG_FUA; + if (!ata_check_nblocks(scmd, n_block)) + goto invalid_fld; break; default: DPRINTK("no-byte command\n"); diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 10aa27882142..4f115adb4ee8 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -658,6 +658,10 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) unsigned int offset; unsigned char *buf; + if (!qc->cursg) { + qc->curbytes = qc->nbytes; + return; + } if (qc->curbytes == qc->nbytes - qc->sect_size) ap->hsm_task_state = HSM_ST_LAST; @@ -683,6 +687,8 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) if (qc->cursg_ofs == qc->cursg->length) { qc->cursg = sg_next(qc->cursg); + if (!qc->cursg) + ap->hsm_task_state = HSM_ST_LAST; qc->cursg_ofs = 0; } } diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index 7c37f2ff09e4..deae466395de 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -158,7 +158,6 @@ static int rb532_pata_driver_probe(struct platform_device *pdev) static int rb532_pata_driver_remove(struct platform_device *pdev) { struct ata_host *ah = platform_get_drvdata(pdev); - struct rb532_cf_info *info = ah->private_data; ata_host_detach(ah); diff --git a/drivers/atm/Kconfig b/drivers/atm/Kconfig index 2e2efa577437..8c37294f1d1e 100644 --- a/drivers/atm/Kconfig +++ b/drivers/atm/Kconfig @@ -200,7 +200,7 @@ config ATM_NICSTAR_USE_SUNI make the card work). config ATM_NICSTAR_USE_IDT77105 - bool "Use IDT77015 PHY driver (25Mbps)" + bool "Use IDT77105 PHY driver (25Mbps)" depends on ATM_NICSTAR help Support for the PHYsical layer chip in ForeRunner LE25 cards. In diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 302cf0ba1600..8c7a996d1f16 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -63,6 +63,7 @@ #include <asm/byteorder.h> #include <linux/vmalloc.h> #include <linux/jiffies.h> +#include <linux/nospec.h> #include "iphase.h" #include "suni.h" #define swap_byte_order(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8)) @@ -2760,8 +2761,11 @@ static int ia_ioctl(struct atm_dev *dev, unsigned int cmd, void __user *arg) } if (copy_from_user(&ia_cmds, arg, sizeof ia_cmds)) return -EFAULT; board = ia_cmds.status; - if ((board < 0) || (board > iadev_count)) - board = 0; + + if ((board < 0) || (board > iadev_count)) + board = 0; + board = array_index_nospec(board, iadev_count + 1); + iadev = ia_dev[board]; switch (ia_cmds.cmd) { case MEMDUMP: diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index dd61fdd400f0..68489d1f00bb 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -448,6 +448,11 @@ config PANEL_BOOT_MESSAGE choice prompt "Backlight initial state" default CHARLCD_BL_FLASH + ---help--- + Select the initial backlight state on boot or module load. + + Previously, there was no option for this: the backlight flashed + briefly on init. Now you can also turn it off/on. config CHARLCD_BL_OFF bool "Off" diff --git a/drivers/auxdisplay/charlcd.c b/drivers/auxdisplay/charlcd.c index 92745efefb54..bef6b85778b6 100644 --- a/drivers/auxdisplay/charlcd.c +++ b/drivers/auxdisplay/charlcd.c @@ -20,7 +20,7 @@ #include <generated/utsrelease.h> -#include <misc/charlcd.h> +#include "charlcd.h" #define LCD_MINOR 156 diff --git a/drivers/auxdisplay/charlcd.h b/drivers/auxdisplay/charlcd.h new file mode 100644 index 000000000000..00911ad0f3de --- /dev/null +++ b/drivers/auxdisplay/charlcd.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Character LCD driver for Linux + * + * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu> + * Copyright (C) 2016-2017 Glider bvba + */ + +#ifndef _CHARLCD_H +#define _CHARLCD_H + +struct charlcd { + const struct charlcd_ops *ops; + const unsigned char *char_conv; /* Optional */ + + int ifwidth; /* 4-bit or 8-bit (default) */ + int height; + int width; + int bwidth; /* Default set by charlcd_alloc() */ + int hwidth; /* Default set by charlcd_alloc() */ + + void *drvdata; /* Set by charlcd_alloc() */ +}; + +struct charlcd_ops { + /* Required */ + void (*write_cmd)(struct charlcd *lcd, int cmd); + void (*write_data)(struct charlcd *lcd, int data); + + /* Optional */ + void (*write_cmd_raw4)(struct charlcd *lcd, int cmd); /* 4-bit only */ + void (*clear_fast)(struct charlcd *lcd); + void (*backlight)(struct charlcd *lcd, int on); +}; + +struct charlcd *charlcd_alloc(unsigned int drvdata_size); +void charlcd_free(struct charlcd *lcd); + +int charlcd_register(struct charlcd *lcd); +int charlcd_unregister(struct charlcd *lcd); + +void charlcd_poke(struct charlcd *lcd); + +#endif /* CHARLCD_H */ diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c index ab15b64707ad..bcbe13092327 100644 --- a/drivers/auxdisplay/hd44780.c +++ b/drivers/auxdisplay/hd44780.c @@ -14,8 +14,7 @@ #include <linux/property.h> #include <linux/slab.h> -#include <misc/charlcd.h> - +#include "charlcd.h" enum hd44780_pin { /* Order does matter due to writing to GPIO array subsets! */ diff --git a/drivers/auxdisplay/ht16k33.c b/drivers/auxdisplay/ht16k33.c index 9c0bb771751d..a2fcde582e2a 100644 --- a/drivers/auxdisplay/ht16k33.c +++ b/drivers/auxdisplay/ht16k33.c @@ -74,7 +74,7 @@ struct ht16k33_priv { struct ht16k33_fbdev fbdev; }; -static struct fb_fix_screeninfo ht16k33_fb_fix = { +static const struct fb_fix_screeninfo ht16k33_fb_fix = { .id = DRIVER_NAME, .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_MONO10, @@ -85,7 +85,7 @@ static struct fb_fix_screeninfo ht16k33_fb_fix = { .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo ht16k33_fb_var = { +static const struct fb_var_screeninfo ht16k33_fb_var = { .xres = HT16K33_MATRIX_LED_MAX_ROWS, .yres = HT16K33_MATRIX_LED_MAX_COLS, .xres_virtual = HT16K33_MATRIX_LED_MAX_ROWS, diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index e06de63497cf..85965953683e 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c @@ -55,7 +55,7 @@ #include <linux/io.h> #include <linux/uaccess.h> -#include <misc/charlcd.h> +#include "charlcd.h" #define KEYPAD_MINOR 185 @@ -1617,6 +1617,8 @@ static void panel_attach(struct parport *port) return; err_lcd_unreg: + if (scan_timer.function) + del_timer_sync(&scan_timer); if (lcd.enabled) charlcd_unregister(lcd.charlcd); err_unreg_device: diff --git a/drivers/base/core.c b/drivers/base/core.c index 636058bbf48a..1669d41fcddc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1823,12 +1823,63 @@ static inline struct kobject *get_glue_dir(struct device *dev) */ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir) { + unsigned int ref; + /* see if we live in a "glue" directory */ if (!live_in_glue_dir(glue_dir, dev)) return; mutex_lock(&gdp_mutex); - if (!kobject_has_children(glue_dir)) + /** + * There is a race condition between removing glue directory + * and adding a new device under the glue directory. + * + * CPU1: CPU2: + * + * device_add() + * get_device_parent() + * class_dir_create_and_add() + * kobject_add_internal() + * create_dir() // create glue_dir + * + * device_add() + * get_device_parent() + * kobject_get() // get glue_dir + * + * device_del() + * cleanup_glue_dir() + * kobject_del(glue_dir) + * + * kobject_add() + * kobject_add_internal() + * create_dir() // in glue_dir + * sysfs_create_dir_ns() + * kernfs_create_dir_ns(sd) + * + * sysfs_remove_dir() // glue_dir->sd=NULL + * sysfs_put() // free glue_dir->sd + * + * // sd is freed + * kernfs_new_node(sd) + * kernfs_get(glue_dir) + * kernfs_add_one() + * kernfs_put() + * + * Before CPU1 remove last child device under glue dir, if CPU2 add + * a new device under glue dir, the glue_dir kobject reference count + * will be increase to 2 in kobject_get(k). And CPU2 has been called + * kernfs_create_dir_ns(). Meanwhile, CPU1 call sysfs_remove_dir() + * and sysfs_put(). This result in glue_dir->sd is freed. + * + * Then the CPU2 will see a stale "empty" but still potentially used + * glue dir around in kernfs_new_node(). + * + * In order to avoid this happening, we also should make sure that + * kernfs_node for glue_dir is released in CPU1 only when refcount + * for glue_dir kobj is 1. + */ + ref = kref_read(&glue_dir->kref); + if (!kobject_has_children(glue_dir) && !--ref) kobject_del(glue_dir); kobject_put(glue_dir); mutex_unlock(&gdp_mutex); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 506a0175a5a7..ec974ba9c0c4 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -157,8 +157,13 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) * the device will only expose one IRQ, and this fallback * allows a common code path across either kind of resource. */ - if (num == 0 && has_acpi_companion(&dev->dev)) - return acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); + if (num == 0 && has_acpi_companion(&dev->dev)) { + int ret = acpi_dev_gpio_irq_get(ACPI_COMPANION(&dev->dev), num); + + /* Our callers expect -ENXIO for missing IRQs. */ + if (ret >= 0 || ret == -EPROBE_DEFER) + return ret; + } return -ENXIO; #endif diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index a4984136c19d..0fd6f97ee523 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -44,7 +44,7 @@ config REGMAP_IRQ config REGMAP_SOUNDWIRE tristate - depends on SOUNDWIRE_BUS + depends on SOUNDWIRE config REGMAP_SCCB tristate diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index e5e1b3a01b1a..e72843fe41df 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -588,14 +588,6 @@ void regmap_debugfs_init(struct regmap *map, const char *name) } map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); - if (!map->debugfs) { - dev_warn(map->dev, - "Failed to create %s debugfs directory\n", name); - - kfree(map->debugfs_name); - map->debugfs_name = NULL; - return; - } debugfs_create_file("name", 0400, map->debugfs, map, ®map_name_fops); @@ -672,10 +664,6 @@ void regmap_debugfs_initcall(void) struct regmap_debugfs_node *node, *tmp; regmap_debugfs_root = debugfs_create_dir("regmap", NULL); - if (!regmap_debugfs_root) { - pr_warn("regmap: Failed to create debugfs root\n"); - return; - } mutex_lock(®map_debugfs_early_lock); list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) { diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index c9dc70ceca5f..3d64c9331a82 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -370,7 +370,6 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) if (ret < 0) { dev_err(map->dev, "IRQ thread failed to resume: %d\n", ret); - pm_runtime_put(map->dev); goto exit; } } @@ -425,8 +424,6 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) dev_err(map->dev, "Failed to read IRQ status %d\n", ret); - if (chip->runtime_pm) - pm_runtime_put(map->dev); goto exit; } } @@ -478,8 +475,6 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); - if (chip->runtime_pm) - pm_runtime_put(map->dev); goto exit; } } @@ -513,10 +508,10 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } +exit: if (chip->runtime_pm) pm_runtime_put(map->dev); -exit: if (chip->handle_post_irq) chip->handle_post_irq(chip->irq_drv_data); diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index 5b49f1b33ebe..e2ea2356da06 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -323,10 +323,14 @@ flush(const char __user *str, size_t cnt, int exiting) } flush_scheduled_work(); - /* pass one: without sleeping, do aoedev_downdev */ + /* pass one: do aoedev_downdev, which might sleep */ +restart1: spin_lock_irqsave(&devlist_lock, flags); for (d = devlist; d; d = d->next) { spin_lock(&d->lock); + if (d->flags & DEVFL_TKILL) + goto cont; + if (exiting) { /* unconditionally take each device down */ } else if (specified) { @@ -338,8 +342,11 @@ flush(const char __user *str, size_t cnt, int exiting) || d->ref) goto cont; + spin_unlock(&d->lock); + spin_unlock_irqrestore(&devlist_lock, flags); aoedev_downdev(d); d->flags |= DEVFL_TKILL; + goto restart1; cont: spin_unlock(&d->lock); } @@ -348,7 +355,7 @@ cont: /* pass two: call freedev, which might sleep, * for aoedevs marked with DEVFL_TKILL */ -restart: +restart2: spin_lock_irqsave(&devlist_lock, flags); for (d = devlist; d; d = d->next) { spin_lock(&d->lock); @@ -357,7 +364,7 @@ restart: spin_unlock(&d->lock); spin_unlock_irqrestore(&devlist_lock, flags); freedev(d); - goto restart; + goto restart2; } spin_unlock(&d->lock); } diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 9bd4ddd12b25..5b248763a672 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -322,6 +322,8 @@ static int drbd_thread_setup(void *arg) thi->name[0], resource->name); + allow_kernel_signal(DRBD_SIGKILL); + allow_kernel_signal(SIGXCPU); restart: retval = thi->function(thi); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 3036883fc9f8..ab7ca5989097 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -885,7 +885,7 @@ static void loop_unprepare_queue(struct loop_device *lo) static int loop_kthread_worker_fn(void *worker_ptr) { - current->flags |= PF_LESS_THROTTLE; + current->flags |= PF_LESS_THROTTLE | PF_MEMALLOC_NOIO; return kthread_worker_fn(worker_ptr); } diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3327192bb71f..c8fb886aebd4 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3038,6 +3038,17 @@ again: } return true; case RBD_OBJ_READ_PARENT: + /* + * The parent image is read only up to the overlap -- zero-fill + * from the overlap to the end of the request. + */ + if (!*result) { + u32 obj_overlap = rbd_obj_img_extents_bytes(obj_req); + + if (obj_overlap < obj_req->ex.oe_len) + rbd_obj_zero_range(obj_req, obj_overlap, + obj_req->ex.oe_len - obj_overlap); + } return true; default: BUG(); diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 3ac6a5d18071..b90dbcd99c03 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -965,6 +965,7 @@ static int read_per_ring_refs(struct xen_blkif_ring *ring, const char *dir) } } + err = -ENOMEM; for (i = 0; i < nr_grefs * XEN_BLKIF_REQS_PER_PAGE; i++) { req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) @@ -987,7 +988,7 @@ static int read_per_ring_refs(struct xen_blkif_ring *ring, const char *dir) err = xen_blkif_map(ring, ring_ref, nr_grefs, evtchn); if (err) { xenbus_dev_fatal(dev, err, "mapping ring-ref port %u", evtchn); - return err; + goto fail; } return 0; @@ -1007,8 +1008,7 @@ fail: } kfree(req); } - return -ENOMEM; - + return err; } static int connect_ring(struct backend_info *be) diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index a0e84538cec8..1fa58c059cbf 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -337,7 +337,7 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) usb_free_urb(urb); - return 0; + return err; } static int bpa10x_set_diag(struct hci_dev *hdev, bool enable) diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 8b33128dccee..0875470a7806 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -99,6 +99,27 @@ static int qca_send_reset(struct hci_dev *hdev) return 0; } +int qca_send_pre_shutdown_cmd(struct hci_dev *hdev) +{ + struct sk_buff *skb; + int err; + + bt_dev_dbg(hdev, "QCA pre shutdown cmd"); + + skb = __hci_cmd_sync(hdev, QCA_PRE_SHUTDOWN_CMD, 0, + NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "QCA preshutdown_cmd failed (%d)", err); + return err; + } + + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd); + static void qca_tlv_check_data(struct rome_config *config, const struct firmware *fw) { @@ -119,6 +140,7 @@ static void qca_tlv_check_data(struct rome_config *config, BT_DBG("Length\t\t : %d bytes", length); config->dnld_mode = ROME_SKIP_EVT_NONE; + config->dnld_type = ROME_SKIP_EVT_NONE; switch (config->type) { case TLV_TYPE_PATCH: @@ -268,7 +290,7 @@ static int qca_inject_cmd_complete_event(struct hci_dev *hdev) evt = skb_put(skb, sizeof(*evt)); evt->ncmd = 1; - evt->opcode = QCA_HCI_CC_OPCODE; + evt->opcode = cpu_to_le16(QCA_HCI_CC_OPCODE); skb_put_u8(skb, QCA_HCI_CC_SUCCESS); @@ -323,7 +345,7 @@ static int qca_download_firmware(struct hci_dev *hdev, */ if (config->dnld_type == ROME_SKIP_EVT_VSE_CC || config->dnld_type == ROME_SKIP_EVT_VSE) - return qca_inject_cmd_complete_event(hdev); + ret = qca_inject_cmd_complete_event(hdev); out: release_firmware(fw); @@ -388,6 +410,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, return err; } + /* Give the controller some time to get ready to receive the NVM */ + msleep(10); + /* Download NVM configuration */ config.type = TLV_TYPE_NVM; if (firmware_name) diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index 6a291a7a5d96..69c5315a65fd 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -13,6 +13,7 @@ #define EDL_PATCH_TLV_REQ_CMD (0x1E) #define EDL_NVM_ACCESS_SET_REQ_CMD (0x01) #define MAX_SIZE_PER_TLV_SEGMENT (243) +#define QCA_PRE_SHUTDOWN_CMD (0xFC08) #define EDL_CMD_REQ_RES_EVT (0x00) #define EDL_PATCH_VER_RES_EVT (0x19) @@ -135,6 +136,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, const char *firmware_name); int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version); int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); +int qca_send_pre_shutdown_cmd(struct hci_dev *hdev); static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type) { return soc_type == QCA_WCN3990 || soc_type == QCA_WCN3998; @@ -167,4 +169,9 @@ static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type) { return false; } + +static inline int qca_send_pre_shutdown_cmd(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} #endif diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3876fee6ad13..ba4149054304 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -384,6 +384,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3526), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x0b05, 0x185c), .driver_info = BTUSB_REALTEK }, + /* Additional Realtek 8822CE Bluetooth devices */ + { USB_DEVICE(0x04ca, 0x4005), .driver_info = BTUSB_REALTEK }, + /* Silicon Wave based devices */ { USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_SWAVE }, @@ -1170,10 +1173,6 @@ static int btusb_open(struct hci_dev *hdev) } data->intf->needs_remote_wakeup = 1; - /* device specific wakeup source enabled and required for USB - * remote wakeup while host is suspended - */ - device_wakeup_enable(&data->udev->dev); if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) goto done; @@ -1238,7 +1237,6 @@ static int btusb_close(struct hci_dev *hdev) goto failed; data->intf->needs_remote_wakeup = 0; - device_wakeup_disable(&data->udev->dev); usb_autopm_put_interface(data->intf); failed: @@ -2762,8 +2760,10 @@ static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname) fw_size = fw->size; /* The size of patch header is 30 bytes, should be skip */ - if (fw_size < 30) + if (fw_size < 30) { + err = -EINVAL; goto err_release_fw; + } fw_size -= 30; fw_ptr += 30; diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 82a0a3691a63..31dec435767b 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -309,13 +309,14 @@ static void qca_wq_awake_device(struct work_struct *work) ws_awake_device); struct hci_uart *hu = qca->hu; unsigned long retrans_delay; + unsigned long flags; BT_DBG("hu %p wq awake device", hu); /* Vote for serial clock */ serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu); - spin_lock(&qca->hci_ibs_lock); + spin_lock_irqsave(&qca->hci_ibs_lock, flags); /* Send wake indication to device */ if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) @@ -327,7 +328,7 @@ static void qca_wq_awake_device(struct work_struct *work) retrans_delay = msecs_to_jiffies(qca->wake_retrans); mod_timer(&qca->wake_retrans_timer, jiffies + retrans_delay); - spin_unlock(&qca->hci_ibs_lock); + spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); /* Actually send the packets */ hci_uart_tx_wakeup(hu); @@ -338,12 +339,13 @@ static void qca_wq_awake_rx(struct work_struct *work) struct qca_data *qca = container_of(work, struct qca_data, ws_awake_rx); struct hci_uart *hu = qca->hu; + unsigned long flags; BT_DBG("hu %p wq awake rx", hu); serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu); - spin_lock(&qca->hci_ibs_lock); + spin_lock_irqsave(&qca->hci_ibs_lock, flags); qca->rx_ibs_state = HCI_IBS_RX_AWAKE; /* Always acknowledge device wake up, @@ -354,7 +356,7 @@ static void qca_wq_awake_rx(struct work_struct *work) qca->ibs_sent_wacks++; - spin_unlock(&qca->hci_ibs_lock); + spin_unlock_irqrestore(&qca->hci_ibs_lock, flags); /* Actually send the packets */ hci_uart_tx_wakeup(hu); @@ -705,7 +707,7 @@ static void device_want_to_sleep(struct hci_uart *hu) unsigned long flags; struct qca_data *qca = hu->priv; - BT_DBG("hu %p want to sleep", hu); + BT_DBG("hu %p want to sleep in %d state", hu, qca->rx_ibs_state); spin_lock_irqsave(&qca->hci_ibs_lock, flags); @@ -720,7 +722,7 @@ static void device_want_to_sleep(struct hci_uart *hu) break; case HCI_IBS_RX_ASLEEP: - /* Fall through */ + break; default: /* Any other state is illegal */ @@ -912,7 +914,7 @@ static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb) if (hdr->evt == HCI_EV_VENDOR) complete(&qca->drop_ev_comp); - kfree(skb); + kfree_skb(skb); return 0; } @@ -1386,6 +1388,9 @@ static int qca_power_off(struct hci_dev *hdev) { struct hci_uart *hu = hci_get_drvdata(hdev); + /* Perform pre shutdown command */ + qca_send_pre_shutdown_cmd(hdev); + qca_power_shutdown(hu); return 0; } diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c index 19d7b6ff2f17..20c957185af2 100644 --- a/drivers/bus/hisi_lpc.c +++ b/drivers/bus/hisi_lpc.c @@ -456,6 +456,17 @@ struct hisi_lpc_acpi_cell { size_t pdata_size; }; +static void hisi_lpc_acpi_remove(struct device *hostdev) +{ + struct acpi_device *adev = ACPI_COMPANION(hostdev); + struct acpi_device *child; + + device_for_each_child(hostdev, NULL, hisi_lpc_acpi_remove_subdev); + + list_for_each_entry(child, &adev->children, node) + acpi_device_clear_enumerated(child); +} + /* * hisi_lpc_acpi_probe - probe children for ACPI FW * @hostdev: LPC host device pointer @@ -555,8 +566,7 @@ static int hisi_lpc_acpi_probe(struct device *hostdev) return 0; fail: - device_for_each_child(hostdev, NULL, - hisi_lpc_acpi_remove_subdev); + hisi_lpc_acpi_remove(hostdev); return ret; } @@ -569,6 +579,10 @@ static int hisi_lpc_acpi_probe(struct device *dev) { return -ENODEV; } + +static void hisi_lpc_acpi_remove(struct device *hostdev) +{ +} #endif // CONFIG_ACPI /* @@ -606,24 +620,27 @@ static int hisi_lpc_probe(struct platform_device *pdev) range->fwnode = dev->fwnode; range->flags = LOGIC_PIO_INDIRECT; range->size = PIO_INDIRECT_SIZE; + range->hostdata = lpcdev; + range->ops = &hisi_lpc_ops; + lpcdev->io_host = range; ret = logic_pio_register_range(range); if (ret) { dev_err(dev, "register IO range failed (%d)!\n", ret); return ret; } - lpcdev->io_host = range; /* register the LPC host PIO resources */ if (acpi_device) ret = hisi_lpc_acpi_probe(dev); else ret = of_platform_populate(dev->of_node, NULL, NULL, dev); - if (ret) + if (ret) { + logic_pio_unregister_range(range); return ret; + } - lpcdev->io_host->hostdata = lpcdev; - lpcdev->io_host->ops = &hisi_lpc_ops; + dev_set_drvdata(dev, lpcdev); io_end = lpcdev->io_host->io_start + lpcdev->io_host->size; dev_info(dev, "registered range [%pa - %pa]\n", @@ -632,6 +649,23 @@ static int hisi_lpc_probe(struct platform_device *pdev) return ret; } +static int hisi_lpc_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct acpi_device *acpi_device = ACPI_COMPANION(dev); + struct hisi_lpc_dev *lpcdev = dev_get_drvdata(dev); + struct logic_pio_hwaddr *range = lpcdev->io_host; + + if (acpi_device) + hisi_lpc_acpi_remove(dev); + else + of_platform_depopulate(dev); + + logic_pio_unregister_range(range); + + return 0; +} + static const struct of_device_id hisi_lpc_of_match[] = { { .compatible = "hisilicon,hip06-lpc", }, { .compatible = "hisilicon,hip07-lpc", }, @@ -645,5 +679,6 @@ static struct platform_driver hisi_lpc_driver = { .acpi_match_table = ACPI_PTR(hisi_lpc_acpi_match), }, .probe = hisi_lpc_probe, + .remove = hisi_lpc_remove, }; builtin_platform_driver(hisi_lpc_driver); diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index e6deabd8305d..2db474ab4c6b 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -949,7 +949,7 @@ static int sysc_best_idle_mode(u32 idlemodes, u32 *best_mode) *best_mode = SYSC_IDLE_SMART_WKUP; else if (idlemodes & BIT(SYSC_IDLE_SMART)) *best_mode = SYSC_IDLE_SMART; - else if (idlemodes & SYSC_IDLE_FORCE) + else if (idlemodes & BIT(SYSC_IDLE_FORCE)) *best_mode = SYSC_IDLE_FORCE; else return -EINVAL; @@ -1267,7 +1267,8 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0), SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902, 0xffff00f0, 0), - SYSC_QUIRK("dcan", 0, 0, -1, -1, 0xffffffff, 0xffffffff, 0), + SYSC_QUIRK("dcan", 0, 0x20, -1, -1, 0xa3170504, 0xffffffff, 0), + SYSC_QUIRK("dcan", 0, 0x20, -1, -1, 0x4edb1902, 0xffffffff, 0), SYSC_QUIRK("dmic", 0, 0, 0x10, -1, 0x50010000, 0xffffffff, 0), SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0), SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0), @@ -1692,10 +1693,7 @@ static int sysc_init_sysc_mask(struct sysc *ddata) if (error) return 0; - if (val) - ddata->cfg.sysc_val = val & ddata->cap->sysc_mask; - else - ddata->cfg.sysc_val = ddata->cap->sysc_mask; + ddata->cfg.sysc_val = val & ddata->cap->sysc_mask; return 0; } @@ -2385,27 +2383,27 @@ static int sysc_probe(struct platform_device *pdev) error = sysc_init_dts_quirks(ddata); if (error) - goto unprepare; + return error; error = sysc_map_and_check_registers(ddata); if (error) - goto unprepare; + return error; error = sysc_init_sysc_mask(ddata); if (error) - goto unprepare; + return error; error = sysc_init_idlemodes(ddata); if (error) - goto unprepare; + return error; error = sysc_init_syss_mask(ddata); if (error) - goto unprepare; + return error; error = sysc_init_pdata(ddata); if (error) - goto unprepare; + return error; sysc_init_early_quirks(ddata); @@ -2415,7 +2413,7 @@ static int sysc_probe(struct platform_device *pdev) error = sysc_init_resets(ddata); if (error) - return error; + goto unprepare; error = sysc_init_module(ddata); if (error) diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 88a3c06fc153..9c37047f4b56 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -164,6 +164,11 @@ config TCG_VTPM_PROXY /dev/vtpmX and a server-side file descriptor on which the vTPM can receive commands. +config TCG_FTPM_TEE + tristate "TEE based fTPM Interface" + depends on TEE && OPTEE + help + This driver proxies for firmware TPM running in TEE. source "drivers/char/tpm/st33zp24/Kconfig" endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index a01c4cab902a..c354cdff9c62 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -33,3 +33,4 @@ obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o obj-$(CONFIG_TCG_CRB) += tpm_crb.o obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o +obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 4838c6a9f0f2..3d6d394a8661 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -287,12 +287,9 @@ static void tpm_devs_release(struct device *dev) * @dev: device to which the chip is associated. * * Issues a TPM2_Shutdown command prior to loss of power, as required by the - * TPM 2.0 spec. - * Then, calls bus- and device- specific shutdown code. + * TPM 2.0 spec. Then, calls bus- and device- specific shutdown code. * - * XXX: This codepath relies on the fact that sysfs is not enabled for - * TPM2: sysfs uses an implicit lock on chip->ops, so this could race if TPM2 - * has sysfs support enabled before TPM sysfs's implicit locking is fixed. + * Return: always 0 (i.e. success) */ static int tpm_class_shutdown(struct device *dev) { diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index d9caedda075b..edfa89160010 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -329,16 +329,9 @@ static const struct attribute_group tpm_dev_group = { void tpm_sysfs_add_device(struct tpm_chip *chip) { - /* XXX: If you wish to remove this restriction, you must first update - * tpm_sysfs to explicitly lock chip->ops. - */ if (chip->flags & TPM_CHIP_FLAG_TPM2) return; - /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del - * is called before ops is null'd and the sysfs core synchronizes this - * removal so that no callbacks are running or can run again - */ WARN_ON(chip->groups_cnt != 0); chip->groups[chip->groups_cnt++] = &tpm_dev_group; } diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c new file mode 100644 index 000000000000..6640a14dbe48 --- /dev/null +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Microsoft Corporation + * + * Implements a firmware TPM as described here: + * https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/ + * + * A reference implementation is available here: + * https://github.com/microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM + */ + +#include <linux/acpi.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/tee_drv.h> +#include <linux/tpm.h> +#include <linux/uuid.h> + +#include "tpm.h" +#include "tpm_ftpm_tee.h" + +/* + * TA_FTPM_UUID: BC50D971-D4C9-42C4-82CB-343FB7F37896 + * + * Randomly generated, and must correspond to the GUID on the TA side. + * Defined here in the reference implementation: + * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42 + */ +static const uuid_t ftpm_ta_uuid = + UUID_INIT(0xBC50D971, 0xD4C9, 0x42C4, + 0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96); + +/** + * ftpm_tee_tpm_op_recv - retrieve fTPM response. + * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h. + * @buf: the buffer to store data. + * @count: the number of bytes to read. + * + * Return: + * In case of success the number of bytes received. + * On failure, -errno. + */ +static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent); + size_t len; + + len = pvt_data->resp_len; + if (count < len) { + dev_err(&chip->dev, + "%s: Invalid size in recv: count=%zd, resp_len=%zd\n", + __func__, count, len); + return -EIO; + } + + memcpy(buf, pvt_data->resp_buf, len); + pvt_data->resp_len = 0; + + return len; +} + +/** + * ftpm_tee_tpm_op_send - send TPM commands through the TEE shared memory. + * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h + * @buf: the buffer to send. + * @len: the number of bytes to send. + * + * Return: + * In case of success, returns 0. + * On failure, -errno + */ +static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent); + size_t resp_len; + int rc; + u8 *temp_buf; + struct tpm_header *resp_header; + struct tee_ioctl_invoke_arg transceive_args; + struct tee_param command_params[4]; + struct tee_shm *shm = pvt_data->shm; + + if (len > MAX_COMMAND_SIZE) { + dev_err(&chip->dev, + "%s: len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM TA\n", + __func__, len); + return -EIO; + } + + memset(&transceive_args, 0, sizeof(transceive_args)); + memset(command_params, 0, sizeof(command_params)); + pvt_data->resp_len = 0; + + /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */ + transceive_args = (struct tee_ioctl_invoke_arg) { + .func = FTPM_OPTEE_TA_SUBMIT_COMMAND, + .session = pvt_data->session, + .num_params = 4, + }; + + /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */ + command_params[0] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + .u.memref = { + .shm = shm, + .size = len, + .shm_offs = 0, + }, + }; + + temp_buf = tee_shm_get_va(shm, 0); + if (IS_ERR(temp_buf)) { + dev_err(&chip->dev, "%s: tee_shm_get_va failed for transmit\n", + __func__); + return PTR_ERR(temp_buf); + } + memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE)); + memcpy(temp_buf, buf, len); + + command_params[1] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, + .u.memref = { + .shm = shm, + .size = MAX_RESPONSE_SIZE, + .shm_offs = MAX_COMMAND_SIZE, + }, + }; + + rc = tee_client_invoke_func(pvt_data->ctx, &transceive_args, + command_params); + if ((rc < 0) || (transceive_args.ret != 0)) { + dev_err(&chip->dev, "%s: SUBMIT_COMMAND invoke error: 0x%x\n", + __func__, transceive_args.ret); + return (rc < 0) ? rc : transceive_args.ret; + } + + temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs); + if (IS_ERR(temp_buf)) { + dev_err(&chip->dev, "%s: tee_shm_get_va failed for receive\n", + __func__); + return PTR_ERR(temp_buf); + } + + resp_header = (struct tpm_header *)temp_buf; + resp_len = be32_to_cpu(resp_header->length); + + /* sanity check resp_len */ + if (resp_len < TPM_HEADER_SIZE) { + dev_err(&chip->dev, "%s: tpm response header too small\n", + __func__); + return -EIO; + } + if (resp_len > MAX_RESPONSE_SIZE) { + dev_err(&chip->dev, + "%s: resp_len=%zd exceeds MAX_RESPONSE_SIZE\n", + __func__, resp_len); + return -EIO; + } + + /* sanity checks look good, cache the response */ + memcpy(pvt_data->resp_buf, temp_buf, resp_len); + pvt_data->resp_len = resp_len; + + return 0; +} + +static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip) +{ + /* not supported */ +} + +static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip) +{ + return 0; +} + +static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status) +{ + return 0; +} + +static const struct tpm_class_ops ftpm_tee_tpm_ops = { + .flags = TPM_OPS_AUTO_STARTUP, + .recv = ftpm_tee_tpm_op_recv, + .send = ftpm_tee_tpm_op_send, + .cancel = ftpm_tee_tpm_op_cancel, + .status = ftpm_tee_tpm_op_status, + .req_complete_mask = 0, + .req_complete_val = 0, + .req_canceled = ftpm_tee_tpm_req_canceled, +}; + +/* + * Check whether this driver supports the fTPM TA in the TEE instance + * represented by the params (ver/data) to this function. + */ +static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data) +{ + /* + * Currently this driver only support GP Complaint OPTEE based fTPM TA + */ + if ((ver->impl_id == TEE_IMPL_ID_OPTEE) && + (ver->gen_caps & TEE_GEN_CAP_GP)) + return 1; + else + return 0; +} + +/** + * ftpm_tee_probe - initialize the fTPM + * @pdev: the platform_device description. + * + * Return: + * On success, 0. On failure, -errno. + */ +static int ftpm_tee_probe(struct platform_device *pdev) +{ + int rc; + struct tpm_chip *chip; + struct device *dev = &pdev->dev; + struct ftpm_tee_private *pvt_data = NULL; + struct tee_ioctl_open_session_arg sess_arg; + + pvt_data = devm_kzalloc(dev, sizeof(struct ftpm_tee_private), + GFP_KERNEL); + if (!pvt_data) + return -ENOMEM; + + dev_set_drvdata(dev, pvt_data); + + /* Open context with TEE driver */ + pvt_data->ctx = tee_client_open_context(NULL, ftpm_tee_match, NULL, + NULL); + if (IS_ERR(pvt_data->ctx)) { + if (PTR_ERR(pvt_data->ctx) == -ENOENT) + return -EPROBE_DEFER; + dev_err(dev, "%s: tee_client_open_context failed\n", __func__); + return PTR_ERR(pvt_data->ctx); + } + + /* Open a session with fTPM TA */ + memset(&sess_arg, 0, sizeof(sess_arg)); + memcpy(sess_arg.uuid, ftpm_ta_uuid.b, TEE_IOCTL_UUID_LEN); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; + sess_arg.num_params = 0; + + rc = tee_client_open_session(pvt_data->ctx, &sess_arg, NULL); + if ((rc < 0) || (sess_arg.ret != 0)) { + dev_err(dev, "%s: tee_client_open_session failed, err=%x\n", + __func__, sess_arg.ret); + rc = -EINVAL; + goto out_tee_session; + } + pvt_data->session = sess_arg.session; + + /* Allocate dynamic shared memory with fTPM TA */ + pvt_data->shm = tee_shm_alloc(pvt_data->ctx, + MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE, + TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); + if (IS_ERR(pvt_data->shm)) { + dev_err(dev, "%s: tee_shm_alloc failed\n", __func__); + rc = -ENOMEM; + goto out_shm_alloc; + } + + /* Allocate new struct tpm_chip instance */ + chip = tpm_chip_alloc(dev, &ftpm_tee_tpm_ops); + if (IS_ERR(chip)) { + dev_err(dev, "%s: tpm_chip_alloc failed\n", __func__); + rc = PTR_ERR(chip); + goto out_chip_alloc; + } + + pvt_data->chip = chip; + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2; + + /* Create a character device for the fTPM */ + rc = tpm_chip_register(pvt_data->chip); + if (rc) { + dev_err(dev, "%s: tpm_chip_register failed with rc=%d\n", + __func__, rc); + goto out_chip; + } + + return 0; + +out_chip: + put_device(&pvt_data->chip->dev); +out_chip_alloc: + tee_shm_free(pvt_data->shm); +out_shm_alloc: + tee_client_close_session(pvt_data->ctx, pvt_data->session); +out_tee_session: + tee_client_close_context(pvt_data->ctx); + + return rc; +} + +/** + * ftpm_tee_remove - remove the TPM device + * @pdev: the platform_device description. + * + * Return: + * 0 always. + */ +static int ftpm_tee_remove(struct platform_device *pdev) +{ + struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev); + + /* Release the chip */ + tpm_chip_unregister(pvt_data->chip); + + /* frees chip */ + put_device(&pvt_data->chip->dev); + + /* Free the shared memory pool */ + tee_shm_free(pvt_data->shm); + + /* close the existing session with fTPM TA*/ + tee_client_close_session(pvt_data->ctx, pvt_data->session); + + /* close the context with TEE driver */ + tee_client_close_context(pvt_data->ctx); + + /* memory allocated with devm_kzalloc() is freed automatically */ + + return 0; +} + +static const struct of_device_id of_ftpm_tee_ids[] = { + { .compatible = "microsoft,ftpm" }, + { } +}; +MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids); + +static struct platform_driver ftpm_tee_driver = { + .driver = { + .name = "ftpm-tee", + .of_match_table = of_match_ptr(of_ftpm_tee_ids), + }, + .probe = ftpm_tee_probe, + .remove = ftpm_tee_remove, +}; + +module_platform_driver(ftpm_tee_driver); + +MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>"); +MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/tpm/tpm_ftpm_tee.h b/drivers/char/tpm/tpm_ftpm_tee.h new file mode 100644 index 000000000000..f98daa7bf68c --- /dev/null +++ b/drivers/char/tpm/tpm_ftpm_tee.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Microsoft Corporation + */ + +#ifndef __TPM_FTPM_TEE_H__ +#define __TPM_FTPM_TEE_H__ + +#include <linux/tee_drv.h> +#include <linux/tpm.h> +#include <linux/uuid.h> + +/* The TAFs ID implemented in this TA */ +#define FTPM_OPTEE_TA_SUBMIT_COMMAND (0) +#define FTPM_OPTEE_TA_EMULATE_PPI (1) + +/* max. buffer size supported by fTPM */ +#define MAX_COMMAND_SIZE 4096 +#define MAX_RESPONSE_SIZE 4096 + +/** + * struct ftpm_tee_private - fTPM's private data + * @chip: struct tpm_chip instance registered with tpm framework. + * @state: internal state + * @session: fTPM TA session identifier. + * @resp_len: cached response buffer length. + * @resp_buf: cached response buffer. + * @ctx: TEE context handler. + * @shm: Memory pool shared with fTPM TA in TEE. + */ +struct ftpm_tee_private { + struct tpm_chip *chip; + u32 session; + size_t resp_len; + u8 resp_buf[MAX_RESPONSE_SIZE]; + struct tee_context *ctx; + struct tee_shm *shm; +}; + +#endif /* __TPM_FTPM_TEE_H__ */ diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index c3181ea9f271..270f43acbb77 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -980,6 +980,8 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, goto out_err; } + tpm_chip_start(chip); + chip->flags |= TPM_CHIP_FLAG_IRQ; if (irq) { tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, irq); @@ -989,6 +991,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, } else { tpm_tis_probe_irq(chip, intmask); } + tpm_chip_stop(chip); } rc = tpm_chip_register(chip); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c0990703ce54..1c46babeb093 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -324,6 +324,25 @@ static struct clk_core *clk_core_lookup(const char *name) return NULL; } +#ifdef CONFIG_OF +static int of_parse_clkspec(const struct device_node *np, int index, + const char *name, struct of_phandle_args *out_args); +static struct clk_hw * +of_clk_get_hw_from_clkspec(struct of_phandle_args *clkspec); +#else +static inline int of_parse_clkspec(const struct device_node *np, int index, + const char *name, + struct of_phandle_args *out_args) +{ + return -ENOENT; +} +static inline struct clk_hw * +of_clk_get_hw_from_clkspec(struct of_phandle_args *clkspec) +{ + return ERR_PTR(-ENOENT); +} +#endif + /** * clk_core_get - Find the clk_core parent of a clk * @core: clk to find parent of @@ -355,8 +374,9 @@ static struct clk_core *clk_core_lookup(const char *name) * }; * * Returns: -ENOENT when the provider can't be found or the clk doesn't - * exist in the provider. -EINVAL when the name can't be found. NULL when the - * provider knows about the clk but it isn't provided on this system. + * exist in the provider or the name can't be found in the DT node or + * in a clkdev lookup. NULL when the provider knows about the clk but it + * isn't provided on this system. * A valid clk_core pointer when the clk can be found in the provider. */ static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index) @@ -367,17 +387,19 @@ static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index) struct device *dev = core->dev; const char *dev_id = dev ? dev_name(dev) : NULL; struct device_node *np = core->of_node; + struct of_phandle_args clkspec; - if (np && (name || index >= 0)) - hw = of_clk_get_hw(np, index, name); - - /* - * If the DT search above couldn't find the provider or the provider - * didn't know about this clk, fallback to looking up via clkdev based - * clk_lookups - */ - if (PTR_ERR(hw) == -ENOENT && name) + if (np && (name || index >= 0) && + !of_parse_clkspec(np, index, name, &clkspec)) { + hw = of_clk_get_hw_from_clkspec(&clkspec); + of_node_put(clkspec.np); + } else if (name) { + /* + * If the DT search above couldn't find the provider fallback to + * looking up via clkdev based clk_lookups. + */ hw = clk_find_hw(dev_id, name); + } if (IS_ERR(hw)) return ERR_CAST(hw); @@ -401,7 +423,7 @@ static void clk_core_fill_parent_index(struct clk_core *core, u8 index) parent = ERR_PTR(-EPROBE_DEFER); } else { parent = clk_core_get(core, index); - if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT) + if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT && entry->name) parent = clk_core_lookup(entry->name); } @@ -1632,7 +1654,8 @@ static int clk_fetch_parent_index(struct clk_core *core, break; /* Fallback to comparing globally unique names */ - if (!strcmp(parent->name, core->parents[i].name)) + if (core->parents[i].name && + !strcmp(parent->name, core->parents[i].name)) break; } diff --git a/drivers/clk/samsung/clk-exynos5-subcmu.c b/drivers/clk/samsung/clk-exynos5-subcmu.c index 91db7894125d..65c82d922b05 100644 --- a/drivers/clk/samsung/clk-exynos5-subcmu.c +++ b/drivers/clk/samsung/clk-exynos5-subcmu.c @@ -14,7 +14,7 @@ #include "clk-exynos5-subcmu.h" static struct samsung_clk_provider *ctx; -static const struct exynos5_subcmu_info *cmu; +static const struct exynos5_subcmu_info **cmu; static int nr_cmus; static void exynos5_subcmu_clk_save(void __iomem *base, @@ -56,17 +56,17 @@ static void exynos5_subcmu_defer_gate(struct samsung_clk_provider *ctx, * when OF-core populates all device-tree nodes. */ void exynos5_subcmus_init(struct samsung_clk_provider *_ctx, int _nr_cmus, - const struct exynos5_subcmu_info *_cmu) + const struct exynos5_subcmu_info **_cmu) { ctx = _ctx; cmu = _cmu; nr_cmus = _nr_cmus; for (; _nr_cmus--; _cmu++) { - exynos5_subcmu_defer_gate(ctx, _cmu->gate_clks, - _cmu->nr_gate_clks); - exynos5_subcmu_clk_save(ctx->reg_base, _cmu->suspend_regs, - _cmu->nr_suspend_regs); + exynos5_subcmu_defer_gate(ctx, (*_cmu)->gate_clks, + (*_cmu)->nr_gate_clks); + exynos5_subcmu_clk_save(ctx->reg_base, (*_cmu)->suspend_regs, + (*_cmu)->nr_suspend_regs); } } @@ -163,9 +163,9 @@ static int __init exynos5_clk_probe(struct platform_device *pdev) if (of_property_read_string(np, "label", &name) < 0) continue; for (i = 0; i < nr_cmus; i++) - if (strcmp(cmu[i].pd_name, name) == 0) + if (strcmp(cmu[i]->pd_name, name) == 0) exynos5_clk_register_subcmu(&pdev->dev, - &cmu[i], np); + cmu[i], np); } return 0; } diff --git a/drivers/clk/samsung/clk-exynos5-subcmu.h b/drivers/clk/samsung/clk-exynos5-subcmu.h index 755ee8aaa3de..9ae5356f25aa 100644 --- a/drivers/clk/samsung/clk-exynos5-subcmu.h +++ b/drivers/clk/samsung/clk-exynos5-subcmu.h @@ -21,6 +21,6 @@ struct exynos5_subcmu_info { }; void exynos5_subcmus_init(struct samsung_clk_provider *ctx, int nr_cmus, - const struct exynos5_subcmu_info *cmu); + const struct exynos5_subcmu_info **cmu); #endif diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index f2b896881768..931c70a4da19 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -681,6 +681,10 @@ static const struct exynos5_subcmu_info exynos5250_disp_subcmu = { .pd_name = "DISP1", }; +static const struct exynos5_subcmu_info *exynos5250_subcmus[] = { + &exynos5250_disp_subcmu, +}; + static const struct samsung_pll_rate_table vpll_24mhz_tbl[] __initconst = { /* sorted in descending order */ /* PLL_36XX_RATE(rate, m, p, s, k) */ @@ -843,7 +847,8 @@ static void __init exynos5250_clk_init(struct device_node *np) samsung_clk_sleep_init(reg_base, exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs)); - exynos5_subcmus_init(ctx, 1, &exynos5250_disp_subcmu); + exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5250_subcmus), + exynos5250_subcmus); samsung_clk_of_add_provider(np, ctx); diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 01bca5a498b2..7670cc596c74 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -534,8 +534,6 @@ static const struct samsung_gate_clock exynos5800_gate_clks[] __initconst = { GATE_BUS_TOP, 24, 0, 0), GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler", GATE_BUS_TOP, 27, CLK_IS_CRITICAL, 0), - GATE(CLK_MAU_EPLL, "mau_epll", "mout_user_mau_epll", - SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0), }; static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = { @@ -577,8 +575,13 @@ static const struct samsung_div_clock exynos5420_div_clks[] __initconst = { static const struct samsung_gate_clock exynos5420_gate_clks[] __initconst = { GATE(CLK_SECKEY, "seckey", "aclk66_psgen", GATE_BUS_PERIS1, 1, 0, 0), + /* Maudio Block */ GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk", SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MAUDIO0, "sclk_maudio0", "dout_maudio0", + GATE_TOP_SCLK_MAU, 0, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MAUPCM0, "sclk_maupcm0", "dout_maupcm0", + GATE_TOP_SCLK_MAU, 1, CLK_SET_RATE_PARENT, 0), }; static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { @@ -890,9 +893,6 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = { /* GSCL Block */ DIV(0, "dout_gscl_blk_333", "aclk333_432_gscl", DIV2_RATIO0, 6, 2), - /* MSCL Block */ - DIV(0, "dout_mscl_blk", "aclk400_mscl", DIV2_RATIO0, 28, 2), - /* PSGEN */ DIV(0, "dout_gen_blk", "mout_user_aclk266", DIV2_RATIO0, 8, 1), DIV(0, "dout_jpg_blk", "aclk166", DIV2_RATIO0, 20, 1), @@ -1017,12 +1017,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = { GATE(CLK_SCLK_DP1, "sclk_dp1", "dout_dp1", GATE_TOP_SCLK_DISP1, 20, CLK_SET_RATE_PARENT, 0), - /* Maudio Block */ - GATE(CLK_SCLK_MAUDIO0, "sclk_maudio0", "dout_maudio0", - GATE_TOP_SCLK_MAU, 0, CLK_SET_RATE_PARENT, 0), - GATE(CLK_SCLK_MAUPCM0, "sclk_maupcm0", "dout_maupcm0", - GATE_TOP_SCLK_MAU, 1, CLK_SET_RATE_PARENT, 0), - /* FSYS Block */ GATE(CLK_TSI, "tsi", "aclk200_fsys", GATE_BUS_FSYS0, 0, 0, 0), GATE(CLK_PDMA0, "pdma0", "aclk200_fsys", GATE_BUS_FSYS0, 1, 0, 0), @@ -1162,17 +1156,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = { GATE(CLK_FIMC_LITE3, "fimc_lite3", "aclk333_432_gscl", GATE_IP_GSCL1, 17, 0, 0), - /* MSCL Block */ - GATE(CLK_MSCL0, "mscl0", "aclk400_mscl", GATE_IP_MSCL, 0, 0, 0), - GATE(CLK_MSCL1, "mscl1", "aclk400_mscl", GATE_IP_MSCL, 1, 0, 0), - GATE(CLK_MSCL2, "mscl2", "aclk400_mscl", GATE_IP_MSCL, 2, 0, 0), - GATE(CLK_SMMU_MSCL0, "smmu_mscl0", "dout_mscl_blk", - GATE_IP_MSCL, 8, 0, 0), - GATE(CLK_SMMU_MSCL1, "smmu_mscl1", "dout_mscl_blk", - GATE_IP_MSCL, 9, 0, 0), - GATE(CLK_SMMU_MSCL2, "smmu_mscl2", "dout_mscl_blk", - GATE_IP_MSCL, 10, 0, 0), - /* ISP */ GATE(CLK_SCLK_UART_ISP, "sclk_uart_isp", "dout_uart_isp", GATE_TOP_SCLK_ISP, 0, CLK_SET_RATE_PARENT, 0), @@ -1281,32 +1264,103 @@ static struct exynos5_subcmu_reg_dump exynos5x_mfc_suspend_regs[] = { { DIV4_RATIO, 0, 0x3 }, /* DIV dout_mfc_blk */ }; -static const struct exynos5_subcmu_info exynos5x_subcmus[] = { - { - .div_clks = exynos5x_disp_div_clks, - .nr_div_clks = ARRAY_SIZE(exynos5x_disp_div_clks), - .gate_clks = exynos5x_disp_gate_clks, - .nr_gate_clks = ARRAY_SIZE(exynos5x_disp_gate_clks), - .suspend_regs = exynos5x_disp_suspend_regs, - .nr_suspend_regs = ARRAY_SIZE(exynos5x_disp_suspend_regs), - .pd_name = "DISP", - }, { - .div_clks = exynos5x_gsc_div_clks, - .nr_div_clks = ARRAY_SIZE(exynos5x_gsc_div_clks), - .gate_clks = exynos5x_gsc_gate_clks, - .nr_gate_clks = ARRAY_SIZE(exynos5x_gsc_gate_clks), - .suspend_regs = exynos5x_gsc_suspend_regs, - .nr_suspend_regs = ARRAY_SIZE(exynos5x_gsc_suspend_regs), - .pd_name = "GSC", - }, { - .div_clks = exynos5x_mfc_div_clks, - .nr_div_clks = ARRAY_SIZE(exynos5x_mfc_div_clks), - .gate_clks = exynos5x_mfc_gate_clks, - .nr_gate_clks = ARRAY_SIZE(exynos5x_mfc_gate_clks), - .suspend_regs = exynos5x_mfc_suspend_regs, - .nr_suspend_regs = ARRAY_SIZE(exynos5x_mfc_suspend_regs), - .pd_name = "MFC", - }, +static const struct samsung_gate_clock exynos5x_mscl_gate_clks[] __initconst = { + /* MSCL Block */ + GATE(CLK_MSCL0, "mscl0", "aclk400_mscl", GATE_IP_MSCL, 0, 0, 0), + GATE(CLK_MSCL1, "mscl1", "aclk400_mscl", GATE_IP_MSCL, 1, 0, 0), + GATE(CLK_MSCL2, "mscl2", "aclk400_mscl", GATE_IP_MSCL, 2, 0, 0), + GATE(CLK_SMMU_MSCL0, "smmu_mscl0", "dout_mscl_blk", + GATE_IP_MSCL, 8, 0, 0), + GATE(CLK_SMMU_MSCL1, "smmu_mscl1", "dout_mscl_blk", + GATE_IP_MSCL, 9, 0, 0), + GATE(CLK_SMMU_MSCL2, "smmu_mscl2", "dout_mscl_blk", + GATE_IP_MSCL, 10, 0, 0), +}; + +static const struct samsung_div_clock exynos5x_mscl_div_clks[] __initconst = { + DIV(0, "dout_mscl_blk", "aclk400_mscl", DIV2_RATIO0, 28, 2), +}; + +static struct exynos5_subcmu_reg_dump exynos5x_mscl_suspend_regs[] = { + { GATE_IP_MSCL, 0xffffffff, 0xffffffff }, /* MSCL gates */ + { SRC_TOP3, 0, BIT(4) }, /* MUX mout_user_aclk400_mscl */ + { DIV2_RATIO0, 0, 0x30000000 }, /* DIV dout_mscl_blk */ +}; + +static const struct samsung_gate_clock exynos5800_mau_gate_clks[] __initconst = { + GATE(CLK_MAU_EPLL, "mau_epll", "mout_user_mau_epll", + SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MAUDIO0, "sclk_maudio0", "dout_maudio0", + GATE_TOP_SCLK_MAU, 0, CLK_SET_RATE_PARENT, 0), + GATE(CLK_SCLK_MAUPCM0, "sclk_maupcm0", "dout_maupcm0", + GATE_TOP_SCLK_MAU, 1, CLK_SET_RATE_PARENT, 0), +}; + +static struct exynos5_subcmu_reg_dump exynos5800_mau_suspend_regs[] = { + { SRC_TOP9, 0, BIT(8) }, /* MUX mout_user_mau_epll */ +}; + +static const struct exynos5_subcmu_info exynos5x_disp_subcmu = { + .div_clks = exynos5x_disp_div_clks, + .nr_div_clks = ARRAY_SIZE(exynos5x_disp_div_clks), + .gate_clks = exynos5x_disp_gate_clks, + .nr_gate_clks = ARRAY_SIZE(exynos5x_disp_gate_clks), + .suspend_regs = exynos5x_disp_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(exynos5x_disp_suspend_regs), + .pd_name = "DISP", +}; + +static const struct exynos5_subcmu_info exynos5x_gsc_subcmu = { + .div_clks = exynos5x_gsc_div_clks, + .nr_div_clks = ARRAY_SIZE(exynos5x_gsc_div_clks), + .gate_clks = exynos5x_gsc_gate_clks, + .nr_gate_clks = ARRAY_SIZE(exynos5x_gsc_gate_clks), + .suspend_regs = exynos5x_gsc_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(exynos5x_gsc_suspend_regs), + .pd_name = "GSC", +}; + +static const struct exynos5_subcmu_info exynos5x_mfc_subcmu = { + .div_clks = exynos5x_mfc_div_clks, + .nr_div_clks = ARRAY_SIZE(exynos5x_mfc_div_clks), + .gate_clks = exynos5x_mfc_gate_clks, + .nr_gate_clks = ARRAY_SIZE(exynos5x_mfc_gate_clks), + .suspend_regs = exynos5x_mfc_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(exynos5x_mfc_suspend_regs), + .pd_name = "MFC", +}; + +static const struct exynos5_subcmu_info exynos5x_mscl_subcmu = { + .div_clks = exynos5x_mscl_div_clks, + .nr_div_clks = ARRAY_SIZE(exynos5x_mscl_div_clks), + .gate_clks = exynos5x_mscl_gate_clks, + .nr_gate_clks = ARRAY_SIZE(exynos5x_mscl_gate_clks), + .suspend_regs = exynos5x_mscl_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(exynos5x_mscl_suspend_regs), + .pd_name = "MSC", +}; + +static const struct exynos5_subcmu_info exynos5800_mau_subcmu = { + .gate_clks = exynos5800_mau_gate_clks, + .nr_gate_clks = ARRAY_SIZE(exynos5800_mau_gate_clks), + .suspend_regs = exynos5800_mau_suspend_regs, + .nr_suspend_regs = ARRAY_SIZE(exynos5800_mau_suspend_regs), + .pd_name = "MAU", +}; + +static const struct exynos5_subcmu_info *exynos5x_subcmus[] = { + &exynos5x_disp_subcmu, + &exynos5x_gsc_subcmu, + &exynos5x_mfc_subcmu, + &exynos5x_mscl_subcmu, +}; + +static const struct exynos5_subcmu_info *exynos5800_subcmus[] = { + &exynos5x_disp_subcmu, + &exynos5x_gsc_subcmu, + &exynos5x_mfc_subcmu, + &exynos5x_mscl_subcmu, + &exynos5800_mau_subcmu, }; static const struct samsung_pll_rate_table exynos5420_pll2550x_24mhz_tbl[] __initconst = { @@ -1539,11 +1593,17 @@ static void __init exynos5x_clk_init(struct device_node *np, samsung_clk_extended_sleep_init(reg_base, exynos5x_clk_regs, ARRAY_SIZE(exynos5x_clk_regs), exynos5420_set_clksrc, ARRAY_SIZE(exynos5420_set_clksrc)); - if (soc == EXYNOS5800) + + if (soc == EXYNOS5800) { samsung_clk_sleep_init(reg_base, exynos5800_clk_regs, ARRAY_SIZE(exynos5800_clk_regs)); - exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5x_subcmus), - exynos5x_subcmus); + + exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5800_subcmus), + exynos5800_subcmus); + } else { + exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5x_subcmus), + exynos5x_subcmus); + } samsung_clk_of_add_provider(np, ctx); } diff --git a/drivers/clk/socfpga/clk-periph-s10.c b/drivers/clk/socfpga/clk-periph-s10.c index 5c50e723ecae..1a191eeeebba 100644 --- a/drivers/clk/socfpga/clk-periph-s10.c +++ b/drivers/clk/socfpga/clk-periph-s10.c @@ -38,7 +38,7 @@ static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk, if (socfpgaclk->fixed_div) { div = socfpgaclk->fixed_div; } else { - if (!socfpgaclk->bypass_reg) + if (socfpgaclk->hw.reg) div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1); } diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c index 5e6038fbf115..09e031176bc6 100644 --- a/drivers/clocksource/timer-riscv.c +++ b/drivers/clocksource/timer-riscv.c @@ -55,7 +55,7 @@ static u64 riscv_sched_clock(void) return get_cycles64(); } -static DEFINE_PER_CPU(struct clocksource, riscv_clocksource) = { +static struct clocksource riscv_clocksource = { .name = "riscv_clocksource", .rating = 300, .mask = CLOCKSOURCE_MASK(64), @@ -92,7 +92,6 @@ void riscv_timer_interrupt(void) static int __init riscv_timer_init_dt(struct device_node *n) { int cpuid, hartid, error; - struct clocksource *cs; hartid = riscv_of_processor_hartid(n); if (hartid < 0) { @@ -112,8 +111,7 @@ static int __init riscv_timer_init_dt(struct device_node *n) pr_info("%s: Registering clocksource cpuid [%d] hartid [%d]\n", __func__, cpuid, hartid); - cs = per_cpu_ptr(&riscv_clocksource, cpuid); - error = clocksource_register_hz(cs, riscv_timebase); + error = clocksource_register_hz(&riscv_clocksource, riscv_timebase); if (error) { pr_err("RISCV timer register failed [%d] for cpu = [%d]\n", error, cpuid); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8dda62367816..c28ebf2810f1 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2528,7 +2528,7 @@ static int cpufreq_boost_set_sw(int state) } ret = dev_pm_qos_update_request(policy->max_freq_req, policy->max); - if (ret) + if (ret < 0) break; } diff --git a/drivers/crypto/ccp/ccp-crypto-aes-galois.c b/drivers/crypto/ccp/ccp-crypto-aes-galois.c index f9fec2ddf56a..94c1ad7eeddf 100644 --- a/drivers/crypto/ccp/ccp-crypto-aes-galois.c +++ b/drivers/crypto/ccp/ccp-crypto-aes-galois.c @@ -58,6 +58,19 @@ static int ccp_aes_gcm_setkey(struct crypto_aead *tfm, const u8 *key, static int ccp_aes_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { + switch (authsize) { + case 16: + case 15: + case 14: + case 13: + case 12: + case 8: + case 4: + break; + default: + return -EINVAL; + } + return 0; } @@ -104,6 +117,7 @@ static int ccp_aes_gcm_crypt(struct aead_request *req, bool encrypt) memset(&rctx->cmd, 0, sizeof(rctx->cmd)); INIT_LIST_HEAD(&rctx->cmd.entry); rctx->cmd.engine = CCP_ENGINE_AES; + rctx->cmd.u.aes.authsize = crypto_aead_authsize(tfm); rctx->cmd.u.aes.type = ctx->u.aes.type; rctx->cmd.u.aes.mode = ctx->u.aes.mode; rctx->cmd.u.aes.action = encrypt; diff --git a/drivers/crypto/ccp/ccp-dev.c b/drivers/crypto/ccp/ccp-dev.c index f79eede71c62..edefa669153f 100644 --- a/drivers/crypto/ccp/ccp-dev.c +++ b/drivers/crypto/ccp/ccp-dev.c @@ -540,6 +540,10 @@ int ccp_dev_suspend(struct sp_device *sp, pm_message_t state) unsigned long flags; unsigned int i; + /* If there's no device there's nothing to do */ + if (!ccp) + return 0; + spin_lock_irqsave(&ccp->cmd_lock, flags); ccp->suspending = 1; @@ -564,6 +568,10 @@ int ccp_dev_resume(struct sp_device *sp) unsigned long flags; unsigned int i; + /* If there's no device there's nothing to do */ + if (!ccp) + return 0; + spin_lock_irqsave(&ccp->cmd_lock, flags); ccp->suspending = 0; diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c index c69ed4bae2eb..9bc3c62157d7 100644 --- a/drivers/crypto/ccp/ccp-ops.c +++ b/drivers/crypto/ccp/ccp-ops.c @@ -622,6 +622,7 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, unsigned long long *final; unsigned int dm_offset; + unsigned int authsize; unsigned int jobid; unsigned int ilen; bool in_place = true; /* Default value */ @@ -643,6 +644,21 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, if (!aes->key) /* Gotta have a key SGL */ return -EINVAL; + /* Zero defaults to 16 bytes, the maximum size */ + authsize = aes->authsize ? aes->authsize : AES_BLOCK_SIZE; + switch (authsize) { + case 16: + case 15: + case 14: + case 13: + case 12: + case 8: + case 4: + break; + default: + return -EINVAL; + } + /* First, decompose the source buffer into AAD & PT, * and the destination buffer into AAD, CT & tag, or * the input into CT & tag. @@ -657,7 +673,7 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, p_tag = scatterwalk_ffwd(sg_tag, p_outp, ilen); } else { /* Input length for decryption includes tag */ - ilen = aes->src_len - AES_BLOCK_SIZE; + ilen = aes->src_len - authsize; p_tag = scatterwalk_ffwd(sg_tag, p_inp, ilen); } @@ -766,8 +782,7 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, while (src.sg_wa.bytes_left) { ccp_prepare_data(&src, &dst, &op, AES_BLOCK_SIZE, true); if (!src.sg_wa.bytes_left) { - unsigned int nbytes = aes->src_len - % AES_BLOCK_SIZE; + unsigned int nbytes = ilen % AES_BLOCK_SIZE; if (nbytes) { op.eom = 1; @@ -839,19 +854,19 @@ static int ccp_run_aes_gcm_cmd(struct ccp_cmd_queue *cmd_q, if (aes->action == CCP_AES_ACTION_ENCRYPT) { /* Put the ciphered tag after the ciphertext. */ - ccp_get_dm_area(&final_wa, 0, p_tag, 0, AES_BLOCK_SIZE); + ccp_get_dm_area(&final_wa, 0, p_tag, 0, authsize); } else { /* Does this ciphered tag match the input? */ - ret = ccp_init_dm_workarea(&tag, cmd_q, AES_BLOCK_SIZE, + ret = ccp_init_dm_workarea(&tag, cmd_q, authsize, DMA_BIDIRECTIONAL); if (ret) goto e_tag; - ret = ccp_set_dm_area(&tag, 0, p_tag, 0, AES_BLOCK_SIZE); + ret = ccp_set_dm_area(&tag, 0, p_tag, 0, authsize); if (ret) goto e_tag; ret = crypto_memneq(tag.address, final_wa.address, - AES_BLOCK_SIZE) ? -EBADMSG : 0; + authsize) ? -EBADMSG : 0; ccp_dm_free(&tag); } @@ -859,11 +874,11 @@ e_tag: ccp_dm_free(&final_wa); e_dst: - if (aes->src_len && !in_place) + if (ilen > 0 && !in_place) ccp_free_data(&dst, cmd_q); e_src: - if (aes->src_len) + if (ilen > 0) ccp_free_data(&src, cmd_q); e_aad: diff --git a/drivers/crypto/ux500/cryp/cryp.c b/drivers/crypto/ux500/cryp/cryp.c index ece83a363e11..f22f6fa612b3 100644 --- a/drivers/crypto/ux500/cryp/cryp.c +++ b/drivers/crypto/ux500/cryp/cryp.c @@ -314,14 +314,17 @@ void cryp_save_device_context(struct cryp_device_data *device_data, case CRYP_KEY_SIZE_256: ctx->key_4_l = readl_relaxed(&src_reg->key_4_l); ctx->key_4_r = readl_relaxed(&src_reg->key_4_r); + /* Fall through */ case CRYP_KEY_SIZE_192: ctx->key_3_l = readl_relaxed(&src_reg->key_3_l); ctx->key_3_r = readl_relaxed(&src_reg->key_3_r); + /* Fall through */ case CRYP_KEY_SIZE_128: ctx->key_2_l = readl_relaxed(&src_reg->key_2_l); ctx->key_2_r = readl_relaxed(&src_reg->key_2_r); + /* Fall through */ default: ctx->key_1_l = readl_relaxed(&src_reg->key_1_l); @@ -361,14 +364,17 @@ void cryp_restore_device_context(struct cryp_device_data *device_data, case CRYP_KEY_SIZE_256: writel_relaxed(ctx->key_4_l, ®->key_4_l); writel_relaxed(ctx->key_4_r, ®->key_4_r); + /* Fall through */ case CRYP_KEY_SIZE_192: writel_relaxed(ctx->key_3_l, ®->key_3_l); writel_relaxed(ctx->key_3_r, ®->key_3_r); + /* Fall through */ case CRYP_KEY_SIZE_128: writel_relaxed(ctx->key_2_l, ®->key_2_l); writel_relaxed(ctx->key_2_r, ®->key_2_r); + /* Fall through */ default: writel_relaxed(ctx->key_1_l, ®->key_1_l); diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 8101ff2f05c1..67100e4e1083 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -37,10 +37,19 @@ #define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14 #define BCM2835_DMA_CHAN_NAME_SIZE 8 +/** + * struct bcm2835_dmadev - BCM2835 DMA controller + * @ddev: DMA device + * @base: base address of register map + * @dma_parms: DMA parameters (to convey 1 GByte max segment size to clients) + * @zero_page: bus address of zero page (to detect transactions copying from + * zero page and avoid accessing memory if so) + */ struct bcm2835_dmadev { struct dma_device ddev; void __iomem *base; struct device_dma_parameters dma_parms; + dma_addr_t zero_page; }; struct bcm2835_dma_cb { @@ -687,11 +696,12 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( size_t period_len, enum dma_transfer_direction direction, unsigned long flags) { + struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device); struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); struct bcm2835_desc *d; dma_addr_t src, dst; u32 info = BCM2835_DMA_WAIT_RESP; - u32 extra = BCM2835_DMA_INT_EN; + u32 extra = 0; size_t max_len = bcm2835_dma_max_frame_length(c); size_t frames; @@ -707,6 +717,11 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( return NULL; } + if (flags & DMA_PREP_INTERRUPT) + extra |= BCM2835_DMA_INT_EN; + else + period_len = buf_len; + /* * warn if buf_len is not a multiple of period_len - this may leed * to unexpected latencies for interrupts and thus audiable clicks @@ -732,6 +747,10 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( dst = c->cfg.dst_addr; src = buf_addr; info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC; + + /* non-lite channels can write zeroes w/o accessing memory */ + if (buf_addr == od->zero_page && !c->is_lite_channel) + info |= BCM2835_DMA_S_IGNORE; } /* calculate number of frames */ @@ -778,7 +797,10 @@ static int bcm2835_dma_terminate_all(struct dma_chan *chan) /* stop DMA activity */ if (c->desc) { - vchan_terminate_vdesc(&c->desc->vd); + if (c->desc->vd.tx.flags & DMA_PREP_INTERRUPT) + vchan_terminate_vdesc(&c->desc->vd); + else + vchan_vdesc_fini(&c->desc->vd); c->desc = NULL; bcm2835_dma_abort(c); } @@ -831,6 +853,9 @@ static void bcm2835_dma_free(struct bcm2835_dmadev *od) list_del(&c->vc.chan.device_node); tasklet_kill(&c->vc.task); } + + dma_unmap_page_attrs(od->ddev.dev, od->zero_page, PAGE_SIZE, + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); } static const struct of_device_id bcm2835_dma_of_match[] = { @@ -907,11 +932,20 @@ static int bcm2835_dma_probe(struct platform_device *pdev) od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV) | BIT(DMA_MEM_TO_MEM); od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + od->ddev.descriptor_reuse = true; od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); platform_set_drvdata(pdev, od); + od->zero_page = dma_map_page_attrs(od->ddev.dev, ZERO_PAGE(0), 0, + PAGE_SIZE, DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(od->ddev.dev, od->zero_page)) { + dev_err(&pdev->dev, "Failed to map zero page\n"); + return -ENOMEM; + } + /* Request DMA channel mask from device tree */ if (of_property_read_u32(pdev->dev.of_node, "brcm,dma-channel-mask", diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index b6cc90cbc9dc..4e5f9f6e901b 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -50,7 +50,7 @@ struct dw_edma_burst { struct dw_edma_region { phys_addr_t paddr; - dma_addr_t vaddr; + void __iomem *vaddr; size_t sz; }; diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 4c96e1c948f2..dc85f55e1bb8 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -130,19 +130,19 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, chip->id = pdev->devfn; chip->irq = pdev->irq; - dw->rg_region.vaddr = (dma_addr_t)pcim_iomap_table(pdev)[pdata->rg_bar]; + dw->rg_region.vaddr = pcim_iomap_table(pdev)[pdata->rg_bar]; dw->rg_region.vaddr += pdata->rg_off; dw->rg_region.paddr = pdev->resource[pdata->rg_bar].start; dw->rg_region.paddr += pdata->rg_off; dw->rg_region.sz = pdata->rg_sz; - dw->ll_region.vaddr = (dma_addr_t)pcim_iomap_table(pdev)[pdata->ll_bar]; + dw->ll_region.vaddr = pcim_iomap_table(pdev)[pdata->ll_bar]; dw->ll_region.vaddr += pdata->ll_off; dw->ll_region.paddr = pdev->resource[pdata->ll_bar].start; dw->ll_region.paddr += pdata->ll_off; dw->ll_region.sz = pdata->ll_sz; - dw->dt_region.vaddr = (dma_addr_t)pcim_iomap_table(pdev)[pdata->dt_bar]; + dw->dt_region.vaddr = pcim_iomap_table(pdev)[pdata->dt_bar]; dw->dt_region.vaddr += pdata->dt_off; dw->dt_region.paddr = pdev->resource[pdata->dt_bar].start; dw->dt_region.paddr += pdata->dt_off; @@ -158,17 +158,17 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, pci_dbg(pdev, "Mode:\t%s\n", dw->mode == EDMA_MODE_LEGACY ? "Legacy" : "Unroll"); - pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%pa, p=%pa)\n", + pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", pdata->rg_bar, pdata->rg_off, pdata->rg_sz, - &dw->rg_region.vaddr, &dw->rg_region.paddr); + dw->rg_region.vaddr, &dw->rg_region.paddr); - pci_dbg(pdev, "L. List:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%pa, p=%pa)\n", + pci_dbg(pdev, "L. List:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", pdata->ll_bar, pdata->ll_off, pdata->ll_sz, - &dw->ll_region.vaddr, &dw->ll_region.paddr); + dw->ll_region.vaddr, &dw->ll_region.paddr); - pci_dbg(pdev, "Data:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%pa, p=%pa)\n", + pci_dbg(pdev, "Data:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n", pdata->dt_bar, pdata->dt_off, pdata->dt_sz, - &dw->dt_region.vaddr, &dw->dt_region.paddr); + dw->dt_region.vaddr, &dw->dt_region.paddr); pci_dbg(pdev, "Nr. IRQs:\t%u\n", dw->nr_irqs); diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index 8a3180ed49a6..692de47b1670 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -25,7 +25,7 @@ enum dw_edma_control { static inline struct dw_edma_v0_regs __iomem *__dw_regs(struct dw_edma *dw) { - return (struct dw_edma_v0_regs __iomem *)dw->rg_region.vaddr; + return dw->rg_region.vaddr; } #define SET(dw, name, value) \ @@ -192,13 +192,12 @@ u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) { struct dw_edma_burst *child; - struct dw_edma_v0_lli *lli; - struct dw_edma_v0_llp *llp; + struct dw_edma_v0_lli __iomem *lli; + struct dw_edma_v0_llp __iomem *llp; u32 control = 0, i = 0; - u64 sar, dar, addr; int j; - lli = (struct dw_edma_v0_lli *)chunk->ll_region.vaddr; + lli = chunk->ll_region.vaddr; if (chunk->cb) control = DW_EDMA_V0_CB; @@ -214,17 +213,15 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) /* Transfer size */ SET_LL(&lli[i].transfer_size, child->sz); /* SAR - low, high */ - sar = cpu_to_le64(child->sar); - SET_LL(&lli[i].sar_low, lower_32_bits(sar)); - SET_LL(&lli[i].sar_high, upper_32_bits(sar)); + SET_LL(&lli[i].sar_low, lower_32_bits(child->sar)); + SET_LL(&lli[i].sar_high, upper_32_bits(child->sar)); /* DAR - low, high */ - dar = cpu_to_le64(child->dar); - SET_LL(&lli[i].dar_low, lower_32_bits(dar)); - SET_LL(&lli[i].dar_high, upper_32_bits(dar)); + SET_LL(&lli[i].dar_low, lower_32_bits(child->dar)); + SET_LL(&lli[i].dar_high, upper_32_bits(child->dar)); i++; } - llp = (struct dw_edma_v0_llp *)&lli[i]; + llp = (void __iomem *)&lli[i]; control = DW_EDMA_V0_LLP | DW_EDMA_V0_TCB; if (!chunk->cb) control |= DW_EDMA_V0_CB; @@ -232,9 +229,8 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) /* Channel control */ SET_LL(&llp->control, control); /* Linked list - low, high */ - addr = cpu_to_le64(chunk->ll_region.paddr); - SET_LL(&llp->llp_low, lower_32_bits(addr)); - SET_LL(&llp->llp_high, upper_32_bits(addr)); + SET_LL(&llp->llp_low, lower_32_bits(chunk->ll_region.paddr)); + SET_LL(&llp->llp_high, upper_32_bits(chunk->ll_region.paddr)); } void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) @@ -242,7 +238,6 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->chip->dw; u32 tmp; - u64 llp; dw_edma_v0_core_write_chunk(chunk); @@ -262,9 +257,10 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) SET_CH(dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); /* Linked list - low, high */ - llp = cpu_to_le64(chunk->ll_region.paddr); - SET_CH(dw, chan->dir, chan->id, llp_low, lower_32_bits(llp)); - SET_CH(dw, chan->dir, chan->id, llp_high, upper_32_bits(llp)); + SET_CH(dw, chan->dir, chan->id, llp_low, + lower_32_bits(chunk->ll_region.paddr)); + SET_CH(dw, chan->dir, chan->id, llp_high, + upper_32_bits(chunk->ll_region.paddr)); } /* Doorbell */ SET_RW(dw, chan->dir, doorbell, diff --git a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c index 3226f528cc11..42739508c0d8 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-debugfs.c +++ b/drivers/dma/dw-edma/dw-edma-v0-debugfs.c @@ -14,7 +14,7 @@ #include "dw-edma-core.h" #define REGS_ADDR(name) \ - ((dma_addr_t *)®s->name) + ((void __force *)®s->name) #define REGISTER(name) \ { #name, REGS_ADDR(name) } @@ -40,36 +40,37 @@ static struct dentry *base_dir; static struct dw_edma *dw; -static struct dw_edma_v0_regs *regs; +static struct dw_edma_v0_regs __iomem *regs; static struct { - void *start; - void *end; + void __iomem *start; + void __iomem *end; } lim[2][EDMA_V0_MAX_NR_CH]; struct debugfs_entries { - char name[24]; + const char *name; dma_addr_t *reg; }; static int dw_edma_debugfs_u32_get(void *data, u64 *val) { + void __iomem *reg = (void __force __iomem *)data; if (dw->mode == EDMA_MODE_LEGACY && - data >= (void *)®s->type.legacy.ch) { - void *ptr = (void *)®s->type.legacy.ch; + reg >= (void __iomem *)®s->type.legacy.ch) { + void __iomem *ptr = ®s->type.legacy.ch; u32 viewport_sel = 0; unsigned long flags; u16 ch; for (ch = 0; ch < dw->wr_ch_cnt; ch++) - if (lim[0][ch].start >= data && data < lim[0][ch].end) { - ptr += (data - lim[0][ch].start); + if (lim[0][ch].start >= reg && reg < lim[0][ch].end) { + ptr += (reg - lim[0][ch].start); goto legacy_sel_wr; } for (ch = 0; ch < dw->rd_ch_cnt; ch++) - if (lim[1][ch].start >= data && data < lim[1][ch].end) { - ptr += (data - lim[1][ch].start); + if (lim[1][ch].start >= reg && reg < lim[1][ch].end) { + ptr += (reg - lim[1][ch].start); goto legacy_sel_rd; } @@ -86,7 +87,7 @@ legacy_sel_wr: raw_spin_unlock_irqrestore(&dw->lock, flags); } else { - *val = readl(data); + *val = readl(reg); } return 0; @@ -105,7 +106,7 @@ static void dw_edma_debugfs_create_x32(const struct debugfs_entries entries[], } } -static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs *regs, +static void dw_edma_debugfs_regs_ch(struct dw_edma_v0_ch_regs __iomem *regs, struct dentry *dir) { int nr_entries; @@ -288,7 +289,7 @@ void dw_edma_v0_debugfs_on(struct dw_edma_chip *chip) if (!dw) return; - regs = (struct dw_edma_v0_regs *)dw->rg_region.vaddr; + regs = dw->rg_region.vaddr; if (!regs) return; diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 23e0a356f167..ad72b3f42ffa 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -1163,6 +1163,7 @@ static int fsl_dma_chan_probe(struct fsldma_device *fdev, switch (chan->feature & FSL_DMA_IP_MASK) { case FSL_DMA_IP_85XX: chan->toggle_ext_pause = fsl_chan_toggle_ext_pause; + /* Fall through */ case FSL_DMA_IP_83XX: chan->toggle_ext_start = fsl_chan_toggle_ext_start; chan->set_src_loop_size = fsl_chan_set_src_loop_size; diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 9c41a4e42575..1072c450c37a 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -192,6 +192,7 @@ struct rcar_dmac_chan { * @iomem: remapped I/O memory base * @n_channels: number of available channels * @channels: array of DMAC channels + * @channels_mask: bitfield of which DMA channels are managed by this driver * @modules: bitmask of client modules in use */ struct rcar_dmac { @@ -202,6 +203,7 @@ struct rcar_dmac { unsigned int n_channels; struct rcar_dmac_chan *channels; + unsigned int channels_mask; DECLARE_BITMAP(modules, 256); }; @@ -438,7 +440,7 @@ static int rcar_dmac_init(struct rcar_dmac *dmac) u16 dmaor; /* Clear all channels and enable the DMAC globally. */ - rcar_dmac_write(dmac, RCAR_DMACHCLR, GENMASK(dmac->n_channels - 1, 0)); + rcar_dmac_write(dmac, RCAR_DMACHCLR, dmac->channels_mask); rcar_dmac_write(dmac, RCAR_DMAOR, RCAR_DMAOR_PRI_FIXED | RCAR_DMAOR_DME); @@ -814,6 +816,9 @@ static void rcar_dmac_stop_all_chan(struct rcar_dmac *dmac) for (i = 0; i < dmac->n_channels; ++i) { struct rcar_dmac_chan *chan = &dmac->channels[i]; + if (!(dmac->channels_mask & BIT(i))) + continue; + /* Stop and reinitialize the channel. */ spin_lock_irq(&chan->lock); rcar_dmac_chan_halt(chan); @@ -1776,6 +1781,8 @@ static int rcar_dmac_chan_probe(struct rcar_dmac *dmac, return 0; } +#define RCAR_DMAC_MAX_CHANNELS 32 + static int rcar_dmac_parse_of(struct device *dev, struct rcar_dmac *dmac) { struct device_node *np = dev->of_node; @@ -1787,12 +1794,16 @@ static int rcar_dmac_parse_of(struct device *dev, struct rcar_dmac *dmac) return ret; } - if (dmac->n_channels <= 0 || dmac->n_channels >= 100) { + /* The hardware and driver don't support more than 32 bits in CHCLR */ + if (dmac->n_channels <= 0 || + dmac->n_channels >= RCAR_DMAC_MAX_CHANNELS) { dev_err(dev, "invalid number of channels %u\n", dmac->n_channels); return -EINVAL; } + dmac->channels_mask = GENMASK(dmac->n_channels - 1, 0); + return 0; } @@ -1802,7 +1813,6 @@ static int rcar_dmac_probe(struct platform_device *pdev) DMA_SLAVE_BUSWIDTH_2_BYTES | DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_8_BYTES | DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES | DMA_SLAVE_BUSWIDTH_64_BYTES; - unsigned int channels_offset = 0; struct dma_device *engine; struct rcar_dmac *dmac; struct resource *mem; @@ -1831,10 +1841,8 @@ static int rcar_dmac_probe(struct platform_device *pdev) * level we can't disable it selectively, so ignore channel 0 for now if * the device is part of an IOMMU group. */ - if (device_iommu_mapped(&pdev->dev)) { - dmac->n_channels--; - channels_offset = 1; - } + if (device_iommu_mapped(&pdev->dev)) + dmac->channels_mask &= ~BIT(0); dmac->channels = devm_kcalloc(&pdev->dev, dmac->n_channels, sizeof(*dmac->channels), GFP_KERNEL); @@ -1892,8 +1900,10 @@ static int rcar_dmac_probe(struct platform_device *pdev) INIT_LIST_HEAD(&engine->channels); for (i = 0; i < dmac->n_channels; ++i) { - ret = rcar_dmac_chan_probe(dmac, &dmac->channels[i], - i + channels_offset); + if (!(dmac->channels_mask & BIT(i))) + continue; + + ret = rcar_dmac_chan_probe(dmac, &dmac->channels[i], i); if (ret < 0) goto error; } diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index baac476c8622..525dc7338fe3 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -908,6 +908,7 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct sprd_dma_chn *schan = to_sprd_dma_chan(chan); struct dma_slave_config *slave_cfg = &schan->slave_cfg; dma_addr_t src = 0, dst = 0; + dma_addr_t start_src = 0, start_dst = 0; struct sprd_dma_desc *sdesc; struct scatterlist *sg; u32 len = 0; @@ -954,6 +955,11 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dst = sg_dma_address(sg); } + if (!i) { + start_src = src; + start_dst = dst; + } + /* * The link-list mode needs at least 2 link-list * configurations. If there is only one sg, it doesn't @@ -970,8 +976,8 @@ sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, } } - ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, src, dst, len, - dir, flags, slave_cfg); + ret = sprd_dma_fill_desc(chan, &sdesc->chn_hw, 0, 0, start_src, + start_dst, len, dir, flags, slave_cfg); if (ret) { kfree(sdesc); return NULL; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 89d710899010..de8bfd9a76e9 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -142,7 +142,7 @@ enum d40_events { * when the DMA hw is powered off. * TODO: Add save/restore of D40_DREG_GCC on dma40 v3 or later, if that works. */ -static u32 d40_backup_regs[] = { +static __maybe_unused u32 d40_backup_regs[] = { D40_DREG_LCPA, D40_DREG_LCLA, D40_DREG_PRMSE, @@ -211,7 +211,7 @@ static u32 d40_backup_regs_v4b[] = { #define BACKUP_REGS_SZ_V4B ARRAY_SIZE(d40_backup_regs_v4b) -static u32 d40_backup_regs_chan[] = { +static __maybe_unused u32 d40_backup_regs_chan[] = { D40_CHAN_REG_SSCFG, D40_CHAN_REG_SSELT, D40_CHAN_REG_SSPTR, diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index d6e919d3936a..1311de74bfdd 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -1366,7 +1366,7 @@ static irqreturn_t stm32_mdma_irq_handler(int irq, void *devid) chan = &dmadev->chan[id]; if (!chan) { - dev_err(chan2dev(chan), "MDMA channel not initialized\n"); + dev_dbg(mdma2dev(dmadev), "MDMA channel not initialized\n"); goto exit; } diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index 2805853e963f..b33cf6e8ab8e 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -712,7 +712,7 @@ static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec, return chan; } -static int tegra_adma_runtime_suspend(struct device *dev) +static int __maybe_unused tegra_adma_runtime_suspend(struct device *dev) { struct tegra_adma *tdma = dev_get_drvdata(dev); struct tegra_adma_chan_regs *ch_reg; @@ -744,7 +744,7 @@ clk_disable: return 0; } -static int tegra_adma_runtime_resume(struct device *dev) +static int __maybe_unused tegra_adma_runtime_resume(struct device *dev) { struct tegra_adma *tdma = dev_get_drvdata(dev); struct tegra_adma_chan_regs *ch_reg; diff --git a/drivers/dma/ti/dma-crossbar.c b/drivers/dma/ti/dma-crossbar.c index ad2f0a4cd6a4..f255056696ee 100644 --- a/drivers/dma/ti/dma-crossbar.c +++ b/drivers/dma/ti/dma-crossbar.c @@ -391,8 +391,10 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev) ret = of_property_read_u32_array(node, pname, (u32 *)rsv_events, nelm * 2); - if (ret) + if (ret) { + kfree(rsv_events); return ret; + } for (i = 0; i < nelm; i++) { ti_dra7_xbar_reserve(rsv_events[i][0], rsv_events[i][1], diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c index ba2489d4ea24..d07c0d5de7a2 100644 --- a/drivers/dma/ti/omap-dma.c +++ b/drivers/dma/ti/omap-dma.c @@ -1234,7 +1234,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_interleaved( if (src_icg) { d->ccr |= CCR_SRC_AMODE_DBLIDX; d->ei = 1; - d->fi = src_icg; + d->fi = src_icg + 1; } else if (xt->src_inc) { d->ccr |= CCR_SRC_AMODE_POSTINC; d->fi = 0; @@ -1249,7 +1249,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_dma_interleaved( if (dst_icg) { d->ccr |= CCR_DST_AMODE_DBLIDX; sg->ei = 1; - sg->fi = dst_icg; + sg->fi = dst_icg + 1; } else if (xt->dst_inc) { d->ccr |= CCR_DST_AMODE_POSTINC; sg->fi = 0; @@ -1540,8 +1540,10 @@ static int omap_dma_probe(struct platform_device *pdev) rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq, IRQF_SHARED, "omap-dma-engine", od); - if (rc) + if (rc) { + omap_dma_free(od); return rc; + } } if (omap_dma_glbl_read(od, CAPS_0) & CAPS_0_SUPPORT_LL123) diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 200c04ce5b0e..2a2603bfb918 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -510,4 +510,11 @@ config EDAC_ASPEED First, ECC must be configured in the bootloader. Then, this driver will expose error counters via the EDAC kernel framework. +config EDAC_BLUEFIELD + tristate "Mellanox BlueField Memory ECC" + depends on ARM64 && ((MELLANOX_PLATFORM && ACPI) || COMPILE_TEST) + help + Support for error detection and correction on the + Mellanox BlueField SoCs. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 165ca65e1a3a..d265ff9311f0 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -85,3 +85,4 @@ obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o obj-$(CONFIG_EDAC_TI) += ti_edac.o obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o +obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index c2e693e34d43..fbda4b876afd 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -222,7 +222,6 @@ static unsigned long get_total_mem(void) static const struct of_device_id altr_sdram_ctrl_of_match[] = { { .compatible = "altr,sdram-edac", .data = &c5_data}, { .compatible = "altr,sdram-edac-a10", .data = &a10_data}, - { .compatible = "altr,sdram-edac-s10", .data = &a10_data}, {}, }; MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match); @@ -1170,6 +1169,24 @@ static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat) return 0; } +/*********************** SDRAM EDAC Device Functions *********************/ + +#ifdef CONFIG_EDAC_ALTERA_SDRAM + +static const struct edac_device_prv_data s10_sdramecc_data = { + .setup = altr_check_ecc_deps, + .ce_clear_mask = ALTR_S10_ECC_SERRPENA, + .ue_clear_mask = ALTR_S10_ECC_DERRPENA, + .ecc_enable_mask = ALTR_S10_ECC_EN, + .ecc_en_ofst = ALTR_S10_ECC_CTRL_SDRAM_OFST, + .ce_set_mask = ALTR_S10_ECC_TSERRA, + .ue_set_mask = ALTR_S10_ECC_TDERRA, + .set_err_ofst = ALTR_S10_ECC_INTTEST_OFST, + .ecc_irq_handler = altr_edac_a10_ecc_irq, + .inject_fops = &altr_edac_a10_device_inject_fops, +}; +#endif /* CONFIG_EDAC_ALTERA_SDRAM */ + /*********************** OCRAM EDAC Device Functions *********************/ #ifdef CONFIG_EDAC_ALTERA_OCRAM @@ -1759,6 +1776,9 @@ static const struct of_device_id altr_edac_a10_device_of_match[] = { #ifdef CONFIG_EDAC_ALTERA_SDMMC { .compatible = "altr,socfpga-sdmmc-ecc", .data = &a10_sdmmcecca_data }, #endif +#ifdef CONFIG_EDAC_ALTERA_SDRAM + { .compatible = "altr,sdram-edac-s10", .data = &s10_sdramecc_data }, +#endif {}, }; MODULE_DEVICE_TABLE(of, altr_edac_a10_device_of_match); @@ -1866,6 +1886,7 @@ static void altr_edac_a10_irq_handler(struct irq_desc *desc) struct altr_arria10_edac *edac = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); int irq = irq_desc_get_irq(desc); + unsigned long bits; dberr = (irq == edac->db_irq) ? 1 : 0; sm_offset = dberr ? A10_SYSMGR_ECC_INTSTAT_DERR_OFST : @@ -1875,7 +1896,8 @@ static void altr_edac_a10_irq_handler(struct irq_desc *desc) regmap_read(edac->ecc_mgr_map, sm_offset, &irq_status); - for_each_set_bit(bit, (unsigned long *)&irq_status, 32) { + bits = irq_status; + for_each_set_bit(bit, &bits, 32) { irq = irq_linear_revmap(edac->domain, dberr * 32 + bit); if (irq) generic_handle_irq(irq); @@ -1889,6 +1911,10 @@ static int validate_parent_available(struct device_node *np) struct device_node *parent; int ret = 0; + /* SDRAM must be present for Linux (implied parent) */ + if (of_device_is_compatible(np, "altr,sdram-edac-s10")) + return 0; + /* Ensure parent device is enabled if parent node exists */ parent = of_parse_phandle(np, "altr,ecc-parent", 0); if (parent && !of_device_is_available(parent)) @@ -1898,6 +1924,22 @@ static int validate_parent_available(struct device_node *np) return ret; } +static int get_s10_sdram_edac_resource(struct device_node *np, + struct resource *res) +{ + struct device_node *parent; + int ret; + + parent = of_parse_phandle(np, "altr,sdr-syscon", 0); + if (!parent) + return -ENODEV; + + ret = of_address_to_resource(parent, 0, res); + of_node_put(parent); + + return ret; +} + static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, struct device_node *np) { @@ -1925,7 +1967,11 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac, if (!devres_open_group(edac->dev, altr_edac_a10_device_add, GFP_KERNEL)) return -ENOMEM; - rc = of_address_to_resource(np, 0, &res); + if (of_device_is_compatible(np, "altr,sdram-edac-s10")) + rc = get_s10_sdram_edac_resource(np, &res); + else + rc = of_address_to_resource(np, 0, &res); + if (rc < 0) { edac_printk(KERN_ERR, EDAC_DEVICE, "%s: no resource address\n", ecc_name); @@ -2231,13 +2277,15 @@ static int altr_edac_a10_probe(struct platform_device *pdev) of_device_is_compatible(child, "altr,socfpga-dma-ecc") || of_device_is_compatible(child, "altr,socfpga-usb-ecc") || of_device_is_compatible(child, "altr,socfpga-qspi-ecc") || +#ifdef CONFIG_EDAC_ALTERA_SDRAM + of_device_is_compatible(child, "altr,sdram-edac-s10") || +#endif of_device_is_compatible(child, "altr,socfpga-sdmmc-ecc")) altr_edac_a10_device_add(edac, child); #ifdef CONFIG_EDAC_ALTERA_SDRAM - else if ((of_device_is_compatible(child, "altr,sdram-edac-a10")) || - (of_device_is_compatible(child, "altr,sdram-edac-s10"))) + else if (of_device_is_compatible(child, "altr,sdram-edac-a10")) of_platform_populate(pdev->dev.of_node, altr_sdram_ctrl_of_match, NULL, &pdev->dev); diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h index 55654cc4bcdf..3727e72c8c2e 100644 --- a/drivers/edac/altera_edac.h +++ b/drivers/edac/altera_edac.h @@ -289,6 +289,29 @@ struct altr_sdram_mc_data { #define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000 /************* Stratix10 Defines **************/ +#define ALTR_S10_ECC_CTRL_SDRAM_OFST 0x00 +#define ALTR_S10_ECC_EN BIT(0) + +#define ALTR_S10_ECC_ERRINTEN_OFST 0x10 +#define ALTR_S10_ECC_ERRINTENS_OFST 0x14 +#define ALTR_S10_ECC_ERRINTENR_OFST 0x18 +#define ALTR_S10_ECC_SERRINTEN BIT(0) + +#define ALTR_S10_ECC_INTMODE_OFST 0x1C +#define ALTR_S10_ECC_INTMODE BIT(0) + +#define ALTR_S10_ECC_INTSTAT_OFST 0x20 +#define ALTR_S10_ECC_SERRPENA BIT(0) +#define ALTR_S10_ECC_DERRPENA BIT(8) +#define ALTR_S10_ECC_ERRPENA_MASK (ALTR_S10_ECC_SERRPENA | \ + ALTR_S10_ECC_DERRPENA) + +#define ALTR_S10_ECC_INTTEST_OFST 0x24 +#define ALTR_S10_ECC_TSERRA BIT(0) +#define ALTR_S10_ECC_TDERRA BIT(8) +#define ALTR_S10_ECC_TSERRB BIT(16) +#define ALTR_S10_ECC_TDERRB BIT(24) + #define ALTR_S10_DERR_ADDRA_OFST 0x2C /* Stratix10 ECC Manager Defines */ @@ -300,7 +323,7 @@ struct altr_sdram_mc_data { #define S10_SYSMGR_UE_ADDR_OFST 0x224 #define S10_DDR0_IRQ_MASK BIT(16) -#define S10_DBE_IRQ_MASK 0x3FE +#define S10_DBE_IRQ_MASK 0x3FFFE /* Define ECC Block Offsets for peripherals */ #define ECC_BLK_ADDRESS_OFST 0x40 diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 873437be86d9..c1d4536ae466 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -788,51 +788,45 @@ static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) (dclr & BIT(15)) ? "yes" : "no"); } -/* - * The Address Mask should be a contiguous set of bits in the non-interleaved - * case. So to check for CS interleaving, find the most- and least-significant - * bits of the mask, generate a contiguous bitmask, and compare the two. - */ -static bool f17_cs_interleaved(struct amd64_pvt *pvt, u8 ctrl, int cs) +#define CS_EVEN_PRIMARY BIT(0) +#define CS_ODD_PRIMARY BIT(1) +#define CS_EVEN_SECONDARY BIT(2) +#define CS_ODD_SECONDARY BIT(3) + +#define CS_EVEN (CS_EVEN_PRIMARY | CS_EVEN_SECONDARY) +#define CS_ODD (CS_ODD_PRIMARY | CS_ODD_SECONDARY) + +static int f17_get_cs_mode(int dimm, u8 ctrl, struct amd64_pvt *pvt) { - u32 mask = pvt->csels[ctrl].csmasks[cs >> 1]; - u32 msb = fls(mask) - 1, lsb = ffs(mask) - 1; - u32 test_mask = GENMASK(msb, lsb); + int cs_mode = 0; - edac_dbg(1, "mask=0x%08x test_mask=0x%08x\n", mask, test_mask); + if (csrow_enabled(2 * dimm, ctrl, pvt)) + cs_mode |= CS_EVEN_PRIMARY; - return mask ^ test_mask; + if (csrow_enabled(2 * dimm + 1, ctrl, pvt)) + cs_mode |= CS_ODD_PRIMARY; + + /* Asymmetric dual-rank DIMM support. */ + if (csrow_sec_enabled(2 * dimm + 1, ctrl, pvt)) + cs_mode |= CS_ODD_SECONDARY; + + return cs_mode; } static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl) { - int dimm, size0, size1, cs0, cs1; + int dimm, size0, size1, cs0, cs1, cs_mode; edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl); - for (dimm = 0; dimm < 4; dimm++) { - size0 = 0; + for (dimm = 0; dimm < 2; dimm++) { cs0 = dimm * 2; - - if (csrow_enabled(cs0, ctrl, pvt)) - size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs0); - - size1 = 0; cs1 = dimm * 2 + 1; - if (csrow_enabled(cs1, ctrl, pvt)) { - /* - * CS interleaving is only supported if both CSes have - * the same amount of memory. Because they are - * interleaved, it will look like both CSes have the - * full amount of memory. Save the size for both as - * half the amount we found on CS0, if interleaved. - */ - if (f17_cs_interleaved(pvt, ctrl, cs1)) - size1 = size0 = (size0 >> 1); - else - size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, cs1); - } + cs_mode = f17_get_cs_mode(dimm, ctrl, pvt); + + size0 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs0); + size1 = pvt->ops->dbam_to_cs(pvt, ctrl, cs_mode, cs1); amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", cs0, size0, @@ -942,89 +936,119 @@ static void prep_chip_selects(struct amd64_pvt *pvt) } else if (pvt->fam == 0x15 && pvt->model == 0x30) { pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4; pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2; + } else if (pvt->fam >= 0x17) { + int umc; + + for_each_umc(umc) { + pvt->csels[umc].b_cnt = 4; + pvt->csels[umc].m_cnt = 2; + } + } else { pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8; pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 4; } } +static void read_umc_base_mask(struct amd64_pvt *pvt) +{ + u32 umc_base_reg, umc_base_reg_sec; + u32 umc_mask_reg, umc_mask_reg_sec; + u32 base_reg, base_reg_sec; + u32 mask_reg, mask_reg_sec; + u32 *base, *base_sec; + u32 *mask, *mask_sec; + int cs, umc; + + for_each_umc(umc) { + umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR; + umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC; + + for_each_chip_select(cs, umc, pvt) { + base = &pvt->csels[umc].csbases[cs]; + base_sec = &pvt->csels[umc].csbases_sec[cs]; + + base_reg = umc_base_reg + (cs * 4); + base_reg_sec = umc_base_reg_sec + (cs * 4); + + if (!amd_smn_read(pvt->mc_node_id, base_reg, base)) + edac_dbg(0, " DCSB%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *base, base_reg); + + if (!amd_smn_read(pvt->mc_node_id, base_reg_sec, base_sec)) + edac_dbg(0, " DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *base_sec, base_reg_sec); + } + + umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK; + umc_mask_reg_sec = get_umc_base(umc) + UMCCH_ADDR_MASK_SEC; + + for_each_chip_select_mask(cs, umc, pvt) { + mask = &pvt->csels[umc].csmasks[cs]; + mask_sec = &pvt->csels[umc].csmasks_sec[cs]; + + mask_reg = umc_mask_reg + (cs * 4); + mask_reg_sec = umc_mask_reg_sec + (cs * 4); + + if (!amd_smn_read(pvt->mc_node_id, mask_reg, mask)) + edac_dbg(0, " DCSM%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *mask, mask_reg); + + if (!amd_smn_read(pvt->mc_node_id, mask_reg_sec, mask_sec)) + edac_dbg(0, " DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *mask_sec, mask_reg_sec); + } + } +} + /* * Function 2 Offset F10_DCSB0; read in the DCS Base and DCS Mask registers */ static void read_dct_base_mask(struct amd64_pvt *pvt) { - int base_reg0, base_reg1, mask_reg0, mask_reg1, cs; + int cs; prep_chip_selects(pvt); - if (pvt->umc) { - base_reg0 = get_umc_base(0) + UMCCH_BASE_ADDR; - base_reg1 = get_umc_base(1) + UMCCH_BASE_ADDR; - mask_reg0 = get_umc_base(0) + UMCCH_ADDR_MASK; - mask_reg1 = get_umc_base(1) + UMCCH_ADDR_MASK; - } else { - base_reg0 = DCSB0; - base_reg1 = DCSB1; - mask_reg0 = DCSM0; - mask_reg1 = DCSM1; - } + if (pvt->umc) + return read_umc_base_mask(pvt); for_each_chip_select(cs, 0, pvt) { - int reg0 = base_reg0 + (cs * 4); - int reg1 = base_reg1 + (cs * 4); + int reg0 = DCSB0 + (cs * 4); + int reg1 = DCSB1 + (cs * 4); u32 *base0 = &pvt->csels[0].csbases[cs]; u32 *base1 = &pvt->csels[1].csbases[cs]; - if (pvt->umc) { - if (!amd_smn_read(pvt->mc_node_id, reg0, base0)) - edac_dbg(0, " DCSB0[%d]=0x%08x reg: 0x%x\n", - cs, *base0, reg0); + if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) + edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", + cs, *base0, reg0); - if (!amd_smn_read(pvt->mc_node_id, reg1, base1)) - edac_dbg(0, " DCSB1[%d]=0x%08x reg: 0x%x\n", - cs, *base1, reg1); - } else { - if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) - edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", - cs, *base0, reg0); - - if (pvt->fam == 0xf) - continue; + if (pvt->fam == 0xf) + continue; - if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) - edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", - cs, *base1, (pvt->fam == 0x10) ? reg1 - : reg0); - } + if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) + edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", + cs, *base1, (pvt->fam == 0x10) ? reg1 + : reg0); } for_each_chip_select_mask(cs, 0, pvt) { - int reg0 = mask_reg0 + (cs * 4); - int reg1 = mask_reg1 + (cs * 4); + int reg0 = DCSM0 + (cs * 4); + int reg1 = DCSM1 + (cs * 4); u32 *mask0 = &pvt->csels[0].csmasks[cs]; u32 *mask1 = &pvt->csels[1].csmasks[cs]; - if (pvt->umc) { - if (!amd_smn_read(pvt->mc_node_id, reg0, mask0)) - edac_dbg(0, " DCSM0[%d]=0x%08x reg: 0x%x\n", - cs, *mask0, reg0); - - if (!amd_smn_read(pvt->mc_node_id, reg1, mask1)) - edac_dbg(0, " DCSM1[%d]=0x%08x reg: 0x%x\n", - cs, *mask1, reg1); - } else { - if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) - edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", - cs, *mask0, reg0); + if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) + edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", + cs, *mask0, reg0); - if (pvt->fam == 0xf) - continue; + if (pvt->fam == 0xf) + continue; - if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) - edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", - cs, *mask1, (pvt->fam == 0x10) ? reg1 - : reg0); - } + if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) + edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", + cs, *mask1, (pvt->fam == 0x10) ? reg1 + : reg0); } } @@ -1556,18 +1580,58 @@ static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, return ddr3_cs_size(cs_mode, false); } -static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc, +static int f17_addr_mask_to_cs_size(struct amd64_pvt *pvt, u8 umc, unsigned int cs_mode, int csrow_nr) { - u32 base_addr = pvt->csels[umc].csbases[csrow_nr]; + u32 addr_mask_orig, addr_mask_deinterleaved; + u32 msb, weight, num_zero_bits; + int dimm, size = 0; - /* Each mask is used for every two base addresses. */ - u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1]; + /* No Chip Selects are enabled. */ + if (!cs_mode) + return size; - /* Register [31:1] = Address [39:9]. Size is in kBs here. */ - u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1; + /* Requested size of an even CS but none are enabled. */ + if (!(cs_mode & CS_EVEN) && !(csrow_nr & 1)) + return size; - edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask); + /* Requested size of an odd CS but none are enabled. */ + if (!(cs_mode & CS_ODD) && (csrow_nr & 1)) + return size; + + /* + * There is one mask per DIMM, and two Chip Selects per DIMM. + * CS0 and CS1 -> DIMM0 + * CS2 and CS3 -> DIMM1 + */ + dimm = csrow_nr >> 1; + + /* Asymmetric dual-rank DIMM support. */ + if ((csrow_nr & 1) && (cs_mode & CS_ODD_SECONDARY)) + addr_mask_orig = pvt->csels[umc].csmasks_sec[dimm]; + else + addr_mask_orig = pvt->csels[umc].csmasks[dimm]; + + /* + * The number of zero bits in the mask is equal to the number of bits + * in a full mask minus the number of bits in the current mask. + * + * The MSB is the number of bits in the full mask because BIT[0] is + * always 0. + */ + msb = fls(addr_mask_orig) - 1; + weight = hweight_long(addr_mask_orig); + num_zero_bits = msb - weight; + + /* Take the number of zero bits off from the top of the mask. */ + addr_mask_deinterleaved = GENMASK_ULL(msb - num_zero_bits, 1); + + edac_dbg(1, "CS%d DIMM%d AddrMasks:\n", csrow_nr, dimm); + edac_dbg(1, " Original AddrMask: 0x%x\n", addr_mask_orig); + edac_dbg(1, " Deinterleaved AddrMask: 0x%x\n", addr_mask_deinterleaved); + + /* Register [31:1] = Address [39:9]. Size is in kBs here. */ + size = (addr_mask_deinterleaved >> 2) + 1; /* Return size in MBs. */ return size >> 10; @@ -2232,7 +2296,7 @@ static struct amd64_family_type family_types[] = { .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6, .ops = { .early_channel_count = f17_early_channel_count, - .dbam_to_cs = f17_base_addr_to_cs_size, + .dbam_to_cs = f17_addr_mask_to_cs_size, } }, [F17_M10H_CPUS] = { @@ -2241,7 +2305,7 @@ static struct amd64_family_type family_types[] = { .f6_id = PCI_DEVICE_ID_AMD_17H_M10H_DF_F6, .ops = { .early_channel_count = f17_early_channel_count, - .dbam_to_cs = f17_base_addr_to_cs_size, + .dbam_to_cs = f17_addr_mask_to_cs_size, } }, [F17_M30H_CPUS] = { @@ -2250,7 +2314,16 @@ static struct amd64_family_type family_types[] = { .f6_id = PCI_DEVICE_ID_AMD_17H_M30H_DF_F6, .ops = { .early_channel_count = f17_early_channel_count, - .dbam_to_cs = f17_base_addr_to_cs_size, + .dbam_to_cs = f17_addr_mask_to_cs_size, + } + }, + [F17_M70H_CPUS] = { + .ctl_name = "F17h_M70h", + .f0_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F0, + .f6_id = PCI_DEVICE_ID_AMD_17H_M70H_DF_F6, + .ops = { + .early_channel_count = f17_early_channel_count, + .dbam_to_cs = f17_addr_mask_to_cs_size, } }, }; @@ -2537,13 +2610,6 @@ static void decode_umc_error(int node_id, struct mce *m) err.channel = find_umc_channel(m); - if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { - err.err_code = ERR_NORM_ADDR; - goto log_error; - } - - error_address_to_page_and_offset(sys_addr, &err); - if (!(m->status & MCI_STATUS_SYNDV)) { err.err_code = ERR_SYND; goto log_error; @@ -2560,6 +2626,13 @@ static void decode_umc_error(int node_id, struct mce *m) err.csrow = m->synd & 0x7; + if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { + err.err_code = ERR_NORM_ADDR; + goto log_error; + } + + error_address_to_page_and_offset(sys_addr, &err); + log_error: __log_ecc_error(mci, &err, ecc_type); } @@ -2809,10 +2882,12 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig) int csrow_nr = csrow_nr_orig; u32 cs_mode, nr_pages; - if (!pvt->umc) + if (!pvt->umc) { csrow_nr >>= 1; - - cs_mode = DBAM_DIMM(csrow_nr, dbam); + cs_mode = DBAM_DIMM(csrow_nr, dbam); + } else { + cs_mode = f17_get_cs_mode(csrow_nr >> 1, dct, pvt); + } nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, csrow_nr); nr_pages <<= 20 - PAGE_SHIFT; @@ -2824,6 +2899,49 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr_orig) return nr_pages; } +static int init_csrows_df(struct mem_ctl_info *mci) +{ + struct amd64_pvt *pvt = mci->pvt_info; + enum edac_type edac_mode = EDAC_NONE; + enum dev_type dev_type = DEV_UNKNOWN; + struct dimm_info *dimm; + int empty = 1; + u8 umc, cs; + + if (mci->edac_ctl_cap & EDAC_FLAG_S16ECD16ED) { + edac_mode = EDAC_S16ECD16ED; + dev_type = DEV_X16; + } else if (mci->edac_ctl_cap & EDAC_FLAG_S8ECD8ED) { + edac_mode = EDAC_S8ECD8ED; + dev_type = DEV_X8; + } else if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) { + edac_mode = EDAC_S4ECD4ED; + dev_type = DEV_X4; + } else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) { + edac_mode = EDAC_SECDED; + } + + for_each_umc(umc) { + for_each_chip_select(cs, umc, pvt) { + if (!csrow_enabled(cs, umc, pvt)) + continue; + + empty = 0; + dimm = mci->csrows[cs]->channels[umc]->dimm; + + edac_dbg(1, "MC node: %d, csrow: %d\n", + pvt->mc_node_id, cs); + + dimm->nr_pages = get_csrow_nr_pages(pvt, umc, cs); + dimm->mtype = pvt->dram_type; + dimm->edac_mode = edac_mode; + dimm->dtype = dev_type; + } + } + + return empty; +} + /* * Initialize the array of csrow attribute instances, based on the values * from pci config hardware registers. @@ -2838,15 +2956,16 @@ static int init_csrows(struct mem_ctl_info *mci) int nr_pages = 0; u32 val; - if (!pvt->umc) { - amd64_read_pci_cfg(pvt->F3, NBCFG, &val); + if (pvt->umc) + return init_csrows_df(mci); - pvt->nbcfg = val; + amd64_read_pci_cfg(pvt->F3, NBCFG, &val); - edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", - pvt->mc_node_id, val, - !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); - } + pvt->nbcfg = val; + + edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", + pvt->mc_node_id, val, + !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); /* * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. @@ -2883,13 +3002,7 @@ static int init_csrows(struct mem_ctl_info *mci) edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); /* Determine DIMM ECC mode: */ - if (pvt->umc) { - if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) - edac_mode = EDAC_S4ECD4ED; - else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) - edac_mode = EDAC_SECDED; - - } else if (pvt->nbcfg & NBCFG_ECC_ENABLE) { + if (pvt->nbcfg & NBCFG_ECC_ENABLE) { edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ? EDAC_S4ECD4ED : EDAC_SECDED; @@ -3137,12 +3250,15 @@ static bool ecc_enabled(struct pci_dev *F3, u16 nid) static inline void f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt) { - u8 i, ecc_en = 1, cpk_en = 1; + u8 i, ecc_en = 1, cpk_en = 1, dev_x4 = 1, dev_x16 = 1; for_each_umc(i) { if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED); cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP); + + dev_x4 &= !!(pvt->umc[i].dimm_cfg & BIT(6)); + dev_x16 &= !!(pvt->umc[i].dimm_cfg & BIT(7)); } } @@ -3150,8 +3266,15 @@ f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt) if (ecc_en) { mci->edac_ctl_cap |= EDAC_FLAG_SECDED; - if (cpk_en) + if (!cpk_en) + return; + + if (dev_x4) mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; + else if (dev_x16) + mci->edac_ctl_cap |= EDAC_FLAG_S16ECD16ED; + else + mci->edac_ctl_cap |= EDAC_FLAG_S8ECD8ED; } } @@ -3241,6 +3364,10 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) fam_type = &family_types[F17_M30H_CPUS]; pvt->ops = &family_types[F17_M30H_CPUS].ops; break; + } else if (pvt->model >= 0x70 && pvt->model <= 0x7f) { + fam_type = &family_types[F17_M70H_CPUS]; + pvt->ops = &family_types[F17_M70H_CPUS].ops; + break; } /* fall through */ case 0x18: diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 8f66472f7adc..8c3cda81e619 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -96,6 +96,7 @@ /* Hardware limit on ChipSelect rows per MC and processors per system */ #define NUM_CHIPSELECTS 8 #define DRAM_RANGES 8 +#define NUM_CONTROLLERS 8 #define ON true #define OFF false @@ -119,6 +120,8 @@ #define PCI_DEVICE_ID_AMD_17H_M10H_DF_F6 0x15ee #define PCI_DEVICE_ID_AMD_17H_M30H_DF_F0 0x1490 #define PCI_DEVICE_ID_AMD_17H_M30H_DF_F6 0x1496 +#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F0 0x1440 +#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F6 0x1446 /* * Function 1 - Address Map @@ -168,7 +171,8 @@ #define DCSM0 0x60 #define DCSM1 0x160 -#define csrow_enabled(i, dct, pvt) ((pvt)->csels[(dct)].csbases[(i)] & DCSB_CS_ENABLE) +#define csrow_enabled(i, dct, pvt) ((pvt)->csels[(dct)].csbases[(i)] & DCSB_CS_ENABLE) +#define csrow_sec_enabled(i, dct, pvt) ((pvt)->csels[(dct)].csbases_sec[(i)] & DCSB_CS_ENABLE) #define DRAM_CONTROL 0x78 @@ -258,7 +262,9 @@ /* UMC CH register offsets */ #define UMCCH_BASE_ADDR 0x0 +#define UMCCH_BASE_ADDR_SEC 0x10 #define UMCCH_ADDR_MASK 0x20 +#define UMCCH_ADDR_MASK_SEC 0x28 #define UMCCH_ADDR_CFG 0x30 #define UMCCH_DIMM_CFG 0x80 #define UMCCH_UMC_CFG 0x100 @@ -285,6 +291,7 @@ enum amd_families { F17_CPUS, F17_M10H_CPUS, F17_M30H_CPUS, + F17_M70H_CPUS, NUM_FAMILIES, }; @@ -311,9 +318,11 @@ struct dram_range { /* A DCT chip selects collection */ struct chip_select { u32 csbases[NUM_CHIPSELECTS]; + u32 csbases_sec[NUM_CHIPSELECTS]; u8 b_cnt; u32 csmasks[NUM_CHIPSELECTS]; + u32 csmasks_sec[NUM_CHIPSELECTS]; u8 m_cnt; }; @@ -351,8 +360,8 @@ struct amd64_pvt { u32 dbam0; /* DRAM Base Address Mapping reg for DCT0 */ u32 dbam1; /* DRAM Base Address Mapping reg for DCT1 */ - /* one for each DCT */ - struct chip_select csels[2]; + /* one for each DCT/UMC */ + struct chip_select csels[NUM_CONTROLLERS]; /* DRAM base and limit pairs F1x[78,70,68,60,58,50,48,40] */ struct dram_range ranges[DRAM_RANGES]; diff --git a/drivers/edac/bluefield_edac.c b/drivers/edac/bluefield_edac.c new file mode 100644 index 000000000000..e4736eb37bfb --- /dev/null +++ b/drivers/edac/bluefield_edac.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Bluefield-specific EDAC driver. + * + * Copyright (c) 2019 Mellanox Technologies. + */ + +#include <linux/acpi.h> +#include <linux/arm-smccc.h> +#include <linux/bitfield.h> +#include <linux/edac.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "edac_module.h" + +#define DRIVER_NAME "bluefield-edac" + +/* + * Mellanox BlueField EMI (External Memory Interface) register definitions. + */ + +#define MLXBF_ECC_CNT 0x340 +#define MLXBF_ECC_CNT__SERR_CNT GENMASK(15, 0) +#define MLXBF_ECC_CNT__DERR_CNT GENMASK(31, 16) + +#define MLXBF_ECC_ERR 0x348 +#define MLXBF_ECC_ERR__SECC BIT(0) +#define MLXBF_ECC_ERR__DECC BIT(16) + +#define MLXBF_ECC_LATCH_SEL 0x354 +#define MLXBF_ECC_LATCH_SEL__START BIT(24) + +#define MLXBF_ERR_ADDR_0 0x358 + +#define MLXBF_ERR_ADDR_1 0x37c + +#define MLXBF_SYNDROM 0x35c +#define MLXBF_SYNDROM__DERR BIT(0) +#define MLXBF_SYNDROM__SERR BIT(1) +#define MLXBF_SYNDROM__SYN GENMASK(25, 16) + +#define MLXBF_ADD_INFO 0x364 +#define MLXBF_ADD_INFO__ERR_PRANK GENMASK(9, 8) + +#define MLXBF_EDAC_MAX_DIMM_PER_MC 2 +#define MLXBF_EDAC_ERROR_GRAIN 8 + +/* + * Request MLNX_SIP_GET_DIMM_INFO + * + * Retrieve information about DIMM on a certain slot. + * + * Call register usage: + * a0: MLNX_SIP_GET_DIMM_INFO + * a1: (Memory controller index) << 16 | (Dimm index in memory controller) + * a2-7: not used. + * + * Return status: + * a0: MLXBF_DIMM_INFO defined below describing the DIMM. + * a1-3: not used. + */ +#define MLNX_SIP_GET_DIMM_INFO 0x82000008 + +/* Format for the SMC response about the memory information */ +#define MLXBF_DIMM_INFO__SIZE_GB GENMASK_ULL(15, 0) +#define MLXBF_DIMM_INFO__IS_RDIMM BIT(16) +#define MLXBF_DIMM_INFO__IS_LRDIMM BIT(17) +#define MLXBF_DIMM_INFO__IS_NVDIMM BIT(18) +#define MLXBF_DIMM_INFO__RANKS GENMASK_ULL(23, 21) +#define MLXBF_DIMM_INFO__PACKAGE_X GENMASK_ULL(31, 24) + +struct bluefield_edac_priv { + int dimm_ranks[MLXBF_EDAC_MAX_DIMM_PER_MC]; + void __iomem *emi_base; + int dimm_per_mc; +}; + +static u64 smc_call1(u64 smc_op, u64 smc_arg) +{ + struct arm_smccc_res res; + + arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res); + + return res.a0; +} + +/* + * Gather the ECC information from the External Memory Interface registers + * and report it to the edac handler. + */ +static void bluefield_gather_report_ecc(struct mem_ctl_info *mci, + int error_cnt, + int is_single_ecc) +{ + struct bluefield_edac_priv *priv = mci->pvt_info; + u32 dram_additional_info, err_prank, edea0, edea1; + u32 ecc_latch_select, dram_syndrom, serr, derr, syndrom; + enum hw_event_mc_err_type ecc_type; + u64 ecc_dimm_addr; + int ecc_dimm; + + ecc_type = is_single_ecc ? HW_EVENT_ERR_CORRECTED : + HW_EVENT_ERR_UNCORRECTED; + + /* + * Tell the External Memory Interface to populate the relevant + * registers with information about the last ECC error occurrence. + */ + ecc_latch_select = MLXBF_ECC_LATCH_SEL__START; + writel(ecc_latch_select, priv->emi_base + MLXBF_ECC_LATCH_SEL); + + /* + * Verify that the ECC reported info in the registers is of the + * same type as the one asked to report. If not, just report the + * error without the detailed information. + */ + dram_syndrom = readl(priv->emi_base + MLXBF_SYNDROM); + serr = FIELD_GET(MLXBF_SYNDROM__SERR, dram_syndrom); + derr = FIELD_GET(MLXBF_SYNDROM__DERR, dram_syndrom); + syndrom = FIELD_GET(MLXBF_SYNDROM__SYN, dram_syndrom); + + if ((is_single_ecc && !serr) || (!is_single_ecc && !derr)) { + edac_mc_handle_error(ecc_type, mci, error_cnt, 0, 0, 0, + 0, 0, -1, mci->ctl_name, ""); + return; + } + + dram_additional_info = readl(priv->emi_base + MLXBF_ADD_INFO); + err_prank = FIELD_GET(MLXBF_ADD_INFO__ERR_PRANK, dram_additional_info); + + ecc_dimm = (err_prank >= 2 && priv->dimm_ranks[0] <= 2) ? 1 : 0; + + edea0 = readl(priv->emi_base + MLXBF_ERR_ADDR_0); + edea1 = readl(priv->emi_base + MLXBF_ERR_ADDR_1); + + ecc_dimm_addr = ((u64)edea1 << 32) | edea0; + + edac_mc_handle_error(ecc_type, mci, error_cnt, + PFN_DOWN(ecc_dimm_addr), + offset_in_page(ecc_dimm_addr), + syndrom, ecc_dimm, 0, 0, mci->ctl_name, ""); +} + +static void bluefield_edac_check(struct mem_ctl_info *mci) +{ + struct bluefield_edac_priv *priv = mci->pvt_info; + u32 ecc_count, single_error_count, double_error_count, ecc_error = 0; + + /* + * The memory controller might not be initialized by the firmware + * when there isn't memory, which may lead to bad register readings. + */ + if (mci->edac_cap == EDAC_FLAG_NONE) + return; + + ecc_count = readl(priv->emi_base + MLXBF_ECC_CNT); + single_error_count = FIELD_GET(MLXBF_ECC_CNT__SERR_CNT, ecc_count); + double_error_count = FIELD_GET(MLXBF_ECC_CNT__DERR_CNT, ecc_count); + + if (single_error_count) { + ecc_error |= MLXBF_ECC_ERR__SECC; + + bluefield_gather_report_ecc(mci, single_error_count, 1); + } + + if (double_error_count) { + ecc_error |= MLXBF_ECC_ERR__DECC; + + bluefield_gather_report_ecc(mci, double_error_count, 0); + } + + /* Write to clear reported errors. */ + if (ecc_count) + writel(ecc_error, priv->emi_base + MLXBF_ECC_ERR); +} + +/* Initialize the DIMMs information for the given memory controller. */ +static void bluefield_edac_init_dimms(struct mem_ctl_info *mci) +{ + struct bluefield_edac_priv *priv = mci->pvt_info; + int mem_ctrl_idx = mci->mc_idx; + struct dimm_info *dimm; + u64 smc_info, smc_arg; + int is_empty = 1, i; + + for (i = 0; i < priv->dimm_per_mc; i++) { + dimm = mci->dimms[i]; + + smc_arg = mem_ctrl_idx << 16 | i; + smc_info = smc_call1(MLNX_SIP_GET_DIMM_INFO, smc_arg); + + if (!FIELD_GET(MLXBF_DIMM_INFO__SIZE_GB, smc_info)) { + dimm->mtype = MEM_EMPTY; + continue; + } + + is_empty = 0; + + dimm->edac_mode = EDAC_SECDED; + + if (FIELD_GET(MLXBF_DIMM_INFO__IS_NVDIMM, smc_info)) + dimm->mtype = MEM_NVDIMM; + else if (FIELD_GET(MLXBF_DIMM_INFO__IS_LRDIMM, smc_info)) + dimm->mtype = MEM_LRDDR4; + else if (FIELD_GET(MLXBF_DIMM_INFO__IS_RDIMM, smc_info)) + dimm->mtype = MEM_RDDR4; + else + dimm->mtype = MEM_DDR4; + + dimm->nr_pages = + FIELD_GET(MLXBF_DIMM_INFO__SIZE_GB, smc_info) * + (SZ_1G / PAGE_SIZE); + dimm->grain = MLXBF_EDAC_ERROR_GRAIN; + + /* Mem controller for BlueField only supports x4, x8 and x16 */ + switch (FIELD_GET(MLXBF_DIMM_INFO__PACKAGE_X, smc_info)) { + case 4: + dimm->dtype = DEV_X4; + break; + case 8: + dimm->dtype = DEV_X8; + break; + case 16: + dimm->dtype = DEV_X16; + break; + default: + dimm->dtype = DEV_UNKNOWN; + } + + priv->dimm_ranks[i] = + FIELD_GET(MLXBF_DIMM_INFO__RANKS, smc_info); + } + + if (is_empty) + mci->edac_cap = EDAC_FLAG_NONE; + else + mci->edac_cap = EDAC_FLAG_SECDED; +} + +static int bluefield_edac_mc_probe(struct platform_device *pdev) +{ + struct bluefield_edac_priv *priv; + struct device *dev = &pdev->dev; + struct edac_mc_layer layers[1]; + struct mem_ctl_info *mci; + struct resource *emi_res; + unsigned int mc_idx, dimm_count; + int rc, ret; + + /* Read the MSS (Memory SubSystem) index from ACPI table. */ + if (device_property_read_u32(dev, "mss_number", &mc_idx)) { + dev_warn(dev, "bf_edac: MSS number unknown\n"); + return -EINVAL; + } + + /* Read the DIMMs per MC from ACPI table. */ + if (device_property_read_u32(dev, "dimm_per_mc", &dimm_count)) { + dev_warn(dev, "bf_edac: DIMMs per MC unknown\n"); + return -EINVAL; + } + + if (dimm_count > MLXBF_EDAC_MAX_DIMM_PER_MC) { + dev_warn(dev, "bf_edac: DIMMs per MC not valid\n"); + return -EINVAL; + } + + emi_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!emi_res) + return -EINVAL; + + layers[0].type = EDAC_MC_LAYER_SLOT; + layers[0].size = dimm_count; + layers[0].is_virt_csrow = true; + + mci = edac_mc_alloc(mc_idx, ARRAY_SIZE(layers), layers, sizeof(*priv)); + if (!mci) + return -ENOMEM; + + priv = mci->pvt_info; + + priv->dimm_per_mc = dimm_count; + priv->emi_base = devm_ioremap_resource(dev, emi_res); + if (IS_ERR(priv->emi_base)) { + dev_err(dev, "failed to map EMI IO resource\n"); + ret = PTR_ERR(priv->emi_base); + goto err; + } + + mci->pdev = dev; + mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_RDDR4 | + MEM_FLAG_LRDDR4 | MEM_FLAG_NVDIMM; + mci->edac_ctl_cap = EDAC_FLAG_SECDED; + + mci->mod_name = DRIVER_NAME; + mci->ctl_name = "BlueField_Memory_Controller"; + mci->dev_name = dev_name(dev); + mci->edac_check = bluefield_edac_check; + + /* Initialize mci with the actual populated DIMM information. */ + bluefield_edac_init_dimms(mci); + + platform_set_drvdata(pdev, mci); + + /* Register with EDAC core */ + rc = edac_mc_add_mc(mci); + if (rc) { + dev_err(dev, "failed to register with EDAC core\n"); + ret = rc; + goto err; + } + + /* Only POLL mode supported so far. */ + edac_op_state = EDAC_OPSTATE_POLL; + + return 0; + +err: + edac_mc_free(mci); + + return ret; + +} + +static int bluefield_edac_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + + return 0; +} + +static const struct acpi_device_id bluefield_mc_acpi_ids[] = { + {"MLNXBF08", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, bluefield_mc_acpi_ids); + +static struct platform_driver bluefield_edac_mc_driver = { + .driver = { + .name = DRIVER_NAME, + .acpi_match_table = bluefield_mc_acpi_ids, + }, + .probe = bluefield_edac_mc_probe, + .remove = bluefield_edac_mc_remove, +}; + +module_platform_driver(bluefield_edac_mc_driver); + +MODULE_DESCRIPTION("Mellanox BlueField memory edac driver"); +MODULE_AUTHOR("Mellanox Technologies"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 64922c8fa7e3..e6fd079783bd 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -114,8 +114,8 @@ static const struct kernel_param_ops edac_report_ops = { module_param_cb(edac_report, &edac_report_ops, &edac_report, 0644); -unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf, - unsigned len) +unsigned int edac_dimm_info_location(struct dimm_info *dimm, char *buf, + unsigned int len) { struct mem_ctl_info *mci = dimm->mci; int i, n, count = 0; @@ -236,9 +236,9 @@ EXPORT_SYMBOL_GPL(edac_mem_types); * At return, the pointer 'p' will be incremented to be used on a next call * to this function. */ -void *edac_align_ptr(void **p, unsigned size, int n_elems) +void *edac_align_ptr(void **p, unsigned int size, int n_elems) { - unsigned align, r; + unsigned int align, r; void *ptr = *p; *p += size * n_elems; @@ -275,38 +275,37 @@ void *edac_align_ptr(void **p, unsigned size, int n_elems) static void _edac_mc_free(struct mem_ctl_info *mci) { - int i, chn, row; struct csrow_info *csr; - const unsigned int tot_dimms = mci->tot_dimms; - const unsigned int tot_channels = mci->num_cschannel; - const unsigned int tot_csrows = mci->nr_csrows; + int i, chn, row; if (mci->dimms) { - for (i = 0; i < tot_dimms; i++) + for (i = 0; i < mci->tot_dimms; i++) kfree(mci->dimms[i]); kfree(mci->dimms); } + if (mci->csrows) { - for (row = 0; row < tot_csrows; row++) { + for (row = 0; row < mci->nr_csrows; row++) { csr = mci->csrows[row]; - if (csr) { - if (csr->channels) { - for (chn = 0; chn < tot_channels; chn++) - kfree(csr->channels[chn]); - kfree(csr->channels); - } - kfree(csr); + if (!csr) + continue; + + if (csr->channels) { + for (chn = 0; chn < mci->num_cschannel; chn++) + kfree(csr->channels[chn]); + kfree(csr->channels); } + kfree(csr); } kfree(mci->csrows); } kfree(mci); } -struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, - unsigned n_layers, +struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, + unsigned int n_layers, struct edac_mc_layer *layers, - unsigned sz_pvt) + unsigned int sz_pvt) { struct mem_ctl_info *mci; struct edac_mc_layer *layer; @@ -314,9 +313,9 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, struct rank_info *chan; struct dimm_info *dimm; u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; - unsigned pos[EDAC_MAX_LAYERS]; - unsigned size, tot_dimms = 1, count = 1; - unsigned tot_csrows = 1, tot_channels = 1, tot_errcount = 0; + unsigned int pos[EDAC_MAX_LAYERS]; + unsigned int size, tot_dimms = 1, count = 1; + unsigned int tot_csrows = 1, tot_channels = 1, tot_errcount = 0; void *pvt, *p, *ptr = NULL; int i, j, row, chn, n, len, off; bool per_rank = false; @@ -1235,9 +1234,13 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, if (p > e->location) *(p - 1) = '\0'; - /* Report the error via the trace interface */ - grain_bits = fls_long(e->grain) + 1; + /* Sanity-check driver-supplied grain value. */ + if (WARN_ON_ONCE(!e->grain)) + e->grain = 1; + grain_bits = fls_long(e->grain - 1); + + /* Report the error via the trace interface */ if (IS_ENABLED(CONFIG_RAS)) trace_mc_event(type, e->msg, e->label, e->error_count, mci->mc_idx, e->top_layer, e->mid_layer, diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h index 4165e15995ad..02aac5c61d00 100644 --- a/drivers/edac/edac_mc.h +++ b/drivers/edac/edac_mc.h @@ -122,10 +122,10 @@ do { \ * On success, return a pointer to struct mem_ctl_info pointer; * %NULL otherwise */ -struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, - unsigned n_layers, +struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, + unsigned int n_layers, struct edac_mc_layer *layers, - unsigned sz_pvt); + unsigned int sz_pvt); /** * edac_get_owner - Return the owner's mod_name of EDAC MC diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 4386ea4b9b5a..32d016f1ecd1 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -131,7 +131,7 @@ static const char * const edac_caps[] = { struct dev_ch_attribute { struct device_attribute attr; - int channel; + unsigned int channel; }; #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ @@ -200,7 +200,7 @@ static ssize_t channel_dimm_label_show(struct device *dev, char *data) { struct csrow_info *csrow = to_csrow(dev); - unsigned chan = to_channel(mattr); + unsigned int chan = to_channel(mattr); struct rank_info *rank = csrow->channels[chan]; /* if field has not been initialized, there is nothing to send */ @@ -216,7 +216,7 @@ static ssize_t channel_dimm_label_store(struct device *dev, const char *data, size_t count) { struct csrow_info *csrow = to_csrow(dev); - unsigned chan = to_channel(mattr); + unsigned int chan = to_channel(mattr); struct rank_info *rank = csrow->channels[chan]; size_t copy_count = count; @@ -240,7 +240,7 @@ static ssize_t channel_ce_count_show(struct device *dev, struct device_attribute *mattr, char *data) { struct csrow_info *csrow = to_csrow(dev); - unsigned chan = to_channel(mattr); + unsigned int chan = to_channel(mattr); struct rank_info *rank = csrow->channels[chan]; return sprintf(data, "%u\n", rank->ce_count); @@ -278,7 +278,7 @@ static void csrow_attr_release(struct device *dev) { struct csrow_info *csrow = container_of(dev, struct csrow_info, dev); - edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev)); + edac_dbg(1, "device %s released\n", dev_name(dev)); kfree(csrow); } @@ -414,14 +414,16 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci, dev_set_name(&csrow->dev, "csrow%d", index); dev_set_drvdata(&csrow->dev, csrow); - edac_dbg(0, "creating (virtual) csrow node %s\n", - dev_name(&csrow->dev)); - err = device_add(&csrow->dev); - if (err) + if (err) { + edac_dbg(1, "failure: create device %s\n", dev_name(&csrow->dev)); put_device(&csrow->dev); + return err; + } - return err; + edac_dbg(0, "device %s created\n", dev_name(&csrow->dev)); + + return 0; } /* Create a CSROW object under specifed edac_mc_device */ @@ -435,12 +437,8 @@ static int edac_create_csrow_objects(struct mem_ctl_info *mci) if (!nr_pages_per_csrow(csrow)) continue; err = edac_create_csrow_object(mci, mci->csrows[i], i); - if (err < 0) { - edac_dbg(1, - "failure: create csrow objects for csrow %d\n", - i); + if (err < 0) goto error; - } } return 0; @@ -624,7 +622,7 @@ static void dimm_attr_release(struct device *dev) { struct dimm_info *dimm = container_of(dev, struct dimm_info, dev); - edac_dbg(1, "Releasing dimm device %s\n", dev_name(dev)); + edac_dbg(1, "device %s released\n", dev_name(dev)); kfree(dimm); } @@ -653,12 +651,21 @@ static int edac_create_dimm_object(struct mem_ctl_info *mci, pm_runtime_forbid(&mci->dev); err = device_add(&dimm->dev); - if (err) + if (err) { + edac_dbg(1, "failure: create device %s\n", dev_name(&dimm->dev)); put_device(&dimm->dev); + return err; + } - edac_dbg(0, "created rank/dimm device %s\n", dev_name(&dimm->dev)); + if (IS_ENABLED(CONFIG_EDAC_DEBUG)) { + char location[80]; - return err; + edac_dimm_info_location(dimm, location, sizeof(location)); + edac_dbg(0, "device %s created at location %s\n", + dev_name(&dimm->dev), location); + } + + return 0; } /* @@ -901,7 +908,7 @@ static void mci_attr_release(struct device *dev) { struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); - edac_dbg(1, "Releasing csrow device %s\n", dev_name(dev)); + edac_dbg(1, "device %s released\n", dev_name(dev)); kfree(mci); } @@ -933,14 +940,15 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, dev_set_drvdata(&mci->dev, mci); pm_runtime_forbid(&mci->dev); - edac_dbg(0, "creating device %s\n", dev_name(&mci->dev)); err = device_add(&mci->dev); if (err < 0) { edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); put_device(&mci->dev); - goto out; + return err; } + edac_dbg(0, "device %s created\n", dev_name(&mci->dev)); + /* * Create the dimm/rank devices */ @@ -950,22 +958,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, if (!dimm->nr_pages) continue; -#ifdef CONFIG_EDAC_DEBUG - edac_dbg(1, "creating dimm%d, located at ", i); - if (edac_debug_level >= 1) { - int lay; - for (lay = 0; lay < mci->n_layers; lay++) - printk(KERN_CONT "%s %d ", - edac_layer_name[mci->layers[lay].type], - dimm->location[lay]); - printk(KERN_CONT "\n"); - } -#endif err = edac_create_dimm_object(mci, dimm, i); - if (err) { - edac_dbg(1, "failure: create dimm %d obj\n", i); + if (err) goto fail_unregister_dimm; - } } #ifdef CONFIG_EDAC_LEGACY_SYSFS @@ -987,7 +982,6 @@ fail_unregister_dimm: } device_unregister(&mci->dev); -out: return err; } @@ -1011,14 +1005,14 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) struct dimm_info *dimm = mci->dimms[i]; if (dimm->nr_pages == 0) continue; - edac_dbg(0, "removing device %s\n", dev_name(&dimm->dev)); + edac_dbg(1, "unregistering device %s\n", dev_name(&dimm->dev)); device_unregister(&dimm->dev); } } void edac_unregister_sysfs(struct mem_ctl_info *mci) { - edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev)); + edac_dbg(1, "unregistering device %s\n", dev_name(&mci->dev)); device_unregister(&mci->dev); } @@ -1029,7 +1023,7 @@ static void mc_attr_release(struct device *dev) * parent device, used to create the /sys/devices/mc sysfs node. * So, there are no attributes on it. */ - edac_dbg(1, "Releasing device %s\n", dev_name(dev)); + edac_dbg(1, "device %s released\n", dev_name(dev)); kfree(dev); } @@ -1044,10 +1038,8 @@ int __init edac_mc_sysfs_init(void) int err; mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL); - if (!mci_pdev) { - err = -ENOMEM; - goto out; - } + if (!mci_pdev) + return -ENOMEM; mci_pdev->bus = edac_get_sysfs_subsys(); mci_pdev->type = &mc_attr_type; @@ -1055,17 +1047,15 @@ int __init edac_mc_sysfs_init(void) dev_set_name(mci_pdev, "mc"); err = device_add(mci_pdev); - if (err < 0) - goto out_put_device; + if (err < 0) { + edac_dbg(1, "failure: create device %s\n", dev_name(mci_pdev)); + put_device(mci_pdev); + return err; + } edac_dbg(0, "device %s created\n", dev_name(mci_pdev)); return 0; - - out_put_device: - put_device(mci_pdev); - out: - return err; } void edac_mc_sysfs_exit(void) diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index 7f19f1c672c3..d413a0bdc9ad 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -68,7 +68,7 @@ struct memdev_dmi_entry { struct ghes_edac_dimm_fill { struct mem_ctl_info *mci; - unsigned count; + unsigned int count; }; static void ghes_edac_count_dimms(const struct dmi_header *dh, void *arg) diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index b506eef6b146..251f2b692785 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -417,7 +417,8 @@ static const char *i5100_err_msg(unsigned err) } /* convert csrow index into a rank (per channel -- 0..5) */ -static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) +static unsigned int i5100_csrow_to_rank(const struct mem_ctl_info *mci, + unsigned int csrow) { const struct i5100_priv *priv = mci->pvt_info; @@ -425,7 +426,8 @@ static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow) } /* convert csrow index into a channel (0..1) */ -static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow) +static unsigned int i5100_csrow_to_chan(const struct mem_ctl_info *mci, + unsigned int csrow) { const struct i5100_priv *priv = mci->pvt_info; @@ -653,11 +655,11 @@ static struct pci_dev *pci_get_device_func(unsigned vendor, return ret; } -static unsigned long i5100_npages(struct mem_ctl_info *mci, int csrow) +static unsigned long i5100_npages(struct mem_ctl_info *mci, unsigned int csrow) { struct i5100_priv *priv = mci->pvt_info; - const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow); - const unsigned chan = i5100_csrow_to_chan(mci, csrow); + const unsigned int chan_rank = i5100_csrow_to_rank(mci, csrow); + const unsigned int chan = i5100_csrow_to_chan(mci, csrow); unsigned addr_lines; /* dimm present? */ @@ -852,8 +854,8 @@ static void i5100_init_csrows(struct mem_ctl_info *mci) for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm; const unsigned long npages = i5100_npages(mci, i); - const unsigned chan = i5100_csrow_to_chan(mci, i); - const unsigned rank = i5100_csrow_to_rank(mci, i); + const unsigned int chan = i5100_csrow_to_chan(mci, i); + const unsigned int rank = i5100_csrow_to_rank(mci, i); if (!npages) continue; diff --git a/drivers/edac/pnd2_edac.c b/drivers/edac/pnd2_edac.c index ca25f8fe57ef..1ad538baaa4a 100644 --- a/drivers/edac/pnd2_edac.c +++ b/drivers/edac/pnd2_edac.c @@ -260,11 +260,14 @@ static u64 get_sideband_reg_base_addr(void) } } +#define DNV_MCHBAR_SIZE 0x8000 +#define DNV_SB_PORT_SIZE 0x10000 static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name) { struct pci_dev *pdev; char *base; u64 addr; + unsigned long size; if (op == 4) { pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL); @@ -279,15 +282,17 @@ static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *na addr = get_mem_ctrl_hub_base_addr(); if (!addr) return -ENODEV; + size = DNV_MCHBAR_SIZE; } else { /* MMIO via sideband register base address */ addr = get_sideband_reg_base_addr(); if (!addr) return -ENODEV; addr += (port << 16); + size = DNV_SB_PORT_SIZE; } - base = ioremap((resource_size_t)addr, 0x10000); + base = ioremap((resource_size_t)addr, size); if (!base) return -ENODEV; diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 1db780c0f07b..3caae7f2cf56 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -927,17 +927,33 @@ fail: return status; } +#define GET_EFI_CONFIG_TABLE(bits) \ +static void *get_efi_config_table##bits(efi_system_table_t *_sys_table, \ + efi_guid_t guid) \ +{ \ + efi_system_table_##bits##_t *sys_table; \ + efi_config_table_##bits##_t *tables; \ + int i; \ + \ + sys_table = (typeof(sys_table))_sys_table; \ + tables = (typeof(tables))(unsigned long)sys_table->tables; \ + \ + for (i = 0; i < sys_table->nr_tables; i++) { \ + if (efi_guidcmp(tables[i].guid, guid) != 0) \ + continue; \ + \ + return (void *)(unsigned long)tables[i].table; \ + } \ + \ + return NULL; \ +} +GET_EFI_CONFIG_TABLE(32) +GET_EFI_CONFIG_TABLE(64) + void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid) { - efi_config_table_t *tables = (efi_config_table_t *)sys_table->tables; - int i; - - for (i = 0; i < sys_table->nr_tables; i++) { - if (efi_guidcmp(tables[i].guid, guid) != 0) - continue; - - return (void *)tables[i].table; - } - - return NULL; + if (efi_is_64bit()) + return get_efi_config_table64(sys_table, guid); + else + return get_efi_config_table32(sys_table, guid); } diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c index a13f224303c6..0221dee8dd4c 100644 --- a/drivers/fpga/altera-ps-spi.c +++ b/drivers/fpga/altera-ps-spi.c @@ -210,7 +210,7 @@ static int altera_ps_write_complete(struct fpga_manager *mgr, return -EIO; } - if (!IS_ERR(conf->confd)) { + if (conf->confd) { if (!gpiod_get_raw_value_cansleep(conf->confd)) { dev_err(&mgr->dev, "CONF_DONE is inactive!\n"); return -EIO; @@ -289,10 +289,13 @@ static int altera_ps_probe(struct spi_device *spi) return PTR_ERR(conf->status); } - conf->confd = devm_gpiod_get(&spi->dev, "confd", GPIOD_IN); + conf->confd = devm_gpiod_get_optional(&spi->dev, "confd", GPIOD_IN); if (IS_ERR(conf->confd)) { - dev_warn(&spi->dev, "Not using confd gpio: %ld\n", - PTR_ERR(conf->confd)); + dev_err(&spi->dev, "Failed to get confd gpio: %ld\n", + PTR_ERR(conf->confd)); + return PTR_ERR(conf->confd); + } else if (!conf->confd) { + dev_warn(&spi->dev, "Not using confd gpio"); } /* Register manager with unique name */ diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c index 343153d47e5b..004dc03ccf09 100644 --- a/drivers/fsi/fsi-scom.c +++ b/drivers/fsi/fsi-scom.c @@ -38,8 +38,7 @@ #define SCOM_STATUS_PIB_RESP_MASK 0x00007000 #define SCOM_STATUS_PIB_RESP_SHIFT 12 -#define SCOM_STATUS_ANY_ERR (SCOM_STATUS_ERR_SUMMARY | \ - SCOM_STATUS_PROTECTION | \ +#define SCOM_STATUS_ANY_ERR (SCOM_STATUS_PROTECTION | \ SCOM_STATUS_PARITY | \ SCOM_STATUS_PIB_ABORT | \ SCOM_STATUS_PIB_RESP_MASK) @@ -251,11 +250,6 @@ static int handle_fsi2pib_status(struct scom_device *scom, uint32_t status) /* Return -EBUSY on PIB abort to force a retry */ if (status & SCOM_STATUS_PIB_ABORT) return -EBUSY; - if (status & SCOM_STATUS_ERR_SUMMARY) { - fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy, - sizeof(uint32_t)); - return -EIO; - } return 0; } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bb13c266c329..e193c76948c4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -288,7 +288,7 @@ config GPIO_IXP4XX depends on ARM # For <asm/mach-types.h> depends on ARCH_IXP4XX select GPIO_GENERIC - select IRQ_DOMAIN + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY help Say yes here to support the GPIO functionality of a number of Intel @@ -311,6 +311,13 @@ config GPIO_LPC18XX Select this option to enable GPIO driver for NXP LPC18XX/43XX devices. +config GPIO_LPC32XX + tristate "NXP LPC32XX GPIO support" + depends on OF_GPIO && (ARCH_LPC32XX || COMPILE_TEST) + help + Select this option to enable GPIO driver for + NXP LPC32XX devices. + config GPIO_LYNXPOINT tristate "Intel Lynxpoint GPIO support" depends on ACPI && X86 @@ -539,6 +546,7 @@ config GPIO_THUNDERX tristate "Cavium ThunderX/OCTEON-TX GPIO" depends on ARCH_THUNDER || (64BIT && COMPILE_TEST) depends on PCI_MSI + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY select IRQ_FASTEOI_HIERARCHY_HANDLERS help @@ -1465,7 +1473,6 @@ endmenu config GPIO_MOCKUP tristate "GPIO Testing Driver" - depends on GPIOLIB select IRQ_SIM help This enables GPIO Testing driver, which provides a way to test GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a4e91175c708..f3e051fb50e6 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -67,14 +67,13 @@ obj-$(CONFIG_GPIO_IT87) += gpio-it87.o obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_KEMPLD) += gpio-kempld.o -obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o -obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o +obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index c07fad975049..5640efe5e750 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -142,7 +142,7 @@ static const struct gpio_chip template_chip = { static int arizona_gpio_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); - struct arizona_pdata *pdata = dev_get_platdata(arizona->dev); + struct arizona_pdata *pdata = &arizona->pdata; struct arizona_gpio *arizona_gpio; int ret; @@ -177,7 +177,7 @@ static int arizona_gpio_probe(struct platform_device *pdev) return -EINVAL; } - if (pdata && pdata->gpio_base) + if (pdata->gpio_base) arizona_gpio->gpio_chip.base = pdata->gpio_base; else arizona_gpio->gpio_chip.base = -1; diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 13d80bfbc3b6..09e53c5f3b0a 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -52,6 +52,7 @@ struct aspeed_gpio_config { */ struct aspeed_gpio { struct gpio_chip chip; + struct irq_chip irqc; spinlock_t lock; void __iomem *base; int irq; @@ -661,12 +662,14 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc) struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct irq_chip *ic = irq_desc_get_chip(desc); struct aspeed_gpio *data = gpiochip_get_data(gc); - unsigned int i, p, girq; + unsigned int i, p, girq, banks; unsigned long reg; + struct aspeed_gpio *gpio = gpiochip_get_data(gc); chained_irq_enter(ic, desc); - for (i = 0; i < ARRAY_SIZE(aspeed_gpio_banks); i++) { + banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); + for (i = 0; i < banks; i++) { const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i]; reg = ioread32(bank_reg(data, bank, reg_irq_status)); @@ -681,16 +684,11 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(ic, desc); } -static struct irq_chip aspeed_gpio_irqchip = { - .name = "aspeed-gpio", - .irq_ack = aspeed_gpio_irq_ack, - .irq_mask = aspeed_gpio_irq_mask, - .irq_unmask = aspeed_gpio_irq_unmask, - .irq_set_type = aspeed_gpio_set_type, -}; - -static void set_irq_valid_mask(struct aspeed_gpio *gpio) +static void aspeed_init_irq_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) { + struct aspeed_gpio *gpio = gpiochip_get_data(gc); const struct aspeed_bank_props *props = gpio->config->props; while (!is_bank_props_sentinel(props)) { @@ -701,42 +699,16 @@ static void set_irq_valid_mask(struct aspeed_gpio *gpio) for_each_clear_bit(offset, &input, 32) { unsigned int i = props->bank * 32 + offset; - if (i >= gpio->config->nr_gpios) + if (i >= gpio->chip.ngpio) break; - clear_bit(i, gpio->chip.irq.valid_mask); + clear_bit(i, valid_mask); } props++; } } -static int aspeed_gpio_setup_irqs(struct aspeed_gpio *gpio, - struct platform_device *pdev) -{ - int rc; - - rc = platform_get_irq(pdev, 0); - if (rc < 0) - return rc; - - gpio->irq = rc; - - set_irq_valid_mask(gpio); - - rc = gpiochip_irqchip_add(&gpio->chip, &aspeed_gpio_irqchip, - 0, handle_bad_irq, IRQ_TYPE_NONE); - if (rc) { - dev_info(&pdev->dev, "Could not add irqchip\n"); - return rc; - } - - gpiochip_set_chained_irqchip(&gpio->chip, &aspeed_gpio_irqchip, - gpio->irq, aspeed_gpio_irq_handler); - - return 0; -} - static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, unsigned int offset, bool enable) { @@ -1040,10 +1012,10 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc, unsigned long flags; if (!gpio->cf_copro_bankmap) - gpio->cf_copro_bankmap = kzalloc(gpio->config->nr_gpios >> 3, GFP_KERNEL); + gpio->cf_copro_bankmap = kzalloc(gpio->chip.ngpio >> 3, GFP_KERNEL); if (!gpio->cf_copro_bankmap) return -ENOMEM; - if (offset < 0 || offset > gpio->config->nr_gpios) + if (offset < 0 || offset > gpio->chip.ngpio) return -EINVAL; bindex = offset >> 3; @@ -1088,7 +1060,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc) if (!gpio->cf_copro_bankmap) return -ENXIO; - if (offset < 0 || offset > gpio->config->nr_gpios) + if (offset < 0 || offset > gpio->chip.ngpio) return -EINVAL; bindex = offset >> 3; @@ -1141,9 +1113,25 @@ static const struct aspeed_gpio_config ast2500_config = /* 232 for simplicity, actual number is 228 (4-GPIO hole in GPIOAB) */ { .nr_gpios = 232, .props = ast2500_bank_props, }; +static const struct aspeed_bank_props ast2600_bank_props[] = { + /* input output */ + {5, 0xffffffff, 0x0000ffff}, /* U/V/W/X */ + {6, 0xffff0000, 0x0fff0000}, /* Y/Z */ + { }, +}; + +static const struct aspeed_gpio_config ast2600_config = + /* + * ast2600 has two controllers one with 208 GPIOs and one with 36 GPIOs. + * We expect ngpio being set in the device tree and this is a fallback + * option. + */ + { .nr_gpios = 208, .props = ast2600_bank_props, }; + static const struct of_device_id aspeed_gpio_of_table[] = { { .compatible = "aspeed,ast2400-gpio", .data = &ast2400_config, }, { .compatible = "aspeed,ast2500-gpio", .data = &ast2500_config, }, + { .compatible = "aspeed,ast2600-gpio", .data = &ast2600_config, }, {} }; MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table); @@ -1152,7 +1140,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) { const struct of_device_id *gpio_id; struct aspeed_gpio *gpio; - int rc, i, banks; + int rc, i, banks, err; + u32 ngpio; gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) @@ -1178,7 +1167,10 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) gpio->config = gpio_id->data; gpio->chip.parent = &pdev->dev; - gpio->chip.ngpio = gpio->config->nr_gpios; + err = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpio); + gpio->chip.ngpio = (u16) ngpio; + if (err) + gpio->chip.ngpio = gpio->config->nr_gpios; gpio->chip.direction_input = aspeed_gpio_dir_in; gpio->chip.direction_output = aspeed_gpio_dir_out; gpio->chip.get_direction = aspeed_gpio_get_direction; @@ -1189,10 +1181,9 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) gpio->chip.set_config = aspeed_gpio_set_config; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; - gpio->chip.irq.need_valid_mask = true; /* Allocate a cache of the output registers */ - banks = gpio->config->nr_gpios >> 5; + banks = DIV_ROUND_UP(gpio->chip.ngpio, 32); gpio->dcache = devm_kcalloc(&pdev->dev, banks, sizeof(u32), GFP_KERNEL); if (!gpio->dcache) @@ -1212,16 +1203,42 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) aspeed_gpio_change_cmd_source(gpio, bank, 3, GPIO_CMDSRC_ARM); } - rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); - if (rc < 0) - return rc; + /* Optionally set up an irqchip if there is an IRQ */ + rc = platform_get_irq(pdev, 0); + if (rc > 0) { + struct gpio_irq_chip *girq; + + gpio->irq = rc; + girq = &gpio->chip.irq; + girq->chip = &gpio->irqc; + girq->chip->name = dev_name(&pdev->dev); + girq->chip->irq_ack = aspeed_gpio_irq_ack; + girq->chip->irq_mask = aspeed_gpio_irq_mask; + girq->chip->irq_unmask = aspeed_gpio_irq_unmask; + girq->chip->irq_set_type = aspeed_gpio_set_type; + girq->parent_handler = aspeed_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = gpio->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + girq->init_valid_mask = aspeed_init_irq_valid_mask; + } gpio->offset_timer = devm_kzalloc(&pdev->dev, gpio->chip.ngpio, GFP_KERNEL); if (!gpio->offset_timer) return -ENOMEM; - return aspeed_gpio_setup_irqs(gpio, pdev); + rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (rc < 0) + return rc; + + return 0; } static struct platform_driver aspeed_gpio_driver = { diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c index fd85605d2dab..0c1ead12d883 100644 --- a/drivers/gpio/gpio-bd70528.c +++ b/drivers/gpio/gpio-bd70528.c @@ -36,7 +36,7 @@ static int bd70528_set_debounce(struct bd70528_gpio *bdgpio, break; default: dev_err(bdgpio->chip.dev, - "Invalid debouce value %u\n", debounce); + "Invalid debounce value %u\n", debounce); return -EINVAL; } return regmap_update_bits(bdgpio->chip.regmap, GPIO_IN_REG(offset), @@ -153,7 +153,7 @@ static int bd70528_gpio_get_i(struct bd70528_gpio *bdgpio, unsigned int offset) static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset) { - int ret = -EINVAL; + int ret; struct bd70528_gpio *bdgpio = gpiochip_get_data(chip); /* diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index af936dcca659..05e3f99ae59c 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -636,10 +636,8 @@ static int brcmstb_gpio_probe(struct platform_device *pdev) if (of_property_read_bool(np, "interrupt-controller")) { priv->parent_irq = platform_get_irq(pdev, 0); - if (priv->parent_irq <= 0) { - dev_err(dev, "Couldn't get IRQ"); + if (priv->parent_irq <= 0) return -ENOENT; - } } else { priv->parent_irq = -ENOENT; } diff --git a/drivers/gpio/gpio-cadence.c b/drivers/gpio/gpio-cadence.c index 712ae212b0b4..a4d3239d2594 100644 --- a/drivers/gpio/gpio-cadence.c +++ b/drivers/gpio/gpio-cadence.c @@ -214,27 +214,33 @@ static int cdns_gpio_probe(struct platform_device *pdev) goto err_revert_dir; } - ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio); - if (ret < 0) { - dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); - goto err_disable_clk; - } - /* - * irq_chip support + * Optional irq_chip support */ irq = platform_get_irq(pdev, 0); if (irq >= 0) { - ret = gpiochip_irqchip_add(&cgpio->gc, &cdns_gpio_irqchip, - 0, handle_level_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(&pdev->dev, "Could not add irqchip, %d\n", - ret); + struct gpio_irq_chip *girq; + + girq = &cgpio->gc.irq; + girq->chip = &cdns_gpio_irqchip; + girq->parent_handler = cdns_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) { + ret = -ENOMEM; goto err_disable_clk; } - gpiochip_set_chained_irqchip(&cgpio->gc, &cdns_gpio_irqchip, - irq, cdns_gpio_irq_handler); + girq->parents[0] = irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + } + + ret = devm_gpiochip_add_data(&pdev->dev, &cgpio->gc, cgpio); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + goto err_disable_clk; } cgpio->bypass_orig = ioread32(cgpio->regs + CDNS_GPIO_BYPASS_MODE); diff --git a/drivers/gpio/gpio-creg-snps.c b/drivers/gpio/gpio-creg-snps.c index 8cbc94d0d424..ff19a8ad5663 100644 --- a/drivers/gpio/gpio-creg-snps.c +++ b/drivers/gpio/gpio-creg-snps.c @@ -137,7 +137,6 @@ static int creg_gpio_probe(struct platform_device *pdev) const struct of_device_id *match; struct device *dev = &pdev->dev; struct creg_gpio *hcg; - struct resource *mem; u32 ngpios; int ret; @@ -145,8 +144,7 @@ static int creg_gpio_probe(struct platform_device *pdev) if (!hcg) return -ENOMEM; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hcg->regs = devm_ioremap_resource(dev, mem); + hcg->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hcg->regs)) return PTR_ERR(hcg->regs); diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 3108be5e208c..92e127e74813 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include "gpiolib.h" +#include "gpiolib-acpi.h" #define GPIO_SWPORTA_DR 0x00 #define GPIO_SWPORTA_DDR 0x04 diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index 7b9ac4a12c20..fe7a73f52329 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -584,10 +584,8 @@ static int sprd_eic_probe(struct platform_device *pdev) sprd_eic->type = pdata->type; sprd_eic->irq = platform_get_irq(pdev, 0); - if (sprd_eic->irq < 0) { - dev_err(&pdev->dev, "Failed to get EIC interrupt.\n"); + if (sprd_eic->irq < 0) return sprd_eic->irq; - } for (i = 0; i < SPRD_EIC_MAX_BANK; i++) { /* diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index a87951293aaa..620f25b7efb4 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -272,11 +272,12 @@ static int em_gio_probe(struct platform_device *pdev) struct resource *io[2], *irq[2]; struct gpio_chip *gpio_chip; struct irq_chip *irq_chip; - const char *name = dev_name(&pdev->dev); + struct device *dev = &pdev->dev; + const char *name = dev_name(dev); unsigned int ngpios; int ret; - p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM; @@ -290,27 +291,27 @@ static int em_gio_probe(struct platform_device *pdev) irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1); if (!io[0] || !io[1] || !irq[0] || !irq[1]) { - dev_err(&pdev->dev, "missing IRQ or IOMEM\n"); + dev_err(dev, "missing IRQ or IOMEM\n"); return -EINVAL; } - p->base0 = devm_ioremap_nocache(&pdev->dev, io[0]->start, + p->base0 = devm_ioremap_nocache(dev, io[0]->start, resource_size(io[0])); if (!p->base0) return -ENOMEM; - p->base1 = devm_ioremap_nocache(&pdev->dev, io[1]->start, + p->base1 = devm_ioremap_nocache(dev, io[1]->start, resource_size(io[1])); if (!p->base1) return -ENOMEM; - if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) { - dev_err(&pdev->dev, "Missing ngpios OF property\n"); + if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { + dev_err(dev, "Missing ngpios OF property\n"); return -EINVAL; } gpio_chip = &p->gpio_chip; - gpio_chip->of_node = pdev->dev.of_node; + gpio_chip->of_node = dev->of_node; gpio_chip->direction_input = em_gio_direction_input; gpio_chip->get = em_gio_get; gpio_chip->direction_output = em_gio_direction_output; @@ -319,7 +320,7 @@ static int em_gio_probe(struct platform_device *pdev) gpio_chip->request = em_gio_request; gpio_chip->free = em_gio_free; gpio_chip->label = name; - gpio_chip->parent = &pdev->dev; + gpio_chip->parent = dev; gpio_chip->owner = THIS_MODULE; gpio_chip->base = -1; gpio_chip->ngpio = ngpios; @@ -333,33 +334,33 @@ static int em_gio_probe(struct platform_device *pdev) irq_chip->irq_release_resources = em_gio_irq_relres; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, ngpios, 0, + p->irq_domain = irq_domain_add_simple(dev->of_node, ngpios, 0, &em_gio_irq_domain_ops, p); if (!p->irq_domain) { - dev_err(&pdev->dev, "cannot initialize irq domain\n"); + dev_err(dev, "cannot initialize irq domain\n"); return -ENXIO; } - ret = devm_add_action_or_reset(&pdev->dev, em_gio_irq_domain_remove, + ret = devm_add_action_or_reset(dev, em_gio_irq_domain_remove, p->irq_domain); if (ret) return ret; - if (devm_request_irq(&pdev->dev, irq[0]->start, + if (devm_request_irq(dev, irq[0]->start, em_gio_irq_handler, 0, name, p)) { - dev_err(&pdev->dev, "failed to request low IRQ\n"); + dev_err(dev, "failed to request low IRQ\n"); return -ENOENT; } - if (devm_request_irq(&pdev->dev, irq[1]->start, + if (devm_request_irq(dev, irq[1]->start, em_gio_irq_handler, 0, name, p)) { - dev_err(&pdev->dev, "failed to request high IRQ\n"); + dev_err(dev, "failed to request high IRQ\n"); return -ENOENT; } - ret = devm_gpiochip_add_data(&pdev->dev, gpio_chip, p); + ret = devm_gpiochip_add_data(dev, gpio_chip, p); if (ret) { - dev_err(&pdev->dev, "failed to add GPIO controller\n"); + dev_err(dev, "failed to add GPIO controller\n"); return ret; } diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index a90870a60c15..226da8df6f10 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -269,56 +269,6 @@ static struct irq_chip ep93xx_gpio_irq_chip = { .irq_set_type = ep93xx_gpio_irq_type, }; -static int ep93xx_gpio_init_irq(struct platform_device *pdev, - struct ep93xx_gpio *epg) -{ - int ab_parent_irq = platform_get_irq(pdev, 0); - struct device *dev = &pdev->dev; - int gpio_irq; - int ret; - int i; - - /* The A bank */ - ret = gpiochip_irqchip_add(&epg->gc[0], &ep93xx_gpio_irq_chip, - 64, handle_level_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "Could not add irqchip 0\n"); - return ret; - } - gpiochip_set_chained_irqchip(&epg->gc[0], &ep93xx_gpio_irq_chip, - ab_parent_irq, - ep93xx_gpio_ab_irq_handler); - - /* The B bank */ - ret = gpiochip_irqchip_add(&epg->gc[1], &ep93xx_gpio_irq_chip, - 72, handle_level_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "Could not add irqchip 1\n"); - return ret; - } - gpiochip_set_chained_irqchip(&epg->gc[1], &ep93xx_gpio_irq_chip, - ab_parent_irq, - ep93xx_gpio_ab_irq_handler); - - /* The F bank */ - for (i = 0; i < 8; i++) { - gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i; - irq_set_chip_data(gpio_irq, &epg->gc[5]); - irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip, - handle_level_irq); - irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); - } - - for (i = 1; i <= 8; i++) - irq_set_chained_handler_and_data(platform_get_irq(pdev, i), - ep93xx_gpio_f_irq_handler, - &epg->gc[i]); - return 0; -} - - /************************************************************************* * gpiolib interface for EP93xx on-chip GPIOs *************************************************************************/ @@ -328,26 +278,33 @@ struct ep93xx_gpio_bank { int dir; int base; bool has_irq; + bool has_hierarchical_irq; + unsigned int irq_base; }; -#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _has_irq) \ +#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _has_irq, _has_hier, _irq_base) \ { \ .label = _label, \ .data = _data, \ .dir = _dir, \ .base = _base, \ .has_irq = _has_irq, \ + .has_hierarchical_irq = _has_hier, \ + .irq_base = _irq_base, \ } static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = { - EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true), /* Bank A has 8 IRQs */ - EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true), /* Bank B has 8 IRQs */ - EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false), - EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false), - EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false), - EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true), /* Bank F has 8 IRQs */ - EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false), - EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false), + /* Bank A has 8 IRQs */ + EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true, false, 64), + /* Bank B has 8 IRQs */ + EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true, false, 72), + EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false, false, 0), + EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false, false, 0), + EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false, false, 0), + /* Bank F has 8 IRQs */ + EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, false, true, 0), + EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false, false, 0), + EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false, false, 0), }; static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset, @@ -369,12 +326,15 @@ static int ep93xx_gpio_f_to_irq(struct gpio_chip *gc, unsigned offset) return EP93XX_GPIO_F_IRQ_BASE + offset; } -static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, +static int ep93xx_gpio_add_bank(struct gpio_chip *gc, + struct platform_device *pdev, struct ep93xx_gpio *epg, struct ep93xx_gpio_bank *bank) { void __iomem *data = epg->base + bank->data; void __iomem *dir = epg->base + bank->dir; + struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; int err; err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0); @@ -384,8 +344,59 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev, gc->label = bank->label; gc->base = bank->base; - if (bank->has_irq) + girq = &gc->irq; + if (bank->has_irq || bank->has_hierarchical_irq) { gc->set_config = ep93xx_gpio_set_config; + girq->chip = &ep93xx_gpio_irq_chip; + } + + if (bank->has_irq) { + int ab_parent_irq = platform_get_irq(pdev, 0); + + girq->parent_handler = ep93xx_gpio_ab_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + girq->parents[0] = ab_parent_irq; + girq->first = bank->irq_base; + } + + /* Only bank F has especially funky IRQ handling */ + if (bank->has_hierarchical_irq) { + int gpio_irq; + int i; + + /* + * FIXME: convert this to use hierarchical IRQ support! + * this requires fixing the root irqchip to be hierarchial. + */ + girq->parent_handler = ep93xx_gpio_f_irq_handler; + girq->num_parents = 8; + girq->parents = devm_kcalloc(dev, 8, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + /* Pick resources 1..8 for these IRQs */ + for (i = 1; i <= 8; i++) + girq->parents[i - 1] = platform_get_irq(pdev, i); + for (i = 0; i < 8; i++) { + gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i; + irq_set_chip_data(gpio_irq, &epg->gc[5]); + irq_set_chip_and_handler(gpio_irq, + &ep93xx_gpio_irq_chip, + handle_level_irq); + irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST); + } + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + gc->to_irq = ep93xx_gpio_f_to_irq; + } return devm_gpiochip_add_data(dev, gc, epg); } @@ -407,16 +418,11 @@ static int ep93xx_gpio_probe(struct platform_device *pdev) struct gpio_chip *gc = &epg->gc[i]; struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i]; - if (ep93xx_gpio_add_bank(gc, &pdev->dev, epg, bank)) + if (ep93xx_gpio_add_bank(gc, pdev, epg, bank)) dev_warn(&pdev->dev, "Unable to add gpio bank %s\n", bank->label); - /* Only bank F has especially funky IRQ handling */ - if (i == 5) - gc->to_irq = ep93xx_gpio_f_to_irq; } - ep93xx_gpio_init_irq(pdev, epg); - return 0; } diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index 250e71f3e688..fbddb1662428 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -290,16 +290,14 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; + if (!girq->parents) { + ret = -ENOMEM; + goto dis_clk; + } girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; girq->parents[0] = irq; - ret = devm_gpiochip_add_data(dev, &g->gc, g); - if (ret) - goto dis_clk; - /* Disable, unmask and clear all interrupts */ writel(0x0, g->base + GPIO_INT_EN); writel(0x0, g->base + GPIO_INT_MASK); @@ -308,6 +306,10 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) /* Clear any use of debounce */ writel(0x0, g->base + GPIO_DEBOUNCE_EN); + ret = devm_gpiochip_add_data(dev, &g->gc, g); + if (ret) + goto dis_clk; + platform_set_drvdata(pdev, g); dev_info(dev, "FTGPIO010 @%p registered\n", g->base); diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 0937b605e134..08234e64993a 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -408,8 +408,6 @@ static int grgpio_probe(struct platform_device *ofdev) * Continue without irq functionality for that * gpio line */ - dev_err(priv->dev, - "Failed to get irq for offset %d\n", i); continue; } priv->uirqs[lirq->index].uirq = ret; diff --git a/drivers/gpio/gpio-hlwd.c b/drivers/gpio/gpio-hlwd.c index e5fa00f8145f..4a17599f6d44 100644 --- a/drivers/gpio/gpio-hlwd.c +++ b/drivers/gpio/gpio-hlwd.c @@ -244,43 +244,45 @@ static int hlwd_gpio_probe(struct platform_device *pdev) ngpios = 32; hlwd->gpioc.ngpio = ngpios; - res = devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); - if (res) - return res; - /* Mask and ack all interrupts */ iowrite32be(0, hlwd->regs + HW_GPIOB_INTMASK); iowrite32be(0xffffffff, hlwd->regs + HW_GPIOB_INTFLAG); /* * If this GPIO controller is not marked as an interrupt controller in - * the DT, return. + * the DT, skip interrupt support. */ - if (!of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) - return 0; - - hlwd->irq = platform_get_irq(pdev, 0); - if (hlwd->irq < 0) { - dev_info(&pdev->dev, "platform_get_irq returned %d\n", - hlwd->irq); - return hlwd->irq; + if (of_property_read_bool(pdev->dev.of_node, "interrupt-controller")) { + struct gpio_irq_chip *girq; + + hlwd->irq = platform_get_irq(pdev, 0); + if (hlwd->irq < 0) { + dev_info(&pdev->dev, "platform_get_irq returned %d\n", + hlwd->irq); + return hlwd->irq; + } + + hlwd->irqc.name = dev_name(&pdev->dev); + hlwd->irqc.irq_mask = hlwd_gpio_irq_mask; + hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask; + hlwd->irqc.irq_enable = hlwd_gpio_irq_enable; + hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type; + + girq = &hlwd->gpioc.irq; + girq->chip = &hlwd->irqc; + girq->parent_handler = hlwd_gpio_irqhandler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = hlwd->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; } - hlwd->irqc.name = dev_name(&pdev->dev); - hlwd->irqc.irq_mask = hlwd_gpio_irq_mask; - hlwd->irqc.irq_unmask = hlwd_gpio_irq_unmask; - hlwd->irqc.irq_enable = hlwd_gpio_irq_enable; - hlwd->irqc.irq_set_type = hlwd_gpio_irq_set_type; - - res = gpiochip_irqchip_add(&hlwd->gpioc, &hlwd->irqc, 0, - handle_level_irq, IRQ_TYPE_NONE); - if (res) - return res; - - gpiochip_set_chained_irqchip(&hlwd->gpioc, &hlwd->irqc, - hlwd->irq, hlwd_gpio_irqhandler); - - return 0; + return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd); } static const struct of_device_id hlwd_gpio_match[] = { diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 9d3ac51a765c..6eb56f7ab9c9 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -118,20 +118,6 @@ static void egpio_handler(struct irq_desc *desc) } } -int htc_egpio_get_wakeup_irq(struct device *dev) -{ - struct egpio_info *ei = dev_get_drvdata(dev); - - /* Read current pins. */ - u16 readval = egpio_readw(ei, ei->ack_register); - /* Ack/unmask interrupts. */ - ack_irqs(ei); - /* Return first set pin. */ - readval &= ei->irqs_enabled; - return ei->irq_start + ffs(readval) - 1; -} -EXPORT_SYMBOL(htc_egpio_get_wakeup_irq); - static inline int egpio_pos(struct egpio_info *ei, int bit) { return bit >> ei->reg_shift; diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c index 4e803baf980e..4d835f9089df 100644 --- a/drivers/gpio/gpio-intel-mid.c +++ b/drivers/gpio/gpio-intel-mid.c @@ -329,6 +329,7 @@ static int intel_gpio_probe(struct pci_dev *pdev, u32 gpio_base; u32 irq_base; int retval; + struct gpio_irq_chip *girq; struct intel_mid_gpio_ddata *ddata = (struct intel_mid_gpio_ddata *)id->driver_data; @@ -369,6 +370,22 @@ static int intel_gpio_probe(struct pci_dev *pdev, spin_lock_init(&priv->lock); + girq = &priv->chip.irq; + girq->chip = &intel_mid_irqchip; + girq->parent_handler = intel_mid_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = pdev->irq; + girq->first = irq_base; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + + intel_mid_irq_init_hw(priv); + pci_set_drvdata(pdev, priv); retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); if (retval) { @@ -376,24 +393,6 @@ static int intel_gpio_probe(struct pci_dev *pdev, return retval; } - retval = gpiochip_irqchip_add(&priv->chip, - &intel_mid_irqchip, - irq_base, - handle_simple_irq, - IRQ_TYPE_NONE); - if (retval) { - dev_err(&pdev->dev, - "could not connect irqchip to gpiochip\n"); - return retval; - } - - intel_mid_irq_init_hw(priv); - - gpiochip_set_chained_irqchip(&priv->chip, - &intel_mid_irqchip, - pdev->irq, - intel_mid_irq_handler); - pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index 670c2a85a35b..b3b050604e0b 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -47,7 +47,6 @@ * @dev: containing device for this instance * @fwnode: the fwnode for this GPIO chip * @gc: gpiochip for this instance - * @domain: irqdomain for this chip instance * @base: remapped I/O-memory base * @irq_edge: Each bit represents an IRQ: 1: edge-triggered, * 0: level triggered @@ -56,48 +55,22 @@ struct ixp4xx_gpio { struct device *dev; struct fwnode_handle *fwnode; struct gpio_chip gc; - struct irq_domain *domain; void __iomem *base; unsigned long long irq_edge; }; -/** - * struct ixp4xx_gpio_map - IXP4 GPIO to parent IRQ map - * @gpio_offset: offset of the IXP4 GPIO line - * @parent_hwirq: hwirq on the parent IRQ controller - */ -struct ixp4xx_gpio_map { - int gpio_offset; - int parent_hwirq; -}; - -/* GPIO lines 0..12 have corresponding IRQs, GPIOs 13..15 have no IRQs */ -const struct ixp4xx_gpio_map ixp4xx_gpiomap[] = { - { .gpio_offset = 0, .parent_hwirq = 6 }, - { .gpio_offset = 1, .parent_hwirq = 7 }, - { .gpio_offset = 2, .parent_hwirq = 19 }, - { .gpio_offset = 3, .parent_hwirq = 20 }, - { .gpio_offset = 4, .parent_hwirq = 21 }, - { .gpio_offset = 5, .parent_hwirq = 22 }, - { .gpio_offset = 6, .parent_hwirq = 23 }, - { .gpio_offset = 7, .parent_hwirq = 24 }, - { .gpio_offset = 8, .parent_hwirq = 25 }, - { .gpio_offset = 9, .parent_hwirq = 26 }, - { .gpio_offset = 10, .parent_hwirq = 27 }, - { .gpio_offset = 11, .parent_hwirq = 28 }, - { .gpio_offset = 12, .parent_hwirq = 29 }, -}; - static void ixp4xx_gpio_irq_ack(struct irq_data *d) { - struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ixp4xx_gpio *g = gpiochip_get_data(gc); __raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS); } static void ixp4xx_gpio_irq_unmask(struct irq_data *d) { - struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ixp4xx_gpio *g = gpiochip_get_data(gc); /* ACK when unmasking if not edge-triggered */ if (!(g->irq_edge & BIT(d->hwirq))) @@ -108,7 +81,8 @@ static void ixp4xx_gpio_irq_unmask(struct irq_data *d) static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type) { - struct ixp4xx_gpio *g = irq_data_get_irq_chip_data(d); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct ixp4xx_gpio *g = gpiochip_get_data(gc); int line = d->hwirq; unsigned long flags; u32 int_style; @@ -187,122 +161,31 @@ static struct irq_chip ixp4xx_gpio_irqchip = { .irq_set_type = ixp4xx_gpio_irq_set_type, }; -static int ixp4xx_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) +static int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) { - struct ixp4xx_gpio *g = gpiochip_get_data(gc); - struct irq_fwspec fwspec; - - fwspec.fwnode = g->fwnode; - fwspec.param_count = 2; - fwspec.param[0] = offset; - fwspec.param[1] = IRQ_TYPE_NONE; - - return irq_create_fwspec_mapping(&fwspec); -} + /* All these interrupts are level high in the CPU */ + *parent_type = IRQ_TYPE_LEVEL_HIGH; -static int ixp4xx_gpio_irq_domain_translate(struct irq_domain *domain, - struct irq_fwspec *fwspec, - unsigned long *hwirq, - unsigned int *type) -{ - int ret; - - /* We support standard DT translation */ - if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { - return irq_domain_translate_twocell(domain, fwspec, - hwirq, type); + /* GPIO lines 0..12 have dedicated IRQs */ + if (child == 0) { + *parent = 6; + return 0; } - - /* This goes away when we transition to DT */ - if (is_fwnode_irqchip(fwspec->fwnode)) { - ret = irq_domain_translate_twocell(domain, fwspec, - hwirq, type); - if (ret) - return ret; - WARN_ON(*type == IRQ_TYPE_NONE); + if (child == 1) { + *parent = 7; return 0; } - return -EINVAL; -} - -static int ixp4xx_gpio_irq_domain_alloc(struct irq_domain *d, - unsigned int irq, unsigned int nr_irqs, - void *data) -{ - struct ixp4xx_gpio *g = d->host_data; - irq_hw_number_t hwirq; - unsigned int type = IRQ_TYPE_NONE; - struct irq_fwspec *fwspec = data; - int ret; - int i; - - ret = ixp4xx_gpio_irq_domain_translate(d, fwspec, &hwirq, &type); - if (ret) - return ret; - - dev_dbg(g->dev, "allocate IRQ %d..%d, hwirq %lu..%lu\n", - irq, irq + nr_irqs - 1, - hwirq, hwirq + nr_irqs - 1); - - for (i = 0; i < nr_irqs; i++) { - struct irq_fwspec parent_fwspec; - const struct ixp4xx_gpio_map *map; - int j; - - /* Not all lines support IRQs */ - for (j = 0; j < ARRAY_SIZE(ixp4xx_gpiomap); j++) { - map = &ixp4xx_gpiomap[j]; - if (map->gpio_offset == hwirq) - break; - } - if (j == ARRAY_SIZE(ixp4xx_gpiomap)) { - dev_err(g->dev, "can't look up hwirq %lu\n", hwirq); - return -EINVAL; - } - dev_dbg(g->dev, "found parent hwirq %u\n", map->parent_hwirq); - - /* - * We set handle_bad_irq because the .set_type() should - * always be invoked and set the right type of handler. - */ - irq_domain_set_info(d, - irq + i, - hwirq + i, - &ixp4xx_gpio_irqchip, - g, - handle_bad_irq, - NULL, NULL); - irq_set_probe(irq + i); - - /* - * Create a IRQ fwspec to send up to the parent irqdomain: - * specify the hwirq we address on the parent and tie it - * all together up the chain. - */ - parent_fwspec.fwnode = d->parent->fwnode; - parent_fwspec.param_count = 2; - parent_fwspec.param[0] = map->parent_hwirq; - /* This parent only handles asserted level IRQs */ - parent_fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH; - dev_dbg(g->dev, "alloc_irqs_parent for %d parent hwirq %d\n", - irq + i, map->parent_hwirq); - ret = irq_domain_alloc_irqs_parent(d, irq + i, 1, - &parent_fwspec); - if (ret) - dev_err(g->dev, - "failed to allocate parent hwirq %d for hwirq %lu\n", - map->parent_hwirq, hwirq); + if (child >= 2 && child <= 12) { + *parent = child + 17; + return 0; } - - return 0; + return -EINVAL; } -static const struct irq_domain_ops ixp4xx_gpio_irqdomain_ops = { - .translate = ixp4xx_gpio_irq_domain_translate, - .alloc = ixp4xx_gpio_irq_domain_alloc, - .free = irq_domain_free_irqs_common, -}; - static int ixp4xx_gpio_probe(struct platform_device *pdev) { unsigned long flags; @@ -311,8 +194,8 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) struct irq_domain *parent; struct resource *res; struct ixp4xx_gpio *g; + struct gpio_irq_chip *girq; int ret; - int i; g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); if (!g) @@ -321,9 +204,36 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); g->base = devm_ioremap_resource(dev, res); - if (IS_ERR(g->base)) { - dev_err(dev, "ioremap error\n"); + if (IS_ERR(g->base)) return PTR_ERR(g->base); + + /* + * When we convert to device tree we will simply look up the + * parent irqdomain using irq_find_host(parent) as parent comes + * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get + * the fwnode. For now we need this boardfile style code. + */ + if (np) { + struct device_node *irq_parent; + + irq_parent = of_irq_find_parent(np); + if (!irq_parent) { + dev_err(dev, "no IRQ parent node\n"); + return -ENODEV; + } + parent = irq_find_host(irq_parent); + if (!parent) { + dev_err(dev, "no IRQ parent domain\n"); + return -ENODEV; + } + g->fwnode = of_node_to_fwnode(np); + } else { + parent = ixp4xx_get_irq_domain(); + g->fwnode = irq_domain_alloc_fwnode(&res->start); + if (!g->fwnode) { + dev_err(dev, "no domain base\n"); + return -ENODEV; + } } /* @@ -360,7 +270,6 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) dev_err(dev, "unable to init generic GPIO\n"); return ret; } - g->gc.to_irq = ixp4xx_gpio_to_irq; g->gc.ngpio = 16; g->gc.label = "IXP4XX_GPIO_CHIP"; /* @@ -372,86 +281,22 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) g->gc.parent = &pdev->dev; g->gc.owner = THIS_MODULE; + girq = &g->gc.irq; + girq->chip = &ixp4xx_gpio_irqchip; + girq->fwnode = g->fwnode; + girq->parent_domain = parent; + girq->child_to_parent_hwirq = ixp4xx_gpio_child_to_parent_hwirq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + ret = devm_gpiochip_add_data(dev, &g->gc, g); if (ret) { dev_err(dev, "failed to add SoC gpiochip\n"); return ret; } - /* - * When we convert to device tree we will simply look up the - * parent irqdomain using irq_find_host(parent) as parent comes - * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get - * the fwnode. For now we need this boardfile style code. - */ - if (np) { - struct device_node *irq_parent; - - irq_parent = of_irq_find_parent(np); - if (!irq_parent) { - dev_err(dev, "no IRQ parent node\n"); - return -ENODEV; - } - parent = irq_find_host(irq_parent); - if (!parent) { - dev_err(dev, "no IRQ parent domain\n"); - return -ENODEV; - } - g->fwnode = of_node_to_fwnode(np); - } else { - parent = ixp4xx_get_irq_domain(); - g->fwnode = irq_domain_alloc_fwnode(g->base); - if (!g->fwnode) { - dev_err(dev, "no domain base\n"); - return -ENODEV; - } - } - g->domain = irq_domain_create_hierarchy(parent, - IRQ_DOMAIN_FLAG_HIERARCHY, - ARRAY_SIZE(ixp4xx_gpiomap), - g->fwnode, - &ixp4xx_gpio_irqdomain_ops, - g); - if (!g->domain) { - irq_domain_free_fwnode(g->fwnode); - dev_err(dev, "no hierarchical irq domain\n"); - return ret; - } - - /* - * After adding OF support, this is no longer needed: irqs - * will be allocated for the respective fwnodes. - */ - if (!np) { - for (i = 0; i < ARRAY_SIZE(ixp4xx_gpiomap); i++) { - const struct ixp4xx_gpio_map *map = &ixp4xx_gpiomap[i]; - struct irq_fwspec fwspec; - - fwspec.fwnode = g->fwnode; - /* This is the hwirq for the GPIO line side of things */ - fwspec.param[0] = map->gpio_offset; - fwspec.param[1] = IRQ_TYPE_EDGE_RISING; - fwspec.param_count = 2; - ret = __irq_domain_alloc_irqs(g->domain, - -1, /* just pick something */ - 1, - NUMA_NO_NODE, - &fwspec, - false, - NULL); - if (ret < 0) { - irq_domain_free_fwnode(g->fwnode); - dev_err(dev, - "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", - map->gpio_offset, map->parent_hwirq, - ret); - return ret; - } - } - } - platform_set_drvdata(pdev, g); - dev_info(dev, "IXP4 GPIO @%p registered\n", g->base); + dev_info(dev, "IXP4 GPIO registered\n"); return 0; } diff --git a/drivers/gpio/gpio-ks8695.c b/drivers/gpio/gpio-ks8695.c deleted file mode 100644 index a0f87c124894..000000000000 --- a/drivers/gpio/gpio-ks8695.c +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * arch/arm/mach-ks8695/gpio.c - * - * Copyright (C) 2006 Andrew Victor - * Updated to GPIOLIB, Copyright 2008 Simtec Electronics - * Daniel Silverstone <dsilvers@simtec.co.uk> - */ -#include <linux/gpio/driver.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/init.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/module.h> -#include <linux/io.h> - -#include <mach/hardware.h> -#include <asm/mach/irq.h> - -#include <mach/regs-gpio.h> -#include <mach/gpio-ks8695.h> - -/* - * Configure a GPIO line for either GPIO function, or its internal - * function (Interrupt, Timer, etc). - */ -static void ks8695_gpio_mode(unsigned int pin, short gpio) -{ - unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN }; - unsigned long x, flags; - - if (pin > KS8695_GPIO_5) /* only GPIO 0..5 have internal functions */ - return; - - local_irq_save(flags); - - x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC); - if (gpio) /* GPIO: set bit to 0 */ - x &= ~enable[pin]; - else /* Internal function: set bit to 1 */ - x |= enable[pin]; - __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPC); - - local_irq_restore(flags); -} - - -static unsigned short gpio_irq[] = { KS8695_IRQ_EXTERN0, KS8695_IRQ_EXTERN1, KS8695_IRQ_EXTERN2, KS8695_IRQ_EXTERN3 }; - -/* - * Configure GPIO pin as external interrupt source. - */ -int ks8695_gpio_interrupt(unsigned int pin, unsigned int type) -{ - unsigned long x, flags; - - if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */ - return -EINVAL; - - local_irq_save(flags); - - /* set pin as input */ - x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); - x &= ~IOPM(pin); - __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM); - - local_irq_restore(flags); - - /* Set IRQ triggering type */ - irq_set_irq_type(gpio_irq[pin], type); - - /* enable interrupt mode */ - ks8695_gpio_mode(pin, 0); - - return 0; -} -EXPORT_SYMBOL(ks8695_gpio_interrupt); - - - -/* .... Generic GPIO interface .............................................. */ - -/* - * Configure the GPIO line as an input. - */ -static int ks8695_gpio_direction_input(struct gpio_chip *gc, unsigned int pin) -{ - unsigned long x, flags; - - if (pin > KS8695_GPIO_15) - return -EINVAL; - - /* set pin to GPIO mode */ - ks8695_gpio_mode(pin, 1); - - local_irq_save(flags); - - /* set pin as input */ - x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); - x &= ~IOPM(pin); - __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM); - - local_irq_restore(flags); - - return 0; -} - - -/* - * Configure the GPIO line as an output, with default state. - */ -static int ks8695_gpio_direction_output(struct gpio_chip *gc, - unsigned int pin, int state) -{ - unsigned long x, flags; - - if (pin > KS8695_GPIO_15) - return -EINVAL; - - /* set pin to GPIO mode */ - ks8695_gpio_mode(pin, 1); - - local_irq_save(flags); - - /* set line state */ - x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); - if (state) - x |= IOPD(pin); - else - x &= ~IOPD(pin); - __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD); - - /* set pin as output */ - x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); - x |= IOPM(pin); - __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPM); - - local_irq_restore(flags); - - return 0; -} - - -/* - * Set the state of an output GPIO line. - */ -static void ks8695_gpio_set_value(struct gpio_chip *gc, - unsigned int pin, int state) -{ - unsigned long x, flags; - - if (pin > KS8695_GPIO_15) - return; - - local_irq_save(flags); - - /* set output line state */ - x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); - if (state) - x |= IOPD(pin); - else - x &= ~IOPD(pin); - __raw_writel(x, KS8695_GPIO_VA + KS8695_IOPD); - - local_irq_restore(flags); -} - - -/* - * Read the state of a GPIO line. - */ -static int ks8695_gpio_get_value(struct gpio_chip *gc, unsigned int pin) -{ - unsigned long x; - - if (pin > KS8695_GPIO_15) - return -EINVAL; - - x = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); - return (x & IOPD(pin)) != 0; -} - - -/* - * Map GPIO line to IRQ number. - */ -static int ks8695_gpio_to_irq(struct gpio_chip *gc, unsigned int pin) -{ - if (pin > KS8695_GPIO_3) /* only GPIO 0..3 can generate IRQ */ - return -EINVAL; - - return gpio_irq[pin]; -} - -/* GPIOLIB interface */ - -static struct gpio_chip ks8695_gpio_chip = { - .label = "KS8695", - .direction_input = ks8695_gpio_direction_input, - .direction_output = ks8695_gpio_direction_output, - .get = ks8695_gpio_get_value, - .set = ks8695_gpio_set_value, - .to_irq = ks8695_gpio_to_irq, - .base = 0, - .ngpio = 16, - .can_sleep = false, -}; - -/* Register the GPIOs */ -void ks8695_register_gpios(void) -{ - if (gpiochip_add_data(&ks8695_gpio_chip, NULL)) - printk(KERN_ERR "Unable to register core GPIOs\n"); -} - -/* .... Debug interface ..................................................... */ - -#ifdef CONFIG_DEBUG_FS - -static int ks8695_gpio_show(struct seq_file *s, void *unused) -{ - unsigned int enable[] = { IOPC_IOEINT0EN, IOPC_IOEINT1EN, IOPC_IOEINT2EN, IOPC_IOEINT3EN, IOPC_IOTIM0EN, IOPC_IOTIM1EN }; - unsigned int intmask[] = { IOPC_IOEINT0TM, IOPC_IOEINT1TM, IOPC_IOEINT2TM, IOPC_IOEINT3TM }; - unsigned long mode, ctrl, data; - int i; - - mode = __raw_readl(KS8695_GPIO_VA + KS8695_IOPM); - ctrl = __raw_readl(KS8695_GPIO_VA + KS8695_IOPC); - data = __raw_readl(KS8695_GPIO_VA + KS8695_IOPD); - - seq_printf(s, "Pin\tI/O\tFunction\tState\n\n"); - - for (i = KS8695_GPIO_0; i <= KS8695_GPIO_15 ; i++) { - seq_printf(s, "%i:\t", i); - - seq_printf(s, "%s\t", (mode & IOPM(i)) ? "Output" : "Input"); - - if (i <= KS8695_GPIO_3) { - if (ctrl & enable[i]) { - seq_printf(s, "EXT%i ", i); - - switch ((ctrl & intmask[i]) >> (4 * i)) { - case IOPC_TM_LOW: - seq_printf(s, "(Low)"); break; - case IOPC_TM_HIGH: - seq_printf(s, "(High)"); break; - case IOPC_TM_RISING: - seq_printf(s, "(Rising)"); break; - case IOPC_TM_FALLING: - seq_printf(s, "(Falling)"); break; - case IOPC_TM_EDGE: - seq_printf(s, "(Edges)"); break; - } - } else - seq_printf(s, "GPIO\t"); - } else if (i <= KS8695_GPIO_5) { - if (ctrl & enable[i]) - seq_printf(s, "TOUT%i\t", i - KS8695_GPIO_4); - else - seq_printf(s, "GPIO\t"); - } else { - seq_printf(s, "GPIO\t"); - } - - seq_printf(s, "\t"); - - seq_printf(s, "%i\n", (data & IOPD(i)) ? 1 : 0); - } - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(ks8695_gpio); - -static int __init ks8695_gpio_debugfs_init(void) -{ - /* /sys/kernel/debug/ks8695_gpio */ - debugfs_create_file("ks8695_gpio", S_IFREG | S_IRUGO, NULL, NULL, - &ks8695_gpio_fops); - return 0; -} -postcore_initcall(ks8695_gpio_debugfs_init); - -#endif diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index 24885b3db3d5..4e626c4235c2 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -16,36 +16,33 @@ #include <linux/platform_device.h> #include <linux/module.h> -#include <mach/hardware.h> -#include <mach/platform.h> - -#define LPC32XX_GPIO_P3_INP_STATE _GPREG(0x000) -#define LPC32XX_GPIO_P3_OUTP_SET _GPREG(0x004) -#define LPC32XX_GPIO_P3_OUTP_CLR _GPREG(0x008) -#define LPC32XX_GPIO_P3_OUTP_STATE _GPREG(0x00C) -#define LPC32XX_GPIO_P2_DIR_SET _GPREG(0x010) -#define LPC32XX_GPIO_P2_DIR_CLR _GPREG(0x014) -#define LPC32XX_GPIO_P2_DIR_STATE _GPREG(0x018) -#define LPC32XX_GPIO_P2_INP_STATE _GPREG(0x01C) -#define LPC32XX_GPIO_P2_OUTP_SET _GPREG(0x020) -#define LPC32XX_GPIO_P2_OUTP_CLR _GPREG(0x024) -#define LPC32XX_GPIO_P2_MUX_SET _GPREG(0x028) -#define LPC32XX_GPIO_P2_MUX_CLR _GPREG(0x02C) -#define LPC32XX_GPIO_P2_MUX_STATE _GPREG(0x030) -#define LPC32XX_GPIO_P0_INP_STATE _GPREG(0x040) -#define LPC32XX_GPIO_P0_OUTP_SET _GPREG(0x044) -#define LPC32XX_GPIO_P0_OUTP_CLR _GPREG(0x048) -#define LPC32XX_GPIO_P0_OUTP_STATE _GPREG(0x04C) -#define LPC32XX_GPIO_P0_DIR_SET _GPREG(0x050) -#define LPC32XX_GPIO_P0_DIR_CLR _GPREG(0x054) -#define LPC32XX_GPIO_P0_DIR_STATE _GPREG(0x058) -#define LPC32XX_GPIO_P1_INP_STATE _GPREG(0x060) -#define LPC32XX_GPIO_P1_OUTP_SET _GPREG(0x064) -#define LPC32XX_GPIO_P1_OUTP_CLR _GPREG(0x068) -#define LPC32XX_GPIO_P1_OUTP_STATE _GPREG(0x06C) -#define LPC32XX_GPIO_P1_DIR_SET _GPREG(0x070) -#define LPC32XX_GPIO_P1_DIR_CLR _GPREG(0x074) -#define LPC32XX_GPIO_P1_DIR_STATE _GPREG(0x078) +#define LPC32XX_GPIO_P3_INP_STATE (0x000) +#define LPC32XX_GPIO_P3_OUTP_SET (0x004) +#define LPC32XX_GPIO_P3_OUTP_CLR (0x008) +#define LPC32XX_GPIO_P3_OUTP_STATE (0x00C) +#define LPC32XX_GPIO_P2_DIR_SET (0x010) +#define LPC32XX_GPIO_P2_DIR_CLR (0x014) +#define LPC32XX_GPIO_P2_DIR_STATE (0x018) +#define LPC32XX_GPIO_P2_INP_STATE (0x01C) +#define LPC32XX_GPIO_P2_OUTP_SET (0x020) +#define LPC32XX_GPIO_P2_OUTP_CLR (0x024) +#define LPC32XX_GPIO_P2_MUX_SET (0x028) +#define LPC32XX_GPIO_P2_MUX_CLR (0x02C) +#define LPC32XX_GPIO_P2_MUX_STATE (0x030) +#define LPC32XX_GPIO_P0_INP_STATE (0x040) +#define LPC32XX_GPIO_P0_OUTP_SET (0x044) +#define LPC32XX_GPIO_P0_OUTP_CLR (0x048) +#define LPC32XX_GPIO_P0_OUTP_STATE (0x04C) +#define LPC32XX_GPIO_P0_DIR_SET (0x050) +#define LPC32XX_GPIO_P0_DIR_CLR (0x054) +#define LPC32XX_GPIO_P0_DIR_STATE (0x058) +#define LPC32XX_GPIO_P1_INP_STATE (0x060) +#define LPC32XX_GPIO_P1_OUTP_SET (0x064) +#define LPC32XX_GPIO_P1_OUTP_CLR (0x068) +#define LPC32XX_GPIO_P1_OUTP_STATE (0x06C) +#define LPC32XX_GPIO_P1_DIR_SET (0x070) +#define LPC32XX_GPIO_P1_DIR_CLR (0x074) +#define LPC32XX_GPIO_P1_DIR_STATE (0x078) #define GPIO012_PIN_TO_BIT(x) (1 << (x)) #define GPIO3_PIN_TO_BIT(x) (1 << ((x) + 25)) @@ -72,12 +69,12 @@ #define LPC32XX_GPO_P3_GRP (LPC32XX_GPI_P3_GRP + LPC32XX_GPI_P3_MAX) struct gpio_regs { - void __iomem *inp_state; - void __iomem *outp_state; - void __iomem *outp_set; - void __iomem *outp_clr; - void __iomem *dir_set; - void __iomem *dir_clr; + unsigned long inp_state; + unsigned long outp_state; + unsigned long outp_set; + unsigned long outp_clr; + unsigned long dir_set; + unsigned long dir_clr; }; /* @@ -165,16 +162,27 @@ static struct gpio_regs gpio_grp_regs_p3 = { struct lpc32xx_gpio_chip { struct gpio_chip chip; struct gpio_regs *gpio_grp; + void __iomem *reg_base; }; +static inline u32 gpreg_read(struct lpc32xx_gpio_chip *group, unsigned long offset) +{ + return __raw_readl(group->reg_base + offset); +} + +static inline void gpreg_write(struct lpc32xx_gpio_chip *group, u32 val, unsigned long offset) +{ + __raw_writel(val, group->reg_base + offset); +} + static void __set_gpio_dir_p012(struct lpc32xx_gpio_chip *group, unsigned pin, int input) { if (input) - __raw_writel(GPIO012_PIN_TO_BIT(pin), + gpreg_write(group, GPIO012_PIN_TO_BIT(pin), group->gpio_grp->dir_clr); else - __raw_writel(GPIO012_PIN_TO_BIT(pin), + gpreg_write(group, GPIO012_PIN_TO_BIT(pin), group->gpio_grp->dir_set); } @@ -184,19 +192,19 @@ static void __set_gpio_dir_p3(struct lpc32xx_gpio_chip *group, u32 u = GPIO3_PIN_TO_BIT(pin); if (input) - __raw_writel(u, group->gpio_grp->dir_clr); + gpreg_write(group, u, group->gpio_grp->dir_clr); else - __raw_writel(u, group->gpio_grp->dir_set); + gpreg_write(group, u, group->gpio_grp->dir_set); } static void __set_gpio_level_p012(struct lpc32xx_gpio_chip *group, unsigned pin, int high) { if (high) - __raw_writel(GPIO012_PIN_TO_BIT(pin), + gpreg_write(group, GPIO012_PIN_TO_BIT(pin), group->gpio_grp->outp_set); else - __raw_writel(GPIO012_PIN_TO_BIT(pin), + gpreg_write(group, GPIO012_PIN_TO_BIT(pin), group->gpio_grp->outp_clr); } @@ -206,31 +214,31 @@ static void __set_gpio_level_p3(struct lpc32xx_gpio_chip *group, u32 u = GPIO3_PIN_TO_BIT(pin); if (high) - __raw_writel(u, group->gpio_grp->outp_set); + gpreg_write(group, u, group->gpio_grp->outp_set); else - __raw_writel(u, group->gpio_grp->outp_clr); + gpreg_write(group, u, group->gpio_grp->outp_clr); } static void __set_gpo_level_p3(struct lpc32xx_gpio_chip *group, unsigned pin, int high) { if (high) - __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_set); + gpreg_write(group, GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_set); else - __raw_writel(GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_clr); + gpreg_write(group, GPO3_PIN_TO_BIT(pin), group->gpio_grp->outp_clr); } static int __get_gpio_state_p012(struct lpc32xx_gpio_chip *group, unsigned pin) { - return GPIO012_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), + return GPIO012_PIN_IN_SEL(gpreg_read(group, group->gpio_grp->inp_state), pin); } static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group, unsigned pin) { - int state = __raw_readl(group->gpio_grp->inp_state); + int state = gpreg_read(group, group->gpio_grp->inp_state); /* * P3 GPIO pin input mapping is not contiguous, GPIOP3-0..4 is mapped @@ -242,13 +250,13 @@ static int __get_gpio_state_p3(struct lpc32xx_gpio_chip *group, static int __get_gpi_state_p3(struct lpc32xx_gpio_chip *group, unsigned pin) { - return GPI3_PIN_IN_SEL(__raw_readl(group->gpio_grp->inp_state), pin); + return GPI3_PIN_IN_SEL(gpreg_read(group, group->gpio_grp->inp_state), pin); } static int __get_gpo_state_p3(struct lpc32xx_gpio_chip *group, unsigned pin) { - return GPO3_PIN_IN_SEL(__raw_readl(group->gpio_grp->outp_state), pin); + return GPO3_PIN_IN_SEL(gpreg_read(group, group->gpio_grp->outp_state), pin); } /* @@ -497,12 +505,18 @@ static int lpc32xx_of_xlate(struct gpio_chip *gc, static int lpc32xx_gpio_probe(struct platform_device *pdev) { int i; + void __iomem *reg_base; + + reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg_base)) + return PTR_ERR(reg_base); for (i = 0; i < ARRAY_SIZE(lpc32xx_gpiochip); i++) { if (pdev->dev.of_node) { lpc32xx_gpiochip[i].chip.of_xlate = lpc32xx_of_xlate; lpc32xx_gpiochip[i].chip.of_gpio_n_cells = 3; lpc32xx_gpiochip[i].chip.of_node = pdev->dev.of_node; + lpc32xx_gpiochip[i].reg_base = reg_base; } devm_gpiochip_add_data(&pdev->dev, &lpc32xx_gpiochip[i].chip, &lpc32xx_gpiochip[i]); @@ -527,3 +541,7 @@ static struct platform_driver lpc32xx_gpio_driver = { }; module_platform_driver(lpc32xx_gpio_driver); + +MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("GPIO driver for LPC32xx SoC"); diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 31b4a091ab60..6bb9741ad036 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -358,25 +358,30 @@ static int lp_gpio_probe(struct platform_device *pdev) gc->can_sleep = false; gc->parent = dev; - ret = devm_gpiochip_add_data(dev, gc, lg); - if (ret) { - dev_err(dev, "failed adding lp-gpio chip\n"); - return ret; - } - /* set up interrupts */ if (irq_rc && irq_rc->start) { + struct gpio_irq_chip *girq; + + girq = &gc->irq; + girq->chip = &lp_irqchip; + girq->parent_handler = lp_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = (unsigned)irq_rc->start; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + lp_gpio_irq_init_hw(lg); - ret = gpiochip_irqchip_add(gc, &lp_irqchip, 0, - handle_simple_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "failed to add irqchip\n"); - return ret; - } + } - gpiochip_set_chained_irqchip(gc, &lp_irqchip, - (unsigned)irq_rc->start, - lp_gpio_irq_handler); + ret = devm_gpiochip_add_data(dev, gc, lg); + if (ret) { + dev_err(dev, "failed adding lp-gpio chip\n"); + return ret; } pm_runtime_enable(dev); diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c index 4dbc837d1215..7086f8b5388f 100644 --- a/drivers/gpio/gpio-madera.c +++ b/drivers/gpio/gpio-madera.c @@ -120,7 +120,7 @@ static const struct gpio_chip madera_gpio_chip = { static int madera_gpio_probe(struct platform_device *pdev) { struct madera *madera = dev_get_drvdata(pdev->dev.parent); - struct madera_pdata *pdata = dev_get_platdata(madera->dev); + struct madera_pdata *pdata = &madera->pdata; struct madera_gpio *madera_gpio; int ret; @@ -136,6 +136,9 @@ static int madera_gpio_probe(struct platform_device *pdev) madera_gpio->gpio_chip.parent = pdev->dev.parent; switch (madera->type) { + case CS47L15: + madera_gpio->gpio_chip.ngpio = CS47L15_NUM_GPIOS; + break; case CS47L35: madera_gpio->gpio_chip.ngpio = CS47L35_NUM_GPIOS; break; @@ -147,13 +150,18 @@ static int madera_gpio_probe(struct platform_device *pdev) case CS47L91: madera_gpio->gpio_chip.ngpio = CS47L90_NUM_GPIOS; break; + case CS42L92: + case CS47L92: + case CS47L93: + madera_gpio->gpio_chip.ngpio = CS47L92_NUM_GPIOS; + break; default: dev_err(&pdev->dev, "Unknown chip variant %d\n", madera->type); return -EINVAL; } /* We want to be usable on systems that don't use devicetree or acpi */ - if (pdata && pdata->gpio_base) + if (pdata->gpio_base) madera_gpio->gpio_chip.base = pdata->gpio_base; else madera_gpio->gpio_chip.base = -1; diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index b7d89e30131e..47d05e357e61 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -270,10 +270,8 @@ static int max77620_gpio_probe(struct platform_device *pdev) int ret; gpio_irq = platform_get_irq(pdev, 0); - if (gpio_irq <= 0) { - dev_err(&pdev->dev, "GPIO irq not available %d\n", gpio_irq); + if (gpio_irq <= 0) return -ENODEV; - } mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL); if (!mgpio) diff --git a/drivers/gpio/gpio-max77650.c b/drivers/gpio/gpio-max77650.c index 3f03f4e8956c..3075f2513c6f 100644 --- a/drivers/gpio/gpio-max77650.c +++ b/drivers/gpio/gpio-max77650.c @@ -188,3 +188,4 @@ module_platform_driver(max77650_gpio_driver); MODULE_DESCRIPTION("MAXIM 77650/77651 GPIO driver"); MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:max77650-gpio"); diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 8f466993cd24..501e89548f53 100644 --- a/drivers/gpio/gpio-mb86s7x.c +++ b/drivers/gpio/gpio-mb86s7x.c @@ -21,6 +21,7 @@ #include <linux/slab.h> #include "gpiolib.h" +#include "gpiolib-acpi.h" /* * Only first 8bits of a register correspond to each pin, diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 3302125e5265..4f27ddfe1e2f 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -397,6 +397,7 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id { const struct mrfld_gpio_pinrange *range; const char *pinctrl_dev_name; + struct gpio_irq_chip *girq; struct mrfld_gpio *priv; u32 gpio_base, irq_base; void __iomem *base; @@ -444,6 +445,21 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id raw_spin_lock_init(&priv->lock); + girq = &priv->chip.irq; + girq->chip = &mrfld_irqchip; + girq->parent_handler = mrfld_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = pdev->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + + mrfld_irq_init_hw(priv); + pci_set_drvdata(pdev, priv); retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); if (retval) { @@ -465,18 +481,6 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id } } - retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base, - handle_bad_irq, IRQ_TYPE_NONE); - if (retval) { - dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n"); - return retval; - } - - mrfld_irq_init_hw(priv); - - gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq, - mrfld_irq_handler); - return 0; } diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index f1a9c0544e3f..213aedc97dc2 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -309,6 +309,7 @@ static const struct file_operations gpio_mockup_debugfs_ops = { .read = gpio_mockup_debugfs_read, .write = gpio_mockup_debugfs_write, .llseek = no_llseek, + .release = single_release, }; static void gpio_mockup_debugfs_setup(struct device *dev, diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index c8673a5d9412..16a47de29c94 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -32,6 +32,7 @@ #define GPIO_IMR 0x10 #define GPIO_ICR 0x14 #define GPIO_ICR2 0x18 +#define GPIO_IBE 0x18 struct mpc8xxx_gpio_chip { struct gpio_chip gc; @@ -45,6 +46,27 @@ struct mpc8xxx_gpio_chip { unsigned int irqn; }; +/* The GPIO Input Buffer Enable register(GPIO_IBE) is used to + * control the input enable of each individual GPIO port. + * When an individual GPIO port’s direction is set to + * input (GPIO_GPDIR[DRn=0]), the associated input enable must be + * set (GPIOxGPIE[IEn]=1) to propagate the port value to the GPIO + * Data Register. + */ +static int ls1028a_gpio_dir_in_init(struct gpio_chip *gc) +{ + unsigned long flags; + struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); + + spin_lock_irqsave(&gc->bgpio_lock, flags); + + gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff); + + spin_unlock_irqrestore(&gc->bgpio_lock, flags); + + return 0; +} + /* * This hardware has a big endian bit assignment such that GPIO line 0 is * connected to bit 31, line 1 to bit 30 ... line 31 to bit 0. @@ -261,6 +283,7 @@ static const struct irq_domain_ops mpc8xxx_gpio_irq_ops = { }; struct mpc8xxx_gpio_devtype { + int (*gpio_dir_in_init)(struct gpio_chip *chip); int (*gpio_dir_out)(struct gpio_chip *, unsigned int, int); int (*gpio_get)(struct gpio_chip *, unsigned int); int (*irq_set_type)(struct irq_data *, unsigned int); @@ -271,6 +294,10 @@ static const struct mpc8xxx_gpio_devtype mpc512x_gpio_devtype = { .irq_set_type = mpc512x_irq_set_type, }; +static const struct mpc8xxx_gpio_devtype ls1028a_gpio_devtype = { + .gpio_dir_in_init = ls1028a_gpio_dir_in_init, +}; + static const struct mpc8xxx_gpio_devtype mpc5125_gpio_devtype = { .gpio_dir_out = mpc5125_gpio_dir_out, .irq_set_type = mpc512x_irq_set_type, @@ -291,6 +318,8 @@ static const struct of_device_id mpc8xxx_gpio_ids[] = { { .compatible = "fsl,mpc5121-gpio", .data = &mpc512x_gpio_devtype, }, { .compatible = "fsl,mpc5125-gpio", .data = &mpc5125_gpio_devtype, }, { .compatible = "fsl,pq3-gpio", }, + { .compatible = "fsl,ls1028a-gpio", .data = &ls1028a_gpio_devtype, }, + { .compatible = "fsl,ls1088a-gpio", .data = &ls1028a_gpio_devtype, }, { .compatible = "fsl,qoriq-gpio", }, {} }; @@ -376,6 +405,9 @@ static int mpc8xxx_probe(struct platform_device *pdev) /* ack and mask all irqs */ gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff); gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0); + /* enable input buffer */ + if (devtype->gpio_dir_in_init) + devtype->gpio_dir_in_init(gc); irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, mpc8xxx_gpio_irq_cascade, mpc8xxx_gc); diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 79654fb2e50f..d1d785f983a7 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -241,13 +241,6 @@ mediatek_gpio_bank_probe(struct device *dev, if (!rg->chip.label) return -ENOMEM; - ret = devm_gpiochip_add_data(dev, &rg->chip, mtk); - if (ret < 0) { - dev_err(dev, "Could not register gpio %d, ret=%d\n", - rg->chip.ngpio, ret); - return ret; - } - rg->irq_chip.name = dev_name(dev); rg->irq_chip.parent_device = dev; rg->irq_chip.irq_unmask = mediatek_gpio_irq_unmask; @@ -256,8 +249,10 @@ mediatek_gpio_bank_probe(struct device *dev, rg->irq_chip.irq_set_type = mediatek_gpio_irq_type; if (mtk->gpio_irq) { + struct gpio_irq_chip *girq; + /* - * Manually request the irq here instead of passing + * Directly request the irq here instead of passing * a flow-handler to gpiochip_set_chained_irqchip, * because the irq is shared. */ @@ -271,15 +266,21 @@ mediatek_gpio_bank_probe(struct device *dev, return ret; } - ret = gpiochip_irqchip_add(&rg->chip, &rg->irq_chip, - 0, handle_simple_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "failed to add gpiochip_irqchip\n"); - return ret; - } + girq = &rg->chip.irq; + girq->chip = &rg->irq_chip; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + } - gpiochip_set_chained_irqchip(&rg->chip, &rg->irq_chip, - mtk->gpio_irq, NULL); + ret = devm_gpiochip_add_data(dev, &rg->chip, mtk); + if (ret < 0) { + dev_err(dev, "Could not register gpio %d, ret=%d\n", + rg->chip.ngpio, ret); + return ret; } /* set polarity to low for all gpios */ diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index b2813580c582..7907a8755866 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -435,12 +435,9 @@ static int mxc_gpio_probe(struct platform_device *pdev) return port->irq; /* the controller clock is optional */ - port->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(port->clk)) { - if (PTR_ERR(port->clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - port->clk = NULL; - } + port->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(port->clk)) + return PTR_ERR(port->clk); err = clk_prepare_enable(port->clk); if (err) { diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 378b206d2dc9..de5d1383f28d 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -9,6 +9,7 @@ */ #include <linux/acpi.h> +#include <linux/bits.h> #include <linux/gpio/driver.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -28,9 +29,9 @@ #define PCA953X_INVERT 0x02 #define PCA953X_DIRECTION 0x03 -#define REG_ADDR_MASK 0x3f -#define REG_ADDR_EXT 0x40 -#define REG_ADDR_AI 0x80 +#define REG_ADDR_MASK GENMASK(5, 0) +#define REG_ADDR_EXT BIT(6) +#define REG_ADDR_AI BIT(7) #define PCA957X_IN 0x00 #define PCA957X_INVRT 0x01 @@ -55,17 +56,17 @@ #define PCAL6524_OUT_INDCONF 0x2c #define PCAL6524_DEBOUNCE 0x2d -#define PCA_GPIO_MASK 0x00FF +#define PCA_GPIO_MASK GENMASK(7, 0) -#define PCAL_GPIO_MASK 0x1f -#define PCAL_PINCTRL_MASK 0x60 +#define PCAL_GPIO_MASK GENMASK(4, 0) +#define PCAL_PINCTRL_MASK GENMASK(6, 5) -#define PCA_INT 0x0100 -#define PCA_PCAL 0x0200 +#define PCA_INT BIT(8) +#define PCA_PCAL BIT(9) #define PCA_LATCH_INT (PCA_PCAL | PCA_INT) -#define PCA953X_TYPE 0x1000 -#define PCA957X_TYPE 0x2000 -#define PCA_TYPE_MASK 0xF000 +#define PCA953X_TYPE BIT(12) +#define PCA957X_TYPE BIT(13) +#define PCA_TYPE_MASK GENMASK(15, 12) #define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK) @@ -565,7 +566,7 @@ static void pca953x_irq_mask(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); - chip->irq_mask[d->hwirq / BANK_SZ] &= ~(1 << (d->hwirq % BANK_SZ)); + chip->irq_mask[d->hwirq / BANK_SZ] &= ~BIT(d->hwirq % BANK_SZ); } static void pca953x_irq_unmask(struct irq_data *d) @@ -573,7 +574,7 @@ static void pca953x_irq_unmask(struct irq_data *d) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); - chip->irq_mask[d->hwirq / BANK_SZ] |= 1 << (d->hwirq % BANK_SZ); + chip->irq_mask[d->hwirq / BANK_SZ] |= BIT(d->hwirq % BANK_SZ); } static int pca953x_irq_set_wake(struct irq_data *d, unsigned int on) @@ -604,10 +605,9 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) u8 new_irqs; int level, i; u8 invert_irq_mask[MAX_BANK]; - int reg_direction[MAX_BANK]; + u8 reg_direction[MAX_BANK]; - regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction, - NBANK(chip)); + pca953x_read_regs(chip, chip->regs->direction, reg_direction); if (chip->driver_data & PCA_PCAL) { /* Enable latch on interrupt-enabled inputs */ @@ -641,7 +641,7 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); int bank_nb = d->hwirq / BANK_SZ; - u8 mask = 1 << (d->hwirq % BANK_SZ); + u8 mask = BIT(d->hwirq % BANK_SZ); if (!(type & IRQ_TYPE_EDGE_BOTH)) { dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", @@ -666,7 +666,7 @@ static void pca953x_irq_shutdown(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct pca953x_chip *chip = gpiochip_get_data(gc); - u8 mask = 1 << (d->hwirq % BANK_SZ); + u8 mask = BIT(d->hwirq % BANK_SZ); chip->irq_trig_raise[d->hwirq / BANK_SZ] &= ~mask; chip->irq_trig_fall[d->hwirq / BANK_SZ] &= ~mask; @@ -679,7 +679,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) bool pending_seen = false; bool trigger_seen = false; u8 trigger[MAX_BANK]; - int reg_direction[MAX_BANK]; + u8 reg_direction[MAX_BANK]; int ret, i; if (chip->driver_data & PCA_PCAL) { @@ -710,8 +710,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) return false; /* Remove output pins from the equation */ - regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction, - NBANK(chip)); + pca953x_read_regs(chip, chip->regs->direction, reg_direction); for (i = 0; i < NBANK(chip); i++) cur_stat[i] &= reg_direction[i]; @@ -768,7 +767,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, { struct i2c_client *client = chip->client; struct irq_chip *irq_chip = &chip->irq_chip; - int reg_direction[MAX_BANK]; + u8 reg_direction[MAX_BANK]; int ret, i; if (!client->irq) @@ -789,8 +788,7 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, * interrupt. We have to rely on the previous read for * this purpose. */ - regmap_bulk_read(chip->regmap, chip->regs->direction, reg_direction, - NBANK(chip)); + pca953x_read_regs(chip, chip->regs->direction, reg_direction); for (i = 0; i < NBANK(chip); i++) chip->irq_stat[i] &= reg_direction[i]; mutex_init(&chip->irq_lock); @@ -849,12 +847,12 @@ static int device_pca95xx_init(struct pca953x_chip *chip, u32 invert) ret = regcache_sync_region(chip->regmap, chip->regs->output, chip->regs->output + NBANK(chip)); - if (ret != 0) + if (ret) goto out; ret = regcache_sync_region(chip->regmap, chip->regs->direction, chip->regs->direction + NBANK(chip)); - if (ret != 0) + if (ret) goto out; /* set platform specific polarity inversion */ @@ -949,19 +947,15 @@ static int pca953x_probe(struct i2c_client *client, if (i2c_id) { chip->driver_data = i2c_id->driver_data; } else { - const struct acpi_device_id *acpi_id; - struct device *dev = &client->dev; - - chip->driver_data = (uintptr_t)of_device_get_match_data(dev); - if (!chip->driver_data) { - acpi_id = acpi_match_device(pca953x_acpi_ids, dev); - if (!acpi_id) { - ret = -ENODEV; - goto err_exit; - } - - chip->driver_data = acpi_id->driver_data; + const void *match; + + match = device_get_match_data(&client->dev); + if (!match) { + ret = -ENODEV; + goto err_exit; } + + chip->driver_data = (uintptr_t)match; } i2c_set_clientdata(client, chip); @@ -1041,8 +1035,7 @@ static int pca953x_remove(struct i2c_client *client) ret = pdata->teardown(client, chip->gpio_chip.base, chip->gpio_chip.ngpio, pdata->context); if (ret < 0) - dev_err(&client->dev, "%s failed, %d\n", - "teardown", ret); + dev_err(&client->dev, "teardown failed, %d\n", ret); } else { ret = 0; } @@ -1064,14 +1057,14 @@ static int pca953x_regcache_sync(struct device *dev) */ ret = regcache_sync_region(chip->regmap, chip->regs->direction, chip->regs->direction + NBANK(chip)); - if (ret != 0) { + if (ret) { dev_err(dev, "Failed to sync GPIO dir registers: %d\n", ret); return ret; } ret = regcache_sync_region(chip->regmap, chip->regs->output, chip->regs->output + NBANK(chip)); - if (ret != 0) { + if (ret) { dev_err(dev, "Failed to sync GPIO out registers: %d\n", ret); return ret; } @@ -1080,7 +1073,7 @@ static int pca953x_regcache_sync(struct device *dev) if (chip->driver_data & PCA_PCAL) { ret = regcache_sync_region(chip->regmap, PCAL953X_IN_LATCH, PCAL953X_IN_LATCH + NBANK(chip)); - if (ret != 0) { + if (ret) { dev_err(dev, "Failed to sync INT latch registers: %d\n", ret); return ret; @@ -1088,7 +1081,7 @@ static int pca953x_regcache_sync(struct device *dev) ret = regcache_sync_region(chip->regmap, PCAL953X_INT_MASK, PCAL953X_INT_MASK + NBANK(chip)); - if (ret != 0) { + if (ret) { dev_err(dev, "Failed to sync INT mask registers: %d\n", ret); return ret; @@ -1120,7 +1113,7 @@ static int pca953x_resume(struct device *dev) if (!atomic_read(&chip->wakeup_path)) { ret = regulator_enable(chip->regulator); - if (ret != 0) { + if (ret) { dev_err(dev, "Failed to enable regulator: %d\n", ret); return 0; } @@ -1133,7 +1126,7 @@ static int pca953x_resume(struct device *dev) return ret; ret = regcache_sync(chip->regmap); - if (ret != 0) { + if (ret) { dev_err(dev, "Failed to restore register map: %d\n", ret); return ret; } diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 1d99293096f2..3f3d9a94b709 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -409,8 +409,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, static int __maybe_unused pch_gpio_suspend(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct pch_gpio *chip = pci_get_drvdata(pdev); + struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; spin_lock_irqsave(&chip->spinlock, flags); @@ -422,8 +421,7 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) static int __maybe_unused pch_gpio_resume(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct pch_gpio *chip = pci_get_drvdata(pdev); + struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; spin_lock_irqsave(&chip->spinlock, flags); diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 24228cf79afc..05000cace9b2 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -305,10 +305,8 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) mutex_init(&pmic_eic->buslock); pmic_eic->irq = platform_get_irq(pdev, 0); - if (pmic_eic->irq < 0) { - dev_err(&pdev->dev, "Failed to get PMIC EIC interrupt.\n"); + if (pmic_eic->irq < 0) return pmic_eic->irq; - } pmic_eic->map = dev_get_regmap(pdev->dev.parent, NULL); if (!pmic_eic->map) diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c index f5c8b3a351d5..d7314d39ab65 100644 --- a/drivers/gpio/gpio-sprd.c +++ b/drivers/gpio/gpio-sprd.c @@ -226,10 +226,8 @@ static int sprd_gpio_probe(struct platform_device *pdev) return -ENOMEM; sprd_gpio->irq = platform_get_irq(pdev, 0); - if (sprd_gpio->irq < 0) { - dev_err(&pdev->dev, "Failed to get GPIO interrupt.\n"); + if (sprd_gpio->irq < 0) return sprd_gpio->irq; - } sprd_gpio->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sprd_gpio->base)) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dbf9cbe36b2b..994d542daf53 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -429,6 +429,23 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) return IRQ_HANDLED; } +static void stmpe_init_irq_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); + int i; + + if (!stmpe_gpio->norequest_mask) + return; + + /* Forbid unused lines to be mapped as IRQs */ + for (i = 0; i < sizeof(u32); i++) { + if (stmpe_gpio->norequest_mask & BIT(i)) + clear_bit(i, valid_mask); + } +} + static int stmpe_gpio_probe(struct platform_device *pdev) { struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); @@ -454,14 +471,21 @@ static int stmpe_gpio_probe(struct platform_device *pdev) stmpe_gpio->chip.parent = &pdev->dev; stmpe_gpio->chip.of_node = np; stmpe_gpio->chip.base = -1; + /* + * REVISIT: this makes sure the valid mask gets allocated and + * filled in when adding the gpio_chip, but the rest of the + * gpio_irqchip is still filled in using the old method + * in gpiochip_irqchip_add_nested() so clean this up once we + * get the gpio_irqchip to initialize while adding the + * gpio_chip also for threaded irqchips. + */ + stmpe_gpio->chip.irq.init_valid_mask = stmpe_init_irq_valid_mask; if (IS_ENABLED(CONFIG_DEBUG_FS)) stmpe_gpio->chip.dbg_show = stmpe_dbg_show; of_property_read_u32(np, "st,norequest-mask", &stmpe_gpio->norequest_mask); - if (stmpe_gpio->norequest_mask) - stmpe_gpio->chip.irq.need_valid_mask = true; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -487,14 +511,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to get irq: %d\n", ret); goto out_disable; } - if (stmpe_gpio->norequest_mask) { - int i; - - /* Forbid unused lines to be mapped as IRQs */ - for (i = 0; i < sizeof(u32); i++) - if (stmpe_gpio->norequest_mask & BIT(i)) - clear_bit(i, stmpe_gpio->chip.irq.valid_mask); - } ret = gpiochip_irqchip_add_nested(&stmpe_gpio->chip, &stmpe_gpio_irq_chip, 0, diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index bd1f3f775ce9..5e375186f90e 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -171,10 +171,8 @@ static int tb10x_gpio_probe(struct platform_device *pdev) struct irq_chip_generic *gc; ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "No interrupt specified.\n"); + if (ret < 0) return ret; - } tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq; tb10x_gpio->irq = ret; diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 0f59161a4701..8a01d3694b28 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -624,10 +624,8 @@ static int tegra_gpio_probe(struct platform_device *pdev) for (i = 0; i < tgi->bank_count; i++) { ret = platform_get_irq(pdev, i); - if (ret < 0) { - dev_err(&pdev->dev, "Missing IRQ resource: %d\n", ret); + if (ret < 0) return ret; - } bank = &tgi->bank_info[i]; bank->bank = i; diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index 715371b5102a..ddad5c7ea617 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -53,7 +53,6 @@ struct thunderx_line { struct thunderx_gpio { struct gpio_chip chip; u8 __iomem *register_base; - struct irq_domain *irqd; struct msix_entry *msix_entries; /* per line MSI-X */ struct thunderx_line *line_entries; /* per line irq info */ raw_spinlock_t lock; @@ -283,54 +282,60 @@ static void thunderx_gpio_set_multiple(struct gpio_chip *chip, } } -static void thunderx_gpio_irq_ack(struct irq_data *data) +static void thunderx_gpio_irq_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask(struct irq_data *data) +static void thunderx_gpio_irq_mask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_mask_ack(struct irq_data *data) +static void thunderx_gpio_irq_mask_ack(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static void thunderx_gpio_irq_unmask(struct irq_data *data) +static void thunderx_gpio_irq_unmask(struct irq_data *d) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); writeq(GPIO_INTR_ENA_W1S, - txline->txgpio->register_base + intr_reg(txline->line)); + txgpio->register_base + intr_reg(irqd_to_hwirq(d))); } -static int thunderx_gpio_irq_set_type(struct irq_data *data, +static int thunderx_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) { - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); + struct thunderx_line *txline = + &txgpio->line_entries[irqd_to_hwirq(d)]; u64 bit_cfg; - irqd_set_trigger_type(data, flow_type); + irqd_set_trigger_type(d, flow_type); bit_cfg = txline->fil_bits | GPIO_BIT_CFG_INT_EN; if (flow_type & IRQ_TYPE_EDGE_BOTH) { - irq_set_handler_locked(data, handle_fasteoi_ack_irq); + irq_set_handler_locked(d, handle_fasteoi_ack_irq); bit_cfg |= GPIO_BIT_CFG_INT_TYPE; } else { - irq_set_handler_locked(data, handle_fasteoi_mask_irq); + irq_set_handler_locked(d, handle_fasteoi_mask_irq); } raw_spin_lock(&txgpio->lock); @@ -359,33 +364,6 @@ static void thunderx_gpio_irq_disable(struct irq_data *data) irq_chip_disable_parent(data); } -static int thunderx_gpio_irq_request_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - int r; - - r = gpiochip_lock_as_irq(&txgpio->chip, txline->line); - if (r) - return r; - - r = irq_chip_request_resources_parent(data); - if (r) - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); - - return r; -} - -static void thunderx_gpio_irq_release_resources(struct irq_data *data) -{ - struct thunderx_line *txline = irq_data_get_irq_chip_data(data); - struct thunderx_gpio *txgpio = txline->txgpio; - - irq_chip_release_resources_parent(data); - - gpiochip_unlock_as_irq(&txgpio->chip, txline->line); -} - /* * Interrupts are chained from underlying MSI-X vectors. We have * these irq_chip functions to be able to handle level triggering @@ -402,48 +380,22 @@ static struct irq_chip thunderx_gpio_irq_chip = { .irq_unmask = thunderx_gpio_irq_unmask, .irq_eoi = irq_chip_eoi_parent, .irq_set_affinity = irq_chip_set_affinity_parent, - .irq_request_resources = thunderx_gpio_irq_request_resources, - .irq_release_resources = thunderx_gpio_irq_release_resources, .irq_set_type = thunderx_gpio_irq_set_type, .flags = IRQCHIP_SET_TYPE_MASKED }; -static int thunderx_gpio_irq_translate(struct irq_domain *d, - struct irq_fwspec *fwspec, - irq_hw_number_t *hwirq, - unsigned int *type) +static int thunderx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) { - struct thunderx_gpio *txgpio = d->host_data; - - if (WARN_ON(fwspec->param_count < 2)) - return -EINVAL; - if (fwspec->param[0] >= txgpio->chip.ngpio) - return -EINVAL; - *hwirq = fwspec->param[0]; - *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; - return 0; -} - -static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq, - unsigned int nr_irqs, void *arg) -{ - struct thunderx_line *txline = arg; + struct thunderx_gpio *txgpio = gpiochip_get_data(gc); - return irq_domain_set_hwirq_and_chip(d, virq, txline->line, - &thunderx_gpio_irq_chip, txline); -} - -static const struct irq_domain_ops thunderx_gpio_irqd_ops = { - .alloc = thunderx_gpio_irq_alloc, - .translate = thunderx_gpio_irq_translate -}; - -static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) -{ - struct thunderx_gpio *txgpio = gpiochip_get_data(chip); - - return irq_find_mapping(txgpio->irqd, offset); + *parent = txgpio->base_msi + (2 * child); + *parent_type = IRQ_TYPE_LEVEL_HIGH; + return 0; } static int thunderx_gpio_probe(struct pci_dev *pdev, @@ -453,6 +405,7 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, struct device *dev = &pdev->dev; struct thunderx_gpio *txgpio; struct gpio_chip *chip; + struct gpio_irq_chip *girq; int ngpio, i; int err = 0; @@ -497,8 +450,8 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, } txgpio->msix_entries = devm_kcalloc(dev, - ngpio, sizeof(struct msix_entry), - GFP_KERNEL); + ngpio, sizeof(struct msix_entry), + GFP_KERNEL); if (!txgpio->msix_entries) { err = -ENOMEM; goto out; @@ -539,27 +492,6 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, if (err < 0) goto out; - /* - * Push GPIO specific irqdomain on hierarchy created as a side - * effect of the pci_enable_msix() - */ - txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, - 0, 0, of_node_to_fwnode(dev->of_node), - &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) { - err = -ENOMEM; - goto out; - } - - /* Push on irq_data and the domain for each line. */ - for (i = 0; i < ngpio; i++) { - err = irq_domain_push_irq(txgpio->irqd, - txgpio->msix_entries[i].vector, - &txgpio->line_entries[i]); - if (err < 0) - dev_err(dev, "irq_domain_push_irq: %d\n", err); - } - chip->label = KBUILD_MODNAME; chip->parent = dev; chip->owner = THIS_MODULE; @@ -574,11 +506,28 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, chip->set = thunderx_gpio_set; chip->set_multiple = thunderx_gpio_set_multiple; chip->set_config = thunderx_gpio_set_config; - chip->to_irq = thunderx_gpio_to_irq; + girq = &chip->irq; + girq->chip = &thunderx_gpio_irq_chip; + girq->fwnode = of_node_to_fwnode(dev->of_node); + girq->parent_domain = + irq_get_irq_data(txgpio->msix_entries[0].vector)->domain; + girq->child_to_parent_hwirq = thunderx_gpio_child_to_parent_hwirq; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + err = devm_gpiochip_add_data(dev, chip, txgpio); if (err) goto out; + /* Push on irq_data and the domain for each line. */ + for (i = 0; i < ngpio; i++) { + err = irq_domain_push_irq(chip->irq.domain, + txgpio->msix_entries[i].vector, + chip); + if (err < 0) + dev_err(dev, "irq_domain_push_irq: %d\n", err); + } + dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", ngpio, chip->base); return 0; @@ -593,10 +542,10 @@ static void thunderx_gpio_remove(struct pci_dev *pdev) struct thunderx_gpio *txgpio = pci_get_drvdata(pdev); for (i = 0; i < txgpio->chip.ngpio; i++) - irq_domain_pop_irq(txgpio->irqd, + irq_domain_pop_irq(txgpio->chip.irq.domain, txgpio->msix_entries[i].vector); - irq_domain_remove(txgpio->irqd); + irq_domain_remove(txgpio->chip.irq.domain); pci_set_drvdata(pdev, NULL); } diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index d5880db7f9d4..a3109bcaa0ac 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -214,11 +214,23 @@ static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = { tqmx86_gpio_runtime_resume, NULL) }; +static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + /* Only GPIOs 4-7 are valid for interrupts. Clear the others */ + clear_bit(0, valid_mask); + clear_bit(1, valid_mask); + clear_bit(2, valid_mask); + clear_bit(3, valid_mask); +} + static int tqmx86_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct tqmx86_gpio_data *gpio; struct gpio_chip *chip; + struct gpio_irq_chip *girq; void __iomem *io_base; struct resource *res; int ret, irq; @@ -259,17 +271,10 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) chip->get = tqmx86_gpio_get; chip->set = tqmx86_gpio_set; chip->ngpio = TQMX86_NGPIO; - chip->irq.need_valid_mask = true; chip->parent = pdev->dev.parent; pm_runtime_enable(&pdev->dev); - ret = devm_gpiochip_add_data(dev, chip, gpio); - if (ret) { - dev_err(dev, "Could not register GPIO chip\n"); - goto out_pm_dis; - } - if (irq) { struct irq_chip *irq_chip = &gpio->irq_chip; u8 irq_status; @@ -287,23 +292,28 @@ static int tqmx86_gpio_probe(struct platform_device *pdev) irq_status = tqmx86_gpio_read(gpio, TQMX86_GPIIS); tqmx86_gpio_write(gpio, irq_status, TQMX86_GPIIS); - ret = gpiochip_irqchip_add(chip, irq_chip, - 0, handle_simple_irq, - IRQ_TYPE_EDGE_BOTH); - if (ret) { - dev_err(dev, "Could not add irq chip\n"); + girq = &chip->irq; + girq->chip = irq_chip; + girq->parent_handler = tqmx86_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) { + ret = -ENOMEM; goto out_pm_dis; } - - gpiochip_set_chained_irqchip(chip, irq_chip, - irq, tqmx86_gpio_irq_handler); + girq->parents[0] = irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->init_valid_mask = tqmx86_init_irq_valid_mask; } - /* Only GPIOs 4-7 are valid for interrupts. Clear the others */ - clear_bit(0, chip->irq.valid_mask); - clear_bit(1, chip->irq.valid_mask); - clear_bit(2, chip->irq.valid_mask); - clear_bit(3, chip->irq.valid_mask); + ret = devm_gpiochip_add_data(dev, chip, gpio); + if (ret) { + dev_err(dev, "Could not register GPIO chip\n"); + goto out_pm_dis; + } dev_info(dev, "GPIO functionality initialized with %d pins\n", chip->ngpio); diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 7ba668db171b..58776f2d69ff 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -243,6 +243,7 @@ static int vf610_gpio_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct vf610_gpio_port *port; struct gpio_chip *gc; + struct gpio_irq_chip *girq; struct irq_chip *ic; int i; int ret; @@ -318,10 +319,6 @@ static int vf610_gpio_probe(struct platform_device *pdev) ic->irq_set_type = vf610_gpio_irq_set_type; ic->irq_set_wake = vf610_gpio_irq_set_wake; - ret = devm_gpiochip_add_data(dev, gc, port); - if (ret < 0) - return ret; - /* Mask all GPIO interrupts */ for (i = 0; i < gc->ngpio; i++) vf610_gpio_writel(0, port->base + PORT_PCR(i)); @@ -329,15 +326,20 @@ static int vf610_gpio_probe(struct platform_device *pdev) /* Clear the interrupt status register for all GPIO's */ vf610_gpio_writel(~0, port->base + PORT_ISFR); - ret = gpiochip_irqchip_add(gc, ic, 0, handle_edge_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "failed to add irqchip\n"); - return ret; - } - gpiochip_set_chained_irqchip(gc, ic, port->irq, - vf610_gpio_irq_handler); + girq = &gc->irq; + girq->chip = ic; + girq->parent_handler = vf610_gpio_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = port->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; - return 0; + return devm_gpiochip_add_data(dev, gc, port); } static struct platform_driver vf610_gpio_driver = { diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c index 9b604f13e302..c301c1d56dd2 100644 --- a/drivers/gpio/gpio-viperboard.c +++ b/drivers/gpio/gpio-viperboard.c @@ -79,7 +79,7 @@ MODULE_PARM_DESC(gpioa_freq, /* ----- begin of gipo a chip -------------------------------------------- */ static int vprbrd_gpioa_get(struct gpio_chip *chip, - unsigned offset) + unsigned int offset) { int ret, answer, error = 0; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); @@ -129,7 +129,7 @@ static int vprbrd_gpioa_get(struct gpio_chip *chip, } static void vprbrd_gpioa_set(struct gpio_chip *chip, - unsigned offset, int value) + unsigned int offset, int value) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); @@ -170,7 +170,7 @@ static void vprbrd_gpioa_set(struct gpio_chip *chip, } static int vprbrd_gpioa_direction_input(struct gpio_chip *chip, - unsigned offset) + unsigned int offset) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); @@ -207,7 +207,7 @@ static int vprbrd_gpioa_direction_input(struct gpio_chip *chip, } static int vprbrd_gpioa_direction_output(struct gpio_chip *chip, - unsigned offset, int value) + unsigned int offset, int value) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); @@ -251,8 +251,8 @@ static int vprbrd_gpioa_direction_output(struct gpio_chip *chip, /* ----- begin of gipo b chip -------------------------------------------- */ -static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset, - unsigned dir) +static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned int offset, + unsigned int dir) { struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; int ret; @@ -273,7 +273,7 @@ static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset, } static int vprbrd_gpiob_get(struct gpio_chip *chip, - unsigned offset) + unsigned int offset) { int ret; u16 val; @@ -305,7 +305,7 @@ static int vprbrd_gpiob_get(struct gpio_chip *chip, } static void vprbrd_gpiob_set(struct gpio_chip *chip, - unsigned offset, int value) + unsigned int offset, int value) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); @@ -338,7 +338,7 @@ static void vprbrd_gpiob_set(struct gpio_chip *chip, } static int vprbrd_gpiob_direction_input(struct gpio_chip *chip, - unsigned offset) + unsigned int offset) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); @@ -359,7 +359,7 @@ static int vprbrd_gpiob_direction_input(struct gpio_chip *chip, } static int vprbrd_gpiob_direction_output(struct gpio_chip *chip, - unsigned offset, int value) + unsigned int offset, int value) { int ret; struct vprbrd_gpio *gpio = gpiochip_get_data(chip); diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index 38c01912c7b2..25d86441666e 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -16,6 +16,7 @@ #include <linux/acpi.h> #include "gpiolib.h" +#include "gpiolib-acpi.h" /* Common property names */ #define XGENE_NIRQ_PROPERTY "apm,nr-irqs" diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index 54d3359444f3..d7b16bb9e4e4 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -290,6 +290,7 @@ MODULE_DEVICE_TABLE(of, xlp_gpio_of_ids); static int xlp_gpio_probe(struct platform_device *pdev) { struct gpio_chip *gc; + struct gpio_irq_chip *girq; struct xlp_gpio_priv *priv; void __iomem *gpio_base; int irq_base, irq, err; @@ -395,27 +396,27 @@ static int xlp_gpio_probe(struct platform_device *pdev) irq_base = 0; } + girq = &gc->irq; + girq->chip = &xlp_gpio_irq_chip; + girq->parent_handler = xlp_gpio_generic_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = irq; + girq->first = irq_base; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + err = gpiochip_add_data(gc, priv); if (err < 0) return err; - err = gpiochip_irqchip_add(gc, &xlp_gpio_irq_chip, irq_base, - handle_level_irq, IRQ_TYPE_NONE); - if (err) { - dev_err(&pdev->dev, "Could not connect irqchip to gpiochip!\n"); - goto out_gpio_remove; - } - - gpiochip_set_chained_irqchip(gc, &xlp_gpio_irq_chip, irq, - xlp_gpio_generic_handler); - dev_info(&pdev->dev, "registered %d GPIOs\n", gc->ngpio); return 0; - -out_gpio_remove: - gpiochip_remove(gc); - return err; } #ifdef CONFIG_ACPI diff --git a/drivers/gpio/gpio-zx.c b/drivers/gpio/gpio-zx.c index 8637adb6bc20..98cbaf0e415e 100644 --- a/drivers/gpio/gpio-zx.c +++ b/drivers/gpio/gpio-zx.c @@ -215,6 +215,7 @@ static int zx_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct zx_gpio *chip; + struct gpio_irq_chip *girq; int irq, id, ret; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); @@ -242,32 +243,30 @@ static int zx_gpio_probe(struct platform_device *pdev) chip->gc.parent = dev; chip->gc.owner = THIS_MODULE; - ret = gpiochip_add_data(&chip->gc, chip); - if (ret) - return ret; - /* * irq_chip support */ writew_relaxed(0xffff, chip->base + ZX_GPIO_IM); writew_relaxed(0, chip->base + ZX_GPIO_IE); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "invalid IRQ\n"); - gpiochip_remove(&chip->gc); - return -ENODEV; - } + if (irq < 0) + return irq; + girq = &chip->gc.irq; + girq->chip = &zx_irqchip; + girq->parent_handler = zx_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; - ret = gpiochip_irqchip_add(&chip->gc, &zx_irqchip, - 0, handle_simple_irq, - IRQ_TYPE_NONE); - if (ret) { - dev_err(dev, "could not add irqchip\n"); - gpiochip_remove(&chip->gc); + ret = gpiochip_add_data(&chip->gc, chip); + if (ret) return ret; - } - gpiochip_set_chained_irqchip(&chip->gc, &zx_irqchip, - irq, zx_irq_handler); platform_set_drvdata(pdev, chip); dev_info(dev, "ZX GPIO chip registered\n"); diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index f241b6c13dbe..cd475ff4bcad 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -830,6 +830,7 @@ static int zynq_gpio_probe(struct platform_device *pdev) int ret, bank_num; struct zynq_gpio *gpio; struct gpio_chip *chip; + struct gpio_irq_chip *girq; const struct of_device_id *match; gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); @@ -849,10 +850,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) return PTR_ERR(gpio->base_addr); gpio->irq = platform_get_irq(pdev, 0); - if (gpio->irq < 0) { - dev_err(&pdev->dev, "invalid IRQ\n"); + if (gpio->irq < 0) return gpio->irq; - } /* configure the gpio chip */ chip = &gpio->chip; @@ -887,34 +886,38 @@ static int zynq_gpio_probe(struct platform_device *pdev) if (ret < 0) goto err_pm_dis; - /* report a bug if gpio chip registration fails */ - ret = gpiochip_add_data(chip, gpio); - if (ret) { - dev_err(&pdev->dev, "Failed to add gpio chip\n"); - goto err_pm_put; - } - /* disable interrupts for all banks */ for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num)); - ret = gpiochip_irqchip_add(chip, &zynq_gpio_edge_irqchip, 0, - handle_level_irq, IRQ_TYPE_NONE); - if (ret) { - dev_err(&pdev->dev, "Failed to add irq chip\n"); - goto err_rm_gpiochip; + /* Set up the GPIO irqchip */ + girq = &chip->irq; + girq->chip = &zynq_gpio_edge_irqchip; + girq->parent_handler = zynq_gpio_irqhandler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) { + ret = -ENOMEM; + goto err_pm_put; } + girq->parents[0] = gpio->irq; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; - gpiochip_set_chained_irqchip(chip, &zynq_gpio_edge_irqchip, gpio->irq, - zynq_gpio_irqhandler); + /* report a bug if gpio chip registration fails */ + ret = gpiochip_add_data(chip, gpio); + if (ret) { + dev_err(&pdev->dev, "Failed to add gpio chip\n"); + goto err_pm_put; + } pm_runtime_put(&pdev->dev); return 0; -err_rm_gpiochip: - gpiochip_remove(chip); err_pm_put: pm_runtime_put(&pdev->dev); err_pm_dis: diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 39f2f9035c11..609ed16ae933 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -7,6 +7,7 @@ * Mika Westerberg <mika.westerberg@linux.intel.com> */ +#include <linux/dmi.h> #include <linux/errno.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> @@ -18,6 +19,12 @@ #include <linux/pinctrl/pinctrl.h> #include "gpiolib.h" +#include "gpiolib-acpi.h" + +static int run_edge_events_on_boot = -1; +module_param(run_edge_events_on_boot, int, 0444); +MODULE_PARM_DESC(run_edge_events_on_boot, + "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); /** * struct acpi_gpio_event - ACPI GPIO event handler data @@ -170,10 +177,13 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, event->irq_requested = true; /* Make sure we trigger the initial state of edge-triggered IRQs */ - value = gpiod_get_raw_value_cansleep(event->desc); - if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || - ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) - event->handler(event->irq, event); + if (run_edge_events_on_boot && + (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { + value = gpiod_get_raw_value_cansleep(event->desc); + if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || + ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) + event->handler(event->irq, event); + } } static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) @@ -382,6 +392,13 @@ int acpi_dev_add_driver_gpios(struct acpi_device *adev, } EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios); +void acpi_dev_remove_driver_gpios(struct acpi_device *adev) +{ + if (adev) + adev->driver_gpios = NULL; +} +EXPORT_SYMBOL_GPL(acpi_dev_remove_driver_gpios); + static void devm_acpi_dev_release_driver_gpios(struct device *dev, void *res) { acpi_dev_remove_driver_gpios(ACPI_COMPANION(dev)); @@ -720,6 +737,16 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, return ret ? ERR_PTR(ret) : lookup.desc; } +static bool acpi_can_fallback_to_crs(struct acpi_device *adev, + const char *con_id) +{ + /* Never allow fallback if the device has properties */ + if (acpi_dev_has_props(adev) || adev->driver_gpios) + return false; + + return con_id == NULL; +} + struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, @@ -1256,15 +1283,6 @@ int acpi_gpio_count(struct device *dev, const char *con_id) return count ? count : -ENOENT; } -bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id) -{ - /* Never allow fallback if the device has properties */ - if (acpi_dev_has_props(adev) || adev->driver_gpios) - return false; - - return con_id == NULL; -} - /* Run deferred acpi_gpiochip_request_irqs() */ static int acpi_gpio_handle_deferred_request_irqs(void) { @@ -1283,3 +1301,28 @@ static int acpi_gpio_handle_deferred_request_irqs(void) } /* We must use _sync so that this runs after the first deferred_probe run */ late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); + +static const struct dmi_system_id run_edge_events_on_boot_blacklist[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), + DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), + } + }, + {} /* Terminating entry */ +}; + +static int acpi_gpio_setup_params(void) +{ + if (run_edge_events_on_boot < 0) { + if (dmi_check_system(run_edge_events_on_boot_blacklist)) + run_edge_events_on_boot = 0; + else + run_edge_events_on_boot = 1; + } + + return 0; +} + +/* Directly after dmi_setup() which runs as core_initcall() */ +postcore_initcall(acpi_gpio_setup_params); diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h new file mode 100644 index 000000000000..1c6d65cf0629 --- /dev/null +++ b/drivers/gpio/gpiolib-acpi.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ACPI helpers for GPIO API + * + * Copyright (C) 2012,2019 Intel Corporation + */ + +#ifndef GPIOLIB_ACPI_H +#define GPIOLIB_ACPI_H + +struct acpi_device; + +/** + * struct acpi_gpio_info - ACPI GPIO specific information + * @adev: reference to ACPI device which consumes GPIO resource + * @flags: GPIO initialization flags + * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo + * @pin_config: pin bias as provided by ACPI + * @polarity: interrupt polarity as provided by ACPI + * @triggering: triggering type as provided by ACPI + * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping + */ +struct acpi_gpio_info { + struct acpi_device *adev; + enum gpiod_flags flags; + bool gpioint; + int pin_config; + int polarity; + int triggering; + unsigned int quirks; +}; + +#ifdef CONFIG_ACPI +void acpi_gpiochip_add(struct gpio_chip *chip); +void acpi_gpiochip_remove(struct gpio_chip *chip); + +void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); +void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); + +int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, + struct acpi_gpio_info *info); +int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, + struct acpi_gpio_info *info); + +struct gpio_desc *acpi_find_gpio(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpiod_flags *dflags, + unsigned long *lookupflags); +struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, + const char *propname, int index, + struct acpi_gpio_info *info); + +int acpi_gpio_count(struct device *dev, const char *con_id); +#else +static inline void acpi_gpiochip_add(struct gpio_chip *chip) { } +static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { } + +static inline void +acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } + +static inline void +acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } + +static inline int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) +{ + return 0; +} +static inline int +acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, + struct acpi_gpio_info *info) +{ + return 0; +} + +static inline struct gpio_desc * +acpi_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, enum gpiod_flags *dflags, + unsigned long *lookupflags) +{ + return ERR_PTR(-ENOENT); +} +static inline struct gpio_desc * +acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, + int index, struct acpi_gpio_info *info) +{ + return ERR_PTR(-ENXIO); +} +static inline int acpi_gpio_count(struct device *dev, const char *con_id) +{ + return -ENODEV; +} +#endif + +#endif /* GPIOLIB_ACPI_H */ diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 0acc2cc6e868..98e3c20d9730 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -59,7 +59,7 @@ struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, { return devm_gpiod_get_index(dev, con_id, 0, flags); } -EXPORT_SYMBOL(devm_gpiod_get); +EXPORT_SYMBOL_GPL(devm_gpiod_get); /** * devm_gpiod_get_optional - Resource-managed gpiod_get_optional() @@ -77,7 +77,7 @@ struct gpio_desc *__must_check devm_gpiod_get_optional(struct device *dev, { return devm_gpiod_get_index_optional(dev, con_id, 0, flags); } -EXPORT_SYMBOL(devm_gpiod_get_optional); +EXPORT_SYMBOL_GPL(devm_gpiod_get_optional); /** * devm_gpiod_get_index - Resource-managed gpiod_get_index() @@ -127,7 +127,7 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, return desc; } -EXPORT_SYMBOL(devm_gpiod_get_index); +EXPORT_SYMBOL_GPL(devm_gpiod_get_index); /** * devm_gpiod_get_from_of_node() - obtain a GPIO from an OF node @@ -182,7 +182,7 @@ struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, return desc; } -EXPORT_SYMBOL(devm_gpiod_get_from_of_node); +EXPORT_SYMBOL_GPL(devm_gpiod_get_from_of_node); /** * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a @@ -239,7 +239,7 @@ struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev, return desc; } -EXPORT_SYMBOL(devm_fwnode_get_index_gpiod_from_child); +EXPORT_SYMBOL_GPL(devm_fwnode_get_index_gpiod_from_child); /** * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() @@ -268,7 +268,7 @@ struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, return desc; } -EXPORT_SYMBOL(devm_gpiod_get_index_optional); +EXPORT_SYMBOL_GPL(devm_gpiod_get_index_optional); /** * devm_gpiod_get_array - Resource-managed gpiod_get_array() @@ -303,7 +303,7 @@ struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev, return descs; } -EXPORT_SYMBOL(devm_gpiod_get_array); +EXPORT_SYMBOL_GPL(devm_gpiod_get_array); /** * devm_gpiod_get_array_optional - Resource-managed gpiod_get_array_optional() @@ -328,7 +328,7 @@ devm_gpiod_get_array_optional(struct device *dev, const char *con_id, return descs; } -EXPORT_SYMBOL(devm_gpiod_get_array_optional); +EXPORT_SYMBOL_GPL(devm_gpiod_get_array_optional); /** * devm_gpiod_put - Resource-managed gpiod_put() @@ -344,7 +344,7 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match, &desc)); } -EXPORT_SYMBOL(devm_gpiod_put); +EXPORT_SYMBOL_GPL(devm_gpiod_put); /** * devm_gpiod_unhinge - Remove resource management from a gpio descriptor @@ -374,7 +374,7 @@ void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc) /* Anything else we should warn about */ WARN_ON(ret); } -EXPORT_SYMBOL(devm_gpiod_unhinge); +EXPORT_SYMBOL_GPL(devm_gpiod_unhinge); /** * devm_gpiod_put_array - Resource-managed gpiod_put_array() @@ -390,7 +390,7 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) WARN_ON(devres_release(dev, devm_gpiod_release_array, devm_gpiod_match_array, &descs)); } -EXPORT_SYMBOL(devm_gpiod_put_array); +EXPORT_SYMBOL_GPL(devm_gpiod_put_array); @@ -444,7 +444,7 @@ int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) return 0; } -EXPORT_SYMBOL(devm_gpio_request); +EXPORT_SYMBOL_GPL(devm_gpio_request); /** * devm_gpio_request_one - request a single GPIO with initial setup @@ -474,7 +474,7 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, return 0; } -EXPORT_SYMBOL(devm_gpio_request_one); +EXPORT_SYMBOL_GPL(devm_gpio_request_one); /** * devm_gpio_free - free a GPIO @@ -492,4 +492,4 @@ void devm_gpio_free(struct device *dev, unsigned int gpio) WARN_ON(devres_release(dev, devm_gpio_release, devm_gpio_match, &gpio)); } -EXPORT_SYMBOL(devm_gpio_free); +EXPORT_SYMBOL_GPL(devm_gpio_free); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 567fb98c0892..1eea2c6c2e1d 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -21,6 +21,34 @@ #include <linux/gpio/machine.h> #include "gpiolib.h" +#include "gpiolib-of.h" + +/* + * This is used by external users of of_gpio_count() from <linux/of_gpio.h> + * + * FIXME: get rid of those external users by converting them to GPIO + * descriptors and let them all use gpiod_get_count() + */ +int of_gpio_get_count(struct device *dev, const char *con_id) +{ + int ret; + char propname[32]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id) + snprintf(propname, sizeof(propname), "%s-%s", + con_id, gpio_suffixes[i]); + else + snprintf(propname, sizeof(propname), "%s", + gpio_suffixes[i]); + + ret = of_gpio_named_count(dev->of_node, propname); + if (ret > 0) + break; + } + return ret ? ret : -ENOENT; +} static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data) { @@ -53,6 +81,23 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, return gpiochip_get_desc(chip, ret); } +/** + * of_gpio_need_valid_mask() - figure out if the OF GPIO driver needs + * to set the .valid_mask + * @dev: the device for the GPIO provider + * @return: true if the valid mask needs to be set + */ +bool of_gpio_need_valid_mask(const struct gpio_chip *gc) +{ + int size; + struct device_node *np = gc->of_node; + + size = of_property_count_u32_elems(np, "gpio-reserved-ranges"); + if (size > 0 && size % 2 == 0) + return true; + return false; +} + static void of_gpio_flags_quirks(struct device_node *np, const char *propname, enum of_gpio_flags *flags, @@ -178,7 +223,7 @@ static void of_gpio_flags_quirks(struct device_node *np, * value on the error condition. If @flags is not NULL the function also fills * in flags for the GPIO. */ -struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, +static struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, const char *propname, int index, enum of_gpio_flags *flags) { struct of_phandle_args gpiospec; @@ -229,7 +274,76 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name, else return desc_to_gpio(desc); } -EXPORT_SYMBOL(of_get_named_gpio_flags); +EXPORT_SYMBOL_GPL(of_get_named_gpio_flags); + +/** + * gpiod_get_from_of_node() - obtain a GPIO from an OF node + * @node: handle of the OF node + * @propname: name of the DT property representing the GPIO + * @index: index of the GPIO to obtain for the consumer + * @dflags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * Returns: + * On successful request the GPIO pin is configured in accordance with + * provided @dflags. + * + * In case of error an ERR_PTR() is returned. + */ +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT; + struct gpio_desc *desc; + enum of_gpio_flags flags; + bool active_low = false; + bool single_ended = false; + bool open_drain = false; + bool transitory = false; + int ret; + + desc = of_get_named_gpiod_flags(node, propname, + index, &flags); + + if (!desc || IS_ERR(desc)) { + return desc; + } + + active_low = flags & OF_GPIO_ACTIVE_LOW; + single_ended = flags & OF_GPIO_SINGLE_ENDED; + open_drain = flags & OF_GPIO_OPEN_DRAIN; + transitory = flags & OF_GPIO_TRANSITORY; + + ret = gpiod_request(desc, label); + if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) + return desc; + if (ret) + return ERR_PTR(ret); + + if (active_low) + lflags |= GPIO_ACTIVE_LOW; + + if (single_ended) { + if (open_drain) + lflags |= GPIO_OPEN_DRAIN; + else + lflags |= GPIO_OPEN_SOURCE; + } + + if (transitory) + lflags |= GPIO_TRANSITORY; + + ret = gpiod_configure_flags(desc, propname, lflags, dflags); + if (ret < 0) { + gpiod_put(desc); + return ERR_PTR(ret); + } + + return desc; +} +EXPORT_SYMBOL_GPL(gpiod_get_from_of_node); /* * The SPI GPIO bindings happened before we managed to establish that GPIO @@ -324,6 +438,19 @@ static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char * return desc; } +static struct gpio_desc *of_find_arizona_gpio(struct device *dev, + const char *con_id, + enum of_gpio_flags *of_flags) +{ + if (!IS_ENABLED(CONFIG_MFD_ARIZONA)) + return ERR_PTR(-ENOENT); + + if (!con_id || strcmp(con_id, "wlf,reset")) + return ERR_PTR(-ENOENT); + + return of_get_named_gpiod_flags(dev->of_node, con_id, 0, of_flags); +} + struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, unsigned long *flags) { @@ -343,36 +470,30 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, &of_flags); - /* - * -EPROBE_DEFER in our case means that we found a - * valid GPIO property, but no controller has been - * registered so far. - * - * This means we don't need to look any further for - * alternate name conventions, and we should really - * preserve the return code for our user to be able to - * retry probing later. - */ - if (IS_ERR(desc) && PTR_ERR(desc) == -EPROBE_DEFER) - return desc; - if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) + if (!IS_ERR(desc) || PTR_ERR(desc) != -ENOENT) break; } - /* Special handling for SPI GPIOs if used */ - if (IS_ERR(desc)) + if (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) { + /* Special handling for SPI GPIOs if used */ desc = of_find_spi_gpio(dev, con_id, &of_flags); - if (IS_ERR(desc)) { + } + + if (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) { /* This quirk looks up flags and all */ desc = of_find_spi_cs_gpio(dev, con_id, idx, flags); if (!IS_ERR(desc)) return desc; } - /* Special handling for regulator GPIOs if used */ - if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) + if (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) { + /* Special handling for regulator GPIOs if used */ desc = of_find_regulator_gpio(dev, con_id, &of_flags); + } + + if (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) + desc = of_find_arizona_gpio(dev, con_id, &of_flags); if (IS_ERR(desc)) return desc; @@ -523,8 +644,9 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip) * GPIO chips. This function performs only one sanity check: whether GPIO * is less than ngpios (that is specified in the gpio_chip). */ -int of_gpio_simple_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, u32 *flags) +static int of_gpio_simple_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) { /* * We're discouraging gpio_cells < 2, since that way you'll have to @@ -548,7 +670,6 @@ int of_gpio_simple_xlate(struct gpio_chip *gc, return gpiospec->args[0]; } -EXPORT_SYMBOL(of_gpio_simple_xlate); /** * of_mm_gpiochip_add_data - Add memory mapped GPIO chip (bank) @@ -605,7 +726,7 @@ err0: pr_err("%pOF: GPIO chip registration failed with status %d\n", np, ret); return ret; } -EXPORT_SYMBOL(of_mm_gpiochip_add_data); +EXPORT_SYMBOL_GPL(of_mm_gpiochip_add_data); /** * of_mm_gpiochip_remove - Remove memory mapped GPIO chip (bank) @@ -622,7 +743,7 @@ void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc) iounmap(mm_gc->regs); kfree(gc->label); } -EXPORT_SYMBOL(of_mm_gpiochip_remove); +EXPORT_SYMBOL_GPL(of_mm_gpiochip_remove); static void of_gpiochip_init_valid_mask(struct gpio_chip *chip) { @@ -734,7 +855,7 @@ static int of_gpiochip_add_pin_range(struct gpio_chip *chip) { return 0; } int of_gpiochip_add(struct gpio_chip *chip) { - int status; + int ret; if (!chip->of_node) return 0; @@ -749,9 +870,9 @@ int of_gpiochip_add(struct gpio_chip *chip) of_gpiochip_init_valid_mask(chip); - status = of_gpiochip_add_pin_range(chip); - if (status) - return status; + ret = of_gpiochip_add_pin_range(chip); + if (ret) + return ret; /* If the chip defines names itself, these take precedence */ if (!chip->names) @@ -760,13 +881,13 @@ int of_gpiochip_add(struct gpio_chip *chip) of_node_get(chip->of_node); - status = of_gpiochip_scan_gpios(chip); - if (status) { + ret = of_gpiochip_scan_gpios(chip); + if (ret) { of_node_put(chip->of_node); gpiochip_remove_pin_ranges(chip); } - return status; + return ret; } void of_gpiochip_remove(struct gpio_chip *chip) diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h new file mode 100644 index 000000000000..9768831b1fe2 --- /dev/null +++ b/drivers/gpio/gpiolib-of.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIOLIB_OF_H +#define GPIOLIB_OF_H + +struct gpio_chip; +enum of_gpio_flags; + +#ifdef CONFIG_OF_GPIO +struct gpio_desc *of_find_gpio(struct device *dev, + const char *con_id, + unsigned int idx, + unsigned long *lookupflags); +int of_gpiochip_add(struct gpio_chip *gc); +void of_gpiochip_remove(struct gpio_chip *gc); +int of_gpio_get_count(struct device *dev, const char *con_id); +bool of_gpio_need_valid_mask(const struct gpio_chip *gc); +#else +static inline struct gpio_desc *of_find_gpio(struct device *dev, + const char *con_id, + unsigned int idx, + unsigned long *lookupflags) +{ + return ERR_PTR(-ENOENT); +} +static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } +static inline void of_gpiochip_remove(struct gpio_chip *gc) { } +static inline int of_gpio_get_count(struct device *dev, const char *con_id) +{ + return 0; +} +static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc) +{ + return false; +} +#endif /* CONFIG_OF_GPIO */ + +#endif /* GPIOLIB_OF_H */ diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f497003f119c..822988818efc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -11,7 +11,6 @@ #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/gpio.h> -#include <linux/of_gpio.h> #include <linux/idr.h> #include <linux/slab.h> #include <linux/acpi.h> @@ -30,6 +29,8 @@ #include <uapi/linux/gpio.h> #include "gpiolib.h" +#include "gpiolib-of.h" +#include "gpiolib-acpi.h" #define CREATE_TRACE_POINTS #include <trace/events/gpio.h> @@ -213,7 +214,7 @@ int gpiod_get_direction(struct gpio_desc *desc) { struct gpio_chip *chip; unsigned offset; - int status; + int ret; chip = gpiod_to_chip(desc); offset = gpio_chip_hwgpio(desc); @@ -221,17 +222,17 @@ int gpiod_get_direction(struct gpio_desc *desc) if (!chip->get_direction) return -ENOTSUPP; - status = chip->get_direction(chip, offset); - if (status > 0) { + ret = chip->get_direction(chip, offset); + if (ret > 0) { /* GPIOF_DIR_IN, or other positive */ - status = 1; + ret = 1; clear_bit(FLAG_IS_OUT, &desc->flags); } - if (status == 0) { + if (ret == 0) { /* GPIOF_DIR_OUT */ set_bit(FLAG_IS_OUT, &desc->flags); } - return status; + return ret; } EXPORT_SYMBOL_GPL(gpiod_get_direction); @@ -350,7 +351,7 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip) { unsigned long *p; - p = kmalloc_array(BITS_TO_LONGS(chip->ngpio), sizeof(*p), GFP_KERNEL); + p = bitmap_alloc(chip->ngpio, GFP_KERNEL); if (!p) return NULL; @@ -360,38 +361,31 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip) return p; } -static int gpiochip_alloc_valid_mask(struct gpio_chip *gpiochip) +static int gpiochip_alloc_valid_mask(struct gpio_chip *gc) { -#ifdef CONFIG_OF_GPIO - int size; - struct device_node *np = gpiochip->of_node; - - size = of_property_count_u32_elems(np, "gpio-reserved-ranges"); - if (size > 0 && size % 2 == 0) - gpiochip->need_valid_mask = true; -#endif - - if (!gpiochip->need_valid_mask) + if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask)) return 0; - gpiochip->valid_mask = gpiochip_allocate_mask(gpiochip); - if (!gpiochip->valid_mask) + gc->valid_mask = gpiochip_allocate_mask(gc); + if (!gc->valid_mask) return -ENOMEM; return 0; } -static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip) +static int gpiochip_init_valid_mask(struct gpio_chip *gc) { - if (gpiochip->init_valid_mask) - return gpiochip->init_valid_mask(gpiochip); + if (gc->init_valid_mask) + return gc->init_valid_mask(gc, + gc->valid_mask, + gc->ngpio); return 0; } static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip) { - kfree(gpiochip->valid_mask); + bitmap_free(gpiochip->valid_mask); gpiochip->valid_mask = NULL; } @@ -536,6 +530,14 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) return -EINVAL; /* + * Do not allow both INPUT & OUTPUT flags to be set as they are + * contradictory. + */ + if ((lflags & GPIOHANDLE_REQUEST_INPUT) && + (lflags & GPIOHANDLE_REQUEST_OUTPUT)) + return -EINVAL; + + /* * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If * the hardware actually supports enabling both at the same time the * electrical result would be disastrous. @@ -857,7 +859,7 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) } ret = kfifo_put(&le->events, ge); - if (ret != 0) + if (ret) wake_up_poll(&le->wait, EPOLLIN); return IRQ_HANDLED; @@ -926,7 +928,9 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) } /* This is just wrong: we don't look for events on output lines */ - if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || + (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) { ret = -EINVAL; goto out_free_label; } @@ -940,10 +944,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &desc->flags); ret = gpiod_direction_input(desc); if (ret) @@ -1091,9 +1091,11 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - lineinfo.flags |= GPIOLINE_FLAG_OPEN_DRAIN; + lineinfo.flags |= (GPIOLINE_FLAG_OPEN_DRAIN | + GPIOLINE_FLAG_IS_OUT); if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - lineinfo.flags |= GPIOLINE_FLAG_OPEN_SOURCE; + lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE | + GPIOLINE_FLAG_IS_OUT); if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; @@ -1174,21 +1176,21 @@ static void gpiodevice_release(struct device *dev) static int gpiochip_setup_dev(struct gpio_device *gdev) { - int status; + int ret; cdev_init(&gdev->chrdev, &gpio_fileops); gdev->chrdev.owner = THIS_MODULE; gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); - status = cdev_device_add(&gdev->chrdev, &gdev->dev); - if (status) - return status; + ret = cdev_device_add(&gdev->chrdev, &gdev->dev); + if (ret) + return ret; chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", MAJOR(gpio_devt), gdev->id); - status = gpiochip_sysfs_register(gdev); - if (status) + ret = gpiochip_sysfs_register(gdev); + if (ret) goto err_remove_device; /* From this point, the .release() function cleans up gpio_device */ @@ -1201,7 +1203,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) err_remove_device: cdev_device_del(&gdev->chrdev, &gdev->dev); - return status; + return ret; } static void gpiochip_machine_hog(struct gpio_chip *chip, struct gpiod_hog *hog) @@ -1242,13 +1244,13 @@ static void machine_gpiochip_add(struct gpio_chip *chip) static void gpiochip_setup_devs(void) { struct gpio_device *gdev; - int err; + int ret; list_for_each_entry(gdev, &gpio_devices, list) { - err = gpiochip_setup_dev(gdev); - if (err) + ret = gpiochip_setup_dev(gdev); + if (ret) pr_err("%s: Failed to initialize gpio device (%d)\n", - dev_name(&gdev->dev), err); + dev_name(&gdev->dev), ret); } } @@ -1257,7 +1259,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, struct lock_class_key *request_key) { unsigned long flags; - int status = 0; + int ret = 0; unsigned i; int base = chip->base; struct gpio_device *gdev; @@ -1287,7 +1289,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL); if (gdev->id < 0) { - status = gdev->id; + ret = gdev->id; goto err_free_gdev; } dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); @@ -1303,13 +1305,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL); if (!gdev->descs) { - status = -ENOMEM; + ret = -ENOMEM; goto err_free_ida; } if (chip->ngpio == 0) { chip_err(chip, "tried to insert a GPIO chip with zero lines\n"); - status = -EINVAL; + ret = -EINVAL; goto err_free_descs; } @@ -1319,7 +1321,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { - status = -ENOMEM; + ret = -ENOMEM; goto err_free_descs; } @@ -1338,7 +1340,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, if (base < 0) { base = gpiochip_find_base(chip->ngpio); if (base < 0) { - status = base; + ret = base; spin_unlock_irqrestore(&gpio_lock, flags); goto err_free_label; } @@ -1352,8 +1354,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, } gdev->base = base; - status = gpiodev_add_to_list(gdev); - if (status) { + ret = gpiodev_add_to_list(gdev); + if (ret) { spin_unlock_irqrestore(&gpio_lock, flags); goto err_free_label; } @@ -1367,28 +1369,20 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, INIT_LIST_HEAD(&gdev->pin_ranges); #endif - status = gpiochip_set_desc_names(chip); - if (status) + ret = gpiochip_set_desc_names(chip); + if (ret) goto err_remove_from_list; - status = gpiochip_irqchip_init_valid_mask(chip); - if (status) + ret = gpiochip_alloc_valid_mask(chip); + if (ret) goto err_remove_from_list; - status = gpiochip_alloc_valid_mask(chip); - if (status) - goto err_remove_irqchip_mask; - - status = gpiochip_add_irqchip(chip, lock_key, request_key); - if (status) + ret = of_gpiochip_add(chip); + if (ret) goto err_free_gpiochip_mask; - status = of_gpiochip_add(chip); - if (status) - goto err_remove_chip; - - status = gpiochip_init_valid_mask(chip); - if (status) + ret = gpiochip_init_valid_mask(chip); + if (ret) goto err_remove_of_chip; for (i = 0; i < chip->ngpio; i++) { @@ -1411,6 +1405,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, machine_gpiochip_add(chip); + ret = gpiochip_irqchip_init_valid_mask(chip); + if (ret) + goto err_remove_acpi_chip; + + ret = gpiochip_add_irqchip(chip, lock_key, request_key); + if (ret) + goto err_remove_irqchip_mask; + /* * By first adding the chardev, and then adding the device, * we get a device node entry in sysfs under @@ -1420,23 +1422,23 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, * Otherwise, defer until later. */ if (gpiolib_initialized) { - status = gpiochip_setup_dev(gdev); - if (status) - goto err_remove_acpi_chip; + ret = gpiochip_setup_dev(gdev); + if (ret) + goto err_remove_irqchip; } return 0; +err_remove_irqchip: + gpiochip_irqchip_remove(chip); +err_remove_irqchip_mask: + gpiochip_irqchip_free_valid_mask(chip); err_remove_acpi_chip: acpi_gpiochip_remove(chip); err_remove_of_chip: gpiochip_free_hogs(chip); of_gpiochip_remove(chip); -err_remove_chip: - gpiochip_irqchip_remove(chip); err_free_gpiochip_mask: gpiochip_free_valid_mask(chip); -err_remove_irqchip_mask: - gpiochip_irqchip_free_valid_mask(chip); err_remove_from_list: spin_lock_irqsave(&gpio_lock, flags); list_del(&gdev->list); @@ -1451,9 +1453,9 @@ err_free_gdev: /* failures here can mean systems won't boot... */ pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, gdev->base, gdev->base + gdev->ngpio - 1, - chip->label ? : "generic", status); + chip->label ? : "generic", ret); kfree(gdev); - return status; + return ret; } EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); @@ -1619,21 +1621,25 @@ static struct gpio_chip *find_chip_by_name(const char *name) * The following is irqchip helper code for gpiochips. */ -static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) +static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gc) { - if (!gpiochip->irq.need_valid_mask) + struct gpio_irq_chip *girq = &gc->irq; + + if (!girq->init_valid_mask) return 0; - gpiochip->irq.valid_mask = gpiochip_allocate_mask(gpiochip); - if (!gpiochip->irq.valid_mask) + girq->valid_mask = gpiochip_allocate_mask(gc); + if (!girq->valid_mask) return -ENOMEM; + girq->init_valid_mask(gc, girq->valid_mask, gc->ngpio); + return 0; } static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) { - kfree(gpiochip->irq.valid_mask); + bitmap_free(gpiochip->irq.valid_mask); gpiochip->irq.valid_mask = NULL; } @@ -1733,6 +1739,273 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, } EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + +/** + * gpiochip_set_hierarchical_irqchip() - connects a hierarchical irqchip + * to a gpiochip + * @gc: the gpiochip to set the irqchip hierarchical handler to + * @irqchip: the irqchip to handle this level of the hierarchy, the interrupt + * will then percolate up to the parent + */ +static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc, + struct irq_chip *irqchip) +{ + /* DT will deal with mapping each IRQ as we go along */ + if (is_of_node(gc->irq.fwnode)) + return; + + /* + * This is for legacy and boardfile "irqchip" fwnodes: allocate + * irqs upfront instead of dynamically since we don't have the + * dynamic type of allocation that hardware description languages + * provide. Once all GPIO drivers using board files are gone from + * the kernel we can delete this code, but for a transitional period + * it is necessary to keep this around. + */ + if (is_fwnode_irqchip(gc->irq.fwnode)) { + int i; + int ret; + + for (i = 0; i < gc->ngpio; i++) { + struct irq_fwspec fwspec; + unsigned int parent_hwirq; + unsigned int parent_type; + struct gpio_irq_chip *girq = &gc->irq; + + /* + * We call the child to parent translation function + * only to check if the child IRQ is valid or not. + * Just pick the rising edge type here as that is what + * we likely need to support. + */ + ret = girq->child_to_parent_hwirq(gc, i, + IRQ_TYPE_EDGE_RISING, + &parent_hwirq, + &parent_type); + if (ret) { + chip_err(gc, "skip set-up on hwirq %d\n", + i); + continue; + } + + fwspec.fwnode = gc->irq.fwnode; + /* This is the hwirq for the GPIO line side of things */ + fwspec.param[0] = girq->child_offset_to_irq(gc, i); + /* Just pick something */ + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + fwspec.param_count = 2; + ret = __irq_domain_alloc_irqs(gc->irq.domain, + /* just pick something */ + -1, + 1, + NUMA_NO_NODE, + &fwspec, + false, + NULL); + if (ret < 0) { + chip_err(gc, + "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n", + i, parent_hwirq, + ret); + } + } + } + + chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__); + + return; +} + +static int gpiochip_hierarchy_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + /* We support standard DT translation */ + if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { + return irq_domain_translate_twocell(d, fwspec, hwirq, type); + } + + /* This is for board files and others not using DT */ + if (is_fwnode_irqchip(fwspec->fwnode)) { + int ret; + + ret = irq_domain_translate_twocell(d, fwspec, hwirq, type); + if (ret) + return ret; + WARN_ON(*type == IRQ_TYPE_NONE); + return 0; + } + return -EINVAL; +} + +static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, + unsigned int irq, + unsigned int nr_irqs, + void *data) +{ + struct gpio_chip *gc = d->host_data; + irq_hw_number_t hwirq; + unsigned int type = IRQ_TYPE_NONE; + struct irq_fwspec *fwspec = data; + struct irq_fwspec parent_fwspec; + unsigned int parent_hwirq; + unsigned int parent_type; + struct gpio_irq_chip *girq = &gc->irq; + int ret; + + /* + * The nr_irqs parameter is always one except for PCI multi-MSI + * so this should not happen. + */ + WARN_ON(nr_irqs != 1); + + ret = gc->irq.child_irq_domain_ops.translate(d, fwspec, &hwirq, &type); + if (ret) + return ret; + + chip_info(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); + + ret = girq->child_to_parent_hwirq(gc, hwirq, type, + &parent_hwirq, &parent_type); + if (ret) { + chip_err(gc, "can't look up hwirq %lu\n", hwirq); + return ret; + } + chip_info(gc, "found parent hwirq %u\n", parent_hwirq); + + /* + * We set handle_bad_irq because the .set_type() should + * always be invoked and set the right type of handler. + */ + irq_domain_set_info(d, + irq, + hwirq, + gc->irq.chip, + gc, + girq->handler, + NULL, NULL); + irq_set_probe(irq); + + /* + * Create a IRQ fwspec to send up to the parent irqdomain: + * specify the hwirq we address on the parent and tie it + * all together up the chain. + */ + parent_fwspec.fwnode = d->parent->fwnode; + /* This parent only handles asserted level IRQs */ + girq->populate_parent_fwspec(gc, &parent_fwspec, parent_hwirq, + parent_type); + chip_info(gc, "alloc_irqs_parent for %d parent hwirq %d\n", + irq, parent_hwirq); + ret = irq_domain_alloc_irqs_parent(d, irq, 1, &parent_fwspec); + if (ret) + chip_err(gc, + "failed to allocate parent hwirq %d for hwirq %lu\n", + parent_hwirq, hwirq); + + return ret; +} + +static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *chip, + unsigned int offset) +{ + return offset; +} + +static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops) +{ + ops->activate = gpiochip_irq_domain_activate; + ops->deactivate = gpiochip_irq_domain_deactivate; + ops->alloc = gpiochip_hierarchy_irq_domain_alloc; + ops->free = irq_domain_free_irqs_common; + + /* + * We only allow overriding the translate() function for + * hierarchical chips, and this should only be done if the user + * really need something other than 1:1 translation. + */ + if (!ops->translate) + ops->translate = gpiochip_hierarchy_irq_domain_translate; +} + +static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) +{ + if (!gc->irq.child_to_parent_hwirq || + !gc->irq.fwnode) { + chip_err(gc, "missing irqdomain vital data\n"); + return -EINVAL; + } + + if (!gc->irq.child_offset_to_irq) + gc->irq.child_offset_to_irq = gpiochip_child_offset_to_irq_noop; + + if (!gc->irq.populate_parent_fwspec) + gc->irq.populate_parent_fwspec = + gpiochip_populate_parent_fwspec_twocell; + + gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops); + + gc->irq.domain = irq_domain_create_hierarchy( + gc->irq.parent_domain, + 0, + gc->ngpio, + gc->irq.fwnode, + &gc->irq.child_irq_domain_ops, + gc); + + if (!gc->irq.domain) + return -ENOMEM; + + gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip); + + return 0; +} + +static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +{ + return !!gc->irq.parent_domain; +} + +void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, + struct irq_fwspec *fwspec, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + fwspec->param_count = 2; + fwspec->param[0] = parent_hwirq; + fwspec->param[1] = parent_type; +} +EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell); + +void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, + struct irq_fwspec *fwspec, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + fwspec->param_count = 4; + fwspec->param[0] = 0; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = 0; + fwspec->param[3] = parent_type; +} +EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell); + +#else + +static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) +{ + return -EINVAL; +} + +static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) +{ + return false; +} + +#endif /* CONFIG_IRQ_DOMAIN_HIERARCHY */ + /** * gpiochip_irq_map() - maps an IRQ into a GPIO irqchip * @d: the irqdomain used by this irqchip @@ -1747,7 +2020,7 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { struct gpio_chip *chip = d->host_data; - int err = 0; + int ret = 0; if (!gpiochip_irqchip_irq_valid(chip, hwirq)) return -ENXIO; @@ -1765,12 +2038,12 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_set_noprobe(irq); if (chip->irq.num_parents == 1) - err = irq_set_parent(irq, chip->irq.parents[0]); + ret = irq_set_parent(irq, chip->irq.parents[0]); else if (chip->irq.map) - err = irq_set_parent(irq, chip->irq.map[hwirq]); + ret = irq_set_parent(irq, chip->irq.map[hwirq]); - if (err < 0) - return err; + if (ret < 0) + return ret; /* * No set-up of the hardware will happen if IRQ_TYPE_NONE @@ -1801,6 +2074,11 @@ static const struct irq_domain_ops gpiochip_domain_ops = { .xlate = irq_domain_xlate_twocell, }; +/* + * TODO: move these activate/deactivate in under the hierarchicial + * irqchip implementation as static once SPMI and SSBI (all external + * users) are phased over. + */ /** * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ * @domain: The IRQ domain used by this IRQ chip @@ -1840,10 +2118,25 @@ EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { + struct irq_domain *domain = chip->irq.domain; + if (!gpiochip_irqchip_irq_valid(chip, offset)) return -ENXIO; - return irq_create_mapping(chip->irq.domain, offset); +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY + if (irq_domain_is_hierarchy(domain)) { + struct irq_fwspec spec; + + spec.fwnode = domain->fwnode; + spec.param_count = 2; + spec.param[0] = chip->irq.child_offset_to_irq(chip, offset); + spec.param[1] = IRQ_TYPE_NONE; + + return irq_create_fwspec_mapping(&spec); + } +#endif + + return irq_create_mapping(domain, offset); } static int gpiochip_irq_reqres(struct irq_data *d) @@ -1920,7 +2213,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, struct lock_class_key *request_key) { struct irq_chip *irqchip = gpiochip->irq.chip; - const struct irq_domain_ops *ops; + const struct irq_domain_ops *ops = NULL; struct device_node *np; unsigned int type; unsigned int i; @@ -1956,16 +2249,25 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, gpiochip->irq.lock_key = lock_key; gpiochip->irq.request_key = request_key; - if (gpiochip->irq.domain_ops) - ops = gpiochip->irq.domain_ops; - else - ops = &gpiochip_domain_ops; - - gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio, - gpiochip->irq.first, - ops, gpiochip); - if (!gpiochip->irq.domain) - return -EINVAL; + /* If a parent irqdomain is provided, let's build a hierarchy */ + if (gpiochip_hierarchy_is_hierarchical(gpiochip)) { + int ret = gpiochip_hierarchy_add_domain(gpiochip); + if (ret) + return ret; + } else { + /* Some drivers provide custom irqdomain ops */ + if (gpiochip->irq.domain_ops) + ops = gpiochip->irq.domain_ops; + + if (!ops) + ops = &gpiochip_domain_ops; + gpiochip->irq.domain = irq_domain_add_simple(np, + gpiochip->ngpio, + gpiochip->irq.first, + ops, gpiochip); + if (!gpiochip->irq.domain) + return -EINVAL; + } if (gpiochip->irq.parent_handler) { void *data = gpiochip->irq.parent_handler_data ?: gpiochip; @@ -2328,7 +2630,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); static int gpiod_request_commit(struct gpio_desc *desc, const char *label) { struct gpio_chip *chip = desc->gdev->chip; - int status; + int ret; unsigned long flags; unsigned offset; @@ -2346,10 +2648,10 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { desc_set_label(desc, label ? : "?"); - status = 0; + ret = 0; } else { kfree_const(label); - status = -EBUSY; + ret = -EBUSY; goto done; } @@ -2358,12 +2660,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) spin_unlock_irqrestore(&gpio_lock, flags); offset = gpio_chip_hwgpio(desc); if (gpiochip_line_is_valid(chip, offset)) - status = chip->request(chip, offset); + ret = chip->request(chip, offset); else - status = -EINVAL; + ret = -EINVAL; spin_lock_irqsave(&gpio_lock, flags); - if (status < 0) { + if (ret < 0) { desc_set_label(desc, NULL); kfree_const(label); clear_bit(FLAG_REQUESTED, &desc->flags); @@ -2378,7 +2680,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) } done: spin_unlock_irqrestore(&gpio_lock, flags); - return status; + return ret; } /* @@ -2421,24 +2723,24 @@ static int validate_desc(const struct gpio_desc *desc, const char *func) int gpiod_request(struct gpio_desc *desc, const char *label) { - int status = -EPROBE_DEFER; + int ret = -EPROBE_DEFER; struct gpio_device *gdev; VALIDATE_DESC(desc); gdev = desc->gdev; if (try_module_get(gdev->owner)) { - status = gpiod_request_commit(desc, label); - if (status < 0) + ret = gpiod_request_commit(desc, label); + if (ret < 0) module_put(gdev->owner); else get_device(&gdev->dev); } - if (status) - gpiod_dbg(desc, "%s: status %d\n", __func__, status); + if (ret) + gpiod_dbg(desc, "%s: status %d\n", __func__, ret); - return status; + return ret; } static bool gpiod_free_commit(struct gpio_desc *desc) @@ -2540,22 +2842,22 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, enum gpiod_flags dflags) { struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum); - int err; + int ret; if (IS_ERR(desc)) { chip_err(chip, "failed to get GPIO descriptor\n"); return desc; } - err = gpiod_request_commit(desc, label); - if (err < 0) - return ERR_PTR(err); + ret = gpiod_request_commit(desc, label); + if (ret < 0) + return ERR_PTR(ret); - err = gpiod_configure_flags(desc, label, lflags, dflags); - if (err) { + ret = gpiod_configure_flags(desc, label, lflags, dflags); + if (ret) { chip_err(chip, "setup of own GPIO %s failed\n", label); gpiod_free_commit(desc); - return ERR_PTR(err); + return ERR_PTR(ret); } return desc; @@ -2618,7 +2920,7 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset, int gpiod_direction_input(struct gpio_desc *desc) { struct gpio_chip *chip; - int status = 0; + int ret = 0; VALIDATE_DESC(desc); chip = desc->gdev->chip; @@ -2642,7 +2944,7 @@ int gpiod_direction_input(struct gpio_desc *desc) * assume we are in input mode after this. */ if (chip->direction_input) { - status = chip->direction_input(chip, gpio_chip_hwgpio(desc)); + ret = chip->direction_input(chip, gpio_chip_hwgpio(desc)); } else if (chip->get_direction && (chip->get_direction(chip, gpio_chip_hwgpio(desc)) != 1)) { gpiod_warn(desc, @@ -2650,7 +2952,7 @@ int gpiod_direction_input(struct gpio_desc *desc) __func__); return -EIO; } - if (status == 0) + if (ret == 0) clear_bit(FLAG_IS_OUT, &desc->flags); if (test_bit(FLAG_PULL_UP, &desc->flags)) @@ -2660,9 +2962,9 @@ int gpiod_direction_input(struct gpio_desc *desc) gpio_set_config(chip, gpio_chip_hwgpio(desc), PIN_CONFIG_BIAS_PULL_DOWN); - trace_gpio_direction(desc_to_gpio(desc), 1, status); + trace_gpio_direction(desc_to_gpio(desc), 1, ret); - return status; + return ret; } EXPORT_SYMBOL_GPL(gpiod_direction_input); @@ -2934,7 +3236,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, struct gpio_array *array_info, unsigned long *value_bitmap) { - int err, i = 0; + int ret, i = 0; /* * Validate array_info against desc_array and its size. @@ -2947,11 +3249,11 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, if (!can_sleep) WARN_ON(array_info->chip->can_sleep); - err = gpio_chip_get_multiple(array_info->chip, + ret = gpio_chip_get_multiple(array_info->chip, array_info->get_mask, value_bitmap); - if (err) - return err; + if (ret) + return ret; if (!raw && !bitmap_empty(array_info->invert_mask, array_size)) bitmap_xor(value_bitmap, value_bitmap, @@ -3139,24 +3441,24 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value); */ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) { - int err = 0; + int ret = 0; struct gpio_chip *chip = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); if (value) { - err = chip->direction_input(chip, offset); - if (!err) + ret = chip->direction_input(chip, offset); + if (!ret) clear_bit(FLAG_IS_OUT, &desc->flags); } else { - err = chip->direction_output(chip, offset, 0); - if (!err) + ret = chip->direction_output(chip, offset, 0); + if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } - trace_gpio_direction(desc_to_gpio(desc), value, err); - if (err < 0) + trace_gpio_direction(desc_to_gpio(desc), value, ret); + if (ret < 0) gpiod_err(desc, "%s: Error in set_value for open drain err %d\n", - __func__, err); + __func__, ret); } /* @@ -3166,24 +3468,24 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) */ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) { - int err = 0; + int ret = 0; struct gpio_chip *chip = desc->gdev->chip; int offset = gpio_chip_hwgpio(desc); if (value) { - err = chip->direction_output(chip, offset, 1); - if (!err) + ret = chip->direction_output(chip, offset, 1); + if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } else { - err = chip->direction_input(chip, offset); - if (!err) + ret = chip->direction_input(chip, offset); + if (!ret) clear_bit(FLAG_IS_OUT, &desc->flags); } - trace_gpio_direction(desc_to_gpio(desc), !value, err); - if (err < 0) + trace_gpio_direction(desc_to_gpio(desc), !value, ret); + if (ret < 0) gpiod_err(desc, "%s: Error in set_value for open source err %d\n", - __func__, err); + __func__, ret); } static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) @@ -4000,27 +4302,6 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, return desc; } -static int dt_gpio_count(struct device *dev, const char *con_id) -{ - int ret; - char propname[32]; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id) - snprintf(propname, sizeof(propname), "%s-%s", - con_id, gpio_suffixes[i]); - else - snprintf(propname, sizeof(propname), "%s", - gpio_suffixes[i]); - - ret = of_gpio_named_count(dev->of_node, propname); - if (ret > 0) - break; - } - return ret ? ret : -ENOENT; -} - static int platform_gpio_count(struct device *dev, const char *con_id) { struct gpiod_lookup_table *table; @@ -4053,7 +4334,7 @@ int gpiod_count(struct device *dev, const char *con_id) int count = -ENOENT; if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) - count = dt_gpio_count(dev, con_id); + count = of_gpio_get_count(dev, con_id); else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) count = acpi_gpio_count(dev, con_id); @@ -4115,7 +4396,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional); int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags) { - int status; + int ret; if (lflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); @@ -4148,9 +4429,9 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, else if (lflags & GPIO_PULL_DOWN) set_bit(FLAG_PULL_DOWN, &desc->flags); - status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); - if (status < 0) - return status; + ret = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); + if (ret < 0) + return ret; /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { @@ -4160,12 +4441,12 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, /* Process flags */ if (dflags & GPIOD_FLAGS_BIT_DIR_OUT) - status = gpiod_direction_output(desc, + ret = gpiod_direction_output(desc, !!(dflags & GPIOD_FLAGS_BIT_DIR_VAL)); else - status = gpiod_direction_input(desc); + ret = gpiod_direction_input(desc); - return status; + return ret; } /** @@ -4189,7 +4470,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, { unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT; struct gpio_desc *desc = NULL; - int status; + int ret; /* Maybe we have a device name, maybe not */ const char *devname = dev ? dev_name(dev) : "?"; @@ -4224,9 +4505,9 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, * If a connection label was passed use that, else attempt to use * the device name as label */ - status = gpiod_request(desc, con_id ? con_id : devname); - if (status < 0) { - if (status == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { + ret = gpiod_request(desc, con_id ? con_id : devname); + if (ret < 0) { + if (ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { /* * This happens when there are several consumers for * the same GPIO line: we just return here without @@ -4239,89 +4520,20 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, con_id ? con_id : devname); return desc; } else { - return ERR_PTR(status); + return ERR_PTR(ret); } } - status = gpiod_configure_flags(desc, con_id, lookupflags, flags); - if (status < 0) { - dev_dbg(dev, "setup of GPIO %s failed\n", con_id); - gpiod_put(desc); - return ERR_PTR(status); - } - - return desc; -} -EXPORT_SYMBOL_GPL(gpiod_get_index); - -/** - * gpiod_get_from_of_node() - obtain a GPIO from an OF node - * @node: handle of the OF node - * @propname: name of the DT property representing the GPIO - * @index: index of the GPIO to obtain for the consumer - * @dflags: GPIO initialization flags - * @label: label to attach to the requested GPIO - * - * Returns: - * On successful request the GPIO pin is configured in accordance with - * provided @dflags. - * - * In case of error an ERR_PTR() is returned. - */ -struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, - const char *propname, int index, - enum gpiod_flags dflags, - const char *label) -{ - unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT; - struct gpio_desc *desc; - enum of_gpio_flags flags; - bool active_low = false; - bool single_ended = false; - bool open_drain = false; - bool transitory = false; - int ret; - - desc = of_get_named_gpiod_flags(node, propname, - index, &flags); - - if (!desc || IS_ERR(desc)) { - return desc; - } - - active_low = flags & OF_GPIO_ACTIVE_LOW; - single_ended = flags & OF_GPIO_SINGLE_ENDED; - open_drain = flags & OF_GPIO_OPEN_DRAIN; - transitory = flags & OF_GPIO_TRANSITORY; - - ret = gpiod_request(desc, label); - if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) - return desc; - if (ret) - return ERR_PTR(ret); - - if (active_low) - lflags |= GPIO_ACTIVE_LOW; - - if (single_ended) { - if (open_drain) - lflags |= GPIO_OPEN_DRAIN; - else - lflags |= GPIO_OPEN_SOURCE; - } - - if (transitory) - lflags |= GPIO_TRANSITORY; - - ret = gpiod_configure_flags(desc, propname, lflags, dflags); + ret = gpiod_configure_flags(desc, con_id, lookupflags, flags); if (ret < 0) { + dev_dbg(dev, "setup of GPIO %s failed\n", con_id); gpiod_put(desc); return ERR_PTR(ret); } return desc; } -EXPORT_SYMBOL(gpiod_get_from_of_node); +EXPORT_SYMBOL_GPL(gpiod_get_index); /** * fwnode_get_named_gpiod - obtain a GPIO from firmware node @@ -4431,7 +4643,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, struct gpio_chip *chip; struct gpio_desc *local_desc; int hwnum; - int status; + int ret; chip = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); @@ -4439,10 +4651,10 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, local_desc = gpiochip_request_own_desc(chip, hwnum, name, lflags, dflags); if (IS_ERR(local_desc)) { - status = PTR_ERR(local_desc); + ret = PTR_ERR(local_desc); pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n", - name, chip->label, hwnum, status); - return status; + name, chip->label, hwnum, ret); + return ret; } /* Mark GPIO as hogged so it can be identified and removed later */ diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 7c52c2442173..b8b10a409c7b 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -16,9 +16,6 @@ #include <linux/module.h> #include <linux/cdev.h> -enum of_gpio_flags; -struct acpi_device; - /** * struct gpio_device - internal state container for GPIO devices * @id: numerical ID number for the GPIO chip @@ -69,126 +66,9 @@ struct gpio_device { #endif }; -/** - * struct acpi_gpio_info - ACPI GPIO specific information - * @adev: reference to ACPI device which consumes GPIO resource - * @flags: GPIO initialization flags - * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo - * @pin_config: pin bias as provided by ACPI - * @polarity: interrupt polarity as provided by ACPI - * @triggering: triggering type as provided by ACPI - * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping - */ -struct acpi_gpio_info { - struct acpi_device *adev; - enum gpiod_flags flags; - bool gpioint; - int pin_config; - int polarity; - int triggering; - unsigned int quirks; -}; - /* gpio suffixes used for ACPI and device tree lookup */ static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" }; -#ifdef CONFIG_OF_GPIO -struct gpio_desc *of_find_gpio(struct device *dev, - const char *con_id, - unsigned int idx, - unsigned long *lookupflags); -struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, - const char *list_name, int index, enum of_gpio_flags *flags); -int of_gpiochip_add(struct gpio_chip *gc); -void of_gpiochip_remove(struct gpio_chip *gc); -#else -static inline struct gpio_desc *of_find_gpio(struct device *dev, - const char *con_id, - unsigned int idx, - unsigned long *lookupflags) -{ - return ERR_PTR(-ENOENT); -} -static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, - const char *list_name, int index, enum of_gpio_flags *flags) -{ - return ERR_PTR(-ENOENT); -} -static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } -static inline void of_gpiochip_remove(struct gpio_chip *gc) { } -#endif /* CONFIG_OF_GPIO */ - -#ifdef CONFIG_ACPI -void acpi_gpiochip_add(struct gpio_chip *chip); -void acpi_gpiochip_remove(struct gpio_chip *chip); - -void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); -void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); - -int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, - struct acpi_gpio_info *info); -int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, - struct acpi_gpio_info *info); - -struct gpio_desc *acpi_find_gpio(struct device *dev, - const char *con_id, - unsigned int idx, - enum gpiod_flags *dflags, - unsigned long *lookupflags); -struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, - const char *propname, int index, - struct acpi_gpio_info *info); - -int acpi_gpio_count(struct device *dev, const char *con_id); - -bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id); -#else -static inline void acpi_gpiochip_add(struct gpio_chip *chip) { } -static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { } - -static inline void -acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } - -static inline void -acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } - -static inline int -acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) -{ - return 0; -} -static inline int -acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, - struct acpi_gpio_info *info) -{ - return 0; -} - -static inline struct gpio_desc * -acpi_find_gpio(struct device *dev, const char *con_id, - unsigned int idx, enum gpiod_flags *dflags, - unsigned long *lookupflags) -{ - return ERR_PTR(-ENOENT); -} -static inline struct gpio_desc * -acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, - int index, struct acpi_gpio_info *info) -{ - return ERR_PTR(-ENXIO); -} -static inline int acpi_gpio_count(struct device *dev, const char *con_id) -{ - return -ENODEV; -} - -static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev, - const char *con_id) -{ - return false; -} -#endif - struct gpio_array { struct gpio_desc **desc; unsigned int size; diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c new file mode 100644 index 000000000000..7e99860ca447 --- /dev/null +++ b/drivers/gpio/sgpio-aspeed.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 American Megatrends International LLC. + * + * Author: Karthikeyan Mani <karthikeyanm@amiindia.co.in> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/hashtable.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/string.h> + +#define MAX_NR_SGPIO 80 + +#define ASPEED_SGPIO_CTRL 0x54 + +#define ASPEED_SGPIO_PINS_MASK GENMASK(9, 6) +#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16) +#define ASPEED_SGPIO_ENABLE BIT(0) + +struct aspeed_sgpio { + struct gpio_chip chip; + struct clk *pclk; + spinlock_t lock; + void __iomem *base; + uint32_t dir_in[3]; + int irq; +}; + +struct aspeed_sgpio_bank { + uint16_t val_regs; + uint16_t rdata_reg; + uint16_t irq_regs; + const char names[4][3]; +}; + +/* + * Note: The "value" register returns the input value when the GPIO is + * configured as an input. + * + * The "rdata" register returns the output value when the GPIO is + * configured as an output. + */ +static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = { + { + .val_regs = 0x0000, + .rdata_reg = 0x0070, + .irq_regs = 0x0004, + .names = { "A", "B", "C", "D" }, + }, + { + .val_regs = 0x001C, + .rdata_reg = 0x0074, + .irq_regs = 0x0020, + .names = { "E", "F", "G", "H" }, + }, + { + .val_regs = 0x0038, + .rdata_reg = 0x0078, + .irq_regs = 0x003C, + .names = { "I", "J" }, + }, +}; + +enum aspeed_sgpio_reg { + reg_val, + reg_rdata, + reg_irq_enable, + reg_irq_type0, + reg_irq_type1, + reg_irq_type2, + reg_irq_status, +}; + +#define GPIO_VAL_VALUE 0x00 +#define GPIO_IRQ_ENABLE 0x00 +#define GPIO_IRQ_TYPE0 0x04 +#define GPIO_IRQ_TYPE1 0x08 +#define GPIO_IRQ_TYPE2 0x0C +#define GPIO_IRQ_STATUS 0x10 + +static void __iomem *bank_reg(struct aspeed_sgpio *gpio, + const struct aspeed_sgpio_bank *bank, + const enum aspeed_sgpio_reg reg) +{ + switch (reg) { + case reg_val: + return gpio->base + bank->val_regs + GPIO_VAL_VALUE; + case reg_rdata: + return gpio->base + bank->rdata_reg; + case reg_irq_enable: + return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE; + case reg_irq_type0: + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0; + case reg_irq_type1: + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1; + case reg_irq_type2: + return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2; + case reg_irq_status: + return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS; + default: + /* acturally if code runs to here, it's an error case */ + BUG_ON(1); + } +} + +#define GPIO_BANK(x) ((x) >> 5) +#define GPIO_OFFSET(x) ((x) & 0x1f) +#define GPIO_BIT(x) BIT(GPIO_OFFSET(x)) + +static const struct aspeed_sgpio_bank *to_bank(unsigned int offset) +{ + unsigned int bank = GPIO_BANK(offset); + + WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks)); + return &aspeed_sgpio_banks[bank]; +} + +static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); + const struct aspeed_sgpio_bank *bank = to_bank(offset); + unsigned long flags; + enum aspeed_sgpio_reg reg; + bool is_input; + int rc = 0; + + spin_lock_irqsave(&gpio->lock, flags); + + is_input = gpio->dir_in[GPIO_BANK(offset)] & GPIO_BIT(offset); + reg = is_input ? reg_val : reg_rdata; + rc = !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset)); + + spin_unlock_irqrestore(&gpio->lock, flags); + + return rc; +} + +static void sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); + const struct aspeed_sgpio_bank *bank = to_bank(offset); + void __iomem *addr; + u32 reg = 0; + + addr = bank_reg(gpio, bank, reg_val); + reg = ioread32(addr); + + if (val) + reg |= GPIO_BIT(offset); + else + reg &= ~GPIO_BIT(offset); + + iowrite32(reg, addr); +} + +static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&gpio->lock, flags); + + sgpio_set_value(gc, offset, val); + + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) +{ + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&gpio->lock, flags); + gpio->dir_in[GPIO_BANK(offset)] |= GPIO_BIT(offset); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) +{ + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&gpio->lock, flags); + + gpio->dir_in[GPIO_BANK(offset)] &= ~GPIO_BIT(offset); + sgpio_set_value(gc, offset, val); + + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + int dir_status; + struct aspeed_sgpio *gpio = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&gpio->lock, flags); + dir_status = gpio->dir_in[GPIO_BANK(offset)] & GPIO_BIT(offset); + spin_unlock_irqrestore(&gpio->lock, flags); + + return dir_status; + +} + +static void irqd_to_aspeed_sgpio_data(struct irq_data *d, + struct aspeed_sgpio **gpio, + const struct aspeed_sgpio_bank **bank, + u32 *bit, int *offset) +{ + struct aspeed_sgpio *internal; + + *offset = irqd_to_hwirq(d); + internal = irq_data_get_irq_chip_data(d); + WARN_ON(!internal); + + *gpio = internal; + *bank = to_bank(*offset); + *bit = GPIO_BIT(*offset); +} + +static void aspeed_sgpio_irq_ack(struct irq_data *d) +{ + const struct aspeed_sgpio_bank *bank; + struct aspeed_sgpio *gpio; + unsigned long flags; + void __iomem *status_addr; + int offset; + u32 bit; + + irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); + + status_addr = bank_reg(gpio, bank, reg_irq_status); + + spin_lock_irqsave(&gpio->lock, flags); + + iowrite32(bit, status_addr); + + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set) +{ + const struct aspeed_sgpio_bank *bank; + struct aspeed_sgpio *gpio; + unsigned long flags; + u32 reg, bit; + void __iomem *addr; + int offset; + + irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); + addr = bank_reg(gpio, bank, reg_irq_enable); + + spin_lock_irqsave(&gpio->lock, flags); + + reg = ioread32(addr); + if (set) + reg |= bit; + else + reg &= ~bit; + + iowrite32(reg, addr); + + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static void aspeed_sgpio_irq_mask(struct irq_data *d) +{ + aspeed_sgpio_irq_set_mask(d, false); +} + +static void aspeed_sgpio_irq_unmask(struct irq_data *d) +{ + aspeed_sgpio_irq_set_mask(d, true); +} + +static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type) +{ + u32 type0 = 0; + u32 type1 = 0; + u32 type2 = 0; + u32 bit, reg; + const struct aspeed_sgpio_bank *bank; + irq_flow_handler_t handler; + struct aspeed_sgpio *gpio; + unsigned long flags; + void __iomem *addr; + int offset; + + irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: + type2 |= bit; + /* fall through */ + case IRQ_TYPE_EDGE_RISING: + type0 |= bit; + /* fall through */ + case IRQ_TYPE_EDGE_FALLING: + handler = handle_edge_irq; + break; + case IRQ_TYPE_LEVEL_HIGH: + type0 |= bit; + /* fall through */ + case IRQ_TYPE_LEVEL_LOW: + type1 |= bit; + handler = handle_level_irq; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&gpio->lock, flags); + + addr = bank_reg(gpio, bank, reg_irq_type0); + reg = ioread32(addr); + reg = (reg & ~bit) | type0; + iowrite32(reg, addr); + + addr = bank_reg(gpio, bank, reg_irq_type1); + reg = ioread32(addr); + reg = (reg & ~bit) | type1; + iowrite32(reg, addr); + + addr = bank_reg(gpio, bank, reg_irq_type2); + reg = ioread32(addr); + reg = (reg & ~bit) | type2; + iowrite32(reg, addr); + + spin_unlock_irqrestore(&gpio->lock, flags); + + irq_set_handler_locked(d, handler); + + return 0; +} + +static void aspeed_sgpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct irq_chip *ic = irq_desc_get_chip(desc); + struct aspeed_sgpio *data = gpiochip_get_data(gc); + unsigned int i, p, girq; + unsigned long reg; + + chained_irq_enter(ic, desc); + + for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { + const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i]; + + reg = ioread32(bank_reg(data, bank, reg_irq_status)); + + for_each_set_bit(p, ®, 32) { + girq = irq_find_mapping(gc->irq.domain, i * 32 + p); + generic_handle_irq(girq); + } + + } + + chained_irq_exit(ic, desc); +} + +static struct irq_chip aspeed_sgpio_irqchip = { + .name = "aspeed-sgpio", + .irq_ack = aspeed_sgpio_irq_ack, + .irq_mask = aspeed_sgpio_irq_mask, + .irq_unmask = aspeed_sgpio_irq_unmask, + .irq_set_type = aspeed_sgpio_set_type, +}; + +static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio, + struct platform_device *pdev) +{ + int rc, i; + const struct aspeed_sgpio_bank *bank; + struct gpio_irq_chip *irq; + + rc = platform_get_irq(pdev, 0); + if (rc < 0) + return rc; + + gpio->irq = rc; + + /* Disable IRQ and clear Interrupt status registers for all SPGIO Pins. */ + for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { + bank = &aspeed_sgpio_banks[i]; + /* disable irq enable bits */ + iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable)); + /* clear status bits */ + iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status)); + } + + irq = &gpio->chip.irq; + irq->chip = &aspeed_sgpio_irqchip; + irq->handler = handle_bad_irq; + irq->default_type = IRQ_TYPE_NONE; + irq->parent_handler = aspeed_sgpio_irq_handler; + irq->parent_handler_data = gpio; + irq->parents = &gpio->irq; + irq->num_parents = 1; + + /* set IRQ settings and Enable Interrupt */ + for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) { + bank = &aspeed_sgpio_banks[i]; + /* set falling or level-low irq */ + iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type0)); + /* trigger type is edge */ + iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type1)); + /* dual edge trigger mode. */ + iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type2)); + /* enable irq */ + iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_enable)); + } + + return 0; +} + +static const struct of_device_id aspeed_sgpio_of_table[] = { + { .compatible = "aspeed,ast2400-sgpio" }, + { .compatible = "aspeed,ast2500-sgpio" }, + {} +}; + +MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table); + +static int __init aspeed_sgpio_probe(struct platform_device *pdev) +{ + struct aspeed_sgpio *gpio; + u32 nr_gpios, sgpio_freq, sgpio_clk_div; + int rc; + unsigned long apb_freq; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); + + rc = of_property_read_u32(pdev->dev.of_node, "ngpios", &nr_gpios); + if (rc < 0) { + dev_err(&pdev->dev, "Could not read ngpios property\n"); + return -EINVAL; + } else if (nr_gpios > MAX_NR_SGPIO) { + dev_err(&pdev->dev, "Number of GPIOs exceeds the maximum of %d: %d\n", + MAX_NR_SGPIO, nr_gpios); + return -EINVAL; + } + + rc = of_property_read_u32(pdev->dev.of_node, "bus-frequency", &sgpio_freq); + if (rc < 0) { + dev_err(&pdev->dev, "Could not read bus-frequency property\n"); + return -EINVAL; + } + + gpio->pclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(gpio->pclk)) { + dev_err(&pdev->dev, "devm_clk_get failed\n"); + return PTR_ERR(gpio->pclk); + } + + apb_freq = clk_get_rate(gpio->pclk); + + /* + * From the datasheet, + * SGPIO period = 1/PCLK * 2 * (GPIO254[31:16] + 1) + * period = 2 * (GPIO254[31:16] + 1) / PCLK + * frequency = 1 / (2 * (GPIO254[31:16] + 1) / PCLK) + * frequency = PCLK / (2 * (GPIO254[31:16] + 1)) + * frequency * 2 * (GPIO254[31:16] + 1) = PCLK + * GPIO254[31:16] = PCLK / (frequency * 2) - 1 + */ + if (sgpio_freq == 0) + return -EINVAL; + + sgpio_clk_div = (apb_freq / (sgpio_freq * 2)) - 1; + + if (sgpio_clk_div > (1 << 16) - 1) + return -EINVAL; + + iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | + FIELD_PREP(ASPEED_SGPIO_PINS_MASK, (nr_gpios / 8)) | + ASPEED_SGPIO_ENABLE, + gpio->base + ASPEED_SGPIO_CTRL); + + spin_lock_init(&gpio->lock); + + gpio->chip.parent = &pdev->dev; + gpio->chip.ngpio = nr_gpios; + gpio->chip.direction_input = aspeed_sgpio_dir_in; + gpio->chip.direction_output = aspeed_sgpio_dir_out; + gpio->chip.get_direction = aspeed_sgpio_get_direction; + gpio->chip.request = NULL; + gpio->chip.free = NULL; + gpio->chip.get = aspeed_sgpio_get; + gpio->chip.set = aspeed_sgpio_set; + gpio->chip.set_config = NULL; + gpio->chip.label = dev_name(&pdev->dev); + gpio->chip.base = -1; + + /* set all SGPIO pins as input (1). */ + memset(gpio->dir_in, 0xff, sizeof(gpio->dir_in)); + + aspeed_sgpio_setup_irqs(gpio, pdev); + + rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (rc < 0) + return rc; + + return 0; +} + +static struct platform_driver aspeed_sgpio_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = aspeed_sgpio_of_table, + }, +}; + +module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe); +MODULE_DESCRIPTION("Aspeed Serial GPIO Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c index 9b384a94d2f3..3e35a8f2c5e5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c @@ -574,6 +574,7 @@ static const struct amdgpu_px_quirk amdgpu_px_quirk_list[] = { { 0x1002, 0x6900, 0x1002, 0x0124, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0x1002, 0x6900, 0x1028, 0x0812, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0x1002, 0x6900, 0x1028, 0x0813, AMDGPU_PX_QUIRK_FORCE_ATPX }, + { 0x1002, 0x699f, 0x1028, 0x0814, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0x1002, 0x6900, 0x1025, 0x125A, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0x1002, 0x6900, 0x17AA, 0x3806, AMDGPU_PX_QUIRK_FORCE_ATPX }, { 0, 0, 0, 0, 0 }, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 4e4094f842e7..8b26c970a3cb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1143,6 +1143,9 @@ static int amdgpu_cs_process_syncobj_out_dep(struct amdgpu_cs_parser *p, num_deps = chunk->length_dw * 4 / sizeof(struct drm_amdgpu_cs_chunk_sem); + if (p->post_deps) + return -EINVAL; + p->post_deps = kmalloc_array(num_deps, sizeof(*p->post_deps), GFP_KERNEL); p->num_post_deps = 0; @@ -1166,8 +1169,7 @@ static int amdgpu_cs_process_syncobj_out_dep(struct amdgpu_cs_parser *p, static int amdgpu_cs_process_syncobj_timeline_out_dep(struct amdgpu_cs_parser *p, - struct amdgpu_cs_chunk - *chunk) + struct amdgpu_cs_chunk *chunk) { struct drm_amdgpu_cs_chunk_syncobj *syncobj_deps; unsigned num_deps; @@ -1177,6 +1179,9 @@ static int amdgpu_cs_process_syncobj_timeline_out_dep(struct amdgpu_cs_parser *p num_deps = chunk->length_dw * 4 / sizeof(struct drm_amdgpu_cs_chunk_syncobj); + if (p->post_deps) + return -EINVAL; + p->post_deps = kmalloc_array(num_deps, sizeof(*p->post_deps), GFP_KERNEL); p->num_post_deps = 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index f539a2a92774..7398b4850649 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -534,21 +534,24 @@ int amdgpu_ctx_wait_prev_fence(struct amdgpu_ctx *ctx, struct drm_sched_entity *entity) { struct amdgpu_ctx_entity *centity = to_amdgpu_ctx_entity(entity); - unsigned idx = centity->sequence & (amdgpu_sched_jobs - 1); - struct dma_fence *other = centity->fences[idx]; + struct dma_fence *other; + unsigned idx; + long r; - if (other) { - signed long r; - r = dma_fence_wait(other, true); - if (r < 0) { - if (r != -ERESTARTSYS) - DRM_ERROR("Error (%ld) waiting for fence!\n", r); + spin_lock(&ctx->ring_lock); + idx = centity->sequence & (amdgpu_sched_jobs - 1); + other = dma_fence_get(centity->fences[idx]); + spin_unlock(&ctx->ring_lock); - return r; - } - } + if (!other) + return 0; - return 0; + r = dma_fence_wait(other, true); + if (r < 0 && r != -ERESTARTSYS) + DRM_ERROR("Error (%ld) waiting for fence!\n", r); + + dma_fence_put(other); + return r; } void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h index df8a23554831..f6ac1e9548f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h @@ -32,7 +32,6 @@ struct amdgpu_gds { uint32_t gws_size; uint32_t oa_size; uint32_t gds_compute_max_wave_id; - uint32_t vgt_gs_max_wave_id; }; struct amdgpu_gds_reg_offset { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h index 99f14fcc1460..19661c645703 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h @@ -30,6 +30,7 @@ #define AMDGPU_VCN_FIRMWARE_OFFSET 256 #define AMDGPU_VCN_MAX_ENC_RINGS 3 +#define VCN_DEC_KMD_CMD 0x80000000 #define VCN_DEC_CMD_FENCE 0x00000000 #define VCN_DEC_CMD_TRAP 0x00000001 #define VCN_DEC_CMD_WRITE_REG 0x00000004 diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 32773b7523d2..f41287f9000d 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -4206,15 +4206,6 @@ static void gfx_v10_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, unsigned vmid = AMDGPU_JOB_GET_VMID(job); u32 header, control = 0; - /* Prevent a hw deadlock due to a wave ID mismatch between ME and GDS. - * This resets the wave ID counters. (needed by transform feedback) - * TODO: This might only be needed on a VMID switch when we change - * the GDS OA mapping, not sure. - */ - amdgpu_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); - amdgpu_ring_write(ring, mmVGT_GS_MAX_WAVE_ID); - amdgpu_ring_write(ring, ring->adev->gds.vgt_gs_max_wave_id); - if (ib->flags & AMDGPU_IB_FLAG_CE) header = PACKET3(PACKET3_INDIRECT_BUFFER_CNST, 2); else @@ -4961,7 +4952,7 @@ static const struct amdgpu_ring_funcs gfx_v10_0_ring_funcs_gfx = { 5 + /* HDP_INVL */ 8 + 8 + /* FENCE x2 */ 2, /* SWITCH_BUFFER */ - .emit_ib_size = 7, /* gfx_v10_0_ring_emit_ib_gfx */ + .emit_ib_size = 4, /* gfx_v10_0_ring_emit_ib_gfx */ .emit_ib = gfx_v10_0_ring_emit_ib_gfx, .emit_fence = gfx_v10_0_ring_emit_fence, .emit_pipeline_sync = gfx_v10_0_ring_emit_pipeline_sync, @@ -5112,7 +5103,6 @@ static void gfx_v10_0_set_gds_init(struct amdgpu_device *adev) default: adev->gds.gds_size = 0x10000; adev->gds.gds_compute_max_wave_id = 0x4ff; - adev->gds.vgt_gs_max_wave_id = 0x3ff; break; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 751567f78567..ee1ccdcf2d30 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -1321,6 +1321,39 @@ static int gfx_v8_0_rlc_init(struct amdgpu_device *adev) return 0; } +static int gfx_v8_0_csb_vram_pin(struct amdgpu_device *adev) +{ + int r; + + r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false); + if (unlikely(r != 0)) + return r; + + r = amdgpu_bo_pin(adev->gfx.rlc.clear_state_obj, + AMDGPU_GEM_DOMAIN_VRAM); + if (!r) + adev->gfx.rlc.clear_state_gpu_addr = + amdgpu_bo_gpu_offset(adev->gfx.rlc.clear_state_obj); + + amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj); + + return r; +} + +static void gfx_v8_0_csb_vram_unpin(struct amdgpu_device *adev) +{ + int r; + + if (!adev->gfx.rlc.clear_state_obj) + return; + + r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, true); + if (likely(r == 0)) { + amdgpu_bo_unpin(adev->gfx.rlc.clear_state_obj); + amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj); + } +} + static void gfx_v8_0_mec_fini(struct amdgpu_device *adev) { amdgpu_bo_free_kernel(&adev->gfx.mec.hpd_eop_obj, NULL, NULL); @@ -4785,6 +4818,10 @@ static int gfx_v8_0_hw_init(void *handle) gfx_v8_0_init_golden_registers(adev); gfx_v8_0_constants_init(adev); + r = gfx_v8_0_csb_vram_pin(adev); + if (r) + return r; + r = adev->gfx.rlc.funcs->resume(adev); if (r) return r; @@ -4901,6 +4938,9 @@ static int gfx_v8_0_hw_fini(void *handle) else pr_err("rlc is busy, skip halt rlc\n"); amdgpu_gfx_rlc_exit_safe_mode(adev); + + gfx_v8_0_csb_vram_unpin(adev); + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 1cf639a51178..c066e1d3f981 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -596,14 +596,18 @@ static void gfx_v9_0_check_if_need_gfxoff(struct amdgpu_device *adev) case CHIP_VEGA20: break; case CHIP_RAVEN: - if (adev->rev_id >= 0x8 || adev->pdev->device == 0x15d8) - break; - if ((adev->gfx.rlc_fw_version != 106 && - adev->gfx.rlc_fw_version < 531) || - (adev->gfx.rlc_fw_version == 53815) || - (adev->gfx.rlc_feature_version < 1) || - !adev->gfx.rlc.is_rlc_v2_1) + if (!(adev->rev_id >= 0x8 || adev->pdev->device == 0x15d8) + &&((adev->gfx.rlc_fw_version != 106 && + adev->gfx.rlc_fw_version < 531) || + (adev->gfx.rlc_fw_version == 53815) || + (adev->gfx.rlc_feature_version < 1) || + !adev->gfx.rlc.is_rlc_v2_1)) adev->pm.pp_feature &= ~PP_GFXOFF_MASK; + + if (adev->pm.pp_feature & PP_GFXOFF_MASK) + adev->pg_flags |= AMD_PG_SUPPORT_GFX_PG | + AMD_PG_SUPPORT_CP | + AMD_PG_SUPPORT_RLC_SMU_HS; break; default: break; @@ -4869,7 +4873,7 @@ static void gfx_v9_0_ring_soft_recovery(struct amdgpu_ring *ring, unsigned vmid) value = REG_SET_FIELD(value, SQ_CMD, MODE, 0x01); value = REG_SET_FIELD(value, SQ_CMD, CHECK_VMID, 1); value = REG_SET_FIELD(value, SQ_CMD, VM_ID, vmid); - WREG32(mmSQ_CMD, value); + WREG32_SOC15(GC, 0, mmSQ_CMD, value); } static void gfx_v9_0_set_gfx_eop_interrupt_state(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/nv.c b/drivers/gpu/drm/amd/amdgpu/nv.c index 662612f89c70..9922bce3fd89 100644 --- a/drivers/gpu/drm/amd/amdgpu/nv.c +++ b/drivers/gpu/drm/amd/amdgpu/nv.c @@ -552,7 +552,6 @@ static int nv_common_early_init(void *handle) AMD_CG_SUPPORT_BIF_LS; adev->pg_flags = AMD_PG_SUPPORT_VCN | AMD_PG_SUPPORT_VCN_DPG | - AMD_PG_SUPPORT_MMHUB | AMD_PG_SUPPORT_ATHUB; adev->external_rev_id = adev->rev_id + 0x1; break; diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index 23265414d448..04fbf05d7176 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -992,11 +992,6 @@ static int soc15_common_early_init(void *handle) adev->pg_flags = AMD_PG_SUPPORT_SDMA | AMD_PG_SUPPORT_VCN; } - - if (adev->pm.pp_feature & PP_GFXOFF_MASK) - adev->pg_flags |= AMD_PG_SUPPORT_GFX_PG | - AMD_PG_SUPPORT_CP | - AMD_PG_SUPPORT_RLC_SMU_HS; break; default: /* FIXME: not supported yet */ diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c index 1cfc2620b2dd..dfde886cc6bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_0.c @@ -1485,7 +1485,7 @@ static void vcn_v2_0_dec_ring_insert_start(struct amdgpu_ring *ring) amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA0_INTERNAL_OFFSET, 0)); amdgpu_ring_write(ring, 0); amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD_INTERNAL_OFFSET, 0)); - amdgpu_ring_write(ring, VCN_DEC_CMD_PACKET_START << 1); + amdgpu_ring_write(ring, VCN_DEC_KMD_CMD | (VCN_DEC_CMD_PACKET_START << 1)); } /** @@ -1498,7 +1498,7 @@ static void vcn_v2_0_dec_ring_insert_start(struct amdgpu_ring *ring) static void vcn_v2_0_dec_ring_insert_end(struct amdgpu_ring *ring) { amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD_INTERNAL_OFFSET, 0)); - amdgpu_ring_write(ring, VCN_DEC_CMD_PACKET_END << 1); + amdgpu_ring_write(ring, VCN_DEC_KMD_CMD | (VCN_DEC_CMD_PACKET_END << 1)); } /** @@ -1543,7 +1543,7 @@ static void vcn_v2_0_dec_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 amdgpu_ring_write(ring, upper_32_bits(addr) & 0xff); amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD_INTERNAL_OFFSET, 0)); - amdgpu_ring_write(ring, VCN_DEC_CMD_FENCE << 1); + amdgpu_ring_write(ring, VCN_DEC_KMD_CMD | (VCN_DEC_CMD_FENCE << 1)); amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA0_INTERNAL_OFFSET, 0)); amdgpu_ring_write(ring, 0); @@ -1553,7 +1553,7 @@ static void vcn_v2_0_dec_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD_INTERNAL_OFFSET, 0)); - amdgpu_ring_write(ring, VCN_DEC_CMD_TRAP << 1); + amdgpu_ring_write(ring, VCN_DEC_KMD_CMD | (VCN_DEC_CMD_TRAP << 1)); } /** @@ -1597,7 +1597,7 @@ static void vcn_v2_0_dec_ring_emit_reg_wait(struct amdgpu_ring *ring, amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD_INTERNAL_OFFSET, 0)); - amdgpu_ring_write(ring, VCN_DEC_CMD_REG_READ_COND_WAIT << 1); + amdgpu_ring_write(ring, VCN_DEC_KMD_CMD | (VCN_DEC_CMD_REG_READ_COND_WAIT << 1)); } static void vcn_v2_0_dec_ring_emit_vm_flush(struct amdgpu_ring *ring, @@ -1626,7 +1626,7 @@ static void vcn_v2_0_dec_ring_emit_wreg(struct amdgpu_ring *ring, amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD_INTERNAL_OFFSET, 0)); - amdgpu_ring_write(ring, VCN_DEC_CMD_WRITE_REG << 1); + amdgpu_ring_write(ring, VCN_DEC_KMD_CMD | (VCN_DEC_CMD_WRITE_REG << 1)); } /** @@ -2079,6 +2079,36 @@ static int vcn_v2_0_process_interrupt(struct amdgpu_device *adev, return 0; } +static int vcn_v2_0_dec_ring_test_ring(struct amdgpu_ring *ring) +{ + struct amdgpu_device *adev = ring->adev; + uint32_t tmp = 0; + unsigned i; + int r; + + WREG32(adev->vcn.external.scratch9, 0xCAFEDEAD); + r = amdgpu_ring_alloc(ring, 4); + if (r) + return r; + amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD_INTERNAL_OFFSET, 0)); + amdgpu_ring_write(ring, VCN_DEC_KMD_CMD | (VCN_DEC_CMD_PACKET_START << 1)); + amdgpu_ring_write(ring, PACKET0(adev->vcn.internal.scratch9, 0)); + amdgpu_ring_write(ring, 0xDEADBEEF); + amdgpu_ring_commit(ring); + for (i = 0; i < adev->usec_timeout; i++) { + tmp = RREG32(adev->vcn.external.scratch9); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + + if (i >= adev->usec_timeout) + r = -ETIMEDOUT; + + return r; +} + + static int vcn_v2_0_set_powergating_state(void *handle, enum amd_powergating_state state) { @@ -2142,7 +2172,7 @@ static const struct amdgpu_ring_funcs vcn_v2_0_dec_ring_vm_funcs = { .emit_ib = vcn_v2_0_dec_ring_emit_ib, .emit_fence = vcn_v2_0_dec_ring_emit_fence, .emit_vm_flush = vcn_v2_0_dec_ring_emit_vm_flush, - .test_ring = amdgpu_vcn_dec_ring_test_ring, + .test_ring = vcn_v2_0_dec_ring_test_ring, .test_ib = amdgpu_vcn_dec_ring_test_ib, .insert_nop = vcn_v2_0_dec_ring_insert_nop, .insert_start = vcn_v2_0_dec_ring_insert_start, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 26b15cc56c31..1d3cd5c50d5f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1567,32 +1567,6 @@ copy_from_user_failed: return err; } -static int kfd_ioctl_alloc_queue_gws(struct file *filep, - struct kfd_process *p, void *data) -{ - int retval; - struct kfd_ioctl_alloc_queue_gws_args *args = data; - struct kfd_dev *dev; - - if (!hws_gws_support) - return -ENODEV; - - dev = kfd_device_by_id(args->gpu_id); - if (!dev) { - pr_debug("Could not find gpu id 0x%x\n", args->gpu_id); - return -ENODEV; - } - if (dev->dqm->sched_policy == KFD_SCHED_POLICY_NO_HWS) - return -ENODEV; - - mutex_lock(&p->mutex); - retval = pqm_set_gws(&p->pqm, args->queue_id, args->num_gws ? dev->gws : NULL); - mutex_unlock(&p->mutex); - - args->first_gws = 0; - return retval; -} - static int kfd_ioctl_get_dmabuf_info(struct file *filep, struct kfd_process *p, void *data) { @@ -1795,8 +1769,6 @@ static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { AMDKFD_IOCTL_DEF(AMDKFD_IOC_IMPORT_DMABUF, kfd_ioctl_import_dmabuf, 0), - AMDKFD_IOCTL_DEF(AMDKFD_IOC_ALLOC_QUEUE_GWS, - kfd_ioctl_alloc_queue_gws, 0), }; #define AMDKFD_CORE_IOCTL_COUNT ARRAY_SIZE(amdkfd_ioctls) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 4a29f72334d0..45be7a2132bb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3131,13 +3131,25 @@ static enum dc_color_depth convert_color_depth_from_display_info(const struct drm_connector *connector, const struct drm_connector_state *state) { - uint32_t bpc = connector->display_info.bpc; + uint8_t bpc = (uint8_t)connector->display_info.bpc; + + /* Assume 8 bpc by default if no bpc is specified. */ + bpc = bpc ? bpc : 8; if (!state) state = connector->state; if (state) { - bpc = state->max_bpc; + /* + * Cap display bpc based on the user requested value. + * + * The value for state->max_bpc may not correctly updated + * depending on when the connector gets added to the state + * or if this was called outside of atomic check, so it + * can't be used directly. + */ + bpc = min(bpc, state->max_requested_bpc); + /* Round down to the nearest even number. */ bpc = bpc - (bpc & 1); } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index fa20201eef3a..cbc480a33376 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -23,6 +23,7 @@ */ #include <linux/slab.h> +#include <linux/mm.h> #include "dm_services.h" @@ -1171,8 +1172,8 @@ bool dc_post_update_surfaces_to_stream(struct dc *dc) struct dc_state *dc_create_state(struct dc *dc) { - struct dc_state *context = kzalloc(sizeof(struct dc_state), - GFP_KERNEL); + struct dc_state *context = kvzalloc(sizeof(struct dc_state), + GFP_KERNEL); if (!context) return NULL; @@ -1192,11 +1193,11 @@ struct dc_state *dc_create_state(struct dc *dc) struct dc_state *dc_copy_state(struct dc_state *src_ctx) { int i, j; - struct dc_state *new_ctx = kmemdup(src_ctx, - sizeof(struct dc_state), GFP_KERNEL); + struct dc_state *new_ctx = kvmalloc(sizeof(struct dc_state), GFP_KERNEL); if (!new_ctx) return NULL; + memcpy(new_ctx, src_ctx, sizeof(struct dc_state)); for (i = 0; i < MAX_PIPES; i++) { struct pipe_ctx *cur_pipe = &new_ctx->res_ctx.pipe_ctx[i]; @@ -1230,7 +1231,7 @@ static void dc_state_free(struct kref *kref) { struct dc_state *context = container_of(kref, struct dc_state, refcount); dc_resource_state_destruct(context); - kfree(context); + kvfree(context); } void dc_release_state(struct dc_state *context) diff --git a/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c b/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c index 0685a3388e38..8a3eadeebdcb 100644 --- a/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/powerplay/amdgpu_smu.c @@ -315,6 +315,8 @@ int smu_get_power_num_states(struct smu_context *smu, int smu_common_read_sensor(struct smu_context *smu, enum amd_pp_sensors sensor, void *data, uint32_t *size) { + struct smu_power_context *smu_power = &smu->smu_power; + struct smu_power_gate *power_gate = &smu_power->power_gate; int ret = 0; switch (sensor) { @@ -339,7 +341,7 @@ int smu_common_read_sensor(struct smu_context *smu, enum amd_pp_sensors sensor, *size = 4; break; case AMDGPU_PP_SENSOR_VCN_POWER_STATE: - *(uint32_t *)data = smu_feature_is_enabled(smu, SMU_FEATURE_VCN_PG_BIT) ? 1 : 0; + *(uint32_t *)data = power_gate->vcn_gated ? 0 : 1; *size = 4; break; default: diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c index f27c6fbb192e..90c4e87ac5ad 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c @@ -2101,7 +2101,11 @@ static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr, if (ret) return ret; - *query = metrics_table.CurrSocketPower << 8; + /* For the 40.46 release, they changed the value name */ + if (hwmgr->smu_version == 0x282e00) + *query = metrics_table.AverageSocketPower << 8; + else + *query = metrics_table.CurrSocketPower << 8; return ret; } @@ -2349,12 +2353,16 @@ static int vega20_force_dpm_highest(struct pp_hwmgr *hwmgr) data->dpm_table.soc_table.dpm_state.soft_max_level = data->dpm_table.soc_table.dpm_levels[soft_level].value; - ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF); + ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK | + FEATURE_DPM_UCLK_MASK | + FEATURE_DPM_SOCCLK_MASK); PP_ASSERT_WITH_CODE(!ret, "Failed to upload boot level to highest!", return ret); - ret = vega20_upload_dpm_max_level(hwmgr, 0xFFFFFFFF); + ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK | + FEATURE_DPM_UCLK_MASK | + FEATURE_DPM_SOCCLK_MASK); PP_ASSERT_WITH_CODE(!ret, "Failed to upload dpm max level to highest!", return ret); @@ -2387,12 +2395,16 @@ static int vega20_force_dpm_lowest(struct pp_hwmgr *hwmgr) data->dpm_table.soc_table.dpm_state.soft_max_level = data->dpm_table.soc_table.dpm_levels[soft_level].value; - ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF); + ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK | + FEATURE_DPM_UCLK_MASK | + FEATURE_DPM_SOCCLK_MASK); PP_ASSERT_WITH_CODE(!ret, "Failed to upload boot level to highest!", return ret); - ret = vega20_upload_dpm_max_level(hwmgr, 0xFFFFFFFF); + ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK | + FEATURE_DPM_UCLK_MASK | + FEATURE_DPM_SOCCLK_MASK); PP_ASSERT_WITH_CODE(!ret, "Failed to upload dpm max level to highest!", return ret); @@ -2403,14 +2415,54 @@ static int vega20_force_dpm_lowest(struct pp_hwmgr *hwmgr) static int vega20_unforce_dpm_levels(struct pp_hwmgr *hwmgr) { + struct vega20_hwmgr *data = + (struct vega20_hwmgr *)(hwmgr->backend); + uint32_t soft_min_level, soft_max_level; int ret = 0; - ret = vega20_upload_dpm_min_level(hwmgr, 0xFFFFFFFF); + /* gfxclk soft min/max settings */ + soft_min_level = + vega20_find_lowest_dpm_level(&(data->dpm_table.gfx_table)); + soft_max_level = + vega20_find_highest_dpm_level(&(data->dpm_table.gfx_table)); + + data->dpm_table.gfx_table.dpm_state.soft_min_level = + data->dpm_table.gfx_table.dpm_levels[soft_min_level].value; + data->dpm_table.gfx_table.dpm_state.soft_max_level = + data->dpm_table.gfx_table.dpm_levels[soft_max_level].value; + + /* uclk soft min/max settings */ + soft_min_level = + vega20_find_lowest_dpm_level(&(data->dpm_table.mem_table)); + soft_max_level = + vega20_find_highest_dpm_level(&(data->dpm_table.mem_table)); + + data->dpm_table.mem_table.dpm_state.soft_min_level = + data->dpm_table.mem_table.dpm_levels[soft_min_level].value; + data->dpm_table.mem_table.dpm_state.soft_max_level = + data->dpm_table.mem_table.dpm_levels[soft_max_level].value; + + /* socclk soft min/max settings */ + soft_min_level = + vega20_find_lowest_dpm_level(&(data->dpm_table.soc_table)); + soft_max_level = + vega20_find_highest_dpm_level(&(data->dpm_table.soc_table)); + + data->dpm_table.soc_table.dpm_state.soft_min_level = + data->dpm_table.soc_table.dpm_levels[soft_min_level].value; + data->dpm_table.soc_table.dpm_state.soft_max_level = + data->dpm_table.soc_table.dpm_levels[soft_max_level].value; + + ret = vega20_upload_dpm_min_level(hwmgr, FEATURE_DPM_GFXCLK_MASK | + FEATURE_DPM_UCLK_MASK | + FEATURE_DPM_SOCCLK_MASK); PP_ASSERT_WITH_CODE(!ret, "Failed to upload DPM Bootup Levels!", return ret); - ret = vega20_upload_dpm_max_level(hwmgr, 0xFFFFFFFF); + ret = vega20_upload_dpm_max_level(hwmgr, FEATURE_DPM_GFXCLK_MASK | + FEATURE_DPM_UCLK_MASK | + FEATURE_DPM_SOCCLK_MASK); PP_ASSERT_WITH_CODE(!ret, "Failed to upload DPM Max Levels!", return ret); diff --git a/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h index 208e6711d506..a78b2e295895 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/powerplay/inc/amdgpu_smu.h @@ -451,6 +451,7 @@ struct smu_dpm_context { struct smu_power_gate { bool uvd_gated; bool vce_gated; + bool vcn_gated; }; struct smu_power_context { @@ -906,8 +907,6 @@ struct smu_funcs ((smu)->funcs->register_irq_handler ? (smu)->funcs->register_irq_handler(smu) : 0) #define smu_set_azalia_d3_pme(smu) \ ((smu)->funcs->set_azalia_d3_pme ? (smu)->funcs->set_azalia_d3_pme((smu)) : 0) -#define smu_get_uclk_dpm_states(smu, clocks_in_khz, num_states) \ - ((smu)->ppt_funcs->get_uclk_dpm_states ? (smu)->ppt_funcs->get_uclk_dpm_states((smu), (clocks_in_khz), (num_states)) : 0) #define smu_get_max_sustainable_clocks_by_dc(smu, max_clocks) \ ((smu)->funcs->get_max_sustainable_clocks_by_dc ? (smu)->funcs->get_max_sustainable_clocks_by_dc((smu), (max_clocks)) : 0) #define smu_get_uclk_dpm_states(smu, clocks_in_khz, num_states) \ diff --git a/drivers/gpu/drm/amd/powerplay/navi10_ppt.c b/drivers/gpu/drm/amd/powerplay/navi10_ppt.c index cc0a3b2256af..b81c7e715dc9 100644 --- a/drivers/gpu/drm/amd/powerplay/navi10_ppt.c +++ b/drivers/gpu/drm/amd/powerplay/navi10_ppt.c @@ -502,6 +502,8 @@ static int navi10_store_powerplay_table(struct smu_context *smu) static int navi10_tables_init(struct smu_context *smu, struct smu_table *tables) { + struct smu_table_context *smu_table = &smu->smu_table; + SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t), @@ -516,9 +518,35 @@ static int navi10_tables_init(struct smu_context *smu, struct smu_table *tables) sizeof(DpmActivityMonitorCoeffInt_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); + smu_table->metrics_table = kzalloc(sizeof(SmuMetrics_t), GFP_KERNEL); + if (!smu_table->metrics_table) + return -ENOMEM; + smu_table->metrics_time = 0; + return 0; } +static int navi10_get_metrics_table(struct smu_context *smu, + SmuMetrics_t *metrics_table) +{ + struct smu_table_context *smu_table= &smu->smu_table; + int ret = 0; + + if (!smu_table->metrics_time || time_after(jiffies, smu_table->metrics_time + HZ / 1000)) { + ret = smu_update_table(smu, SMU_TABLE_SMU_METRICS, 0, + (void *)smu_table->metrics_table, false); + if (ret) { + pr_info("Failed to export SMU metrics table!\n"); + return ret; + } + smu_table->metrics_time = jiffies; + } + + memcpy(metrics_table, smu_table->metrics_table, sizeof(SmuMetrics_t)); + + return ret; +} + static int navi10_allocate_dpm_context(struct smu_context *smu) { struct smu_dpm_context *smu_dpm = &smu->smu_dpm; @@ -577,20 +605,27 @@ static int navi10_set_default_dpm_table(struct smu_context *smu) static int navi10_dpm_set_uvd_enable(struct smu_context *smu, bool enable) { + struct smu_power_context *smu_power = &smu->smu_power; + struct smu_power_gate *power_gate = &smu_power->power_gate; int ret = 0; if (enable) { - ret = smu_send_smc_msg_with_param(smu, SMU_MSG_PowerUpVcn, 1); - if (ret) - return ret; + /* vcn dpm on is a prerequisite for vcn power gate messages */ + if (smu_feature_is_enabled(smu, SMU_FEATURE_VCN_PG_BIT)) { + ret = smu_send_smc_msg_with_param(smu, SMU_MSG_PowerUpVcn, 1); + if (ret) + return ret; + } + power_gate->vcn_gated = false; } else { - ret = smu_send_smc_msg(smu, SMU_MSG_PowerDownVcn); - if (ret) - return ret; + if (smu_feature_is_enabled(smu, SMU_FEATURE_VCN_PG_BIT)) { + ret = smu_send_smc_msg(smu, SMU_MSG_PowerDownVcn); + if (ret) + return ret; + } + power_gate->vcn_gated = true; } - ret = smu_feature_set_enabled(smu, SMU_FEATURE_VCN_PG_BIT, enable); - return ret; } @@ -598,15 +633,10 @@ static int navi10_get_current_clk_freq_by_table(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *value) { - static SmuMetrics_t metrics; int ret = 0, clk_id = 0; + SmuMetrics_t metrics; - if (!value) - return -EINVAL; - - memset(&metrics, 0, sizeof(metrics)); - - ret = smu_update_table(smu, SMU_TABLE_SMU_METRICS, 0, (void *)&metrics, false); + ret = navi10_get_metrics_table(smu, &metrics); if (ret) return ret; @@ -894,8 +924,9 @@ static int navi10_get_gpu_power(struct smu_context *smu, uint32_t *value) if (!value) return -EINVAL; - ret = smu_update_table(smu, SMU_TABLE_SMU_METRICS, 0, (void *)&metrics, - false); + ret = navi10_get_metrics_table(smu, &metrics); + if (ret) + return ret; if (ret) return ret; @@ -914,10 +945,7 @@ static int navi10_get_current_activity_percent(struct smu_context *smu, if (!value) return -EINVAL; - msleep(1); - - ret = smu_update_table(smu, SMU_TABLE_SMU_METRICS, 0, - (void *)&metrics, false); + ret = navi10_get_metrics_table(smu, &metrics); if (ret) return ret; @@ -956,10 +984,9 @@ static int navi10_get_fan_speed_rpm(struct smu_context *smu, if (!speed) return -EINVAL; - memset(&metrics, 0, sizeof(metrics)); - - ret = smu_update_table(smu, SMU_TABLE_SMU_METRICS, 0, - (void *)&metrics, false); + ret = navi10_get_metrics_table(smu, &metrics); + if (ret) + return ret; if (ret) return ret; @@ -1307,7 +1334,7 @@ static int navi10_thermal_get_temperature(struct smu_context *smu, if (!value) return -EINVAL; - ret = smu_update_table(smu, SMU_TABLE_SMU_METRICS, 0, (void *)&metrics, false); + ret = navi10_get_metrics_table(smu, &metrics); if (ret) return ret; diff --git a/drivers/gpu/drm/amd/powerplay/smu_v11_0.c b/drivers/gpu/drm/amd/powerplay/smu_v11_0.c index ac5b26228e75..53097961bf2b 100644 --- a/drivers/gpu/drm/amd/powerplay/smu_v11_0.c +++ b/drivers/gpu/drm/amd/powerplay/smu_v11_0.c @@ -326,7 +326,8 @@ static int smu_v11_0_setup_pptable(struct smu_context *smu) struct amdgpu_device *adev = smu->adev; const struct smc_firmware_header_v1_0 *hdr; int ret, index; - uint32_t size; + uint32_t size = 0; + uint16_t atom_table_size; uint8_t frev, crev; void *table; uint16_t version_major, version_minor; @@ -354,10 +355,11 @@ static int smu_v11_0_setup_pptable(struct smu_context *smu) index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, powerplayinfo); - ret = smu_get_atom_data_table(smu, index, (uint16_t *)&size, &frev, &crev, + ret = smu_get_atom_data_table(smu, index, &atom_table_size, &frev, &crev, (uint8_t **)&table); if (ret) return ret; + size = atom_table_size; } if (!smu->smu_table.power_play_table) @@ -1391,7 +1393,7 @@ smu_v11_0_smc_fan_control(struct smu_context *smu, bool start) { int ret = 0; - if (smu_feature_is_supported(smu, SMU_FEATURE_FAN_CONTROL_BIT)) + if (!smu_feature_is_supported(smu, SMU_FEATURE_FAN_CONTROL_BIT)) return 0; ret = smu_feature_set_enabled(smu, SMU_FEATURE_FAN_CONTROL_BIT, start); diff --git a/drivers/gpu/drm/amd/powerplay/vega20_ppt.c b/drivers/gpu/drm/amd/powerplay/vega20_ppt.c index dd6fd1c8bf24..6a14497257e4 100644 --- a/drivers/gpu/drm/amd/powerplay/vega20_ppt.c +++ b/drivers/gpu/drm/amd/powerplay/vega20_ppt.c @@ -3050,6 +3050,7 @@ static int vega20_get_fan_speed_percent(struct smu_context *smu, static int vega20_get_gpu_power(struct smu_context *smu, uint32_t *value) { + uint32_t smu_version; int ret = 0; SmuMetrics_t metrics; @@ -3060,7 +3061,15 @@ static int vega20_get_gpu_power(struct smu_context *smu, uint32_t *value) if (ret) return ret; - *value = metrics.CurrSocketPower << 8; + ret = smu_get_smc_version(smu, NULL, &smu_version); + if (ret) + return ret; + + /* For the 40.46 release, they changed the value name */ + if (smu_version == 0x282e00) + *value = metrics.AverageSocketPower << 8; + else + *value = metrics.CurrSocketPower << 8; return 0; } diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c index 5a118984de33..9d4d5075cc64 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c @@ -8,6 +8,7 @@ #include <linux/iommu.h> #include <linux/of_device.h> #include <linux/of_graph.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #ifdef CONFIG_DEBUG_FS @@ -126,7 +127,7 @@ static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np) pipe->of_output_port = of_graph_get_port_by_id(np, KOMEDA_OF_PORT_OUTPUT); - pipe->of_node = np; + pipe->of_node = of_node_get(np); return 0; } @@ -143,6 +144,12 @@ static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev) return mdev->irq; } + /* Get the optional framebuffer memory resource */ + ret = of_reserved_mem_device_init(dev); + if (ret && ret != -ENODEV) + return ret; + ret = 0; + for_each_available_child_of_node(np, child) { if (of_node_cmp(child->name, "pipeline") == 0) { ret = komeda_parse_pipe_dt(mdev, child); @@ -289,6 +296,8 @@ void komeda_dev_destroy(struct komeda_dev *mdev) mdev->n_pipelines = 0; + of_reserved_mem_device_release(dev); + if (funcs && funcs->cleanup) funcs->cleanup(mdev); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c index cd4d9f53ddef..c9a1edb9a000 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.c @@ -35,6 +35,25 @@ komeda_get_format_caps(struct komeda_format_caps_table *table, return NULL; } +u32 komeda_get_afbc_format_bpp(const struct drm_format_info *info, u64 modifier) +{ + u32 bpp; + + switch (info->format) { + case DRM_FORMAT_YUV420_8BIT: + bpp = 12; + break; + case DRM_FORMAT_YUV420_10BIT: + bpp = 15; + break; + default: + bpp = info->cpp[0] * 8; + break; + } + + return bpp; +} + /* Two assumptions * 1. RGB always has YTR * 2. Tiled RGB always has SC diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h index 3631910d33b5..32273cf18f7c 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_format_caps.h @@ -97,6 +97,9 @@ const struct komeda_format_caps * komeda_get_format_caps(struct komeda_format_caps_table *table, u32 fourcc, u64 modifier); +u32 komeda_get_afbc_format_bpp(const struct drm_format_info *info, + u64 modifier); + u32 *komeda_get_layer_fourcc_list(struct komeda_format_caps_table *table, u32 layer_type, u32 *n_fmts); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c index 3b0a70ed6aa0..1b01a625f40e 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c @@ -43,7 +43,7 @@ komeda_fb_afbc_size_check(struct komeda_fb *kfb, struct drm_file *file, struct drm_framebuffer *fb = &kfb->base; const struct drm_format_info *info = fb->format; struct drm_gem_object *obj; - u32 alignment_w = 0, alignment_h = 0, alignment_header, n_blocks; + u32 alignment_w = 0, alignment_h = 0, alignment_header, n_blocks, bpp; u64 min_size; obj = drm_gem_object_lookup(file, mode_cmd->handles[0]); @@ -88,8 +88,9 @@ komeda_fb_afbc_size_check(struct komeda_fb *kfb, struct drm_file *file, kfb->offset_payload = ALIGN(n_blocks * AFBC_HEADER_SIZE, alignment_header); + bpp = komeda_get_afbc_format_bpp(info, fb->modifier); kfb->afbc_size = kfb->offset_payload + n_blocks * - ALIGN(info->cpp[0] * AFBC_SUPERBLK_PIXELS, + ALIGN(bpp * AFBC_SUPERBLK_PIXELS / 8, AFBC_SUPERBLK_ALIGNMENT); min_size = kfb->afbc_size + fb->offsets[0]; if (min_size > obj->size) { diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index 419a8b0e5de8..69d9e26c60c8 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -14,6 +14,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_irq.h> +#include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> #include "komeda_dev.h" @@ -146,7 +147,6 @@ static int komeda_crtc_normalize_zpos(struct drm_crtc *crtc, struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(crtc_st); struct komeda_plane_state *kplane_st; struct drm_plane_state *plane_st; - struct drm_framebuffer *fb; struct drm_plane *plane; struct list_head zorder_list; int order = 0, err; @@ -172,7 +172,6 @@ static int komeda_crtc_normalize_zpos(struct drm_crtc *crtc, list_for_each_entry(kplane_st, &zorder_list, zlist_node) { plane_st = &kplane_st->base; - fb = plane_st->fb; plane = plane_st->plane; plane_st->normalized_zpos = order++; @@ -205,7 +204,7 @@ static int komeda_kms_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_st, *new_crtc_st; + struct drm_crtc_state *new_crtc_st; int i, err; err = drm_atomic_helper_check_modeset(dev, state); @@ -216,7 +215,7 @@ static int komeda_kms_check(struct drm_device *dev, * so need to add all affected_planes (even unchanged) to * drm_atomic_state. */ - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_st, new_crtc_st, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_st, i) { err = drm_atomic_add_affected_planes(state, crtc); if (err) return err; @@ -307,24 +306,33 @@ struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev) komeda_kms_irq_handler, IRQF_SHARED, drm->driver->name, drm); if (err) - goto cleanup_mode_config; + goto free_component_binding; err = mdev->funcs->enable_irq(mdev); if (err) - goto cleanup_mode_config; + goto free_component_binding; drm->irq_enabled = true; + drm_kms_helper_poll_init(drm); + err = drm_dev_register(drm, 0); if (err) - goto cleanup_mode_config; + goto free_interrupts; return kms; -cleanup_mode_config: +free_interrupts: + drm_kms_helper_poll_fini(drm); drm->irq_enabled = false; + mdev->funcs->disable_irq(mdev); +free_component_binding: + component_unbind_all(mdev->dev, drm); +cleanup_mode_config: drm_mode_config_cleanup(drm); komeda_kms_cleanup_private_objs(kms); + drm->dev_private = NULL; + drm_dev_put(drm); free_kms: kfree(kms); return ERR_PTR(err); @@ -335,12 +343,14 @@ void komeda_kms_detach(struct komeda_kms_dev *kms) struct drm_device *drm = &kms->base; struct komeda_dev *mdev = drm->dev_private; + drm_dev_unregister(drm); + drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); drm->irq_enabled = false; mdev->funcs->disable_irq(mdev); - drm_dev_unregister(drm); component_unbind_all(mdev->dev, drm); - komeda_kms_cleanup_private_objs(kms); drm_mode_config_cleanup(drm); + komeda_kms_cleanup_private_objs(kms); drm->dev_private = NULL; drm_dev_put(drm); } diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h index a90bcbb3cb23..14b683164544 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline.h @@ -480,6 +480,7 @@ void komeda_pipeline_dump_register(struct komeda_pipeline *pipe, struct seq_file *sf); /* component APIs */ +extern __printf(10, 11) struct komeda_component * komeda_component_add(struct komeda_pipeline *pipe, size_t comp_sz, u32 id, u32 hw_id, diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c index 617e1f7b8472..2851cac94d86 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c @@ -148,7 +148,7 @@ static int komeda_wb_connector_add(struct komeda_kms_dev *kms, if (!kcrtc->master->wb_layer) return 0; - kwb_conn = kzalloc(sizeof(*wb_conn), GFP_KERNEL); + kwb_conn = kzalloc(sizeof(*kwb_conn), GFP_KERNEL); if (!kwb_conn) return -ENOMEM; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 4c7e31cb45ff..a5d1494a3dc4 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -131,8 +131,8 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) /* Enable extended register access */ - ast_enable_mmio(dev); ast_open_key(ast); + ast_enable_mmio(dev); /* Find out whether P2A works or whether to use device-tree */ ast_detect_config_mode(dev, &scu_rev); @@ -576,6 +576,9 @@ void ast_driver_unload(struct drm_device *dev) { struct ast_private *ast = dev->dev_private; + /* enable standard VGA decode */ + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x04); + ast_release_firmware(dev); kfree(ast->dp501_fw_addr); ast_mode_fini(dev); diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index ffccbef962a4..a1cb020e07e5 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -604,7 +604,7 @@ static int ast_crtc_mode_set(struct drm_crtc *crtc, return -EINVAL; ast_open_key(ast); - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06); ast_set_std_reg(crtc, adjusted_mode, &vbios_mode); ast_set_crtc_reg(crtc, adjusted_mode, &vbios_mode); diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index f7d421359d56..c1d1ac51d1c2 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -46,7 +46,7 @@ void ast_enable_mmio(struct drm_device *dev) { struct ast_private *ast = dev->dev_private; - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04); + ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa1, 0x06); } diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 80fcd5dc1558..c814bcef18a4 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1454,6 +1454,7 @@ static int drm_mode_parse_cmdline_refresh(const char *str, char **end_ptr, } static int drm_mode_parse_cmdline_extra(const char *str, int length, + bool freestanding, const struct drm_connector *connector, struct drm_cmdline_mode *mode) { @@ -1462,9 +1463,15 @@ static int drm_mode_parse_cmdline_extra(const char *str, int length, for (i = 0; i < length; i++) { switch (str[i]) { case 'i': + if (freestanding) + return -EINVAL; + mode->interlace = true; break; case 'm': + if (freestanding) + return -EINVAL; + mode->margins = true; break; case 'D': @@ -1542,6 +1549,7 @@ static int drm_mode_parse_cmdline_res_mode(const char *str, unsigned int length, if (extras) { int ret = drm_mode_parse_cmdline_extra(end_ptr + i, 1, + false, connector, mode); if (ret) @@ -1669,6 +1677,22 @@ static int drm_mode_parse_cmdline_options(char *str, size_t len, return 0; } +static const char * const drm_named_modes_whitelist[] = { + "NTSC", + "PAL", +}; + +static bool drm_named_mode_is_in_whitelist(const char *mode, unsigned int size) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) + if (!strncmp(mode, drm_named_modes_whitelist[i], size)) + return true; + + return false; +} + /** * drm_mode_parse_command_line_for_connector - parse command line modeline for connector * @mode_option: optional per connector mode option @@ -1725,16 +1749,30 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, * bunch of things: * - We need to make sure that the first character (which * would be our resolution in X) is a digit. - * - However, if the X resolution is missing, then we end up - * with something like x<yres>, with our first character - * being an alpha-numerical character, which would be - * considered a named mode. + * - If not, then it's either a named mode or a force on/off. + * To distinguish between the two, we need to run the + * extra parsing function, and if not, then we consider it + * a named mode. * * If this isn't enough, we should add more heuristics here, * and matching unit-tests. */ - if (!isdigit(name[0]) && name[0] != 'x') + if (!isdigit(name[0]) && name[0] != 'x') { + unsigned int namelen = strlen(name); + + /* + * Only the force on/off options can be in that case, + * and they all take a single character. + */ + if (namelen == 1) { + ret = drm_mode_parse_cmdline_extra(name, namelen, true, + connector, mode); + if (!ret) + return true; + } + named_mode = true; + } /* Try to locate the bpp and refresh specifiers, if any */ bpp_ptr = strchr(name, '-'); @@ -1770,7 +1808,13 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, } if (named_mode) { - strncpy(mode->name, name, mode_end); + if (mode_end + 1 > DRM_DISPLAY_MODE_LEN) + return false; + + if (!drm_named_mode_is_in_whitelist(name, mode_end)) + return false; + + strscpy(mode->name, name, mode_end + 1); } else { ret = drm_mode_parse_cmdline_res_mode(name, mode_end, parse_extras, @@ -1809,7 +1853,7 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, extra_ptr != options_ptr) { int len = strlen(name) - (extra_ptr - name); - ret = drm_mode_parse_cmdline_extra(extra_ptr, len, + ret = drm_mode_parse_cmdline_extra(extra_ptr, len, false, connector, mode); if (ret) return false; diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 7925a176f900..1cb1fa74cfbc 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -1465,8 +1465,8 @@ static void ddi_dotclock_get(struct intel_crtc_state *pipe_config) else if (intel_crtc_has_dp_encoder(pipe_config)) dotclock = intel_dotclock_calculate(pipe_config->port_clock, &pipe_config->dp_m_n); - else if (pipe_config->has_hdmi_sink && pipe_config->pipe_bpp == 36) - dotclock = pipe_config->port_clock * 2 / 3; + else if (pipe_config->has_hdmi_sink && pipe_config->pipe_bpp > 24) + dotclock = pipe_config->port_clock * 24 / pipe_config->pipe_bpp; else dotclock = pipe_config->port_clock; diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 60652ebbdf61..8aa6a31e8ad0 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -128,7 +128,15 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, limits.max_lane_count = intel_dp_max_lane_count(intel_dp); limits.min_bpp = intel_dp_min_bpp(pipe_config); - limits.max_bpp = pipe_config->pipe_bpp; + /* + * FIXME: If all the streams can't fit into the link with + * their current pipe_bpp we should reduce pipe_bpp across + * the board until things start to fit. Until then we + * limit to <= 8bpc since that's what was hardcoded for all + * MST streams previously. This hack should be removed once + * we have the proper retry logic in place. + */ + limits.max_bpp = min(pipe_config->pipe_bpp, 24); intel_dp_adjust_compliance_config(intel_dp, pipe_config, &limits); @@ -539,7 +547,15 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); - drm_connector_attach_max_bpc_property(connector, 6, 12); + + /* + * Reuse the prop from the SST connector because we're + * not allowed to create new props after device registration. + */ + connector->max_bpc_property = + intel_dp->attached_connector->base.max_bpc_property; + if (connector->max_bpc_property) + drm_connector_attach_max_bpc_property(connector, 6, 12); return connector; diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index bc3a94d491c4..27bd7276a82d 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -536,7 +536,8 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) if (drm_hdcp_check_ksvs_revoked(dev, ksv_fifo, num_downstream)) { DRM_ERROR("Revoked Ksv(s) in ksv_fifo\n"); - return -EPERM; + ret = -EPERM; + goto err; } /* diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c index ffec807b8960..f413904a3e96 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc.c +++ b/drivers/gpu/drm/i915/display/intel_vdsc.c @@ -541,7 +541,7 @@ static void intel_configure_pps_for_dsc_encoder(struct intel_encoder *encoder, pps_val |= DSC_PIC_HEIGHT(vdsc_cfg->pic_height) | DSC_PIC_WIDTH(vdsc_cfg->pic_width / num_vdsc_instances); DRM_INFO("PPS2 = 0x%08x\n", pps_val); - if (encoder->type == INTEL_OUTPUT_EDP) { + if (cpu_transcoder == TRANSCODER_EDP) { I915_WRITE(DSCA_PICTURE_PARAMETER_SET_2, pps_val); /* * If 2 VDSC instances are needed, configure PPS for second diff --git a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c index 99cc3e2e9c2c..f016a776a39e 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c @@ -396,8 +396,8 @@ static void glk_dsi_program_esc_clock(struct drm_device *dev, else txesc2_div = 10; - I915_WRITE(MIPIO_TXESC_CLK_DIV1, txesc1_div & GLK_TX_ESC_CLK_DIV1_MASK); - I915_WRITE(MIPIO_TXESC_CLK_DIV2, txesc2_div & GLK_TX_ESC_CLK_DIV2_MASK); + I915_WRITE(MIPIO_TXESC_CLK_DIV1, (1 << (txesc1_div - 1)) & GLK_TX_ESC_CLK_DIV1_MASK); + I915_WRITE(MIPIO_TXESC_CLK_DIV2, (1 << (txesc2_div - 1)) & GLK_TX_ESC_CLK_DIV2_MASK); } /* Program BXT Mipi clocks and dividers */ diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index 5fae0e50aad0..41dab9ea33cd 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -1628,6 +1628,7 @@ static int check_relocations(const struct drm_i915_gem_exec_object2 *entry) static int eb_copy_relocations(const struct i915_execbuffer *eb) { + struct drm_i915_gem_relocation_entry *relocs; const unsigned int count = eb->buffer_count; unsigned int i; int err; @@ -1635,7 +1636,6 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb) for (i = 0; i < count; i++) { const unsigned int nreloc = eb->exec[i].relocation_count; struct drm_i915_gem_relocation_entry __user *urelocs; - struct drm_i915_gem_relocation_entry *relocs; unsigned long size; unsigned long copied; @@ -1663,14 +1663,8 @@ static int eb_copy_relocations(const struct i915_execbuffer *eb) if (__copy_from_user((char *)relocs + copied, (char __user *)urelocs + copied, - len)) { -end_user: - user_access_end(); -end: - kvfree(relocs); - err = -EFAULT; - goto err; - } + len)) + goto end; copied += len; } while (copied < size); @@ -1699,10 +1693,14 @@ end: return 0; +end_user: + user_access_end(); +end: + kvfree(relocs); + err = -EFAULT; err: while (i--) { - struct drm_i915_gem_relocation_entry *relocs = - u64_to_ptr(typeof(*relocs), eb->exec[i].relocs_ptr); + relocs = u64_to_ptr(typeof(*relocs), eb->exec[i].relocs_ptr); if (eb->exec[i].relocation_count) kvfree(relocs); } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c index 2caa594322bc..528b61678334 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c @@ -664,15 +664,7 @@ i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj, for_each_sgt_page(page, sgt_iter, pages) { if (obj->mm.dirty) - /* - * As this may not be anonymous memory (e.g. shmem) - * but exist on a real mapping, we have to lock - * the page in order to dirty it -- holding - * the page reference is not sufficient to - * prevent the inode from being truncated. - * Play safe and take the lock. - */ - set_page_dirty_lock(page); + set_page_dirty(page); mark_page_accessed(page); put_page(page); diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c index 98dfb086320f..99e8242194c0 100644 --- a/drivers/gpu/drm/i915/gt/intel_workarounds.c +++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c @@ -308,11 +308,6 @@ static void gen9_ctx_workarounds_init(struct intel_engine_cs *engine, FLOW_CONTROL_ENABLE | PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE); - /* Syncing dependencies between camera and graphics:skl,bxt,kbl */ - if (!IS_COFFEELAKE(i915)) - WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3, - GEN9_DISABLE_OCL_OOB_SUPPRESS_LOGIC); - /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl,glk,cfl */ /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl,cfl */ WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7, diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 9f3fd7d96a69..75baff657e43 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -1528,9 +1528,9 @@ intel_vgpu_create_workload(struct intel_vgpu *vgpu, int ring_id, if (!intel_gvt_ggtt_validate_range(vgpu, workload->wa_ctx.indirect_ctx.guest_gma, workload->wa_ctx.indirect_ctx.size)) { - kmem_cache_free(s->workloads, workload); gvt_vgpu_err("invalid wa_ctx at: 0x%lx\n", workload->wa_ctx.indirect_ctx.guest_gma); + kmem_cache_free(s->workloads, workload); return ERR_PTR(-EINVAL); } } @@ -1542,9 +1542,9 @@ intel_vgpu_create_workload(struct intel_vgpu *vgpu, int ring_id, if (!intel_gvt_ggtt_validate_range(vgpu, workload->wa_ctx.per_ctx.guest_gma, CACHELINE_BYTES)) { - kmem_cache_free(s->workloads, workload); gvt_vgpu_err("invalid per_ctx at: 0x%lx\n", workload->wa_ctx.per_ctx.guest_gma); + kmem_cache_free(s->workloads, workload); return ERR_PTR(-EINVAL); } } diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f62e3397d936..bac1ee94f63f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1598,6 +1598,12 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv) pci_set_master(pdev); + /* + * We don't have a max segment size, so set it to the max so sg's + * debugging layer doesn't complain + */ + dma_set_max_seg_size(&pdev->dev, UINT_MAX); + /* overlay on gen2 is broken and can't address above 1G */ if (IS_GEN(dev_priv, 2)) { ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(30)); diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index 94d3992b599d..724627afdedc 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -101,6 +101,9 @@ static struct _balloon_info_ bl_info; static void vgt_deballoon_space(struct i915_ggtt *ggtt, struct drm_mm_node *node) { + if (!drm_mm_node_allocated(node)) + return; + DRM_DEBUG_DRIVER("deballoon space: range [0x%llx - 0x%llx] %llu KiB.\n", node->start, node->start + node->size, diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1d58f7ec5d84..f11979879e7b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -829,7 +829,7 @@ struct intel_crtc_state { /* * Frequence the dpll for the port should run at. Differs from the - * adjusted dotclock e.g. for DP or 12bpc hdmi mode. This is also + * adjusted dotclock e.g. for DP or 10/12bpc hdmi mode. This is also * already multiplied by pixel_multiplier. */ int port_clock; diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index e9f9e9fb9b17..6381652a8829 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -656,10 +656,9 @@ static int ingenic_drm_probe(struct platform_device *pdev) return ret; } - if (panel) { + if (panel) bridge = devm_drm_panel_bridge_add(dev, panel, - DRM_MODE_CONNECTOR_Unknown); - } + DRM_MODE_CONNECTOR_DPI); priv->dma_hwdesc = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc), &priv->dma_hwdesc_phys, diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index 477c0f766663..b609dc030d6c 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c @@ -342,7 +342,7 @@ int lima_gem_wait(struct drm_file *file, u32 handle, u32 op, s64 timeout_ns) timeout = drm_timeout_abs_to_jiffies(timeout_ns); ret = drm_gem_reservation_object_wait(file, handle, write, timeout); - if (ret == 0) + if (ret == -ETIME) ret = timeout ? -ETIMEDOUT : -EBUSY; return ret; diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 95fdbd0fbcac..945bc20f1d33 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -17,6 +17,7 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/dma-mapping.h> #include "mtk_drm_crtc.h" #include "mtk_drm_ddp.h" @@ -213,6 +214,7 @@ static int mtk_drm_kms_init(struct drm_device *drm) struct mtk_drm_private *private = drm->dev_private; struct platform_device *pdev; struct device_node *np; + struct device *dma_dev; int ret; if (!iommu_present(&platform_bus_type)) @@ -275,7 +277,29 @@ static int mtk_drm_kms_init(struct drm_device *drm) goto err_component_unbind; } - private->dma_dev = &pdev->dev; + dma_dev = &pdev->dev; + private->dma_dev = dma_dev; + + /* + * Configure the DMA segment size to make sure we get contiguous IOVA + * when importing PRIME buffers. + */ + if (!dma_dev->dma_parms) { + private->dma_parms_allocated = true; + dma_dev->dma_parms = + devm_kzalloc(drm->dev, sizeof(*dma_dev->dma_parms), + GFP_KERNEL); + } + if (!dma_dev->dma_parms) { + ret = -ENOMEM; + goto err_component_unbind; + } + + ret = dma_set_max_seg_size(dma_dev, (unsigned int)DMA_BIT_MASK(32)); + if (ret) { + dev_err(dma_dev, "Failed to set DMA segment size\n"); + goto err_unset_dma_parms; + } /* * We don't use the drm_irq_install() helpers provided by the DRM @@ -285,13 +309,16 @@ static int mtk_drm_kms_init(struct drm_device *drm) drm->irq_enabled = true; ret = drm_vblank_init(drm, MAX_CRTC); if (ret < 0) - goto err_component_unbind; + goto err_unset_dma_parms; drm_kms_helper_poll_init(drm); drm_mode_config_reset(drm); return 0; +err_unset_dma_parms: + if (private->dma_parms_allocated) + dma_dev->dma_parms = NULL; err_component_unbind: component_unbind_all(drm->dev, drm); err_config_cleanup: @@ -302,9 +329,14 @@ err_config_cleanup: static void mtk_drm_kms_deinit(struct drm_device *drm) { + struct mtk_drm_private *private = drm->dev_private; + drm_kms_helper_poll_fini(drm); drm_atomic_helper_shutdown(drm); + if (private->dma_parms_allocated) + private->dma_dev->dma_parms = NULL; + component_unbind_all(drm->dev, drm); drm_mode_config_cleanup(drm); } @@ -320,6 +352,18 @@ static const struct file_operations mtk_drm_fops = { .compat_ioctl = drm_compat_ioctl, }; +/* + * We need to override this because the device used to import the memory is + * not dev->dev, as drm_gem_prime_import() expects. + */ +struct drm_gem_object *mtk_drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct mtk_drm_private *private = dev->dev_private; + + return drm_gem_prime_import_dev(dev, dma_buf, private->dma_dev); +} + static struct drm_driver mtk_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, @@ -331,7 +375,7 @@ static struct drm_driver mtk_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = drm_gem_prime_export, - .gem_prime_import = drm_gem_prime_import, + .gem_prime_import = mtk_drm_gem_prime_import, .gem_prime_get_sg_table = mtk_gem_prime_get_sg_table, .gem_prime_import_sg_table = mtk_gem_prime_import_sg_table, .gem_prime_mmap = mtk_drm_gem_mmap_buf, @@ -524,12 +568,15 @@ static int mtk_drm_probe(struct platform_device *pdev) comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); if (!comp) { ret = -ENOMEM; + of_node_put(node); goto err_node; } ret = mtk_ddp_comp_init(dev, node, comp, comp_id, NULL); - if (ret) + if (ret) { + of_node_put(node); goto err_node; + } private->ddp_comp[comp_id] = comp; } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h index 598ff3e70446..e03fea12ff59 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h @@ -51,6 +51,8 @@ struct mtk_drm_private { } commit; struct drm_atomic_state *suspend_state; + + bool dma_parms_allocated; }; extern struct platform_driver mtk_ddp_driver; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 126703816794..5c36c75232e6 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -771,16 +771,20 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); int slots; - /* When restoring duplicated states, we need to make sure that the - * bw remains the same and avoid recalculating it, as the connector's - * bpc may have changed after the state was duplicated - */ - if (!state->duplicated) - asyh->dp.pbn = - drm_dp_calc_pbn_mode(crtc_state->adjusted_mode.clock, - connector->display_info.bpc * 3); + if (crtc_state->mode_changed || crtc_state->connectors_changed) { + /* + * When restoring duplicated states, we need to make sure that + * the bw remains the same and avoid recalculating it, as the + * connector's bpc may have changed after the state was + * duplicated + */ + if (!state->duplicated) { + const int bpp = connector->display_info.bpc * 3; + const int clock = crtc_state->adjusted_mode.clock; + + asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, bpp); + } - if (crtc_state->mode_changed) { slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port, asyh->dp.pbn); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c index b4e7404fe660..a11637b0f6cc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c @@ -40,8 +40,7 @@ nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) u8 *ptr = msg->buf; while (remaining) { - u8 cnt = (remaining > 16) ? 16 : remaining; - u8 cmd; + u8 cnt, retries, cmd; if (msg->flags & I2C_M_RD) cmd = 1; @@ -51,10 +50,19 @@ nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (mcnt || remaining > 16) cmd |= 4; /* MOT */ - ret = aux->func->xfer(aux, true, cmd, msg->addr, ptr, &cnt); - if (ret < 0) { - nvkm_i2c_aux_release(aux); - return ret; + for (retries = 0, cnt = 0; + retries < 32 && !cnt; + retries++) { + cnt = min_t(u8, remaining, 16); + ret = aux->func->xfer(aux, true, cmd, + msg->addr, ptr, &cnt); + if (ret < 0) + goto out; + } + if (!cnt) { + AUX_TRACE(aux, "no data after 32 retries"); + ret = -EIO; + goto out; } ptr += cnt; @@ -64,8 +72,10 @@ nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) msg++; } + ret = num; +out: nvkm_i2c_aux_release(aux); - return num; + return ret; } static u32 diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c index 84a2f243ed9b..4695f1c8e33f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c @@ -190,6 +190,9 @@ MODULE_FIRMWARE("nvidia/gp102/nvdec/scrubber.bin"); MODULE_FIRMWARE("nvidia/gp102/sec2/desc.bin"); MODULE_FIRMWARE("nvidia/gp102/sec2/image.bin"); MODULE_FIRMWARE("nvidia/gp102/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gp102/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp102/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp102/sec2/sig-1.bin"); MODULE_FIRMWARE("nvidia/gp104/acr/bl.bin"); MODULE_FIRMWARE("nvidia/gp104/acr/unload_bl.bin"); MODULE_FIRMWARE("nvidia/gp104/acr/ucode_load.bin"); @@ -210,6 +213,9 @@ MODULE_FIRMWARE("nvidia/gp104/nvdec/scrubber.bin"); MODULE_FIRMWARE("nvidia/gp104/sec2/desc.bin"); MODULE_FIRMWARE("nvidia/gp104/sec2/image.bin"); MODULE_FIRMWARE("nvidia/gp104/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp104/sec2/sig-1.bin"); MODULE_FIRMWARE("nvidia/gp106/acr/bl.bin"); MODULE_FIRMWARE("nvidia/gp106/acr/unload_bl.bin"); MODULE_FIRMWARE("nvidia/gp106/acr/ucode_load.bin"); @@ -230,6 +236,9 @@ MODULE_FIRMWARE("nvidia/gp106/nvdec/scrubber.bin"); MODULE_FIRMWARE("nvidia/gp106/sec2/desc.bin"); MODULE_FIRMWARE("nvidia/gp106/sec2/image.bin"); MODULE_FIRMWARE("nvidia/gp106/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp106/sec2/sig-1.bin"); MODULE_FIRMWARE("nvidia/gp107/acr/bl.bin"); MODULE_FIRMWARE("nvidia/gp107/acr/unload_bl.bin"); MODULE_FIRMWARE("nvidia/gp107/acr/ucode_load.bin"); @@ -250,3 +259,6 @@ MODULE_FIRMWARE("nvidia/gp107/nvdec/scrubber.bin"); MODULE_FIRMWARE("nvidia/gp107/sec2/desc.bin"); MODULE_FIRMWARE("nvidia/gp107/sec2/image.bin"); MODULE_FIRMWARE("nvidia/gp107/sec2/sig.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/desc-1.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/image-1.bin"); +MODULE_FIRMWARE("nvidia/gp107/sec2/sig-1.bin"); diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index de0f882f0f7b..14b41de44ebc 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -4,6 +4,7 @@ * Author: Archit Taneja <archit@ti.com> */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -20,7 +21,8 @@ int omapdss_device_init_output(struct omap_dss_device *out) { struct device_node *remote_node; - remote_node = of_graph_get_remote_node(out->dev->of_node, 0, 0); + remote_node = of_graph_get_remote_node(out->dev->of_node, + ffs(out->of_ports) - 1, 0); if (!remote_node) { dev_dbg(out->dev, "failed to find video sink\n"); return 0; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 288c59dae56a..1bad0a2cc5c6 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -669,7 +669,7 @@ static int pdev_probe(struct platform_device *pdev) if (omapdss_is_initialized() == false) return -EPROBE_DEFER; - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { dev_err(&pdev->dev, "Failed to set the DMA mask\n"); return ret; diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 92ac995dd9c6..6e8145c36e93 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -222,7 +222,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) size_t unmapped_page; size_t pgsize = get_pgsize(iova, len - unmapped_len); - unmapped_page = ops->unmap(ops, iova, pgsize); + unmapped_page = ops->unmap(ops, iova, pgsize, NULL); if (!unmapped_page) break; @@ -247,20 +247,28 @@ static void mmu_tlb_inv_context_s1(void *cookie) mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM); } -static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size, - size_t granule, bool leaf, void *cookie) -{} - static void mmu_tlb_sync_context(void *cookie) { //struct panfrost_device *pfdev = cookie; // TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X } -static const struct iommu_gather_ops mmu_tlb_ops = { +static void mmu_tlb_flush_walk(unsigned long iova, size_t size, size_t granule, + void *cookie) +{ + mmu_tlb_sync_context(cookie); +} + +static void mmu_tlb_flush_leaf(unsigned long iova, size_t size, size_t granule, + void *cookie) +{ + mmu_tlb_sync_context(cookie); +} + +static const struct iommu_flush_ops mmu_tlb_ops = { .tlb_flush_all = mmu_tlb_inv_context_s1, - .tlb_add_flush = mmu_tlb_inv_range_nosync, - .tlb_sync = mmu_tlb_sync_context, + .tlb_flush_walk = mmu_tlb_flush_walk, + .tlb_flush_leaf = mmu_tlb_flush_leaf, }; static const char *access_type_name(struct panfrost_device *pfdev, diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index f33e349c4ec5..952201c6d821 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -59,6 +59,11 @@ module_param_named(num_heads, qxl_num_crtc, int, 0400); static struct drm_driver qxl_driver; static struct pci_driver qxl_pci_driver; +static bool is_vga(struct pci_dev *pdev) +{ + return pdev->class == PCI_CLASS_DISPLAY_VGA << 8; +} + static int qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -83,9 +88,17 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto disable_pci; + if (is_vga(pdev)) { + ret = vga_get_interruptible(pdev, VGA_RSRC_LEGACY_IO); + if (ret) { + DRM_ERROR("can't get legacy vga ioports\n"); + goto disable_pci; + } + } + ret = qxl_device_init(qdev, &qxl_driver, pdev); if (ret) - goto disable_pci; + goto put_vga; ret = qxl_modeset_init(qdev); if (ret) @@ -105,6 +118,9 @@ modeset_cleanup: qxl_modeset_fini(qdev); unload: qxl_device_fini(qdev); +put_vga: + if (is_vga(pdev)) + vga_put(pdev, VGA_RSRC_LEGACY_IO); disable_pci: pci_disable_device(pdev); free_dev: @@ -122,6 +138,8 @@ qxl_pci_remove(struct pci_dev *pdev) qxl_modeset_fini(qdev); qxl_device_fini(qdev); + if (is_vga(pdev)) + vga_put(pdev, VGA_RSRC_LEGACY_IO); dev->dev_private = NULL; kfree(qdev); diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 1c62578590f4..082d02c84024 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -673,10 +673,8 @@ static int rcar_lvds_parse_dt_companion(struct rcar_lvds *lvds) /* Locate the companion LVDS encoder for dual-link operation, if any. */ companion = of_parse_phandle(dev->of_node, "renesas,companion", 0); - if (!companion) { - dev_err(dev, "Companion LVDS encoder not found\n"); - return -ENXIO; - } + if (!companion) + return 0; /* * Sanity check: the companion encoder must have the same compatible diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 95e5c517a15f..9aae3d8e99ef 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -432,7 +432,7 @@ static int rockchip_dp_resume(struct device *dev) static const struct dev_pm_ops rockchip_dp_pm_ops = { #ifdef CONFIG_PM_SLEEP - .suspend = rockchip_dp_suspend, + .suspend_late = rockchip_dp_suspend, .resume_early = rockchip_dp_resume, #endif }; diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index 35ddbec1375a..671c90f34ede 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -95,7 +95,7 @@ static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity) rmb(); /* for list_empty to work without lock */ if (list_empty(&entity->list) || - spsc_queue_peek(&entity->job_queue) == NULL) + spsc_queue_count(&entity->job_queue) == 0) return true; return false; @@ -281,7 +281,7 @@ void drm_sched_entity_fini(struct drm_sched_entity *entity) /* Consumption of existing IBs wasn't completed. Forcefully * remove them here. */ - if (spsc_queue_peek(&entity->job_queue)) { + if (spsc_queue_count(&entity->job_queue)) { if (sched) { /* Park the kernel for a moment to make sure it isn't processing * our enity. diff --git a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h index b45824ec7c8f..6d61a0eb5d64 100644 --- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h +++ b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h @@ -9,6 +9,13 @@ #define cmdline_test(test) selftest(test, test) +cmdline_test(drm_cmdline_test_force_d_only) +cmdline_test(drm_cmdline_test_force_D_only_dvi) +cmdline_test(drm_cmdline_test_force_D_only_hdmi) +cmdline_test(drm_cmdline_test_force_D_only_not_digital) +cmdline_test(drm_cmdline_test_force_e_only) +cmdline_test(drm_cmdline_test_margin_only) +cmdline_test(drm_cmdline_test_interlace_only) cmdline_test(drm_cmdline_test_res) cmdline_test(drm_cmdline_test_res_missing_x) cmdline_test(drm_cmdline_test_res_missing_y) diff --git a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c index 14c96edb13df..013de9d27c35 100644 --- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c +++ b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c @@ -17,6 +17,136 @@ static const struct drm_connector no_connector = {}; +static int drm_cmdline_test_force_e_only(void *ignored) +{ + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("e", + &no_connector, + &mode)); + FAIL_ON(mode.specified); + FAIL_ON(mode.refresh_specified); + FAIL_ON(mode.bpp_specified); + + FAIL_ON(mode.rb); + FAIL_ON(mode.cvt); + FAIL_ON(mode.interlace); + FAIL_ON(mode.margins); + FAIL_ON(mode.force != DRM_FORCE_ON); + + return 0; +} + +static int drm_cmdline_test_force_D_only_not_digital(void *ignored) +{ + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("D", + &no_connector, + &mode)); + FAIL_ON(mode.specified); + FAIL_ON(mode.refresh_specified); + FAIL_ON(mode.bpp_specified); + + FAIL_ON(mode.rb); + FAIL_ON(mode.cvt); + FAIL_ON(mode.interlace); + FAIL_ON(mode.margins); + FAIL_ON(mode.force != DRM_FORCE_ON); + + return 0; +} + +static const struct drm_connector connector_hdmi = { + .connector_type = DRM_MODE_CONNECTOR_HDMIB, +}; + +static int drm_cmdline_test_force_D_only_hdmi(void *ignored) +{ + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("D", + &connector_hdmi, + &mode)); + FAIL_ON(mode.specified); + FAIL_ON(mode.refresh_specified); + FAIL_ON(mode.bpp_specified); + + FAIL_ON(mode.rb); + FAIL_ON(mode.cvt); + FAIL_ON(mode.interlace); + FAIL_ON(mode.margins); + FAIL_ON(mode.force != DRM_FORCE_ON_DIGITAL); + + return 0; +} + +static const struct drm_connector connector_dvi = { + .connector_type = DRM_MODE_CONNECTOR_DVII, +}; + +static int drm_cmdline_test_force_D_only_dvi(void *ignored) +{ + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("D", + &connector_dvi, + &mode)); + FAIL_ON(mode.specified); + FAIL_ON(mode.refresh_specified); + FAIL_ON(mode.bpp_specified); + + FAIL_ON(mode.rb); + FAIL_ON(mode.cvt); + FAIL_ON(mode.interlace); + FAIL_ON(mode.margins); + FAIL_ON(mode.force != DRM_FORCE_ON_DIGITAL); + + return 0; +} + +static int drm_cmdline_test_force_d_only(void *ignored) +{ + struct drm_cmdline_mode mode = { }; + + FAIL_ON(!drm_mode_parse_command_line_for_connector("d", + &no_connector, + &mode)); + FAIL_ON(mode.specified); + FAIL_ON(mode.refresh_specified); + FAIL_ON(mode.bpp_specified); + + FAIL_ON(mode.rb); + FAIL_ON(mode.cvt); + FAIL_ON(mode.interlace); + FAIL_ON(mode.margins); + FAIL_ON(mode.force != DRM_FORCE_OFF); + + return 0; +} + +static int drm_cmdline_test_margin_only(void *ignored) +{ + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("m", + &no_connector, + &mode)); + + return 0; +} + +static int drm_cmdline_test_interlace_only(void *ignored) +{ + struct drm_cmdline_mode mode = { }; + + FAIL_ON(drm_mode_parse_command_line_for_connector("i", + &no_connector, + &mode)); + + return 0; +} + static int drm_cmdline_test_res(void *ignored) { struct drm_cmdline_mode mode = { }; diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 64c43ee6bd92..df0cc8f46d7b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -314,6 +314,7 @@ static void sun4i_tcon0_mode_set_dithering(struct sun4i_tcon *tcon, /* R and B components are only 5 bits deep */ val |= SUN4I_TCON0_FRM_CTL_MODE_R; val |= SUN4I_TCON0_FRM_CTL_MODE_B; + /* Fall through */ case MEDIA_BUS_FMT_RGB666_1X18: case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* Fall through: enable dithering */ diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index a1fc8b520985..b889ad3e86e1 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -993,6 +993,7 @@ static ssize_t sun6i_dsi_transfer(struct mipi_dsi_host *host, ret = sun6i_dsi_dcs_read(dsi, msg); break; } + /* Else, fall through */ default: ret = -EINVAL; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 274cb955e2e1..bdcaa4c7168c 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -126,8 +126,12 @@ int tegra_output_probe(struct tegra_output *output) "nvidia,hpd-gpio", 0, GPIOD_IN, "HDMI hotplug detect"); - if (IS_ERR(output->hpd_gpio)) - return PTR_ERR(output->hpd_gpio); + if (IS_ERR(output->hpd_gpio)) { + if (PTR_ERR(output->hpd_gpio) != -ENOENT) + return PTR_ERR(output->hpd_gpio); + + output->hpd_gpio = NULL; + } if (output->hpd_gpio) { err = gpiod_to_irq(output->hpd_gpio); diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index b2da31310d24..09b526518f5a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -204,6 +204,7 @@ int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, .interruptible = false, .no_wait_gpu = false }; + size_t max_segment; /* wtf swapping */ if (bo->pages) @@ -215,8 +216,13 @@ int virtio_gpu_object_get_sg_table(struct virtio_gpu_device *qdev, if (!bo->pages) goto out; - ret = sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0, - nr_pages << PAGE_SHIFT, GFP_KERNEL); + max_segment = virtio_max_dma_size(qdev->vdev); + max_segment &= PAGE_MASK; + if (max_segment > SCATTERLIST_MAX_SEGMENT) + max_segment = SCATTERLIST_MAX_SEGMENT; + ret = __sg_alloc_table_from_pages(bo->pages, pages, nr_pages, 0, + nr_pages << PAGE_SHIFT, + max_segment, GFP_KERNEL); if (ret) goto out; return 0; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c index e4e09d47c5c0..0af048d1a815 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c @@ -353,7 +353,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB)); if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) { kfree(reply); - + reply = NULL; if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry. */ continue; @@ -377,7 +377,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { kfree(reply); - + reply = NULL; if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) { /* A checkpoint occurred. Retry. */ continue; @@ -389,7 +389,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, break; } - if (retries == RETRIES) + if (!reply) return -EINVAL; *msg_len = reply_len; diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 98bf694626f7..3a8c4a5971f7 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -23,12 +23,36 @@ #define A4_2WHEEL_MOUSE_HACK_7 0x01 #define A4_2WHEEL_MOUSE_HACK_B8 0x02 +#define A4_WHEEL_ORIENTATION (HID_UP_GENDESK | 0x000000b8) + struct a4tech_sc { unsigned long quirks; unsigned int hw_wheel; __s32 delayed_value; }; +static int a4_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct a4tech_sc *a4 = hid_get_drvdata(hdev); + + if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8 && + usage->hid == A4_WHEEL_ORIENTATION) { + /* + * We do not want to have this usage mapped to anything as it's + * nonstandard and doesn't really behave like an HID report. + * It's only selecting the orientation (vertical/horizontal) of + * the previous mouse wheel report. The input_events will be + * generated once both reports are recorded in a4_event(). + */ + return -1; + } + + return 0; + +} + static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -52,8 +76,7 @@ static int a4_event(struct hid_device *hdev, struct hid_field *field, struct a4tech_sc *a4 = hid_get_drvdata(hdev); struct input_dev *input; - if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || - !usage->type) + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput) return 0; input = field->hidinput->input; @@ -64,7 +87,7 @@ static int a4_event(struct hid_device *hdev, struct hid_field *field, return 1; } - if (usage->hid == 0x000100b8) { + if (usage->hid == A4_WHEEL_ORIENTATION) { input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, a4->delayed_value); input_event(input, EV_REL, value ? REL_HWHEEL_HI_RES : @@ -131,6 +154,7 @@ MODULE_DEVICE_TABLE(hid, a4_devices); static struct hid_driver a4_driver = { .name = "a4tech", .id_table = a4_devices, + .input_mapping = a4_input_mapping, .input_mapped = a4_input_mapped, .event = a4_event, .probe = a4_probe, diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 2310c96ccf4a..db1b55df0d13 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -1153,8 +1153,6 @@ static unsigned int cp2112_gpio_irq_startup(struct irq_data *d) INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback); - cp2112_gpio_direction_input(gc, d->hwirq); - if (!dev->gpio_poll) { dev->gpio_poll = true; schedule_delayed_work(&dev->gpio_poll_worker, 0); @@ -1204,6 +1202,12 @@ static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev, return PTR_ERR(dev->desc[pin]); } + ret = cp2112_gpio_direction_input(&dev->gc, pin); + if (ret < 0) { + dev_err(dev->gc.parent, "Failed to set GPIO to input dir\n"); + goto err_desc; + } + ret = gpiochip_lock_as_irq(&dev->gc, pin); if (ret) { dev_err(dev->gc.parent, "Failed to lock GPIO as interrupt\n"); diff --git a/drivers/hid/hid-holtek-kbd.c b/drivers/hid/hid-holtek-kbd.c index b3d502421b79..0a38e8e9bc78 100644 --- a/drivers/hid/hid-holtek-kbd.c +++ b/drivers/hid/hid-holtek-kbd.c @@ -123,9 +123,14 @@ static int holtek_kbd_input_event(struct input_dev *dev, unsigned int type, /* Locate the boot interface, to receive the LED change events */ struct usb_interface *boot_interface = usb_ifnum_to_if(usb_dev, 0); + struct hid_device *boot_hid; + struct hid_input *boot_hid_input; - struct hid_device *boot_hid = usb_get_intfdata(boot_interface); - struct hid_input *boot_hid_input = list_first_entry(&boot_hid->inputs, + if (unlikely(boot_interface == NULL)) + return -ENODEV; + + boot_hid = usb_get_intfdata(boot_interface); + boot_hid_input = list_first_entry(&boot_hid->inputs, struct hid_input, list); return boot_hid_input->input->event(boot_hid_input->input, type, code, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 0d695f8e1b2c..0a00be19f7a0 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -568,6 +568,7 @@ #define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A 0x094a +#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641 0x0641 #define USB_VENDOR_ID_HUION 0x256c #define USB_DEVICE_ID_HUION_TABLET 0x006e @@ -768,7 +769,8 @@ #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER 0xc52f #define USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2 0xc532 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2 0xc534 -#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_GAMING 0xc539 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED 0xc539 +#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 #define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 #define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 @@ -989,6 +991,7 @@ #define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7 #define USB_DEVICE_ID_SAITEK_RAT9 0x0cfa #define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0 +#define USB_DEVICE_ID_SAITEK_X52 0x075c #define USB_VENDOR_ID_SAMSUNG 0x0419 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 6196217a7d93..cc47f948c1d0 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -1125,7 +1125,7 @@ static int logi_dj_recv_query_hidpp_devices(struct dj_receiver_dev *djrcv_dev) HID_REQ_SET_REPORT); kfree(hidpp_report); - return retval; + return (retval < 0) ? retval : 0; } static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) @@ -1832,13 +1832,17 @@ static const struct hid_device_id logi_dj_receivers[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2), .driver_data = recvr_type_hidpp}, - { /* Logitech gaming receiver (0xc539) */ + { /* Logitech lightspeed receiver (0xc539) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, - USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_GAMING), + USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED), .driver_data = recvr_type_gaming_hidpp}, { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), .driver_data = recvr_type_27mhz}, + { /* Logitech powerplay receiver (0xc53a) */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY), + .driver_data = recvr_type_gaming_hidpp}, { /* Logitech 27 MHz HID++ 1.0 receiver (0xc517) */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index e3b6245bf4b2..0179f7ed77e5 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -3749,15 +3749,23 @@ static const struct hid_device_id hidpp_devices[] = { { L27MHZ_DEVICE(HID_ANY_ID) }, - { /* Logitech G403 Gaming Mouse over USB */ + { /* Logitech G403 Wireless Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082) }, - { /* Logitech G700 Gaming Mouse over USB */ - HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC06B) }, + { /* Logitech G703 Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC087) }, + { /* Logitech G703 Hero Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC090) }, { /* Logitech G900 Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) }, + { /* Logitech G903 Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) }, + { /* Logitech G903 Hero Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) }, { /* Logitech G920 Wheel over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL), .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, + { /* Logitech G Pro Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) }, { /* MX5000 keyboard over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305), diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 185a577c46f6..166f41f3173b 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -92,6 +92,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6680), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X), HID_QUIRK_MULTI_INPUT }, @@ -141,6 +142,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_RETROUSB, USB_DEVICE_ID_RETROUSB_SNES_RETROPAD), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, { HID_USB_DEVICE(USB_VENDOR_ID_RETROUSB, USB_DEVICE_ID_RETROUSB_SNES_RETROPORT), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD), HID_QUIRK_BADPAD }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET }, diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 93942063b51b..49dd2d905c7f 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -585,10 +585,14 @@ static void sony_set_leds(struct sony_sc *sc); static inline void sony_schedule_work(struct sony_sc *sc, enum sony_worker which) { + unsigned long flags; + switch (which) { case SONY_WORKER_STATE: - if (!sc->defer_initialization) + spin_lock_irqsave(&sc->lock, flags); + if (!sc->defer_initialization && sc->state_worker_initialized) schedule_work(&sc->state_worker); + spin_unlock_irqrestore(&sc->lock, flags); break; case SONY_WORKER_HOTPLUG: if (sc->hotplug_worker_initialized) @@ -2558,13 +2562,18 @@ static inline void sony_init_output_report(struct sony_sc *sc, static inline void sony_cancel_work_sync(struct sony_sc *sc) { + unsigned long flags; + if (sc->hotplug_worker_initialized) cancel_work_sync(&sc->hotplug_worker); - if (sc->state_worker_initialized) + if (sc->state_worker_initialized) { + spin_lock_irqsave(&sc->lock, flags); + sc->state_worker_initialized = 0; + spin_unlock_irqrestore(&sc->lock, flags); cancel_work_sync(&sc->state_worker); + } } - static int sony_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index e12f2588ddeb..bdfc5ff3b2c5 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -22,6 +22,8 @@ #include "hid-ids.h" +#define THRUSTMASTER_DEVICE_ID_2_IN_1_DT 0xb320 + static const signed short ff_rumble[] = { FF_RUMBLE, -1 @@ -76,6 +78,7 @@ static int tmff_play(struct input_dev *dev, void *data, struct hid_field *ff_field = tmff->ff_field; int x, y; int left, right; /* Rumbling */ + int motor_swap; switch (effect->type) { case FF_CONSTANT: @@ -100,6 +103,13 @@ static int tmff_play(struct input_dev *dev, void *data, ff_field->logical_minimum, ff_field->logical_maximum); + /* 2-in-1 strong motor is left */ + if (hid->product == THRUSTMASTER_DEVICE_ID_2_IN_1_DT) { + motor_swap = left; + left = right; + right = motor_swap; + } + dbg_hid("(left,right)=(%08x, %08x)\n", left, right); ff_field->value[0] = left; ff_field->value[1] = right; @@ -226,6 +236,8 @@ static const struct hid_device_id tm_devices[] = { .driver_data = (unsigned long)ff_rumble }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), /* FireStorm Dual Power 2 (and 3) */ .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, THRUSTMASTER_DEVICE_ID_2_IN_1_DT), /* Dual Trigger 2-in-1 */ + .driver_data = (unsigned long)ff_rumble }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323), /* Dual Trigger 3-in-1 (PC Mode) */ .driver_data = (unsigned long)ff_rumble }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324), /* Dual Trigger 3-in-1 (PS3 Mode) */ diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h index 1065692f90e2..5792a104000a 100644 --- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h @@ -24,6 +24,7 @@ #define ICL_MOBILE_DEVICE_ID 0x34FC #define SPT_H_DEVICE_ID 0xA135 #define CML_LP_DEVICE_ID 0x02FC +#define EHL_Ax_DEVICE_ID 0x4BB3 #define REVISION_ID_CHT_A0 0x6 #define REVISION_ID_CHT_Ax_SI 0x0 diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index aa80b4d3b740..279567baca3d 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -33,6 +33,7 @@ static const struct pci_device_id ish_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)}, {0, } }; MODULE_DEVICE_TABLE(pci, ish_pci_tbl); diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 55b72573066b..4e11cc6fc34b 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -284,6 +284,14 @@ static int hiddev_open(struct inode *inode, struct file *file) spin_unlock_irq(&list->hiddev->list_lock); mutex_lock(&hiddev->existancelock); + /* + * recheck exist with existance lock held to + * avoid opening a disconnected device + */ + if (!list->hiddev->exist) { + res = -ENODEV; + goto bail_unlock; + } if (!list->hiddev->open++) if (list->hiddev->exist) { struct hid_device *hid = hiddev->hid; @@ -300,6 +308,10 @@ bail_normal_power: hid_hw_power(hid, PM_HINT_NORMAL); bail_unlock: mutex_unlock(&hiddev->existancelock); + + spin_lock_irq(&list->hiddev->list_lock); + list_del(&list->node); + spin_unlock_irq(&list->hiddev->list_lock); bail: file->private_data = NULL; vfree(list); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 8fc36a28081b..1713235d28cb 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -533,14 +533,14 @@ static int wacom_intuos_pad(struct wacom_wac *wacom) */ buttons = (data[4] << 1) | (data[3] & 0x01); } else if (features->type == CINTIQ_COMPANION_2) { - /* d-pad right -> data[4] & 0x10 - * d-pad up -> data[4] & 0x20 - * d-pad left -> data[4] & 0x40 - * d-pad down -> data[4] & 0x80 - * d-pad center -> data[3] & 0x01 + /* d-pad right -> data[2] & 0x10 + * d-pad up -> data[2] & 0x20 + * d-pad left -> data[2] & 0x40 + * d-pad down -> data[2] & 0x80 + * d-pad center -> data[1] & 0x01 */ buttons = ((data[2] >> 4) << 7) | - ((data[1] & 0x04) << 6) | + ((data[1] & 0x04) << 4) | ((data[2] & 0x0F) << 2) | (data[1] & 0x03); } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) { @@ -846,6 +846,8 @@ static int wacom_intuos_general(struct wacom_wac *wacom) y >>= 1; distance >>= 1; } + if (features->type == INTUOSHT2) + distance = features->distance_max - distance; input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); input_report_abs(input, ABS_DISTANCE, distance); @@ -1059,7 +1061,7 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) input_report_key(input, BTN_BASE2, (data[11] & 0x02)); if (data[12] & 0x80) - input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f)); + input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f) - 1); else input_report_abs(input, ABS_WHEEL, 0); @@ -1290,7 +1292,8 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) } if (wacom->tool[0]) { input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5])); - if (wacom->features.type == INTUOSP2_BT) { + if (wacom->features.type == INTUOSP2_BT || + wacom->features.type == INTUOSP2S_BT) { input_report_abs(pen_input, ABS_DISTANCE, range ? frame[13] : wacom->features.distance_max); } else { diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 5f9505a087f6..23f358cb7f49 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -26,7 +26,7 @@ static unsigned long virt_to_hvpfn(void *addr) { - unsigned long paddr; + phys_addr_t paddr; if (is_vmalloc_addr(addr)) paddr = page_to_phys(vmalloc_to_page(addr)) + diff --git a/drivers/hv/hv_trace.h b/drivers/hv/hv_trace.h index 999f80a63bff..e70783e33680 100644 --- a/drivers/hv/hv_trace.h +++ b/drivers/hv/hv_trace.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM #define TRACE_SYSTEM hyperv diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 362e70e9d145..fb16a622e8ab 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -146,8 +146,6 @@ struct hv_context { */ u64 guestid; - void *tsc_page; - struct hv_per_cpu_context __percpu *cpu_context; /* diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 650dd71f9724..2ca5668bdb62 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -246,6 +246,16 @@ config SENSORS_ADT7475 This driver can also be built as a module. If so, the module will be called adt7475. +config SENSORS_AS370 + tristate "Synaptics AS370 SoC hardware monitoring driver" + help + If you say yes here you get support for the PVT sensors of + the Synaptics AS370 SoC + + This driver can also be built as a module. If so, the module + will be called as370-hwmon. + + config SENSORS_ASC7621 tristate "Andigilog aSC7621" depends on I2C @@ -1382,8 +1392,8 @@ config SENSORS_SHTC1 tristate "Sensiron humidity and temperature sensors. SHTC1 and compat." depends on I2C help - If you say yes here you get support for the Sensiron SHTC1 and SHTW1 - humidity and temperature sensors. + If you say yes here you get support for the Sensiron SHTC1, SHTW1, + and SHTC3 humidity and temperature sensors. This driver can also be built as a module. If so, the module will be called shtc1. @@ -1570,16 +1580,6 @@ config SENSORS_ADC128D818 This driver can also be built as a module. If so, the module will be called adc128d818. -config SENSORS_ADS1015 - tristate "Texas Instruments ADS1015" - depends on I2C - help - If you say yes here you get support for Texas Instruments - ADS1015/ADS1115 12/16-bit 4-input ADC device. - - This driver can also be built as a module. If so, the module - will be called ads1015. - config SENSORS_ADS7828 tristate "Texas Instruments ADS7828 and compatibles" depends on I2C @@ -1834,17 +1834,12 @@ config SENSORS_W83795 will be called w83795. config SENSORS_W83795_FANCTRL - bool "Include automatic fan control support (DANGEROUS)" + bool "Include automatic fan control support" depends on SENSORS_W83795 help If you say yes here, support for automatic fan speed control will be included in the driver. - This part of the code wasn't carefully reviewed and tested yet, - so enabling this option is strongly discouraged on production - servers. Only developers and testers should enable it for the - time being. - Please also note that this option will create sysfs attribute files which may change in the future, so you shouldn't rely on them being stable. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8db472ea04f0..c86ce4d3d36b 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -35,7 +35,6 @@ obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o -obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o @@ -48,6 +47,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o +obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 6ba1a08253f0..4cf25458f0b9 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -681,8 +681,8 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) if (resource->caps.flags & POWER_METER_CAN_CAP) { if (!can_cap_in_hardware()) { - dev_err(&resource->acpi_dev->dev, - "Ignoring unsafe software power cap!\n"); + dev_warn(&resource->acpi_dev->dev, + "Ignoring unsafe software power cap!\n"); goto skip_unsafe_cap; } diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c deleted file mode 100644 index 3727a3762eb8..000000000000 --- a/drivers/hwmon/ads1015.c +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * ads1015.c - lm_sensors driver for ads1015 12-bit 4-input ADC - * (C) Copyright 2010 - * Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de> - * - * Based on the ads7828 driver by Steve Hardy. - * - * Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads1015.pdf - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/of_device.h> -#include <linux/of.h> - -#include <linux/platform_data/ads1015.h> - -/* ADS1015 registers */ -enum { - ADS1015_CONVERSION = 0, - ADS1015_CONFIG = 1, -}; - -/* PGA fullscale voltages in mV */ -static const unsigned int fullscale_table[8] = { - 6144, 4096, 2048, 1024, 512, 256, 256, 256 }; - -/* Data rates in samples per second */ -static const unsigned int data_rate_table_1015[8] = { - 128, 250, 490, 920, 1600, 2400, 3300, 3300 -}; - -static const unsigned int data_rate_table_1115[8] = { - 8, 16, 32, 64, 128, 250, 475, 860 -}; - -#define ADS1015_DEFAULT_CHANNELS 0xff -#define ADS1015_DEFAULT_PGA 2 -#define ADS1015_DEFAULT_DATA_RATE 4 - -enum ads1015_chips { - ads1015, - ads1115, -}; - -struct ads1015_data { - struct device *hwmon_dev; - struct mutex update_lock; /* mutex protect updates */ - struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; - enum ads1015_chips id; -}; - -static int ads1015_read_adc(struct i2c_client *client, unsigned int channel) -{ - u16 config; - struct ads1015_data *data = i2c_get_clientdata(client); - unsigned int pga = data->channel_data[channel].pga; - unsigned int data_rate = data->channel_data[channel].data_rate; - unsigned int conversion_time_ms; - const unsigned int * const rate_table = data->id == ads1115 ? - data_rate_table_1115 : data_rate_table_1015; - int res; - - mutex_lock(&data->update_lock); - - /* get channel parameters */ - res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); - if (res < 0) - goto err_unlock; - config = res; - conversion_time_ms = DIV_ROUND_UP(1000, rate_table[data_rate]); - - /* setup and start single conversion */ - config &= 0x001f; - config |= (1 << 15) | (1 << 8); - config |= (channel & 0x0007) << 12; - config |= (pga & 0x0007) << 9; - config |= (data_rate & 0x0007) << 5; - - res = i2c_smbus_write_word_swapped(client, ADS1015_CONFIG, config); - if (res < 0) - goto err_unlock; - - /* wait until conversion finished */ - msleep(conversion_time_ms); - res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); - if (res < 0) - goto err_unlock; - config = res; - if (!(config & (1 << 15))) { - /* conversion not finished in time */ - res = -EIO; - goto err_unlock; - } - - res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION); - -err_unlock: - mutex_unlock(&data->update_lock); - return res; -} - -static int ads1015_reg_to_mv(struct i2c_client *client, unsigned int channel, - s16 reg) -{ - struct ads1015_data *data = i2c_get_clientdata(client); - unsigned int pga = data->channel_data[channel].pga; - int fullscale = fullscale_table[pga]; - const int mask = data->id == ads1115 ? 0x7fff : 0x7ff0; - - return DIV_ROUND_CLOSEST(reg * fullscale, mask); -} - -/* sysfs callback function */ -static ssize_t in_show(struct device *dev, struct device_attribute *da, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); - int res; - int index = attr->index; - - res = ads1015_read_adc(client, index); - if (res < 0) - return res; - - return sprintf(buf, "%d\n", ads1015_reg_to_mv(client, index, res)); -} - -static const struct sensor_device_attribute ads1015_in[] = { - SENSOR_ATTR_RO(in0_input, in, 0), - SENSOR_ATTR_RO(in1_input, in, 1), - SENSOR_ATTR_RO(in2_input, in, 2), - SENSOR_ATTR_RO(in3_input, in, 3), - SENSOR_ATTR_RO(in4_input, in, 4), - SENSOR_ATTR_RO(in5_input, in, 5), - SENSOR_ATTR_RO(in6_input, in, 6), - SENSOR_ATTR_RO(in7_input, in, 7), -}; - -/* - * Driver interface - */ - -static int ads1015_remove(struct i2c_client *client) -{ - struct ads1015_data *data = i2c_get_clientdata(client); - int k; - - hwmon_device_unregister(data->hwmon_dev); - for (k = 0; k < ADS1015_CHANNELS; ++k) - device_remove_file(&client->dev, &ads1015_in[k].dev_attr); - return 0; -} - -#ifdef CONFIG_OF -static int ads1015_get_channels_config_of(struct i2c_client *client) -{ - struct ads1015_data *data = i2c_get_clientdata(client); - struct device_node *node; - - if (!client->dev.of_node - || !of_get_next_child(client->dev.of_node, NULL)) - return -EINVAL; - - for_each_child_of_node(client->dev.of_node, node) { - u32 pval; - unsigned int channel; - unsigned int pga = ADS1015_DEFAULT_PGA; - unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; - - if (of_property_read_u32(node, "reg", &pval)) { - dev_err(&client->dev, "invalid reg on %pOF\n", node); - continue; - } - - channel = pval; - if (channel >= ADS1015_CHANNELS) { - dev_err(&client->dev, - "invalid channel index %d on %pOF\n", - channel, node); - continue; - } - - if (!of_property_read_u32(node, "ti,gain", &pval)) { - pga = pval; - if (pga > 6) { - dev_err(&client->dev, "invalid gain on %pOF\n", - node); - return -EINVAL; - } - } - - if (!of_property_read_u32(node, "ti,datarate", &pval)) { - data_rate = pval; - if (data_rate > 7) { - dev_err(&client->dev, - "invalid data_rate on %pOF\n", node); - return -EINVAL; - } - } - - data->channel_data[channel].enabled = true; - data->channel_data[channel].pga = pga; - data->channel_data[channel].data_rate = data_rate; - } - - return 0; -} -#endif - -static void ads1015_get_channels_config(struct i2c_client *client) -{ - unsigned int k; - struct ads1015_data *data = i2c_get_clientdata(client); - struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); - - /* prefer platform data */ - if (pdata) { - memcpy(data->channel_data, pdata->channel_data, - sizeof(data->channel_data)); - return; - } - -#ifdef CONFIG_OF - if (!ads1015_get_channels_config_of(client)) - return; -#endif - - /* fallback on default configuration */ - for (k = 0; k < ADS1015_CHANNELS; ++k) { - data->channel_data[k].enabled = true; - data->channel_data[k].pga = ADS1015_DEFAULT_PGA; - data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE; - } -} - -static int ads1015_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ads1015_data *data; - int err; - unsigned int k; - - data = devm_kzalloc(&client->dev, sizeof(struct ads1015_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - if (client->dev.of_node) - data->id = (enum ads1015_chips) - of_device_get_match_data(&client->dev); - else - data->id = id->driver_data; - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - - /* build sysfs attribute group */ - ads1015_get_channels_config(client); - for (k = 0; k < ADS1015_CHANNELS; ++k) { - if (!data->channel_data[k].enabled) - continue; - err = device_create_file(&client->dev, &ads1015_in[k].dev_attr); - if (err) - goto exit_remove; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - for (k = 0; k < ADS1015_CHANNELS; ++k) - device_remove_file(&client->dev, &ads1015_in[k].dev_attr); - return err; -} - -static const struct i2c_device_id ads1015_id[] = { - { "ads1015", ads1015}, - { "ads1115", ads1115}, - { } -}; -MODULE_DEVICE_TABLE(i2c, ads1015_id); - -static const struct of_device_id __maybe_unused ads1015_of_match[] = { - { - .compatible = "ti,ads1015", - .data = (void *)ads1015 - }, - { - .compatible = "ti,ads1115", - .data = (void *)ads1115 - }, - { }, -}; -MODULE_DEVICE_TABLE(of, ads1015_of_match); - -static struct i2c_driver ads1015_driver = { - .driver = { - .name = "ads1015", - .of_match_table = of_match_ptr(ads1015_of_match), - }, - .probe = ads1015_probe, - .remove = ads1015_remove, - .id_table = ads1015_id, -}; - -module_i2c_driver(ads1015_driver); - -MODULE_AUTHOR("Dirk Eibach <eibach@gdsys.de>"); -MODULE_DESCRIPTION("ADS1015 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index c3c6031a7285..6c64d50c9aae 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -187,7 +187,7 @@ static const struct of_device_id __maybe_unused adt7475_of_match[] = { MODULE_DEVICE_TABLE(of, adt7475_of_match); struct adt7475_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex lock; unsigned long measure_updated; @@ -212,6 +212,7 @@ struct adt7475_data { u8 vid; u8 vrm; + const struct attribute_group *groups[9]; }; static struct i2c_driver adt7475_driver; @@ -346,8 +347,8 @@ static ssize_t voltage_store(struct device *dev, { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg; long val; @@ -440,8 +441,8 @@ static ssize_t temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg = 0; u8 out; int temp; @@ -542,8 +543,7 @@ static ssize_t temp_st_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); long val; switch (sattr->index) { @@ -570,8 +570,8 @@ static ssize_t temp_st_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg; int shift, idx; ulong val; @@ -647,8 +647,8 @@ static ssize_t point2_show(struct device *dev, struct device_attribute *attr, static ssize_t point2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); int temp; long val; @@ -710,8 +710,8 @@ static ssize_t tach_store(struct device *dev, struct device_attribute *attr, { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; if (kstrtoul(buf, 10, &val)) @@ -769,8 +769,8 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *attr, { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg = 0; long val; @@ -818,8 +818,8 @@ static ssize_t stall_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + u8 mask = BIT(5 + sattr->index); return sprintf(buf, "%d\n", !!(data->enh_acoustics[0] & mask)); @@ -830,8 +830,8 @@ static ssize_t stall_disable_store(struct device *dev, const char *buf, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; u8 mask = BIT(5 + sattr->index); @@ -914,8 +914,8 @@ static ssize_t pwmchan_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int r; long val; @@ -938,8 +938,8 @@ static ssize_t pwmctrl_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int r; long val; @@ -982,8 +982,8 @@ static ssize_t pwmfreq_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int out; long val; @@ -1022,8 +1022,8 @@ static ssize_t pwm_use_point2_pwm_at_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; if (kstrtol(buf, 10, &val)) @@ -1342,26 +1342,6 @@ static int adt7475_detect(struct i2c_client *client, return 0; } -static void adt7475_remove_files(struct i2c_client *client, - struct adt7475_data *data) -{ - sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); - if (data->has_fan4) - sysfs_remove_group(&client->dev.kobj, &fan4_attr_group); - if (data->has_pwm2) - sysfs_remove_group(&client->dev.kobj, &pwm2_attr_group); - if (data->has_voltage & (1 << 0)) - sysfs_remove_group(&client->dev.kobj, &in0_attr_group); - if (data->has_voltage & (1 << 3)) - sysfs_remove_group(&client->dev.kobj, &in3_attr_group); - if (data->has_voltage & (1 << 4)) - sysfs_remove_group(&client->dev.kobj, &in4_attr_group); - if (data->has_voltage & (1 << 5)) - sysfs_remove_group(&client->dev.kobj, &in5_attr_group); - if (data->has_vid) - sysfs_remove_group(&client->dev.kobj, &vid_attr_group); -} - static int adt7475_update_limits(struct i2c_client *client) { struct adt7475_data *data = i2c_get_clientdata(client); @@ -1489,7 +1469,8 @@ static int adt7475_probe(struct i2c_client *client, }; struct adt7475_data *data; - int i, ret = 0, revision; + struct device *hwmon_dev; + int i, ret = 0, revision, group_num = 0; u8 config2, config3; data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); @@ -1497,6 +1478,7 @@ static int adt7475_probe(struct i2c_client *client, return -ENOMEM; mutex_init(&data->lock); + data->client = client; i2c_set_clientdata(client, data); if (client->dev.of_node) @@ -1590,52 +1572,40 @@ static int adt7475_probe(struct i2c_client *client, break; } - ret = sysfs_create_group(&client->dev.kobj, &adt7475_attr_group); - if (ret) - return ret; + data->groups[group_num++] = &adt7475_attr_group; /* Features that can be disabled individually */ if (data->has_fan4) { - ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &fan4_attr_group; } if (data->has_pwm2) { - ret = sysfs_create_group(&client->dev.kobj, &pwm2_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &pwm2_attr_group; } if (data->has_voltage & (1 << 0)) { - ret = sysfs_create_group(&client->dev.kobj, &in0_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in0_attr_group; } if (data->has_voltage & (1 << 3)) { - ret = sysfs_create_group(&client->dev.kobj, &in3_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in3_attr_group; } if (data->has_voltage & (1 << 4)) { - ret = sysfs_create_group(&client->dev.kobj, &in4_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in4_attr_group; } if (data->has_voltage & (1 << 5)) { - ret = sysfs_create_group(&client->dev.kobj, &in5_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in5_attr_group; } if (data->has_vid) { data->vrm = vid_which_vrm(); - ret = sysfs_create_group(&client->dev.kobj, &vid_attr_group); - if (ret) - goto eremove; + data->groups[group_num] = &vid_attr_group; } - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto eremove; + /* register device with all the acquired attributes */ + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); + return ret; } dev_info(&client->dev, "%s device, revision %d\n", @@ -1657,21 +1627,7 @@ static int adt7475_probe(struct i2c_client *client, /* Limits and settings, should never change update more than once */ ret = adt7475_update_limits(client); if (ret) - goto eremove; - - return 0; - -eremove: - adt7475_remove_files(client, data); - return ret; -} - -static int adt7475_remove(struct i2c_client *client) -{ - struct adt7475_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - adt7475_remove_files(client, data); + return ret; return 0; } @@ -1683,7 +1639,6 @@ static struct i2c_driver adt7475_driver = { .of_match_table = of_match_ptr(adt7475_of_match), }, .probe = adt7475_probe, - .remove = adt7475_remove, .id_table = adt7475_id, .detect = adt7475_detect, .address_list = normal_i2c, @@ -1757,8 +1712,8 @@ static void adt7475_read_pwm(struct i2c_client *client, int index) static int adt7475_update_measure(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u16 ext; int i; int ret; @@ -1854,8 +1809,7 @@ static int adt7475_update_measure(struct device *dev) static struct adt7475_data *adt7475_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); int ret; mutex_lock(&data->lock); diff --git a/drivers/hwmon/as370-hwmon.c b/drivers/hwmon/as370-hwmon.c new file mode 100644 index 000000000000..464244ba8d58 --- /dev/null +++ b/drivers/hwmon/as370-hwmon.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics AS370 SoC Hardware Monitoring Driver + * + * Copyright (C) 2018 Synaptics Incorporated + * Author: Jisheng Zhang <jszhang@kernel.org> + */ + +#include <linux/bitops.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> + +#define CTRL 0x0 +#define PD BIT(0) +#define EN BIT(1) +#define T_SEL BIT(2) +#define V_SEL BIT(3) +#define NMOS_SEL BIT(8) +#define PMOS_SEL BIT(9) +#define STS 0x4 +#define BN_MASK GENMASK(11, 0) +#define EOC BIT(12) + +struct as370_hwmon { + void __iomem *base; +}; + +static void init_pvt(struct as370_hwmon *hwmon) +{ + u32 val; + void __iomem *addr = hwmon->base + CTRL; + + val = PD; + writel_relaxed(val, addr); + val |= T_SEL; + writel_relaxed(val, addr); + val |= EN; + writel_relaxed(val, addr); + val &= ~PD; + writel_relaxed(val, addr); +} + +static int as370_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int val; + struct as370_hwmon *hwmon = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + val = readl_relaxed(hwmon->base + STS) & BN_MASK; + *temp = DIV_ROUND_CLOSEST(val * 251802, 4096) - 85525; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static umode_t +as370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static const u32 as370_hwmon_temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info as370_hwmon_temp = { + .type = hwmon_temp, + .config = as370_hwmon_temp_config, +}; + +static const struct hwmon_channel_info *as370_hwmon_info[] = { + &as370_hwmon_temp, + NULL +}; + +static const struct hwmon_ops as370_hwmon_ops = { + .is_visible = as370_hwmon_is_visible, + .read = as370_hwmon_read, +}; + +static const struct hwmon_chip_info as370_chip_info = { + .ops = &as370_hwmon_ops, + .info = as370_hwmon_info, +}; + +static int as370_hwmon_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + struct as370_hwmon *hwmon; + struct device *dev = &pdev->dev; + + hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hwmon->base)) + return PTR_ERR(hwmon->base); + + init_pvt(hwmon); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, + "as370", + hwmon, + &as370_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id as370_hwmon_match[] = { + { .compatible = "syna,as370-hwmon" }, + {}, +}; +MODULE_DEVICE_TABLE(of, as370_hwmon_match); + +static struct platform_driver as370_hwmon_driver = { + .probe = as370_hwmon_probe, + .driver = { + .name = "as370-hwmon", + .of_match_table = as370_hwmon_match, + }, +}; +module_platform_driver(as370_hwmon_driver); + +MODULE_AUTHOR("Jisheng Zhang<jszhang@kernel.org>"); +MODULE_DESCRIPTION("Synaptics AS370 SoC hardware monitor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index c9fa84b25678..4c609e23a4ef 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -706,21 +706,21 @@ static int asb100_detect_subclients(struct i2c_client *client) goto ERROR_SC_2; } - data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]); - if (!data->lm75[0]) { + data->lm75[0] = i2c_new_dummy_device(adapter, sc_addr[0]); + if (IS_ERR(data->lm75[0])) { dev_err(&client->dev, "subclient %d registration at address 0x%x failed.\n", 1, sc_addr[0]); - err = -ENOMEM; + err = PTR_ERR(data->lm75[0]); goto ERROR_SC_2; } - data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]); - if (!data->lm75[1]) { + data->lm75[1] = i2c_new_dummy_device(adapter, sc_addr[1]); + if (IS_ERR(data->lm75[1])) { dev_err(&client->dev, "subclient %d registration at address 0x%x failed.\n", 2, sc_addr[1]); - err = -ENOMEM; + err = PTR_ERR(data->lm75[1]); goto ERROR_SC_3; } diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index fe6618e49dc4..d855c78fb8be 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -736,7 +736,7 @@ static int __init coretemp_init(void) err = platform_driver_register(&coretemp_driver); if (err) - return err; + goto outzone; err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", coretemp_cpu_online, coretemp_cpu_offline); @@ -747,6 +747,7 @@ static int __init coretemp_init(void) outdrv: platform_driver_unregister(&coretemp_driver); +outzone: kfree(zone_devices); return err; } diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index f1c2d5faedf0..b85a125dd86f 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -44,12 +44,20 @@ static ssize_t iio_hwmon_read_val(struct device *dev, int ret; struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); struct iio_hwmon_state *state = dev_get_drvdata(dev); + struct iio_channel *chan = &state->channels[sattr->index]; + enum iio_chan_type type; + + ret = iio_read_channel_processed(chan, &result); + if (ret < 0) + return ret; - ret = iio_read_channel_processed(&state->channels[sattr->index], - &result); + ret = iio_get_channel_type(chan, &type); if (ret < 0) return ret; + if (type == IIO_POWER) + result *= 1000; /* mili-Watts to micro-Watts conversion */ + return sprintf(buf, "%d\n", result); } @@ -59,7 +67,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) struct iio_hwmon_state *st; struct sensor_device_attribute *a; int ret, i; - int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; + int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1, power_i = 1; enum iio_chan_type type; struct iio_channel *channels; struct device *hwmon_dev; @@ -114,6 +122,10 @@ static int iio_hwmon_probe(struct platform_device *pdev) n = curr_i++; prefix = "curr"; break; + case IIO_POWER: + n = power_i++; + prefix = "power"; + break; case IIO_HUMIDITYRELATIVE: n = humidity_i++; prefix = "humidity"; diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index c77e89239dcd..5c1dddde193c 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -349,6 +349,7 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, {} }; diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 4994c90c8929..f73bd4eceb28 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -10,10 +10,8 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/jiffies.h> #include <linux/pci.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include <asm/processor.h> @@ -24,108 +22,18 @@ #define SEL_CORE 0x04 struct k8temp_data { - struct device *hwmon_dev; struct mutex update_lock; - const char *name; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ /* registers values */ u8 sensorsp; /* sensor presence bits - SEL_CORE, SEL_PLACE */ - u32 temp[2][2]; /* core, place */ u8 swap_core_select; /* meaning of SEL_CORE is inverted */ u32 temp_offset; }; -static struct k8temp_data *k8temp_update_device(struct device *dev) -{ - struct k8temp_data *data = dev_get_drvdata(dev); - struct pci_dev *pdev = to_pci_dev(dev); - u8 tmp; - - mutex_lock(&data->update_lock); - - if (!data->valid - || time_after(jiffies, data->last_updated + HZ)) { - pci_read_config_byte(pdev, REG_TEMP, &tmp); - tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]); - - if (data->sensorsp & SEL_PLACE) { - tmp |= SEL_PLACE; /* Select sensor 1, core0 */ - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, - &data->temp[0][1]); - } - - if (data->sensorsp & SEL_CORE) { - tmp &= ~SEL_PLACE; /* Select sensor 0, core1 */ - tmp |= SEL_CORE; - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, - &data->temp[1][0]); - - if (data->sensorsp & SEL_PLACE) { - tmp |= SEL_PLACE; /* Select sensor 1, core1 */ - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, - &data->temp[1][1]); - } - } - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - return data; -} - -/* - * Sysfs stuff - */ - -static ssize_t name_show(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct k8temp_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", data->name); -} - - -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute_2 *attr = - to_sensor_dev_attr_2(devattr); - int core = attr->nr; - int place = attr->index; - int temp; - struct k8temp_data *data = k8temp_update_device(dev); - - if (data->swap_core_select && (data->sensorsp & SEL_CORE)) - core = core ? 0 : 1; - - temp = TEMP_FROM_REG(data->temp[core][place]) + data->temp_offset; - - return sprintf(buf, "%d\n", temp); -} - -/* core, place */ - -static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0, 0); -static SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, 0, 1); -static SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, 1, 0); -static SENSOR_DEVICE_ATTR_2_RO(temp4_input, temp, 1, 1); -static DEVICE_ATTR_RO(name); - static const struct pci_device_id k8temp_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, { 0 }, }; - MODULE_DEVICE_TABLE(pci, k8temp_ids); static int is_rev_g_desktop(u8 model) @@ -159,14 +67,76 @@ static int is_rev_g_desktop(u8 model) return 1; } +static umode_t +k8temp_is_visible(const void *drvdata, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct k8temp_data *data = drvdata; + + if ((channel & 1) && !(data->sensorsp & SEL_PLACE)) + return 0; + + if ((channel & 2) && !(data->sensorsp & SEL_CORE)) + return 0; + + return 0444; +} + +static int +k8temp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct k8temp_data *data = dev_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev->parent); + int core, place; + u32 temp; + u8 tmp; + + core = (channel >> 1) & 1; + place = channel & 1; + + core ^= data->swap_core_select; + + mutex_lock(&data->update_lock); + pci_read_config_byte(pdev, REG_TEMP, &tmp); + tmp &= ~(SEL_PLACE | SEL_CORE); + if (core) + tmp |= SEL_CORE; + if (place) + tmp |= SEL_PLACE; + pci_write_config_byte(pdev, REG_TEMP, tmp); + pci_read_config_dword(pdev, REG_TEMP, &temp); + mutex_unlock(&data->update_lock); + + *val = TEMP_FROM_REG(temp) + data->temp_offset; + + return 0; +} + +static const struct hwmon_ops k8temp_ops = { + .is_visible = k8temp_is_visible, + .read = k8temp_read, +}; + +static const struct hwmon_channel_info *k8temp_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_chip_info k8temp_chip_info = { + .ops = &k8temp_ops, + .info = k8temp_info, +}; + static int k8temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int err; u8 scfg; u32 temp; u8 model, stepping; struct k8temp_data *data; + struct device *hwmon_dev; data = devm_kzalloc(&pdev->dev, sizeof(struct k8temp_data), GFP_KERNEL); if (!data) @@ -231,86 +201,21 @@ static int k8temp_probe(struct pci_dev *pdev, data->sensorsp &= ~SEL_CORE; } - data->name = "k8temp"; mutex_init(&data->update_lock); - pci_set_drvdata(pdev, data); - - /* Register sysfs hooks */ - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp1_input.dev_attr); - if (err) - goto exit_remove; - - /* sensor can be changed and reports something */ - if (data->sensorsp & SEL_PLACE) { - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp2_input.dev_attr); - if (err) - goto exit_remove; - } - - /* core can be changed and reports something */ - if (data->sensorsp & SEL_CORE) { - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp3_input.dev_attr); - if (err) - goto exit_remove; - if (data->sensorsp & SEL_PLACE) { - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp4_input. - dev_attr); - if (err) - goto exit_remove; - } - } - - err = device_create_file(&pdev->dev, &dev_attr_name); - if (err) - goto exit_remove; - data->hwmon_dev = hwmon_device_register(&pdev->dev); + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "k8temp", + data, + &k8temp_chip_info, + NULL); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp3_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp4_input.dev_attr); - device_remove_file(&pdev->dev, &dev_attr_name); - return err; -} - -static void k8temp_remove(struct pci_dev *pdev) -{ - struct k8temp_data *data = pci_get_drvdata(pdev); - - hwmon_device_unregister(data->hwmon_dev); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp3_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp4_input.dev_attr); - device_remove_file(&pdev->dev, &dev_attr_name); + return PTR_ERR_OR_ZERO(hwmon_dev); } static struct pci_driver k8temp_driver = { .name = "k8temp", .id_table = k8temp_ids, .probe = k8temp_probe, - .remove = k8temp_remove, }; module_pci_driver(k8temp_driver); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 3fb9c0a2d6d0..5e6392294c03 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -16,9 +16,9 @@ #include <linux/of_device.h> #include <linux/of.h> #include <linux/regmap.h> +#include <linux/util_macros.h> #include "lm75.h" - /* * This driver handles the LM75 and compatible digital temperature sensors. */ @@ -36,6 +36,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ max6626, max31725, mcp980x, + pct2075, stds75, stlm75, tcn75, @@ -50,6 +51,41 @@ enum lm75_type { /* keep sorted in alphabetical order */ tmp75c, }; +/** + * struct lm75_params - lm75 configuration parameters. + * @set_mask: Bits to set in configuration register when configuring + * the chip. + * @clr_mask: Bits to clear in configuration register when configuring + * the chip. + * @default_resolution: Default number of bits to represent the temperature + * value. + * @resolution_limits: Limit register resolution. Optional. Should be set if + * the resolution of limit registers does not match the + * resolution of the temperature register. + * @resolutions: List of resolutions associated with sample times. + * Optional. Should be set if num_sample_times is larger + * than 1, and if the resolution changes with sample times. + * If set, number of entries must match num_sample_times. + * @default_sample_time:Sample time to be set by default. + * @num_sample_times: Number of possible sample times to be set. Optional. + * Should be set if the number of sample times is larger + * than one. + * @sample_times: All the possible sample times to be set. Mandatory if + * num_sample_times is larger than 1. If set, number of + * entries must match num_sample_times. + */ + +struct lm75_params { + u8 set_mask; + u8 clr_mask; + u8 default_resolution; + u8 resolution_limits; + const u8 *resolutions; + unsigned int default_sample_time; + u8 num_sample_times; + const unsigned int *sample_times; +}; + /* Addresses scanned */ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; @@ -59,24 +95,231 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, #define LM75_REG_CONF 0x01 #define LM75_REG_HYST 0x02 #define LM75_REG_MAX 0x03 +#define PCT2075_REG_IDLE 0x04 /* Each client has this additional data */ struct lm75_data { - struct i2c_client *client; - struct regmap *regmap; - u8 orig_conf; - u8 resolution; /* In bits, between 9 and 16 */ - u8 resolution_limits; - unsigned int sample_time; /* In ms */ + struct i2c_client *client; + struct regmap *regmap; + u8 orig_conf; + u8 current_conf; + u8 resolution; /* In bits, 9 to 16 */ + unsigned int sample_time; /* In ms */ + enum lm75_type kind; + const struct lm75_params *params; }; /*-----------------------------------------------------------------------*/ +static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 }; + +#define LM75_SAMPLE_CLEAR_MASK (3 << 5) + +/* The structure below stores the configuration values of the supported devices. + * In case of being supported multiple configurations, the default one must + * always be the first element of the array + */ +static const struct lm75_params device_params[] = { + [adt75] = { + .clr_mask = 1 << 5, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [ds1775] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 500, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 500, 1000 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [ds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stlm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 6, + }, + [ds7505] = { + .set_mask = 3 << 5, /* 12-bit mode*/ + .default_resolution = 12, + .default_sample_time = 200, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 25, 50, 100, 200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [g751] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75a] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75b] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [max6625] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 7, + }, + [max6626] = { + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 7, + .resolution_limits = 9, + }, + [max31725] = { + .default_resolution = 16, + .default_sample_time = MSEC_PER_SEC / 20, + }, + [tcn75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 18, + }, + [pct2075] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + .num_sample_times = 31, + .sample_times = (unsigned int []){ 100, 200, 300, 400, 500, 600, + 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, + 2800, 2900, 3000, 3100 }, + }, + [mcp980x] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .resolution_limits = 9, + .default_sample_time = 240, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 30, 60, 120, 240 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp100] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp101] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp105] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp112] = { + .set_mask = 3 << 5, /* 8 samples / second */ + .clr_mask = 1 << 7, /* no one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 125, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 1000, 4000 }, + }, + [tmp175] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp275] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75b] = { /* not one-shot mode, Conversion rate 37Hz */ + .clr_mask = 1 << 7 | 3 << 5, + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 37, + .sample_times = (unsigned int []){ MSEC_PER_SEC / 37, + MSEC_PER_SEC / 18, + MSEC_PER_SEC / 9, MSEC_PER_SEC / 4 }, + .num_sample_times = 4, + }, + [tmp75c] = { + .clr_mask = 1 << 5, /*not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 12, + } +}; + static inline long lm75_reg_to_mc(s16 temp, u8 resolution) { return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); } +static int lm75_write_config(struct lm75_data *data, u8 set_mask, + u8 clr_mask) +{ + u8 value; + + clr_mask |= LM75_SHUTDOWN; + value = data->current_conf & ~clr_mask; + value |= set_mask; + + if (data->current_conf != value) { + s32 err; + + err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF, + value); + if (err) + return err; + data->current_conf = value; + } + return 0; +} + static int lm75_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -120,16 +363,12 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type, return 0; } -static int lm75_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long temp) +static int lm75_write_temp(struct device *dev, u32 attr, long temp) { struct lm75_data *data = dev_get_drvdata(dev); u8 resolution; int reg; - if (type != hwmon_temp) - return -EINVAL; - switch (attr) { case hwmon_temp_max: reg = LM75_REG_MAX; @@ -145,8 +384,8 @@ static int lm75_write(struct device *dev, enum hwmon_sensor_types type, * Resolution of limit registers is assumed to be the same as the * temperature input register resolution unless given explicitly. */ - if (data->resolution_limits) - resolution = data->resolution_limits; + if (data->params->resolution_limits) + resolution = data->params->resolution_limits; else resolution = data->resolution; @@ -154,16 +393,88 @@ static int lm75_write(struct device *dev, enum hwmon_sensor_types type, temp = DIV_ROUND_CLOSEST(temp << (resolution - 8), 1000) << (16 - resolution); - return regmap_write(data->regmap, reg, temp); + return regmap_write(data->regmap, reg, (u16)temp); +} + +static int lm75_update_interval(struct device *dev, long val) +{ + struct lm75_data *data = dev_get_drvdata(dev); + unsigned int reg; + u8 index; + s32 err; + + index = find_closest(val, data->params->sample_times, + (int)data->params->num_sample_times); + + switch (data->kind) { + default: + err = lm75_write_config(data, lm75_sample_set_masks[index], + LM75_SAMPLE_CLEAR_MASK); + if (err) + return err; + + data->sample_time = data->params->sample_times[index]; + if (data->params->resolutions) + data->resolution = data->params->resolutions[index]; + break; + case tmp112: + err = regmap_read(data->regmap, LM75_REG_CONF, ®); + if (err < 0) + return err; + reg &= ~0x00c0; + reg |= (3 - index) << 6; + err = regmap_write(data->regmap, LM75_REG_CONF, reg); + if (err < 0) + return err; + data->sample_time = data->params->sample_times[index]; + break; + case pct2075: + err = i2c_smbus_write_byte_data(data->client, PCT2075_REG_IDLE, + index + 1); + if (err) + return err; + data->sample_time = data->params->sample_times[index]; + break; + } + return 0; +} + +static int lm75_write_chip(struct device *dev, u32 attr, long val) +{ + switch (attr) { + case hwmon_chip_update_interval: + return lm75_update_interval(dev, val); + default: + return -EINVAL; + } + return 0; +} + +static int lm75_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return lm75_write_chip(dev, attr, val); + case hwmon_temp: + return lm75_write_temp(dev, attr, val); + default: + return -EINVAL; + } + return 0; } static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) { + const struct lm75_data *config_data = data; + switch (type) { case hwmon_chip: switch (attr) { case hwmon_chip_update_interval: + if (config_data->params->num_sample_times > 1) + return 0644; return 0444; } break; @@ -208,13 +519,13 @@ static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg) static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg) { - return reg == LM75_REG_TEMP; + return reg == LM75_REG_TEMP || reg == LM75_REG_CONF; } static const struct regmap_config lm75_regmap_config = { .reg_bits = 8, .val_bits = 16, - .max_register = LM75_REG_MAX, + .max_register = PCT2075_REG_IDLE, .writeable_reg = lm75_is_writeable_reg, .volatile_reg = lm75_is_volatile_reg, .val_format_endian = REGMAP_ENDIAN_BIG, @@ -238,8 +549,6 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) struct device *hwmon_dev; struct lm75_data *data; int status, err; - u8 set_mask, clr_mask; - int new; enum lm75_type kind; if (client->dev.of_node) @@ -256,6 +565,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) return -ENOMEM; data->client = client; + data->kind = kind; data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config); if (IS_ERR(data->regmap)) @@ -264,113 +574,30 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Set to LM75 resolution (9 bits, 1/2 degree C) and range. * Then tweak to be more precise when appropriate. */ - set_mask = 0; - clr_mask = LM75_SHUTDOWN; /* continuous conversions */ - - switch (kind) { - case adt75: - clr_mask |= 1 << 5; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 8; - break; - case ds1775: - case ds75: - case stds75: - clr_mask |= 3 << 5; - set_mask |= 2 << 5; /* 11-bit mode */ - data->resolution = 11; - data->sample_time = MSEC_PER_SEC; - break; - case stlm75: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 5; - break; - case ds7505: - set_mask |= 3 << 5; /* 12-bit mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 4; - break; - case g751: - case lm75: - case lm75a: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 2; - break; - case lm75b: - data->resolution = 11; - data->sample_time = MSEC_PER_SEC / 4; - break; - case max6625: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 4; - break; - case max6626: - data->resolution = 12; - data->resolution_limits = 9; - data->sample_time = MSEC_PER_SEC / 4; - break; - case max31725: - data->resolution = 16; - data->sample_time = MSEC_PER_SEC / 8; - break; - case tcn75: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 8; - break; - case mcp980x: - data->resolution_limits = 9; - /* fall through */ - case tmp100: - case tmp101: - set_mask |= 3 << 5; /* 12-bit mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC; - clr_mask |= 1 << 7; /* not one-shot mode */ - break; - case tmp112: - set_mask |= 3 << 5; /* 12-bit mode */ - clr_mask |= 1 << 7; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 4; - break; - case tmp105: - case tmp175: - case tmp275: - case tmp75: - set_mask |= 3 << 5; /* 12-bit mode */ - clr_mask |= 1 << 7; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 2; - break; - case tmp75b: /* not one-shot mode, Conversion rate 37Hz */ - clr_mask |= 1 << 15 | 0x3 << 13; - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 37; - break; - case tmp75c: - clr_mask |= 1 << 5; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 4; - break; - } - /* configure as specified */ + data->params = &device_params[data->kind]; + + /* Save default sample time and resolution*/ + data->sample_time = data->params->default_sample_time; + data->resolution = data->params->default_resolution; + + /* Cache original configuration */ status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); if (status < 0) { dev_dbg(dev, "Can't read config? %d\n", status); return status; } data->orig_conf = status; - new = status & ~clr_mask; - new |= set_mask; - if (status != new) - i2c_smbus_write_byte_data(client, LM75_REG_CONF, new); + data->current_conf = status; - err = devm_add_action_or_reset(dev, lm75_remove, data); + err = lm75_write_config(data, data->params->set_mask, + data->params->clr_mask); if (err) return err; - dev_dbg(dev, "Config %02x\n", new); + err = devm_add_action_or_reset(dev, lm75_remove, data); + if (err) + return err; hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &lm75_chip_info, @@ -397,6 +624,7 @@ static const struct i2c_device_id lm75_ids[] = { { "max31725", max31725, }, { "max31726", max31725, }, { "mcp980x", mcp980x, }, + { "pct2075", pct2075, }, { "stds75", stds75, }, { "stlm75", stlm75, }, { "tcn75", tcn75, }, @@ -467,6 +695,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = { .data = (void *)mcp980x }, { + .compatible = "nxp,pct2075", + .data = (void *)pct2075 + }, + { .compatible = "st,stds75", .data = (void *)stds75 }, diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c index f9431ad43ad3..53ff5051774c 100644 --- a/drivers/hwmon/ltc2990.c +++ b/drivers/hwmon/ltc2990.c @@ -13,7 +13,7 @@ #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/property.h> #define LTC2990_STATUS 0x00 #define LTC2990_CONTROL 0x01 @@ -206,7 +206,6 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c, int ret; struct device *hwmon_dev; struct ltc2990_data *data; - struct device_node *of_node = i2c->dev.of_node; if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -218,9 +217,10 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c, data->i2c = i2c; - if (of_node) { - ret = of_property_read_u32_array(of_node, "lltc,meas-mode", - data->mode, 2); + if (dev_fwnode(&i2c->dev)) { + ret = device_property_read_u32_array(&i2c->dev, + "lltc,meas-mode", + data->mode, 2); if (ret < 0) return ret; diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index d42bc0883a32..7efa6bfef060 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -20,6 +20,7 @@ * * Chip #vin #fan #pwm #temp chip IDs man ID * nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3 + * nct6116d 9 5 5 3+3 0xd280 0xc1 0x5ca3 * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3 * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 @@ -58,12 +59,13 @@ #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6796, nct6797, nct6798 }; +enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, + nct6793, nct6795, nct6796, nct6797, nct6798 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { "nct6106", + "nct6116", "nct6775", "nct6776", "nct6779", @@ -78,6 +80,7 @@ static const char * const nct6775_device_names[] = { static const char * const nct6775_sio_names[] __initconst = { "NCT6106D", + "NCT6116D", "NCT6775F", "NCT6776D/F", "NCT6779D", @@ -115,6 +118,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_NCT6106_ID 0xc450 +#define SIO_NCT6116_ID 0xd280 #define SIO_NCT6775_ID 0xb470 #define SIO_NCT6776_ID 0xc330 #define SIO_NCT6779_ID 0xc560 @@ -825,10 +829,8 @@ static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4 }; static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 }; static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 }; -static const u16 NCT6106_REG_PWM[] = { 0x119, 0x129, 0x139 }; static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c }; static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 }; -static const u16 NCT6106_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130 }; static const u16 NCT6106_REG_TEMP_SOURCE[] = { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 }; @@ -896,6 +898,70 @@ static const u16 NCT6106_REG_TEMP_CRIT[32] = { [12] = 0x205, }; +/* NCT6112D/NCT6114D/NCT6116D specific data */ + +static const u16 NCT6116_REG_FAN[] = { 0x20, 0x22, 0x24, 0x26, 0x28 }; +static const u16 NCT6116_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4, 0xe6, 0xe8 }; +static const u16 NCT6116_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6, 0xf6, 0xf5 }; +static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; + +static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; +static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; +static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; +static const u16 NCT6116_REG_TEMP_SOURCE[] = { + 0xb0, 0xb1, 0xb2 }; + +static const u16 NCT6116_REG_CRITICAL_TEMP[] = { + 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; +static const u16 NCT6116_REG_CRITICAL_TEMP_TOLERANCE[] = { + 0x11b, 0x12b, 0x13b, 0x19b, 0x1ab }; + +static const u16 NCT6116_REG_CRITICAL_PWM_ENABLE[] = { + 0x11c, 0x12c, 0x13c, 0x19c, 0x1ac }; +static const u16 NCT6116_REG_CRITICAL_PWM[] = { + 0x11d, 0x12d, 0x13d, 0x19d, 0x1ad }; + +static const u16 NCT6116_REG_FAN_STEP_UP_TIME[] = { + 0x114, 0x124, 0x134, 0x194, 0x1a4 }; +static const u16 NCT6116_REG_FAN_STEP_DOWN_TIME[] = { + 0x115, 0x125, 0x135, 0x195, 0x1a5 }; +static const u16 NCT6116_REG_FAN_STOP_OUTPUT[] = { + 0x116, 0x126, 0x136, 0x196, 0x1a6 }; +static const u16 NCT6116_REG_FAN_START_OUTPUT[] = { + 0x117, 0x127, 0x137, 0x197, 0x1a7 }; +static const u16 NCT6116_REG_FAN_STOP_TIME[] = { + 0x118, 0x128, 0x138, 0x198, 0x1a8 }; +static const u16 NCT6116_REG_TOLERANCE_H[] = { + 0x112, 0x122, 0x132, 0x192, 0x1a2 }; + +static const u16 NCT6116_REG_TARGET[] = { + 0x111, 0x121, 0x131, 0x191, 0x1a1 }; + +static const u16 NCT6116_REG_AUTO_TEMP[] = { + 0x160, 0x170, 0x180, 0x1d0, 0x1e0 }; +static const u16 NCT6116_REG_AUTO_PWM[] = { + 0x164, 0x174, 0x184, 0x1d4, 0x1e4 }; + +static const s8 NCT6116_ALARM_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, -1, -1, -1, -1, -1, -1, /* in8..in9 */ + -1, /* unused */ + 32, 33, 34, 35, 36, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, -1, -1, -1, /* temp1..temp6 */ + 48, -1 /* intrusion0, intrusion1 */ +}; + +static const s8 NCT6116_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */ + 32, /* global beep enable */ + 24, 25, 26, 27, 28, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, -1, -1, -1, /* temp1..temp6 */ + 34, -1 /* intrusion0, intrusion1 */ +}; + static enum pwm_enable reg_to_pwm_enable(int pwm, int mode) { if (mode == 0 && pwm == 255) @@ -1294,6 +1360,11 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) return reg == 0x20 || reg == 0x22 || reg == 0x24 || reg == 0xe0 || reg == 0xe2 || reg == 0xe4 || reg == 0x111 || reg == 0x121 || reg == 0x131; + case nct6116: + return reg == 0x20 || reg == 0x22 || reg == 0x24 || + reg == 0x26 || reg == 0x28 || reg == 0xe0 || reg == 0xe2 || + reg == 0xe4 || reg == 0xe6 || reg == 0xe8 || reg == 0x111 || + reg == 0x121 || reg == 0x131 || reg == 0x191 || reg == 0x1a1; case nct6775: return (((reg & 0xff00) == 0x100 || (reg & 0xff00) == 0x200) && @@ -1673,6 +1744,7 @@ static void nct6775_update_pwm_limits(struct device *dev) data->auto_pwm[i][data->auto_pwm_num] = 0xff; break; case nct6106: + case nct6116: case nct6779: case nct6791: case nct6792: @@ -3109,6 +3181,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6776: break; /* always enabled, nothing to do */ case nct6106: + case nct6116: case nct6779: case nct6791: case nct6792: @@ -3535,6 +3608,23 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan3pin = !(cr24 & 0x80); pwm3pin = cr24 & 0x08; + } else if (data->kind == nct6116) { + int cr1a = superio_inb(sioreg, 0x1a); + int cr1b = superio_inb(sioreg, 0x1b); + int cr24 = superio_inb(sioreg, 0x24); + int cr2a = superio_inb(sioreg, 0x2a); + int cr2b = superio_inb(sioreg, 0x2b); + int cr2f = superio_inb(sioreg, 0x2f); + + fan3pin = !(cr2b & 0x10); + fan4pin = (cr2b & 0x80) || // pin 1(2) + (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66) + fan5pin = (cr2b & 0x80) || // pin 126(127) + (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96) + + pwm3pin = fan3pin && (cr24 & 0x08); + pwm4pin = fan4pin; + pwm5pin = fan5pin; } else { /* * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, @@ -3765,7 +3855,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME; data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME; data->REG_TOLERANCE_H = NCT6106_REG_TOLERANCE_H; - data->REG_PWM[0] = NCT6106_REG_PWM; + data->REG_PWM[0] = NCT6116_REG_PWM; data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT; data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT; data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP; @@ -3784,7 +3874,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM; data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; - data->REG_TEMP_SEL = NCT6106_REG_TEMP_SEL; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL; @@ -3807,6 +3897,79 @@ static int nct6775_probe(struct platform_device *pdev) reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H; break; + case nct6116: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 4; + data->temp_fixed_num = 3; + data->num_temp_alarms = 3; + data->num_temp_beeps = 3; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + + data->temp_label = nct6776_temp_label; + data->temp_mask = NCT6776_TEMP_MASK; + data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK; + + data->REG_VBAT = NCT6106_REG_VBAT; + data->REG_DIODE = NCT6106_REG_DIODE; + data->DIODE_MASK = NCT6106_DIODE_MASK; + data->REG_VIN = NCT6106_REG_IN; + data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX; + data->REG_TARGET = NCT6116_REG_TARGET; + data->REG_FAN = NCT6116_REG_FAN; + data->REG_FAN_MODE = NCT6116_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6116_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6116_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6116_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6116_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6116_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6116_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6116_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6116_REG_PWM; + data->REG_PWM[1] = NCT6116_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6116_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6106_REG_PWM_READ; + data->REG_PWM_MODE = NCT6106_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6116_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6116_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6116_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6116_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6116_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6106_REG_ALARM; + data->ALARM_BITS = NCT6116_ALARM_BITS; + data->REG_BEEP = NCT6106_REG_BEEP; + data->BEEP_BITS = NCT6116_BEEP_BITS; + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; + num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; + reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H; + + break; case nct6775: data->in_num = 9; data->pwm_num = 3; @@ -4352,6 +4515,7 @@ static int nct6775_probe(struct platform_device *pdev) data->have_vid = (cr2a & 0x60) == 0x40; break; case nct6106: + case nct6116: case nct6779: case nct6791: case nct6792: @@ -4381,6 +4545,7 @@ static int nct6775_probe(struct platform_device *pdev) NCT6775_REG_CR_FAN_DEBOUNCE); switch (data->kind) { case nct6106: + case nct6116: tmp |= 0xe0; break; case nct6775: @@ -4576,6 +4741,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6106_ID: sio_data->kind = nct6106; break; + case SIO_NCT6116_ID: + sio_data->kind = nct6116; + break; case SIO_NCT6775_ID: sio_data->kind = nct6775; break; diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index ec7bcf8d7cd6..f3dd2a17bd42 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -704,7 +704,7 @@ static struct attribute *nct7802_in_attrs[] = { &sensor_dev_attr_in3_alarm.dev_attr.attr, &sensor_dev_attr_in3_beep.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, /* 17 */ + &sensor_dev_attr_in4_input.dev_attr.attr, /* 16 */ &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, @@ -730,9 +730,9 @@ static umode_t nct7802_in_is_visible(struct kobject *kobj, if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */ return 0; - if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */ + if (index >= 11 && index < 16 && (reg & 0x0c) != 0x0c) /* VSEN2 */ return 0; - if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */ + if (index >= 16 && (reg & 0x30) != 0x30) /* VSEN3 */ return 0; return attr->mode; diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 710c30562fc1..95b447cfa24c 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -46,10 +46,34 @@ #define DTS_T_CTRL1_REG 0x27 #define VT_ADC_MD_REG 0x2E +#define VSEN1_HV_LL_REG 0x02 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define VSEN1_LV_LL_REG 0x03 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define VSEN1_HV_HL_REG 0x00 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define VSEN1_LV_HL_REG 0x01 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define SMI_STS1_REG 0xC1 /* Bank 0; SMI Status Register */ +#define SMI_STS3_REG 0xC3 /* Bank 0; SMI Status Register */ +#define SMI_STS5_REG 0xC5 /* Bank 0; SMI Status Register */ +#define SMI_STS7_REG 0xC7 /* Bank 0; SMI Status Register */ +#define SMI_STS8_REG 0xC8 /* Bank 0; SMI Status Register */ + #define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */ #define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */ #define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */ +#define LTD_HV_HL_REG 0x44 /* Bank 1; 1 reg for LTD */ +#define LTD_LV_HL_REG 0x45 /* Bank 1; 1 reg for LTD */ +#define LTD_HV_LL_REG 0x46 /* Bank 1; 1 reg for LTD */ +#define LTD_LV_LL_REG 0x47 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_CH_REG 0x05 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_W_REG 0x06 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_WH_REG 0x07 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_C_REG 0x04 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_C_REG 0x90 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_CH_REG 0x91 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_W_REG 0x92 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_WH_REG 0x93 /* Bank 1; 1 reg per sensor */ #define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */ +#define FANIN1_HV_HL_REG 0x60 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define FANIN1_LV_HL_REG 0x61 /* Bank 1; 2 regs (HV/LV) per sensor */ #define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */ #define PRTS_REG 0x03 /* Bank 2 */ @@ -58,6 +82,8 @@ #define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */ #define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */ +#define ENABLE_TSI BIT(1) + static const unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END }; @@ -72,6 +98,7 @@ struct nct7904_data { u8 fan_mode[FANCTL_MAX]; u8 enable_dts; u8 has_dts; + u8 temp_mode; /* 0: TR mode, 1: TD mode */ }; /* Access functions */ @@ -170,6 +197,25 @@ static int nct7904_read_fan(struct device *dev, u32 attr, int channel, rpm = 1350000 / cnt; *val = rpm; return 0; + case hwmon_fan_min: + ret = nct7904_read_reg16(data, BANK_1, + FANIN1_HV_HL_REG + channel * 2); + if (ret < 0) + return ret; + cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); + if (cnt == 0x1fff) + rpm = 0; + else + rpm = 1350000 / cnt; + *val = rpm; + return 0; + case hwmon_fan_alarm: + ret = nct7904_read_reg(data, BANK_0, + SMI_STS5_REG + (channel >> 3)); + if (ret < 0) + return ret; + *val = (ret >> (channel & 0x07)) & 1; + return 0; default: return -EOPNOTSUPP; } @@ -179,8 +225,20 @@ static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel) { const struct nct7904_data *data = _data; - if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel)) - return 0444; + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_alarm: + if (data->fanin_mask & (1 << channel)) + return 0444; + break; + case hwmon_fan_min: + if (data->fanin_mask & (1 << channel)) + return 0644; + break; + default: + break; + } + return 0; } @@ -211,6 +269,37 @@ static int nct7904_read_in(struct device *dev, u32 attr, int channel, volt *= 6; /* 0.006V scale */ *val = volt; return 0; + case hwmon_in_min: + ret = nct7904_read_reg16(data, BANK_1, + VSEN1_HV_LL_REG + index * 4); + if (ret < 0) + return ret; + volt = ((ret & 0xff00) >> 5) | (ret & 0x7); + if (index < 14) + volt *= 2; /* 0.002V scale */ + else + volt *= 6; /* 0.006V scale */ + *val = volt; + return 0; + case hwmon_in_max: + ret = nct7904_read_reg16(data, BANK_1, + VSEN1_HV_HL_REG + index * 4); + if (ret < 0) + return ret; + volt = ((ret & 0xff00) >> 5) | (ret & 0x7); + if (index < 14) + volt *= 2; /* 0.002V scale */ + else + volt *= 6; /* 0.006V scale */ + *val = volt; + return 0; + case hwmon_in_alarm: + ret = nct7904_read_reg(data, BANK_0, + SMI_STS1_REG + (index >> 3)); + if (ret < 0) + return ret; + *val = (ret >> (index & 0x07)) & 1; + return 0; default: return -EOPNOTSUPP; } @@ -221,9 +310,20 @@ static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel) const struct nct7904_data *data = _data; int index = nct7904_chan_to_index[channel]; - if (channel > 0 && attr == hwmon_in_input && - (data->vsen_mask & BIT(index))) - return 0444; + switch (attr) { + case hwmon_in_input: + case hwmon_in_alarm: + if (channel > 0 && (data->vsen_mask & BIT(index))) + return 0444; + break; + case hwmon_in_min: + case hwmon_in_max: + if (channel > 0 && (data->vsen_mask & BIT(index))) + return 0644; + break; + default: + break; + } return 0; } @@ -233,6 +333,7 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel, { struct nct7904_data *data = dev_get_drvdata(dev); int ret, temp; + unsigned int reg1, reg2, reg3; switch (attr) { case hwmon_temp_input: @@ -250,16 +351,106 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel, temp = ((ret & 0xff00) >> 5) | (ret & 0x7); *val = sign_extend32(temp, 10) * 125; return 0; + case hwmon_temp_alarm: + if (channel == 4) { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS3_REG); + if (ret < 0) + return ret; + *val = (ret >> 1) & 1; + } else if (channel < 4) { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS1_REG); + if (ret < 0) + return ret; + *val = (ret >> (((channel * 2) + 1) & 0x07)) & 1; + } else { + if ((channel - 5) < 4) { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS7_REG + + ((channel - 5) >> 3)); + if (ret < 0) + return ret; + *val = (ret >> ((channel - 5) & 0x07)) & 1; + } else { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS8_REG + + ((channel - 5) >> 3)); + if (ret < 0) + return ret; + *val = (ret >> (((channel - 5) & 0x07) - 4)) + & 1; + } + } + return 0; + case hwmon_temp_type: + if (channel < 5) { + if ((data->tcpu_mask >> channel) & 0x01) { + if ((data->temp_mode >> channel) & 0x01) + *val = 3; /* TD */ + else + *val = 4; /* TR */ + } else { + *val = 0; + } + } else { + if ((data->has_dts >> (channel - 5)) & 0x01) { + if (data->enable_dts & ENABLE_TSI) + *val = 5; /* TSI */ + else + *val = 6; /* PECI */ + } else { + *val = 0; + } + } + return 0; + case hwmon_temp_max: + reg1 = LTD_HV_LL_REG; + reg2 = TEMP_CH1_W_REG; + reg3 = DTS_T_CPU1_W_REG; + break; + case hwmon_temp_max_hyst: + reg1 = LTD_LV_LL_REG; + reg2 = TEMP_CH1_WH_REG; + reg3 = DTS_T_CPU1_WH_REG; + break; + case hwmon_temp_crit: + reg1 = LTD_HV_HL_REG; + reg2 = TEMP_CH1_C_REG; + reg3 = DTS_T_CPU1_C_REG; + break; + case hwmon_temp_crit_hyst: + reg1 = LTD_LV_HL_REG; + reg2 = TEMP_CH1_CH_REG; + reg3 = DTS_T_CPU1_CH_REG; + break; default: return -EOPNOTSUPP; } + + if (channel == 4) + ret = nct7904_read_reg(data, BANK_1, reg1); + else if (channel < 5) + ret = nct7904_read_reg(data, BANK_1, + reg2 + channel * 8); + else + ret = nct7904_read_reg(data, BANK_1, + reg3 + (channel - 5) * 4); + + if (ret < 0) + return ret; + *val = ret * 1000; + return 0; } static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel) { const struct nct7904_data *data = _data; - if (attr == hwmon_temp_input) { + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_alarm: + case hwmon_temp_type: if (channel < 5) { if (data->tcpu_mask & BIT(channel)) return 0444; @@ -267,6 +458,21 @@ static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel) if (data->has_dts & BIT(channel - 5)) return 0444; } + break; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + if (channel < 5) { + if (data->tcpu_mask & BIT(channel)) + return 0644; + } else { + if (data->has_dts & BIT(channel - 5)) + return 0644; + } + break; + default: + break; } return 0; @@ -297,6 +503,137 @@ static int nct7904_read_pwm(struct device *dev, u32 attr, int channel, } } +static int nct7904_write_temp(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + unsigned int reg1, reg2, reg3; + + val = clamp_val(val / 1000, -128, 127); + + switch (attr) { + case hwmon_temp_max: + reg1 = LTD_HV_LL_REG; + reg2 = TEMP_CH1_W_REG; + reg3 = DTS_T_CPU1_W_REG; + break; + case hwmon_temp_max_hyst: + reg1 = LTD_LV_LL_REG; + reg2 = TEMP_CH1_WH_REG; + reg3 = DTS_T_CPU1_WH_REG; + break; + case hwmon_temp_crit: + reg1 = LTD_HV_HL_REG; + reg2 = TEMP_CH1_C_REG; + reg3 = DTS_T_CPU1_C_REG; + break; + case hwmon_temp_crit_hyst: + reg1 = LTD_LV_HL_REG; + reg2 = TEMP_CH1_CH_REG; + reg3 = DTS_T_CPU1_CH_REG; + break; + default: + return -EOPNOTSUPP; + } + if (channel == 4) + ret = nct7904_write_reg(data, BANK_1, reg1, val); + else if (channel < 5) + ret = nct7904_write_reg(data, BANK_1, + reg2 + channel * 8, val); + else + ret = nct7904_write_reg(data, BANK_1, + reg3 + (channel - 5) * 4, val); + + return ret; +} + +static int nct7904_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + u8 tmp; + + switch (attr) { + case hwmon_fan_min: + if (val <= 0) + return -EINVAL; + + val = clamp_val(DIV_ROUND_CLOSEST(1350000, val), 1, 0x1fff); + tmp = (val >> 5) & 0xff; + ret = nct7904_write_reg(data, BANK_1, + FANIN1_HV_HL_REG + channel * 2, tmp); + if (ret < 0) + return ret; + tmp = val & 0x1f; + ret = nct7904_write_reg(data, BANK_1, + FANIN1_LV_HL_REG + channel * 2, tmp); + return ret; + default: + return -EOPNOTSUPP; + } +} + +static int nct7904_write_in(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret, index, tmp; + + index = nct7904_chan_to_index[channel]; + + if (index < 14) + val = val / 2; /* 0.002V scale */ + else + val = val / 6; /* 0.006V scale */ + + val = clamp_val(val, 0, 0x7ff); + + switch (attr) { + case hwmon_in_min: + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_LV_LL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp &= ~0x7; + tmp |= val & 0x7; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_LV_LL_REG + index * 4, tmp); + if (ret < 0) + return ret; + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_HV_LL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp = (val >> 3) & 0xff; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_HV_LL_REG + index * 4, tmp); + return ret; + case hwmon_in_max: + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_LV_HL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp &= ~0x7; + tmp |= val & 0x7; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_LV_HL_REG + index * 4, tmp); + if (ret < 0) + return ret; + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_HV_HL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp = (val >> 3) & 0xff; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_HV_HL_REG + index * 4, tmp); + return ret; + default: + return -EOPNOTSUPP; + } +} + static int nct7904_write_pwm(struct device *dev, u32 attr, int channel, long val) { @@ -354,8 +691,14 @@ static int nct7904_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { switch (type) { + case hwmon_in: + return nct7904_write_in(dev, attr, channel, val); + case hwmon_fan: + return nct7904_write_fan(dev, attr, channel, val); case hwmon_pwm: return nct7904_write_pwm(dev, attr, channel, val); + case hwmon_temp: + return nct7904_write_temp(dev, attr, channel, val); default: return -EOPNOTSUPP; } @@ -404,51 +747,91 @@ static int nct7904_detect(struct i2c_client *client, static const struct hwmon_channel_info *nct7904_info[] = { HWMON_CHANNEL_INFO(in, - HWMON_I_INPUT, /* dummy, skipped in is_visible */ - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT), + /* dummy, skipped in is_visible */ + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM), HWMON_CHANNEL_INFO(fan, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT), + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM), HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), HWMON_CHANNEL_INFO(temp, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT), + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST), NULL }; @@ -530,11 +913,14 @@ static int nct7904_probe(struct i2c_client *client, if (ret < 0) return ret; + data->temp_mode = 0; for (i = 0; i < 4; i++) { val = (ret & (0x03 << i)) >> (i * 2); bit = (1 << i); if (val == 0) data->tcpu_mask &= ~bit; + else if (val == 0x1 || val == 0x2) + data->temp_mode |= bit; } /* PECI */ @@ -557,7 +943,7 @@ static int nct7904_probe(struct i2c_client *client, if (ret < 0) return ret; data->has_dts = ret & 0xF; - if (data->enable_dts & 0x2) { + if (data->enable_dts & ENABLE_TSI) { ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG); if (ret < 0) return ret; diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 09aaefa6fdb8..11a28609da3c 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -967,10 +967,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) spin_lock_init(&data->fan_lock[i]); data->fan_irq[i] = platform_get_irq(pdev, i); - if (data->fan_irq[i] < 0) { - dev_err(dev, "get IRQ fan%d failed\n", i); + if (data->fan_irq[i] < 0) return data->fan_irq[i]; - } sprintf(name, "NPCM7XX-FAN-MD%d", i); ret = devm_request_irq(dev, data->fan_irq[i], npcm7xx_fan_isr, diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index b6588483fae1..d62d69bb7e49 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -46,6 +46,15 @@ config SENSORS_IBM_CFFPS This driver can also be built as a module. If so, the module will be called ibm-cffps. +config SENSORS_INSPUR_IPSPS + tristate "INSPUR Power System Power Supply" + help + If you say yes here you get hardware monitoring support for the INSPUR + Power System power supply. + + This driver can also be built as a module. If so, the module will + be called inspur-ipsps. + config SENSORS_IR35221 tristate "Infineon IR35221" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index c950ea9a5d00..03bacfcfd660 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o +obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o obj-$(CONFIG_SENSORS_IR38064) += ir38064.o obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index ee2ee9e3ffd7..d44745e498e7 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -12,6 +12,7 @@ #include <linux/leds.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/of_device.h> #include <linux/pmbus.h> #include "pmbus.h" @@ -20,8 +21,9 @@ #define CFFPS_PN_CMD 0x9B #define CFFPS_SN_CMD 0x9E #define CFFPS_CCIN_CMD 0xBD -#define CFFPS_FW_CMD_START 0xFA -#define CFFPS_FW_NUM_BYTES 4 +#define CFFPS_FW_CMD 0xFA +#define CFFPS1_FW_NUM_BYTES 4 +#define CFFPS2_FW_NUM_WORDS 3 #define CFFPS_SYS_CONFIG_CMD 0xDA #define CFFPS_INPUT_HISTORY_CMD 0xD6 @@ -52,6 +54,8 @@ enum { CFFPS_DEBUGFS_NUM_ENTRIES }; +enum versions { cffps1, cffps2 }; + struct ibm_cffps_input_history { struct mutex update_lock; unsigned long last_update; @@ -61,6 +65,7 @@ struct ibm_cffps_input_history { }; struct ibm_cffps { + enum versions version; struct i2c_client *client; struct ibm_cffps_input_history input_history; @@ -132,6 +137,8 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf, struct ibm_cffps *psu = to_psu(idxp, idx); char data[I2C_SMBUS_BLOCK_MAX] = { 0 }; + pmbus_set_page(psu->client, 0); + switch (idx) { case CFFPS_DEBUGFS_INPUT_HISTORY: return ibm_cffps_read_input_history(psu, buf, count, ppos); @@ -152,16 +159,36 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf, rc = snprintf(data, 5, "%04X", rc); goto done; case CFFPS_DEBUGFS_FW: - for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) { - rc = i2c_smbus_read_byte_data(psu->client, - CFFPS_FW_CMD_START + i); - if (rc < 0) - return rc; + switch (psu->version) { + case cffps1: + for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) { + rc = i2c_smbus_read_byte_data(psu->client, + CFFPS_FW_CMD + + i); + if (rc < 0) + return rc; + + snprintf(&data[i * 2], 3, "%02X", rc); + } - snprintf(&data[i * 2], 3, "%02X", rc); - } + rc = i * 2; + break; + case cffps2: + for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) { + rc = i2c_smbus_read_word_data(psu->client, + CFFPS_FW_CMD + + i); + if (rc < 0) + return rc; + + snprintf(&data[i * 4], 5, "%04X", rc); + } - rc = i * 2; + rc = i * 4; + break; + default: + return -EOPNOTSUPP; + } goto done; default: return -EINVAL; @@ -279,6 +306,8 @@ static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev, psu->led_state = CFFPS_LED_ON; } + pmbus_set_page(psu->client, 0); + rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, psu->led_state); if (rc < 0) @@ -299,6 +328,8 @@ static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev, if (led_cdev->brightness == LED_OFF) return 0; + pmbus_set_page(psu->client, 0); + rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, CFFPS_LED_BLINK); if (rc < 0) @@ -328,15 +359,32 @@ static void ibm_cffps_create_led_class(struct ibm_cffps *psu) dev_warn(dev, "failed to register led class: %d\n", rc); } -static struct pmbus_driver_info ibm_cffps_info = { - .pages = 1, - .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | - PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | - PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | - PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | - PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, - .read_byte_data = ibm_cffps_read_byte_data, - .read_word_data = ibm_cffps_read_word_data, +static struct pmbus_driver_info ibm_cffps_info[] = { + [cffps1] = { + .pages = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_FAN12, + .read_byte_data = ibm_cffps_read_byte_data, + .read_word_data = ibm_cffps_read_word_data, + }, + [cffps2] = { + .pages = 2, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_FAN12, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT, + .read_byte_data = ibm_cffps_read_byte_data, + .read_word_data = ibm_cffps_read_word_data, + }, }; static struct pmbus_platform_data ibm_cffps_pdata = { @@ -347,12 +395,21 @@ static int ibm_cffps_probe(struct i2c_client *client, const struct i2c_device_id *id) { int i, rc; + enum versions vs; struct dentry *debugfs; struct dentry *ibm_cffps_dir; struct ibm_cffps *psu; + const void *md = of_device_get_match_data(&client->dev); + + if (md) + vs = (enum versions)md; + else if (id) + vs = (enum versions)id->driver_data; + else + vs = cffps1; client->dev.platform_data = &ibm_cffps_pdata; - rc = pmbus_do_probe(client, id, &ibm_cffps_info); + rc = pmbus_do_probe(client, id, &ibm_cffps_info[vs]); if (rc) return rc; @@ -364,6 +421,7 @@ static int ibm_cffps_probe(struct i2c_client *client, if (!psu) return 0; + psu->version = vs; psu->client = client; mutex_init(&psu->input_history.update_lock); psu->input_history.last_update = jiffies - HZ; @@ -405,13 +463,21 @@ static int ibm_cffps_probe(struct i2c_client *client, } static const struct i2c_device_id ibm_cffps_id[] = { - { "ibm_cffps1", 1 }, + { "ibm_cffps1", cffps1 }, + { "ibm_cffps2", cffps2 }, {} }; MODULE_DEVICE_TABLE(i2c, ibm_cffps_id); static const struct of_device_id ibm_cffps_of_match[] = { - { .compatible = "ibm,cffps1" }, + { + .compatible = "ibm,cffps1", + .data = (void *)cffps1 + }, + { + .compatible = "ibm,cffps2", + .data = (void *)cffps2 + }, {} }; MODULE_DEVICE_TABLE(of, ibm_cffps_of_match); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c new file mode 100644 index 000000000000..42e01549184a --- /dev/null +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Inspur Corp. + */ + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include <linux/hwmon-sysfs.h> + +#include "pmbus.h" + +#define IPSPS_REG_VENDOR_ID 0x99 +#define IPSPS_REG_MODEL 0x9A +#define IPSPS_REG_FW_VERSION 0x9B +#define IPSPS_REG_PN 0x9C +#define IPSPS_REG_SN 0x9E +#define IPSPS_REG_HW_VERSION 0xB0 +#define IPSPS_REG_MODE 0xFC + +#define MODE_ACTIVE 0x55 +#define MODE_STANDBY 0x0E +#define MODE_REDUNDANCY 0x00 + +#define MODE_ACTIVE_STRING "active" +#define MODE_STANDBY_STRING "standby" +#define MODE_REDUNDANCY_STRING "redundancy" + +enum ipsps_index { + vendor, + model, + fw_version, + part_number, + serial_number, + hw_version, + mode, + num_regs, +}; + +static const u8 ipsps_regs[num_regs] = { + [vendor] = IPSPS_REG_VENDOR_ID, + [model] = IPSPS_REG_MODEL, + [fw_version] = IPSPS_REG_FW_VERSION, + [part_number] = IPSPS_REG_PN, + [serial_number] = IPSPS_REG_SN, + [hw_version] = IPSPS_REG_HW_VERSION, + [mode] = IPSPS_REG_MODE, +}; + +static ssize_t ipsps_string_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + u8 reg; + int rc; + char *p; + char data[I2C_SMBUS_BLOCK_MAX + 1]; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + rc = i2c_smbus_read_block_data(client, reg, data); + if (rc < 0) + return rc; + + /* filled with printable characters, ending with # */ + p = memscan(data, '#', rc); + *p = '\0'; + + return snprintf(buf, PAGE_SIZE, "%s\n", data); +} + +static ssize_t ipsps_fw_version_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + u8 reg; + int rc; + u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 }; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + rc = i2c_smbus_read_block_data(client, reg, data); + if (rc < 0) + return rc; + + if (rc != 6) + return -EPROTO; + + return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n", + data[1], data[2]/* < 100 */, data[3]/*< 10*/, + data[4], data[5]/* < 100 */); +} + +static ssize_t ipsps_mode_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u8 reg; + int rc; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + rc = i2c_smbus_read_byte_data(client, reg); + if (rc < 0) + return rc; + + switch (rc) { + case MODE_ACTIVE: + return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + case MODE_STANDBY: + return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + case MODE_REDUNDANCY: + return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + default: + return snprintf(buf, PAGE_SIZE, "unspecified\n"); + } +} + +static ssize_t ipsps_mode_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + u8 reg; + int rc; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + if (sysfs_streq(MODE_STANDBY_STRING, buf)) { + rc = i2c_smbus_write_byte_data(client, reg, + MODE_STANDBY); + if (rc < 0) + return rc; + return count; + } else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) { + rc = i2c_smbus_write_byte_data(client, reg, + MODE_ACTIVE); + if (rc < 0) + return rc; + return count; + } + + return -EINVAL; +} + +static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); +static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); +static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); +static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); +static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); +static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); +static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); + +static struct attribute *ipsps_attrs[] = { + &sensor_dev_attr_vendor.dev_attr.attr, + &sensor_dev_attr_model.dev_attr.attr, + &sensor_dev_attr_part_number.dev_attr.attr, + &sensor_dev_attr_serial_number.dev_attr.attr, + &sensor_dev_attr_hw_version.dev_attr.attr, + &sensor_dev_attr_fw_version.dev_attr.attr, + &sensor_dev_attr_mode.dev_attr.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(ipsps); + +static struct pmbus_driver_info ipsps_info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | + PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, + .groups = ipsps_groups, +}; + +static struct pmbus_platform_data ipsps_pdata = { + .flags = PMBUS_SKIP_STATUS_CHECK, +}; + +static int ipsps_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + client->dev.platform_data = &ipsps_pdata; + return pmbus_do_probe(client, id, &ipsps_info); +} + +static const struct i2c_device_id ipsps_id[] = { + { "ipsps1", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ipsps_id); + +#ifdef CONFIG_OF +static const struct of_device_id ipsps_of_match[] = { + { .compatible = "inspur,ipsps1" }, + {} +}; +MODULE_DEVICE_TABLE(of, ipsps_of_match); +#endif + +static struct i2c_driver ipsps_driver = { + .driver = { + .name = "inspur-ipsps", + .of_match_table = of_match_ptr(ipsps_of_match), + }, + .probe = ipsps_probe, + .remove = pmbus_do_remove, + .id_table = ipsps_id, +}; + +module_i2c_driver(ipsps_driver); + +MODULE_AUTHOR("John Wang"); +MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 69d9029ea410..254b0f98c755 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -244,8 +244,6 @@ static int max31785_write_word_data(struct i2c_client *client, int page, #define MAX31785_VOUT_FUNCS \ (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT) -#define MAX37185_NUM_FAN_PAGES 6 - static const struct pmbus_driver_info max31785_info = { .pages = MAX31785_NR_PAGES, diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index c846759bc1c0..a9229c6b0e84 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/pmbus.h> -#include <linux/gpio.h> #include <linux/gpio/driver.h> #include "pmbus.h" diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index efe4bb1ff221..d3a64a35f7a9 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -146,7 +146,7 @@ static struct platform_driver rpi_hwmon_driver = { }; module_platform_driver(rpi_hwmon_driver); -MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); +MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:raspberrypi-hwmon"); diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index 83fe08185ac7..a0078ccede03 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -24,19 +24,33 @@ static const unsigned char shtc1_cmd_measure_blocking_lpm[] = { 0x64, 0x58 }; static const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c }; /* command for reading the ID register */ -static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 }; +static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 }; -/* constants for reading the ID register */ -#define SHTC1_ID 0x07 -#define SHTC1_ID_REG_MASK 0x1f +/* + * constants for reading the ID register + * SHTC1: 0x0007 with mask 0x003f + * SHTW1: 0x0007 with mask 0x003f + * SHTC3: 0x0807 with mask 0x083f + */ +#define SHTC3_ID 0x0807 +#define SHTC3_ID_MASK 0x083f +#define SHTC1_ID 0x0007 +#define SHTC1_ID_MASK 0x003f /* delays for non-blocking i2c commands, both in us */ #define SHTC1_NONBLOCKING_WAIT_TIME_HPM 14400 #define SHTC1_NONBLOCKING_WAIT_TIME_LPM 1000 +#define SHTC3_NONBLOCKING_WAIT_TIME_HPM 12100 +#define SHTC3_NONBLOCKING_WAIT_TIME_LPM 800 #define SHTC1_CMD_LENGTH 2 #define SHTC1_RESPONSE_LENGTH 6 +enum shtcx_chips { + shtc1, + shtc3, +}; + struct shtc1_data { struct i2c_client *client; struct mutex update_lock; @@ -47,6 +61,7 @@ struct shtc1_data { unsigned int nonblocking_wait_time; /* in us */ struct shtc1_platform_data setup; + enum shtcx_chips chip; int temperature; /* 1000 * temperature in dgr C */ int humidity; /* 1000 * relative humidity in %RH */ @@ -157,13 +172,16 @@ static void shtc1_select_command(struct shtc1_data *data) data->command = data->setup.blocking_io ? shtc1_cmd_measure_blocking_hpm : shtc1_cmd_measure_nonblocking_hpm; - data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_HPM; - + data->nonblocking_wait_time = (data->chip == shtc1) ? + SHTC1_NONBLOCKING_WAIT_TIME_HPM : + SHTC3_NONBLOCKING_WAIT_TIME_HPM; } else { data->command = data->setup.blocking_io ? shtc1_cmd_measure_blocking_lpm : shtc1_cmd_measure_nonblocking_lpm; - data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_LPM; + data->nonblocking_wait_time = (data->chip == shtc1) ? + SHTC1_NONBLOCKING_WAIT_TIME_LPM : + SHTC3_NONBLOCKING_WAIT_TIME_LPM; } } @@ -171,9 +189,11 @@ static int shtc1_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; - char id_reg[2]; + u16 id_reg; + char id_reg_buf[2]; struct shtc1_data *data; struct device *hwmon_dev; + enum shtcx_chips chip = id->driver_data; struct i2c_adapter *adap = client->adapter; struct device *dev = &client->dev; @@ -187,13 +207,20 @@ static int shtc1_probe(struct i2c_client *client, dev_err(dev, "could not send read_id_reg command: %d\n", ret); return ret < 0 ? ret : -ENODEV; } - ret = i2c_master_recv(client, id_reg, sizeof(id_reg)); - if (ret != sizeof(id_reg)) { + ret = i2c_master_recv(client, id_reg_buf, sizeof(id_reg_buf)); + if (ret != sizeof(id_reg_buf)) { dev_err(dev, "could not read ID register: %d\n", ret); return -ENODEV; } - if ((id_reg[1] & SHTC1_ID_REG_MASK) != SHTC1_ID) { - dev_err(dev, "ID register doesn't match\n"); + + id_reg = be16_to_cpup((__be16 *)id_reg_buf); + if (chip == shtc3) { + if ((id_reg & SHTC3_ID_MASK) != SHTC3_ID) { + dev_err(dev, "SHTC3 ID register does not match\n"); + return -ENODEV; + } + } else if ((id_reg & SHTC1_ID_MASK) != SHTC1_ID) { + dev_err(dev, "SHTC1 ID register does not match\n"); return -ENODEV; } @@ -204,6 +231,7 @@ static int shtc1_probe(struct i2c_client *client, data->setup.blocking_io = false; data->setup.high_precision = true; data->client = client; + data->chip = chip; if (client->dev.platform_data) data->setup = *(struct shtc1_platform_data *)dev->platform_data; @@ -222,8 +250,9 @@ static int shtc1_probe(struct i2c_client *client, /* device ID table */ static const struct i2c_device_id shtc1_id[] = { - { "shtc1", 0 }, - { "shtw1", 0 }, + { "shtc1", shtc1 }, + { "shtw1", shtc1 }, + { "shtc3", shtc3 }, { } }; MODULE_DEVICE_TABLE(i2c, shtc1_id); diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c index d8c91c2cb8cf..6eff14fe395d 100644 --- a/drivers/hwmon/smm665.c +++ b/drivers/hwmon/smm665.c @@ -586,10 +586,10 @@ static int smm665_probe(struct i2c_client *client, data->client = client; data->type = id->driver_data; - data->cmdreg = i2c_new_dummy(adapter, (client->addr & ~SMM665_REGMASK) + data->cmdreg = i2c_new_dummy_device(adapter, (client->addr & ~SMM665_REGMASK) | SMM665_CMDREG_BASE); - if (!data->cmdreg) - return -ENOMEM; + if (IS_ERR(data->cmdreg)) + return PTR_ERR(data->cmdreg); switch (data->type) { case smm465: diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index d2c04b6a3f2b..015f1ea31966 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -894,12 +894,12 @@ w83781d_detect_subclients(struct i2c_client *new_client) } for (i = 0; i < num_sc; i++) { - data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]); - if (!data->lm75[i]) { + data->lm75[i] = i2c_new_dummy_device(adapter, sc_addr[i]); + if (IS_ERR(data->lm75[i])) { dev_err(&new_client->dev, "Subclient %d registration at address 0x%x failed.\n", i, sc_addr[i]); - err = -ENOMEM; + err = PTR_ERR(data->lm75[i]); if (i == 1) goto ERROR_SC_3; goto ERROR_SC_2; diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 050ad4201691..aad8d4da5802 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1260,7 +1260,7 @@ static int w83791d_detect_subclients(struct i2c_client *client) struct i2c_adapter *adapter = client->adapter; struct w83791d_data *data = i2c_get_clientdata(client); int address = client->addr; - int i, id, err; + int i, id; u8 val; id = i2c_adapter_id(adapter); @@ -1272,8 +1272,7 @@ static int w83791d_detect_subclients(struct i2c_client *client) "invalid subclient " "address %d; must be 0x48-0x4f\n", force_subclients[i]); - err = -ENODEV; - goto error_sc_0; + return -ENODEV; } } w83791d_write(client, W83791D_REG_I2C_SUBADDR, @@ -1283,29 +1282,22 @@ static int w83791d_detect_subclients(struct i2c_client *client) val = w83791d_read(client, W83791D_REG_I2C_SUBADDR); if (!(val & 0x08)) - data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7)); + data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + (val & 0x7)); if (!(val & 0x80)) { - if ((data->lm75[0] != NULL) && + if (!IS_ERR(data->lm75[0]) && ((val & 0x7) == ((val >> 4) & 0x7))) { dev_err(&client->dev, "duplicate addresses 0x%x, " "use force_subclient\n", data->lm75[0]->addr); - err = -ENODEV; - goto error_sc_1; + return -ENODEV; } - data->lm75[1] = i2c_new_dummy(adapter, - 0x48 + ((val >> 4) & 0x7)); + data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + ((val >> 4) & 0x7)); } return 0; - -/* Undo inits in case of errors */ - -error_sc_1: - i2c_unregister_device(data->lm75[0]); -error_sc_0: - return err; } @@ -1394,7 +1386,7 @@ static int w83791d_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &w83791d_group); if (err) - goto error3; + return err; /* Check if pins of fan/pwm 4-5 are in use as GPIO */ has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10; @@ -1419,9 +1411,6 @@ error5: sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45); error4: sysfs_remove_group(&client->dev.kobj, &w83791d_group); -error3: - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); return err; } @@ -1432,9 +1421,6 @@ static int w83791d_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &w83791d_group); - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); - return 0; } diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index da8a6d62aa23..7fc8a1160c8f 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -924,7 +924,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr, static int w83792d_detect_subclients(struct i2c_client *new_client) { - int i, id, err; + int i, id; int address = new_client->addr; u8 val; struct i2c_adapter *adapter = new_client->adapter; @@ -938,8 +938,7 @@ w83792d_detect_subclients(struct i2c_client *new_client) dev_err(&new_client->dev, "invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); - err = -ENODEV; - goto ERROR_SC_0; + return -ENODEV; } } w83792d_write_value(new_client, W83792D_REG_I2C_SUBADDR, @@ -949,28 +948,21 @@ w83792d_detect_subclients(struct i2c_client *new_client) val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR); if (!(val & 0x08)) - data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7)); + data->lm75[0] = devm_i2c_new_dummy_device(&new_client->dev, adapter, + 0x48 + (val & 0x7)); if (!(val & 0x80)) { - if ((data->lm75[0] != NULL) && + if (!IS_ERR(data->lm75[0]) && ((val & 0x7) == ((val >> 4) & 0x7))) { dev_err(&new_client->dev, "duplicate addresses 0x%x, use force_subclient\n", data->lm75[0]->addr); - err = -ENODEV; - goto ERROR_SC_1; + return -ENODEV; } - data->lm75[1] = i2c_new_dummy(adapter, - 0x48 + ((val >> 4) & 0x7)); + data->lm75[1] = devm_i2c_new_dummy_device(&new_client->dev, adapter, + 0x48 + ((val >> 4) & 0x7)); } return 0; - -/* Undo inits in case of errors */ - -ERROR_SC_1: - i2c_unregister_device(data->lm75[0]); -ERROR_SC_0: - return err; } static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); @@ -1396,7 +1388,7 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Register sysfs hooks */ err = sysfs_create_group(&dev->kobj, &w83792d_group); if (err) - goto exit_i2c_unregister; + return err; /* * Read GPIO enable register to check if pins for fan 4,5 are used as @@ -1441,9 +1433,6 @@ exit_remove_files: sysfs_remove_group(&dev->kobj, &w83792d_group); for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++) sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]); -exit_i2c_unregister: - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); return err; } @@ -1459,9 +1448,6 @@ w83792d_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &w83792d_group_fan[i]); - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); - return 0; } diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 46f5dfec8d0a..9df48b70c70c 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1551,9 +1551,6 @@ static int w83793_remove(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) device_remove_file(dev, &w83793_temp[i].dev_attr); - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); - /* Decrease data reference counter */ mutex_lock(&watchdog_data_mutex); kref_put(&data->kref, w83793_release_resources); @@ -1565,7 +1562,7 @@ static int w83793_remove(struct i2c_client *client) static int w83793_detect_subclients(struct i2c_client *client) { - int i, id, err; + int i, id; int address = client->addr; u8 tmp; struct i2c_adapter *adapter = client->adapter; @@ -1580,8 +1577,7 @@ w83793_detect_subclients(struct i2c_client *client) "invalid subclient " "address %d; must be 0x48-0x4f\n", force_subclients[i]); - err = -EINVAL; - goto ERROR_SC_0; + return -EINVAL; } } w83793_write_value(client, W83793_REG_I2C_SUBADDR, @@ -1591,28 +1587,21 @@ w83793_detect_subclients(struct i2c_client *client) tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR); if (!(tmp & 0x08)) - data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (tmp & 0x7)); + data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + (tmp & 0x7)); if (!(tmp & 0x80)) { - if ((data->lm75[0] != NULL) + if (!IS_ERR(data->lm75[0]) && ((tmp & 0x7) == ((tmp >> 4) & 0x7))) { dev_err(&client->dev, "duplicate addresses 0x%x, " "use force_subclients\n", data->lm75[0]->addr); - err = -ENODEV; - goto ERROR_SC_1; + return -ENODEV; } - data->lm75[1] = i2c_new_dummy(adapter, - 0x48 + ((tmp >> 4) & 0x7)); + data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + ((tmp >> 4) & 0x7)); } return 0; - - /* Undo inits in case of errors */ - -ERROR_SC_1: - i2c_unregister_device(data->lm75[0]); -ERROR_SC_0: - return err; } /* Return 0 if detection is successful, -ENODEV otherwise */ @@ -1945,9 +1934,6 @@ exit_remove: for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) device_remove_file(dev, &w83793_temp[i].dev_attr); - - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); free_mem: kfree(data); exit: diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 5c1ca0df5cb0..84f1dcb69827 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -544,6 +544,7 @@ int etm_perf_add_symlink_sink(struct coresight_device *csdev) /* See function coresight_get_sink_by_id() to know where this is used */ hash = hashlen_hash(hashlen_string(NULL, name)); + sysfs_attr_init(&ea->attr.attr); ea->attr.attr.name = devm_kstrdup(dev, name, GFP_KERNEL); if (!ea->attr.attr.name) return -ENOMEM; diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h index 574c16004cb2..13d9b141daaa 100644 --- a/drivers/hwtracing/intel_th/msu.h +++ b/drivers/hwtracing/intel_th/msu.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Intel(R) Trace Hub Memory Storage Unit (MSU) data structures * diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index c0378c3de9a4..91dfeba62485 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -165,6 +165,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { .driver_data = (kernel_ulong_t)0, }, { + /* Lewisburg PCH */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa226), + .driver_data = (kernel_ulong_t)0, + }, + { /* Gemini Lake */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e), .driver_data = (kernel_ulong_t)&intel_th_2x, @@ -199,6 +204,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x45c5), .driver_data = (kernel_ulong_t)&intel_th_2x, }, + { + /* Tiger Lake PCH */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa0a6), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, { 0 }, }; diff --git a/drivers/hwtracing/intel_th/pti.h b/drivers/hwtracing/intel_th/pti.h index e9381babc84c..7dfc0431333b 100644 --- a/drivers/hwtracing/intel_th/pti.h +++ b/drivers/hwtracing/intel_th/pti.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Intel(R) Trace Hub PTI output data structures * diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index e55b902560de..181e7ff1ec4f 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -1276,7 +1276,6 @@ int stm_source_register_device(struct device *parent, err: put_device(&src->dev); - kfree(src); return err; } diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index d7fd76baec92..19ef2b0c682a 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -790,7 +790,10 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) { - u32 val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + u32 val; + + /* We do not support the SMBUS Quick command */ + val = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); if (adap->algo->reg_slave) val |= I2C_FUNC_SLAVE; diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c index e7f9305b2dd9..f5f001738df5 100644 --- a/drivers/i2c/busses/i2c-designware-slave.c +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -94,6 +94,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave) dev->disable_int(dev); dev->disable(dev); + synchronize_irq(dev->irq); dev->slave = NULL; pm_runtime_put(dev->dev); diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index 35b302d983e0..959d4912ec0d 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -69,6 +69,7 @@ struct em_i2c_device { struct completion msg_done; struct clk *sclk; struct i2c_client *slave; + int irq; }; static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg) @@ -339,6 +340,12 @@ static int em_i2c_unreg_slave(struct i2c_client *slave) writeb(0, priv->base + I2C_OFS_SVA0); + /* + * Wait for interrupt to finish. New slave irqs cannot happen because we + * cleared the slave address and, thus, only extension codes will be + * detected which do not use the slave ptr. + */ + synchronize_irq(priv->irq); priv->slave = NULL; return 0; @@ -355,7 +362,7 @@ static int em_i2c_probe(struct platform_device *pdev) { struct em_i2c_device *priv; struct resource *r; - int irq, ret; + int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -390,8 +397,8 @@ static int em_i2c_probe(struct platform_device *pdev) em_i2c_reset(&priv->adap); - irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(&pdev->dev, irq, em_i2c_irq_handler, 0, + priv->irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0, "em_i2c", priv); if (ret) goto err_clk; @@ -401,7 +408,8 @@ static int em_i2c_probe(struct platform_device *pdev) if (ret) goto err_clk; - dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, irq); + dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, + priv->irq); return 0; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index f2956936c3f2..2e08b4722dc4 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1194,19 +1194,28 @@ static acpi_status check_acpi_smo88xx_device(acpi_handle obj_handle, int i; status = acpi_get_object_info(obj_handle, &info); - if (!ACPI_SUCCESS(status) || !(info->valid & ACPI_VALID_HID)) + if (ACPI_FAILURE(status)) return AE_OK; + if (!(info->valid & ACPI_VALID_HID)) + goto smo88xx_not_found; + hid = info->hardware_id.string; if (!hid) - return AE_OK; + goto smo88xx_not_found; i = match_string(acpi_smo8800_ids, ARRAY_SIZE(acpi_smo8800_ids), hid); if (i < 0) - return AE_OK; + goto smo88xx_not_found; + + kfree(info); *((bool *)return_value) = true; return AE_CTRL_TERMINATE; + +smo88xx_not_found: + kfree(info); + return AE_OK; } static bool is_dell_system_with_lis3lv02d(void) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index b1b8b938d7f4..15f6cde6452f 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -273,8 +273,8 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx, } /* Functions for DMA support */ -static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, - dma_addr_t phy_addr) +static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, + dma_addr_t phy_addr) { struct imx_i2c_dma *dma; struct dma_slave_config dma_sconfig; @@ -283,7 +283,7 @@ static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); if (!dma) - return -ENOMEM; + return; dma->chan_tx = dma_request_chan(dev, "tx"); if (IS_ERR(dma->chan_tx)) { @@ -328,7 +328,7 @@ static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx, dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n", dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx)); - return 0; + return; fail_rx: dma_release_channel(dma->chan_rx); @@ -336,8 +336,6 @@ fail_tx: dma_release_channel(dma->chan_tx); fail_al: devm_kfree(dev, dma); - /* return successfully if there is no dma support */ - return ret == -ENODEV ? 0 : ret; } static void i2c_imx_dma_callback(void *arg) @@ -1165,17 +1163,13 @@ static int i2c_imx_probe(struct platform_device *pdev) dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n", i2c_imx->adapter.name); + dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); /* Init DMA config if supported */ - ret = i2c_imx_dma_request(i2c_imx, phy_addr); - if (ret < 0) - goto del_adapter; + i2c_imx_dma_request(i2c_imx, phy_addr); - dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); return 0; /* Return OK */ -del_adapter: - i2c_del_adapter(&i2c_imx->adapter); clk_notifier_unregister: clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb); rpm_disable: diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 252edb433fdf..29eae1bf4f86 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -234,6 +234,10 @@ static const struct i2c_adapter_quirks mt7622_i2c_quirks = { .max_num_msgs = 255, }; +static const struct i2c_adapter_quirks mt8183_i2c_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + static const struct mtk_i2c_compatible mt2712_compat = { .regs = mt_i2c_regs_v1, .pmic_i2c = 0, @@ -298,6 +302,7 @@ static const struct mtk_i2c_compatible mt8173_compat = { }; static const struct mtk_i2c_compatible mt8183_compat = { + .quirks = &mt8183_i2c_quirks, .regs = mt_i2c_regs_v2, .pmic_i2c = 0, .dcm = 0, @@ -870,7 +875,11 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id) static u32 mtk_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + if (adap->quirks->flags & I2C_AQ_NO_ZERO_LEN) + return I2C_FUNC_I2C | + (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); + else + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } static const struct i2c_algorithm mtk_i2c_algorithm = { diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index c46c4bddc7ca..cba325eb852f 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -91,7 +91,7 @@ #define SB800_PIIX4_PORT_IDX_MASK 0x06 #define SB800_PIIX4_PORT_IDX_SHIFT 1 -/* On kerncz, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */ +/* On kerncz and Hudson2, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */ #define SB800_PIIX4_PORT_IDX_KERNCZ 0x02 #define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18 #define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3 @@ -358,18 +358,16 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, /* Find which register is used for port selection */ if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD || PIIX4_dev->vendor == PCI_VENDOR_ID_HYGON) { - switch (PIIX4_dev->device) { - case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS: + if (PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS || + (PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && + PIIX4_dev->revision >= 0x1F)) { piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ; piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK_KERNCZ; piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ; - break; - case PCI_DEVICE_ID_AMD_HUDSON2_SMBUS: - default: + } else { piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT; piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; - break; } } else { if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index d39a4606f72d..531c01100b56 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -139,6 +139,7 @@ struct rcar_i2c_priv { enum dma_data_direction dma_direction; struct reset_control *rstc; + int irq; }; #define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent) @@ -861,9 +862,11 @@ static int rcar_unreg_slave(struct i2c_client *slave) WARN_ON(!priv->slave); + /* disable irqs and ensure none is running before clearing ptr */ rcar_i2c_write(priv, ICSIER, 0); rcar_i2c_write(priv, ICSCR, 0); + synchronize_irq(priv->irq); priv->slave = NULL; pm_runtime_put(rcar_i2c_priv_to_dev(priv)); @@ -918,7 +921,7 @@ static int rcar_i2c_probe(struct platform_device *pdev) struct i2c_adapter *adap; struct device *dev = &pdev->dev; struct i2c_timings i2c_t; - int irq, ret; + int ret; /* Otherwise logic will break because some bytes must always use PIO */ BUILD_BUG_ON_MSG(RCAR_MIN_DMA_LEN < 3, "Invalid min DMA length"); @@ -984,10 +987,10 @@ static int rcar_i2c_probe(struct platform_device *pdev) pm_runtime_put(dev); - irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(dev, irq, rcar_i2c_irq, 0, dev_name(dev), priv); + priv->irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv); if (ret < 0) { - dev_err(dev, "cannot get irq %d\n", irq); + dev_err(dev, "cannot get irq %d\n", priv->irq); goto out_pm_disable; } diff --git a/drivers/i2c/busses/i2c-stm32.h b/drivers/i2c/busses/i2c-stm32.h index 868755f82f88..2c21893905a3 100644 --- a/drivers/i2c/busses/i2c-stm32.h +++ b/drivers/i2c/busses/i2c-stm32.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * i2c-stm32.h * diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index f26ed495d384..9c440fa6a3dd 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -832,7 +832,7 @@ EXPORT_SYMBOL_GPL(i2c_new_device); */ void i2c_unregister_device(struct i2c_client *client) { - if (!client) + if (IS_ERR_OR_NULL(client)) return; if (client->dev.of_node) { diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c index 69cc040c3a1c..9e2e1406f85e 100644 --- a/drivers/i3c/device.c +++ b/drivers/i3c/device.c @@ -201,6 +201,59 @@ struct i3c_device *dev_to_i3cdev(struct device *dev) EXPORT_SYMBOL_GPL(dev_to_i3cdev); /** + * i3c_device_match_id() - Returns the i3c_device_id entry matching @i3cdev + * @i3cdev: I3C device + * @id_table: I3C device match table + * + * Return: a pointer to an i3c_device_id object or NULL if there's no match. + */ +const struct i3c_device_id * +i3c_device_match_id(struct i3c_device *i3cdev, + const struct i3c_device_id *id_table) +{ + struct i3c_device_info devinfo; + const struct i3c_device_id *id; + + i3c_device_get_info(i3cdev, &devinfo); + + /* + * The lower 32bits of the provisional ID is just filled with a random + * value, try to match using DCR info. + */ + if (!I3C_PID_RND_LOWER_32BITS(devinfo.pid)) { + u16 manuf = I3C_PID_MANUF_ID(devinfo.pid); + u16 part = I3C_PID_PART_ID(devinfo.pid); + u16 ext_info = I3C_PID_EXTRA_INFO(devinfo.pid); + + /* First try to match by manufacturer/part ID. */ + for (id = id_table; id->match_flags != 0; id++) { + if ((id->match_flags & I3C_MATCH_MANUF_AND_PART) != + I3C_MATCH_MANUF_AND_PART) + continue; + + if (manuf != id->manuf_id || part != id->part_id) + continue; + + if ((id->match_flags & I3C_MATCH_EXTRA_INFO) && + ext_info != id->extra_info) + continue; + + return id; + } + } + + /* Fallback to DCR match. */ + for (id = id_table; id->match_flags != 0; id++) { + if ((id->match_flags & I3C_MATCH_DCR) && + id->dcr == devinfo.dcr) + return id; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(i3c_device_match_id); + +/** * i3c_driver_register_with_owner() - register an I3C device driver * * @drv: driver to register diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index d6f8b038a896..5c051dba32a5 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -123,7 +123,7 @@ static struct i3c_dev_desc *dev_to_i3cdesc(struct device *dev) if (dev->type == &i3c_device_type) return dev_to_i3cdev(dev)->desc; - master = container_of(dev, struct i3c_master_controller, dev); + master = dev_to_i3cmaster(dev); return master->this; } @@ -276,51 +276,6 @@ static const struct device_type i3c_device_type = { .uevent = i3c_device_uevent, }; -static const struct i3c_device_id * -i3c_device_match_id(struct i3c_device *i3cdev, - const struct i3c_device_id *id_table) -{ - struct i3c_device_info devinfo; - const struct i3c_device_id *id; - - i3c_device_get_info(i3cdev, &devinfo); - - /* - * The lower 32bits of the provisional ID is just filled with a random - * value, try to match using DCR info. - */ - if (!I3C_PID_RND_LOWER_32BITS(devinfo.pid)) { - u16 manuf = I3C_PID_MANUF_ID(devinfo.pid); - u16 part = I3C_PID_PART_ID(devinfo.pid); - u16 ext_info = I3C_PID_EXTRA_INFO(devinfo.pid); - - /* First try to match by manufacturer/part ID. */ - for (id = id_table; id->match_flags != 0; id++) { - if ((id->match_flags & I3C_MATCH_MANUF_AND_PART) != - I3C_MATCH_MANUF_AND_PART) - continue; - - if (manuf != id->manuf_id || part != id->part_id) - continue; - - if ((id->match_flags & I3C_MATCH_EXTRA_INFO) && - ext_info != id->extra_info) - continue; - - return id; - } - } - - /* Fallback to DCR match. */ - for (id = id_table; id->match_flags != 0; id++) { - if ((id->match_flags & I3C_MATCH_DCR) && - id->dcr == devinfo.dcr) - return id; - } - - return NULL; -} - static int i3c_device_match(struct device *dev, struct device_driver *drv) { struct i3c_device *i3cdev; @@ -645,6 +600,8 @@ i3c_master_alloc_i2c_dev(struct i3c_master_controller *master, dev->common.master = master; dev->boardinfo = boardinfo; + dev->addr = boardinfo->base.addr; + dev->lvr = boardinfo->lvr; return dev; } @@ -963,8 +920,8 @@ int i3c_master_defslvs_locked(struct i3c_master_controller *master) desc = defslvs->slaves; i3c_bus_for_each_i2cdev(bus, i2cdev) { - desc->lvr = i2cdev->boardinfo->lvr; - desc->static_addr = i2cdev->boardinfo->base.addr << 1; + desc->lvr = i2cdev->lvr; + desc->static_addr = i2cdev->addr << 1; desc++; } @@ -1084,8 +1041,10 @@ static int i3c_master_getmwl_locked(struct i3c_master_controller *master, if (ret) goto out; - if (dest.payload.len != sizeof(*mwl)) - return -EIO; + if (dest.payload.len != sizeof(*mwl)) { + ret = -EIO; + goto out; + } info->max_write_len = be16_to_cpu(mwl->len); @@ -1631,8 +1590,8 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master) common.node) { i3c_master_detach_i2c_dev(i2cdev); i3c_bus_set_addr_slot_status(&master->bus, - i2cdev->boardinfo->base.addr, - I3C_ADDR_SLOT_FREE); + i2cdev->addr, + I3C_ADDR_SLOT_FREE); i3c_master_free_i2c_dev(i2cdev); } } @@ -2093,8 +2052,10 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master) for_each_available_child_of_node(i3cbus_np, node) { ret = of_i3c_master_add_dev(master, node); - if (ret) + if (ret) { + of_node_put(node); return ret; + } } /* diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 09912d75c6d5..b0ff0e12d84c 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1033,12 +1033,12 @@ static int dw_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev) return -ENOMEM; data->index = pos; - master->addrs[pos] = dev->boardinfo->base.addr; + master->addrs[pos] = dev->addr; master->free_pos &= ~BIT(pos); i2c_dev_set_master_data(dev, data); writel(DEV_ADDR_TABLE_LEGACY_I2C_DEV | - DEV_ADDR_TABLE_STATIC_ADDR(dev->boardinfo->base.addr), + DEV_ADDR_TABLE_STATIC_ADDR(dev->addr), master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index)); diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 237f24adddc6..10db0bf0655a 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -903,7 +903,8 @@ static void cdns_i3c_master_upd_i3c_addr(struct i3c_dev_desc *dev) static int cdns_i3c_master_get_rr_slot(struct cdns_i3c_master *master, u8 dyn_addr) { - u32 activedevs, rr; + unsigned long activedevs; + u32 rr; int i; if (!dyn_addr) { @@ -913,13 +914,10 @@ static int cdns_i3c_master_get_rr_slot(struct cdns_i3c_master *master, return ffs(master->free_rr_slots) - 1; } - activedevs = readl(master->regs + DEVS_CTRL) & - DEVS_CTRL_DEVS_ACTIVE_MASK; - - for (i = 1; i <= master->maxdevs; i++) { - if (!(BIT(i) & activedevs)) - continue; + activedevs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK; + activedevs &= ~BIT(0); + for_each_set_bit(i, &activedevs, master->maxdevs + 1) { rr = readl(master->regs + DEV_ID_RR0(i)); if (!(rr & DEV_ID_RR0_IS_I3C) || DEV_ID_RR0_GET_DEV_ADDR(rr) != dyn_addr) @@ -1005,9 +1003,9 @@ static int cdns_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev) master->free_rr_slots &= ~BIT(slot); i2c_dev_set_master_data(dev, data); - writel(prepare_rr0_dev_address(dev->boardinfo->base.addr), + writel(prepare_rr0_dev_address(dev->addr), master->regs + DEV_ID_RR0(data->id)); - writel(dev->boardinfo->lvr, master->regs + DEV_ID_RR2(data->id)); + writel(dev->lvr, master->regs + DEV_ID_RR2(data->id)); writel(readl(master->regs + DEVS_CTRL) | DEVS_CTRL_DEV_ACTIVE(data->id), master->regs + DEVS_CTRL); @@ -1126,18 +1124,16 @@ static void cdns_i3c_master_upd_i3c_scl_lim(struct cdns_i3c_master *master) static int cdns_i3c_master_do_daa(struct i3c_master_controller *m) { struct cdns_i3c_master *master = to_cdns_i3c_master(m); - u32 olddevs, newdevs; + unsigned long olddevs, newdevs; int ret, slot; u8 addrs[MAX_DEVS] = { }; u8 last_addr = 0; olddevs = readl(master->regs + DEVS_CTRL) & DEVS_CTRL_DEVS_ACTIVE_MASK; + olddevs |= BIT(0); /* Prepare RR slots before launching DAA. */ - for (slot = 1; slot <= master->maxdevs; slot++) { - if (olddevs & BIT(slot)) - continue; - + for_each_clear_bit(slot, &olddevs, master->maxdevs + 1) { ret = i3c_master_get_free_addr(m, last_addr + 1); if (ret < 0) return -ENOSPC; @@ -1161,10 +1157,8 @@ static int cdns_i3c_master_do_daa(struct i3c_master_controller *m) * Clear all retaining registers filled during DAA. We already * have the addressed assigned to them in the addrs array. */ - for (slot = 1; slot <= master->maxdevs; slot++) { - if (newdevs & BIT(slot)) - i3c_master_add_i3c_dev_locked(m, addrs[slot]); - } + for_each_set_bit(slot, &newdevs, master->maxdevs + 1) + i3c_master_add_i3c_dev_locked(m, addrs[slot]); /* * Clear slots that ended up not being used. Can be caused by I3C diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c index 46bb2e421bb9..ad19d9c716f4 100644 --- a/drivers/iio/accel/cros_ec_accel_legacy.c +++ b/drivers/iio/accel/cros_ec_accel_legacy.c @@ -319,7 +319,6 @@ static const struct iio_chan_spec_ext_info cros_ec_accel_legacy_ext_info[] = { .modified = 1, \ .info_mask_separate = \ BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \ .ext_info = cros_ec_accel_legacy_ext_info, \ diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 7e3286265a38..f0af3a42f53c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -958,7 +958,7 @@ config TI_ADC161S626 config TI_ADS1015 tristate "Texas Instruments ADS1015 ADC" - depends on I2C && !SENSORS_ADS1015 + depends on I2C select REGMAP_I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c index 92b1d5037ac9..e234970b7150 100644 --- a/drivers/iio/adc/ingenic-adc.c +++ b/drivers/iio/adc/ingenic-adc.c @@ -11,6 +11,7 @@ #include <linux/iio/iio.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> @@ -22,8 +23,11 @@ #define JZ_ADC_REG_ADTCH 0x18 #define JZ_ADC_REG_ADBDAT 0x1c #define JZ_ADC_REG_ADSDAT 0x20 +#define JZ_ADC_REG_ADCLK 0x28 #define JZ_ADC_REG_CFG_BAT_MD BIT(4) +#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0 +#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB 16 #define JZ_ADC_AUX_VREF 3300 #define JZ_ADC_AUX_VREF_BITS 12 @@ -34,6 +38,8 @@ #define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) #define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 +struct ingenic_adc; + struct ingenic_adc_soc_data { unsigned int battery_high_vref; unsigned int battery_high_vref_bits; @@ -41,6 +47,7 @@ struct ingenic_adc_soc_data { size_t battery_raw_avail_size; const int *battery_scale_avail; size_t battery_scale_avail_size; + int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc); }; struct ingenic_adc { @@ -151,6 +158,42 @@ static const int jz4740_adc_battery_scale_avail[] = { JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, }; +static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc) +{ + struct clk *parent_clk; + unsigned long parent_rate, rate; + unsigned int div_main, div_10us; + + parent_clk = clk_get_parent(adc->clk); + if (!parent_clk) { + dev_err(dev, "ADC clock has no parent\n"); + return -ENODEV; + } + parent_rate = clk_get_rate(parent_clk); + + /* + * The JZ4725B ADC works at 500 kHz to 8 MHz. + * We pick the highest rate possible. + * In practice we typically get 6 MHz, half of the 12 MHz EXT clock. + */ + div_main = DIV_ROUND_UP(parent_rate, 8000000); + div_main = clamp(div_main, 1u, 64u); + rate = parent_rate / div_main; + if (rate < 500000 || rate > 8000000) { + dev_err(dev, "No valid divider for ADC main clock\n"); + return -EINVAL; + } + + /* We also need a divider that produces a 10us clock. */ + div_10us = DIV_ROUND_UP(rate, 100000); + + writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) | + (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, + adc->base + JZ_ADC_REG_ADCLK); + + return 0; +} + static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF, .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, @@ -158,6 +201,7 @@ static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), .battery_scale_avail = jz4725b_adc_battery_scale_avail, .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), + .init_clk_div = jz4725b_adc_init_clk_div, }; static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { @@ -167,6 +211,7 @@ static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), .battery_scale_avail = jz4740_adc_battery_scale_avail, .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), + .init_clk_div = NULL, /* no ADCLK register on JZ4740 */ }; static int ingenic_adc_read_avail(struct iio_dev *iio_dev, @@ -317,6 +362,15 @@ static int ingenic_adc_probe(struct platform_device *pdev) return ret; } + /* Set clock dividers. */ + if (soc_data->init_clk_div) { + ret = soc_data->init_clk_div(dev, adc); + if (ret) { + clk_disable_unprepare(adc->clk); + return ret; + } + } + /* Put hardware in a known passive state. */ writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); writeb(0xff, adc->base + JZ_ADC_REG_CTRL); diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 917223d5ff5b..da073d72f649 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -83,7 +83,7 @@ #define MAX9611_TEMP_MAX_POS 0x7f80 #define MAX9611_TEMP_MAX_NEG 0xff80 #define MAX9611_TEMP_MIN_NEG 0xd980 -#define MAX9611_TEMP_MASK GENMASK(7, 15) +#define MAX9611_TEMP_MASK GENMASK(15, 7) #define MAX9611_TEMP_SHIFT 0x07 #define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT) #define MAX9611_TEMP_SCALE_NUM 1000000 @@ -480,7 +480,7 @@ static int max9611_init(struct max9611_dev *max9611) if (ret) return ret; - regval = ret & MAX9611_TEMP_MASK; + regval &= MAX9611_TEMP_MASK; if ((regval > MAX9611_TEMP_MAX_POS && regval < MAX9611_TEMP_MIN_NEG) || diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 2d685730f867..c37f201294b2 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -382,7 +382,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) dev_err(dev, "Only %i channels supported with %pOFn, but reg = <%i>.\n", num_channels, child, reg); - return ret; + return -EINVAL; } } @@ -391,7 +391,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) dev_err(dev, "Channel %i uses different ADC mode than the rest.\n", reg); - return ret; + return -EINVAL; } /* Channel is valid, grab the regulator. */ diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index e48f15cc9ab5..ff82863cbf42 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -276,11 +276,11 @@ static int adf4371_set_freq(struct adf4371_state *st, unsigned long long freq, st->buf[0] = st->integer >> 8; st->buf[1] = 0x40; /* REG12 default */ st->buf[2] = 0x00; - st->buf[3] = st->fract2 & 0xFF; - st->buf[4] = st->fract2 >> 7; - st->buf[5] = st->fract2 >> 15; + st->buf[3] = st->fract1 & 0xFF; + st->buf[4] = st->fract1 >> 8; + st->buf[5] = st->fract1 >> 16; st->buf[6] = ADF4371_FRAC2WORD_L(st->fract2 & 0x7F) | - ADF4371_FRAC1WORD(st->fract1 >> 23); + ADF4371_FRAC1WORD(st->fract1 >> 24); st->buf[7] = ADF4371_FRAC2WORD_H(st->fract2 >> 7); st->buf[8] = st->mod2 & 0xFF; st->buf[9] = ADF4371_MOD2WORD(st->mod2 >> 8); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 53a59957cc54..8a704cd5bddb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -845,6 +845,25 @@ static const struct iio_chan_spec inv_mpu_channels[] = { INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), }; +static const unsigned long inv_mpu_scan_masks[] = { + /* 3-axis accel */ + BIT(INV_MPU6050_SCAN_ACCL_X) + | BIT(INV_MPU6050_SCAN_ACCL_Y) + | BIT(INV_MPU6050_SCAN_ACCL_Z), + /* 3-axis gyro */ + BIT(INV_MPU6050_SCAN_GYRO_X) + | BIT(INV_MPU6050_SCAN_GYRO_Y) + | BIT(INV_MPU6050_SCAN_GYRO_Z), + /* 6-axis accel + gyro */ + BIT(INV_MPU6050_SCAN_ACCL_X) + | BIT(INV_MPU6050_SCAN_ACCL_Y) + | BIT(INV_MPU6050_SCAN_ACCL_Z) + | BIT(INV_MPU6050_SCAN_GYRO_X) + | BIT(INV_MPU6050_SCAN_GYRO_Y) + | BIT(INV_MPU6050_SCAN_GYRO_Z), + 0, +}; + static const struct iio_chan_spec inv_icm20602_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP), { @@ -871,6 +890,28 @@ static const struct iio_chan_spec inv_icm20602_channels[] = { INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z), }; +static const unsigned long inv_icm20602_scan_masks[] = { + /* 3-axis accel + temp (mandatory) */ + BIT(INV_ICM20602_SCAN_ACCL_X) + | BIT(INV_ICM20602_SCAN_ACCL_Y) + | BIT(INV_ICM20602_SCAN_ACCL_Z) + | BIT(INV_ICM20602_SCAN_TEMP), + /* 3-axis gyro + temp (mandatory) */ + BIT(INV_ICM20602_SCAN_GYRO_X) + | BIT(INV_ICM20602_SCAN_GYRO_Y) + | BIT(INV_ICM20602_SCAN_GYRO_Z) + | BIT(INV_ICM20602_SCAN_TEMP), + /* 6-axis accel + gyro + temp (mandatory) */ + BIT(INV_ICM20602_SCAN_ACCL_X) + | BIT(INV_ICM20602_SCAN_ACCL_Y) + | BIT(INV_ICM20602_SCAN_ACCL_Z) + | BIT(INV_ICM20602_SCAN_GYRO_X) + | BIT(INV_ICM20602_SCAN_GYRO_Y) + | BIT(INV_ICM20602_SCAN_GYRO_Z) + | BIT(INV_ICM20602_SCAN_TEMP), + 0, +}; + /* * The user can choose any frequency between INV_MPU6050_MIN_FIFO_RATE and * INV_MPU6050_MAX_FIFO_RATE, but only these frequencies are matched by the @@ -1130,9 +1171,11 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, if (chip_type == INV_ICM20602) { indio_dev->channels = inv_icm20602_channels; indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels); + indio_dev->available_scan_masks = inv_icm20602_scan_masks; } else { indio_dev->channels = inv_mpu_channels; indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + indio_dev->available_scan_masks = inv_mpu_scan_masks; } indio_dev->info = &mpu_info; diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 19f1730a4f24..a68d0ccf67a4 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -4724,10 +4724,14 @@ static int __init cma_init(void) if (ret) goto err; - cma_configfs_init(); + ret = cma_configfs_init(); + if (ret) + goto err_ib; return 0; +err_ib: + ib_unregister_client(&cma_client); err: unregister_netdevice_notifier(&cma_nb); ib_sa_unregister_client(&sa_client); diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c index 45d5164e9574..af8c85d18e62 100644 --- a/drivers/infiniband/core/counters.c +++ b/drivers/infiniband/core/counters.c @@ -38,6 +38,9 @@ int rdma_counter_set_auto_mode(struct ib_device *dev, u8 port, int ret; port_counter = &dev->port_data[port].port_counter; + if (!port_counter->hstats) + return -EOPNOTSUPP; + mutex_lock(&port_counter->lock); if (on) { ret = __counter_set_mode(&port_counter->mode, @@ -146,13 +149,11 @@ static bool auto_mode_match(struct ib_qp *qp, struct rdma_counter *counter, struct auto_mode_param *param = &counter->mode.param; bool match = true; - if (rdma_is_kernel_res(&counter->res) != rdma_is_kernel_res(&qp->res)) + if (!rdma_is_visible_in_pid_ns(&qp->res)) return false; - /* Ensure that counter belong to right PID */ - if (!rdma_is_kernel_res(&counter->res) && - !rdma_is_kernel_res(&qp->res) && - (task_pid_vnr(counter->res.task) != current->pid)) + /* Ensure that counter belongs to the right PID */ + if (task_pid_nr(counter->res.task) != task_pid_nr(qp->res.task)) return false; if (auto_mask & RDMA_COUNTER_MASK_QP_TYPE) @@ -421,7 +422,7 @@ static struct ib_qp *rdma_counter_get_qp(struct ib_device *dev, u32 qp_num) return qp; err: - rdma_restrack_put(&qp->res); + rdma_restrack_put(res); return NULL; } @@ -509,6 +510,9 @@ int rdma_counter_bind_qpn_alloc(struct ib_device *dev, u8 port, if (!rdma_is_port_valid(dev, port)) return -EINVAL; + if (!dev->port_data[port].port_counter.hstats) + return -EOPNOTSUPP; + qp = rdma_counter_get_qp(dev, qp_num); if (!qp) return -ENOENT; diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index 783e465e7c41..020c26976558 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -382,8 +382,7 @@ static int fill_res_info(struct sk_buff *msg, struct ib_device *device) for (i = 0; i < RDMA_RESTRACK_MAX; i++) { if (!names[i]) continue; - curr = rdma_restrack_count(device, i, - task_active_pid_ns(current)); + curr = rdma_restrack_count(device, i); ret = fill_res_info_entry(msg, names[i], curr); if (ret) goto err; @@ -1952,12 +1951,16 @@ static int stat_get_doit_qp(struct sk_buff *skb, struct nlmsghdr *nlh, if (fill_nldev_handle(msg, device) || nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port) || - nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_MODE, mode)) + nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_MODE, mode)) { + ret = -EMSGSIZE; goto err_msg; + } if ((mode == RDMA_COUNTER_MODE_AUTO) && - nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask)) + nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask)) { + ret = -EMSGSIZE; goto err_msg; + } nlmsg_end(msg, nlh); ib_device_put(device); diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index bddff426ee0f..a07665f7ef8c 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -107,10 +107,8 @@ void rdma_restrack_clean(struct ib_device *dev) * rdma_restrack_count() - the current usage of specific object * @dev: IB device * @type: actual type of object to operate - * @ns: PID namespace */ -int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, - struct pid_namespace *ns) +int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type) { struct rdma_restrack_root *rt = &dev->res[type]; struct rdma_restrack_entry *e; @@ -119,10 +117,9 @@ int rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type, xa_lock(&rt->xa); xas_for_each(&xas, e, U32_MAX) { - if (ns == &init_pid_ns || - (!rdma_is_kernel_res(e) && - ns == task_active_pid_ns(e->task))) - cnt++; + if (!rdma_is_visible_in_pid_ns(e)) + continue; + cnt++; } xa_unlock(&rt->xa); return cnt; @@ -360,5 +357,7 @@ bool rdma_is_visible_in_pid_ns(struct rdma_restrack_entry *res) */ if (rdma_is_kernel_res(res)) return task_active_pid_ns(current) == &init_pid_ns; - return task_active_pid_ns(current) == task_active_pid_ns(res->task); + + /* PID 0 means that resource is not found in current namespace */ + return task_pid_vnr(res->task); } diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 08da840ed7ee..56553668256f 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -379,14 +379,9 @@ EXPORT_SYMBOL(ib_umem_release); int ib_umem_page_count(struct ib_umem *umem) { - int i; - int n; + int i, n = 0; struct scatterlist *sg; - if (umem->is_odp) - return ib_umem_num_pages(umem); - - n = 0; for_each_sg(umem->sg_head.sgl, sg, umem->nmap, i) n += sg_dma_len(sg) >> PAGE_SHIFT; diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index 2a75c6f8d827..c0e15db34680 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -112,10 +112,6 @@ static int ib_umem_notifier_release_trampoline(struct ib_umem_odp *umem_odp, * prevent any further fault handling on this MR. */ ib_umem_notifier_start_account(umem_odp); - umem_odp->dying = 1; - /* Make sure that the fact the umem is dying is out before we release - * all pending page faults. */ - smp_wmb(); complete_all(&umem_odp->notifier_completion); umem_odp->umem.context->invalidate_range( umem_odp, ib_umem_start(umem_odp), ib_umem_end(umem_odp)); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index 48b04d2f175f..60c8f76aab33 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -136,6 +136,13 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, struct cmdq_base *req, spin_unlock_irqrestore(&cmdq->lock, flags); return -EBUSY; } + + size = req->cmd_size; + /* change the cmd_size to the number of 16byte cmdq unit. + * req->cmd_size is modified here + */ + bnxt_qplib_set_cmd_slots(req); + memset(resp, 0, sizeof(*resp)); crsqe->resp = (struct creq_qp_event *)resp; crsqe->resp->cookie = req->cookie; @@ -150,7 +157,6 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, struct cmdq_base *req, cmdq_ptr = (struct bnxt_qplib_cmdqe **)cmdq->pbl_ptr; preq = (u8 *)req; - size = req->cmd_size * BNXT_QPLIB_CMDQE_UNITS; do { /* Locate the next cmdq slot */ sw_prod = HWQ_CMP(cmdq->prod, cmdq); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h index 2138533bb642..dfeadc192e17 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h @@ -55,9 +55,7 @@ do { \ memset(&(req), 0, sizeof((req))); \ (req).opcode = CMDQ_BASE_OPCODE_##CMD; \ - (req).cmd_size = (sizeof((req)) + \ - BNXT_QPLIB_CMDQE_UNITS - 1) / \ - BNXT_QPLIB_CMDQE_UNITS; \ + (req).cmd_size = sizeof((req)); \ (req).flags = cpu_to_le16(cmd_flags); \ } while (0) @@ -95,6 +93,13 @@ static inline u32 bnxt_qplib_cmdqe_cnt_per_pg(u32 depth) BNXT_QPLIB_CMDQE_UNITS); } +/* Set the cmd_size to a factor of CMDQE unit */ +static inline void bnxt_qplib_set_cmd_slots(struct cmdq_base *req) +{ + req->cmd_size = (req->cmd_size + BNXT_QPLIB_CMDQE_UNITS - 1) / + BNXT_QPLIB_CMDQE_UNITS; +} + #define MAX_CMDQ_IDX(depth) ((depth) - 1) static inline u32 bnxt_qplib_max_cmdq_idx_per_pg(u32 depth) diff --git a/drivers/infiniband/hw/hfi1/fault.c b/drivers/infiniband/hw/hfi1/fault.c index 93613e5def9b..986c12153e62 100644 --- a/drivers/infiniband/hw/hfi1/fault.c +++ b/drivers/infiniband/hw/hfi1/fault.c @@ -141,12 +141,14 @@ static ssize_t fault_opcodes_write(struct file *file, const char __user *buf, if (!data) return -ENOMEM; copy = min(len, datalen - 1); - if (copy_from_user(data, buf, copy)) - return -EFAULT; + if (copy_from_user(data, buf, copy)) { + ret = -EFAULT; + goto free_data; + } ret = debugfs_file_get(file->f_path.dentry); if (unlikely(ret)) - return ret; + goto free_data; ptr = data; token = ptr; for (ptr = data; *ptr; ptr = end + 1, token = ptr) { @@ -195,6 +197,7 @@ static ssize_t fault_opcodes_write(struct file *file, const char __user *buf, ret = len; debugfs_file_put(file->f_path.dentry); +free_data: kfree(data); return ret; } @@ -214,7 +217,7 @@ static ssize_t fault_opcodes_read(struct file *file, char __user *buf, return -ENOMEM; ret = debugfs_file_get(file->f_path.dentry); if (unlikely(ret)) - return ret; + goto free_data; bit = find_first_bit(fault->opcodes, bitsize); while (bit < bitsize) { zero = find_next_zero_bit(fault->opcodes, bitsize, bit); @@ -232,6 +235,7 @@ static ssize_t fault_opcodes_read(struct file *file, char __user *buf, data[size - 1] = '\n'; data[size] = '\0'; ret = simple_read_from_buffer(buf, len, pos, data, size); +free_data: kfree(data); return ret; } diff --git a/drivers/infiniband/hw/hfi1/tid_rdma.c b/drivers/infiniband/hw/hfi1/tid_rdma.c index 996fc298207e..6141f4edc6bf 100644 --- a/drivers/infiniband/hw/hfi1/tid_rdma.c +++ b/drivers/infiniband/hw/hfi1/tid_rdma.c @@ -2574,18 +2574,9 @@ void hfi1_kern_read_tid_flow_free(struct rvt_qp *qp) hfi1_kern_clear_hw_flow(priv->rcd, qp); } -static bool tid_rdma_tid_err(struct hfi1_ctxtdata *rcd, - struct hfi1_packet *packet, u8 rcv_type, - u8 opcode) +static bool tid_rdma_tid_err(struct hfi1_packet *packet, u8 rcv_type) { struct rvt_qp *qp = packet->qp; - struct hfi1_qp_priv *qpriv = qp->priv; - u32 ipsn; - struct ib_other_headers *ohdr = packet->ohdr; - struct rvt_ack_entry *e; - struct tid_rdma_request *req; - struct rvt_dev_info *rdi = ib_to_rvt(qp->ibqp.device); - u32 i; if (rcv_type >= RHF_RCV_TYPE_IB) goto done; @@ -2602,41 +2593,9 @@ static bool tid_rdma_tid_err(struct hfi1_ctxtdata *rcd, if (rcv_type == RHF_RCV_TYPE_EAGER) { hfi1_restart_rc(qp, qp->s_last_psn + 1, 1); hfi1_schedule_send(qp); - goto done_unlock; - } - - /* - * For TID READ response, error out QP after freeing the tid - * resources. - */ - if (opcode == TID_OP(READ_RESP)) { - ipsn = mask_psn(be32_to_cpu(ohdr->u.tid_rdma.r_rsp.verbs_psn)); - if (cmp_psn(ipsn, qp->s_last_psn) > 0 && - cmp_psn(ipsn, qp->s_psn) < 0) { - hfi1_kern_read_tid_flow_free(qp); - spin_unlock(&qp->s_lock); - rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR); - goto done; - } - goto done_unlock; - } - - /* - * Error out the qp for TID RDMA WRITE - */ - hfi1_kern_clear_hw_flow(qpriv->rcd, qp); - for (i = 0; i < rvt_max_atomic(rdi); i++) { - e = &qp->s_ack_queue[i]; - if (e->opcode == TID_OP(WRITE_REQ)) { - req = ack_to_tid_req(e); - hfi1_kern_exp_rcv_clear_all(req); - } } - spin_unlock(&qp->s_lock); - rvt_rc_error(qp, IB_WC_LOC_LEN_ERR); - goto done; -done_unlock: + /* Since no payload is delivered, just drop the packet */ spin_unlock(&qp->s_lock); done: return true; @@ -2687,12 +2646,12 @@ static bool handle_read_kdeth_eflags(struct hfi1_ctxtdata *rcd, u32 fpsn; lockdep_assert_held(&qp->r_lock); + spin_lock(&qp->s_lock); /* If the psn is out of valid range, drop the packet */ if (cmp_psn(ibpsn, qp->s_last_psn) < 0 || cmp_psn(ibpsn, qp->s_psn) > 0) - return ret; + goto s_unlock; - spin_lock(&qp->s_lock); /* * Note that NAKs implicitly ACK outstanding SEND and RDMA write * requests and implicitly NAK RDMA read and atomic requests issued @@ -2740,9 +2699,12 @@ static bool handle_read_kdeth_eflags(struct hfi1_ctxtdata *rcd, wqe = do_rc_completion(qp, wqe, ibp); if (qp->s_acked == qp->s_tail) - break; + goto s_unlock; } + if (qp->s_acked == qp->s_tail) + goto s_unlock; + /* Handle the eflags for the request */ if (wqe->wr.opcode != IB_WR_TID_RDMA_READ) goto s_unlock; @@ -2922,7 +2884,7 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, if (lnh == HFI1_LRH_GRH) goto r_unlock; - if (tid_rdma_tid_err(rcd, packet, rcv_type, opcode)) + if (tid_rdma_tid_err(packet, rcv_type)) goto r_unlock; } @@ -2942,8 +2904,15 @@ bool hfi1_handle_kdeth_eflags(struct hfi1_ctxtdata *rcd, */ spin_lock(&qp->s_lock); qpriv = qp->priv; + if (qpriv->r_tid_tail == HFI1_QP_WQE_INVALID || + qpriv->r_tid_tail == qpriv->r_tid_head) + goto unlock; e = &qp->s_ack_queue[qpriv->r_tid_tail]; + if (e->opcode != TID_OP(WRITE_REQ)) + goto unlock; req = ack_to_tid_req(e); + if (req->comp_seg == req->cur_seg) + goto unlock; flow = &req->flows[req->clear_tail]; trace_hfi1_eflags_err_write(qp, rcv_type, rte, psn); trace_hfi1_rsp_handle_kdeth_eflags(qp, psn); @@ -4509,7 +4478,7 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) struct rvt_swqe *wqe; struct tid_rdma_request *req; struct tid_rdma_flow *flow; - u32 aeth, psn, req_psn, ack_psn, resync_psn, ack_kpsn; + u32 aeth, psn, req_psn, ack_psn, flpsn, resync_psn, ack_kpsn; unsigned long flags; u16 fidx; @@ -4538,6 +4507,9 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) ack_kpsn--; } + if (unlikely(qp->s_acked == qp->s_tail)) + goto ack_op_err; + wqe = rvt_get_swqe_ptr(qp, qp->s_acked); if (wqe->wr.opcode != IB_WR_TID_RDMA_WRITE) @@ -4550,7 +4522,8 @@ void hfi1_rc_rcv_tid_rdma_ack(struct hfi1_packet *packet) trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail, flow); /* Drop stale ACK/NAK */ - if (cmp_psn(psn, full_flow_psn(flow, flow->flow_state.spsn)) < 0) + if (cmp_psn(psn, full_flow_psn(flow, flow->flow_state.spsn)) < 0 || + cmp_psn(req_psn, flow->flow_state.resp_ib_psn) < 0) goto ack_op_err; while (cmp_psn(ack_kpsn, @@ -4712,7 +4685,12 @@ done: switch ((aeth >> IB_AETH_CREDIT_SHIFT) & IB_AETH_CREDIT_MASK) { case 0: /* PSN sequence error */ + if (!req->flows) + break; flow = &req->flows[req->acked_tail]; + flpsn = full_flow_psn(flow, flow->flow_state.lpsn); + if (cmp_psn(psn, flpsn) > 0) + break; trace_hfi1_tid_flow_rcv_tid_ack(qp, req->acked_tail, flow); req->r_ack_psn = mask_psn(be32_to_cpu(ohdr->bth[2])); diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 68c951491a08..57079110af9b 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -1677,8 +1677,6 @@ tx_err: tx_buf_size, DMA_TO_DEVICE); kfree(tun_qp->tx_ring[i].buf.addr); } - kfree(tun_qp->tx_ring); - tun_qp->tx_ring = NULL; i = MLX4_NUM_TUNNEL_BUFS; err: while (i > 0) { @@ -1687,6 +1685,8 @@ err: rx_buf_size, DMA_FROM_DEVICE); kfree(tun_qp->ring[i].addr); } + kfree(tun_qp->tx_ring); + tun_qp->tx_ring = NULL; kfree(tun_qp->ring); tun_qp->ring = NULL; return -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c index ec4370f99381..af5bbb35c058 100644 --- a/drivers/infiniband/hw/mlx5/devx.c +++ b/drivers/infiniband/hw/mlx5/devx.c @@ -2026,7 +2026,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_SUBSCRIBE_EVENT)( event_sub->eventfd = eventfd_ctx_fdget(redirect_fd); - if (IS_ERR(event_sub)) { + if (IS_ERR(event_sub->eventfd)) { err = PTR_ERR(event_sub->eventfd); event_sub->eventfd = NULL; goto err; @@ -2644,12 +2644,13 @@ static int devx_async_event_close(struct inode *inode, struct file *filp) struct devx_async_event_file *ev_file = filp->private_data; struct devx_event_subscription *event_sub, *event_sub_tmp; struct devx_async_event_data *entry, *tmp; + struct mlx5_ib_dev *dev = ev_file->dev; - mutex_lock(&ev_file->dev->devx_event_table.event_xa_lock); + mutex_lock(&dev->devx_event_table.event_xa_lock); /* delete the subscriptions which are related to this FD */ list_for_each_entry_safe(event_sub, event_sub_tmp, &ev_file->subscribed_events_list, file_list) { - devx_cleanup_subscription(ev_file->dev, event_sub); + devx_cleanup_subscription(dev, event_sub); if (event_sub->eventfd) eventfd_ctx_put(event_sub->eventfd); @@ -2658,7 +2659,7 @@ static int devx_async_event_close(struct inode *inode, struct file *filp) kfree_rcu(event_sub, rcu); } - mutex_unlock(&ev_file->dev->devx_event_table.event_xa_lock); + mutex_unlock(&dev->devx_event_table.event_xa_lock); /* free the pending events allocation */ if (!ev_file->omit_data) { @@ -2670,7 +2671,7 @@ static int devx_async_event_close(struct inode *inode, struct file *filp) } uverbs_close_fd(filp); - put_device(&ev_file->dev->ib_dev.dev); + put_device(&dev->ib_dev.dev); return 0; } diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index e12a4404096b..0569bcab02d4 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1023,7 +1023,7 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, props->timestamp_mask = 0x7FFFFFFFFFFFFFFFULL; if (IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING)) { - if (MLX5_CAP_GEN(mdev, pg)) + if (dev->odp_caps.general_caps & IB_ODP_SUPPORT) props->device_cap_flags |= IB_DEVICE_ON_DEMAND_PAGING; props->odp_caps = dev->odp_caps; } @@ -6139,6 +6139,8 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev) dev->port[i].roce.last_port_state = IB_PORT_DOWN; } + mlx5_ib_internal_fill_odp_caps(dev); + err = mlx5_ib_init_multiport_master(dev); if (err) return err; @@ -6563,8 +6565,6 @@ static void mlx5_ib_stage_dev_res_cleanup(struct mlx5_ib_dev *dev) static int mlx5_ib_stage_odp_init(struct mlx5_ib_dev *dev) { - mlx5_ib_internal_fill_odp_caps(dev); - return mlx5_ib_odp_init_one(dev); } diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c index fe1a76d8531c..a40e0abf2338 100644 --- a/drivers/infiniband/hw/mlx5/mem.c +++ b/drivers/infiniband/hw/mlx5/mem.c @@ -57,9 +57,10 @@ void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int entry; if (umem->is_odp) { - unsigned int page_shift = to_ib_umem_odp(umem)->page_shift; + struct ib_umem_odp *odp = to_ib_umem_odp(umem); + unsigned int page_shift = odp->page_shift; - *ncont = ib_umem_page_count(umem); + *ncont = ib_umem_odp_num_pages(odp); *count = *ncont << (page_shift - PAGE_SHIFT); *shift = page_shift; if (order) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index f6a53455bf8b..9ae587b74b12 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -1475,4 +1475,18 @@ int bfregn_to_uar_index(struct mlx5_ib_dev *dev, bool dyn_bfreg); int mlx5_ib_qp_set_counter(struct ib_qp *qp, struct rdma_counter *counter); + +static inline bool mlx5_ib_can_use_umr(struct mlx5_ib_dev *dev, + bool do_modify_atomic) +{ + if (MLX5_CAP_GEN(dev->mdev, umr_modify_entity_size_disabled)) + return false; + + if (do_modify_atomic && + MLX5_CAP_GEN(dev->mdev, atomic) && + MLX5_CAP_GEN(dev->mdev, umr_modify_atomic_disabled)) + return false; + + return true; +} #endif /* MLX5_IB_H */ diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index b74fad08412f..3401f5f6792e 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -1293,9 +1293,7 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, if (err < 0) return ERR_PTR(err); - use_umr = !MLX5_CAP_GEN(dev->mdev, umr_modify_entity_size_disabled) && - (!MLX5_CAP_GEN(dev->mdev, umr_modify_atomic_disabled) || - !MLX5_CAP_GEN(dev->mdev, atomic)); + use_umr = mlx5_ib_can_use_umr(dev, true); if (order <= mr_cache_max_order(dev) && use_umr) { mr = alloc_mr_from_cache(pd, umem, virt_addr, length, ncont, @@ -1448,7 +1446,8 @@ int mlx5_ib_rereg_user_mr(struct ib_mr *ib_mr, int flags, u64 start, goto err; } - if (flags & IB_MR_REREG_TRANS && !use_umr_mtt_update(mr, addr, len)) { + if (!mlx5_ib_can_use_umr(dev, true) || + (flags & IB_MR_REREG_TRANS && !use_umr_mtt_update(mr, addr, len))) { /* * UMR can't be used - MKey needs to be replaced. */ diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 81da82050d05..0a59912a4cef 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -301,7 +301,8 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) memset(caps, 0, sizeof(*caps)); - if (!MLX5_CAP_GEN(dev->mdev, pg)) + if (!MLX5_CAP_GEN(dev->mdev, pg) || + !mlx5_ib_can_use_umr(dev, true)) return; caps->general_caps = IB_ODP_SUPPORT; @@ -355,7 +356,8 @@ void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) if (MLX5_CAP_GEN(dev->mdev, fixed_buffer_size) && MLX5_CAP_GEN(dev->mdev, null_mkey) && - MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) + MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset) && + !MLX5_CAP_GEN(dev->mdev, umr_indirect_mkey_disabled)) caps->general_caps |= IB_ODP_SUPPORT_IMPLICIT; return; @@ -579,7 +581,6 @@ static int pagefault_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr, u32 flags) { int npages = 0, current_seq, page_shift, ret, np; - bool implicit = false; struct ib_umem_odp *odp_mr = to_ib_umem_odp(mr->umem); bool downgrade = flags & MLX5_PF_FLAGS_DOWNGRADE; bool prefetch = flags & MLX5_PF_FLAGS_PREFETCH; @@ -594,7 +595,6 @@ static int pagefault_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr, if (IS_ERR(odp)) return PTR_ERR(odp); mr = odp->private; - implicit = true; } else { odp = odp_mr; } @@ -682,19 +682,15 @@ next_mr: out: if (ret == -EAGAIN) { - if (implicit || !odp->dying) { - unsigned long timeout = - msecs_to_jiffies(MMU_NOTIFIER_TIMEOUT); - - if (!wait_for_completion_timeout( - &odp->notifier_completion, - timeout)) { - mlx5_ib_warn(dev, "timeout waiting for mmu notifier. seq %d against %d. notifiers_count=%d\n", - current_seq, odp->notifiers_seq, odp->notifiers_count); - } - } else { - /* The MR is being killed, kill the QP as well. */ - ret = -EFAULT; + unsigned long timeout = msecs_to_jiffies(MMU_NOTIFIER_TIMEOUT); + + if (!wait_for_completion_timeout(&odp->notifier_completion, + timeout)) { + mlx5_ib_warn( + dev, + "timeout waiting for mmu notifier. seq %d against %d. notifiers_count=%d\n", + current_seq, odp->notifiers_seq, + odp->notifiers_count); } } @@ -1628,8 +1624,10 @@ int mlx5_ib_odp_init_one(struct mlx5_ib_dev *dev) { int ret = 0; - if (dev->odp_caps.general_caps & IB_ODP_SUPPORT) - ib_set_device_ops(&dev->ib_dev, &mlx5_ib_dev_odp_ops); + if (!(dev->odp_caps.general_caps & IB_ODP_SUPPORT)) + return ret; + + ib_set_device_ops(&dev->ib_dev, &mlx5_ib_dev_odp_ops); if (dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT) { ret = mlx5_cmd_null_mkey(dev->mdev, &dev->null_mkey); @@ -1639,9 +1637,6 @@ int mlx5_ib_odp_init_one(struct mlx5_ib_dev *dev) } } - if (!MLX5_CAP_GEN(dev->mdev, pg)) - return ret; - ret = mlx5_ib_create_pf_eq(dev, &dev->odp_pf_eq); return ret; @@ -1649,7 +1644,7 @@ int mlx5_ib_odp_init_one(struct mlx5_ib_dev *dev) void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *dev) { - if (!MLX5_CAP_GEN(dev->mdev, pg)) + if (!(dev->odp_caps.general_caps & IB_ODP_SUPPORT)) return; mlx5_ib_destroy_pf_eq(dev, &dev->odp_pf_eq); diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 379328b2598f..72869ff4a334 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -4162,7 +4162,7 @@ static u64 get_xlt_octo(u64 bytes) MLX5_IB_UMR_OCTOWORD; } -static __be64 frwr_mkey_mask(void) +static __be64 frwr_mkey_mask(bool atomic) { u64 result; @@ -4175,10 +4175,12 @@ static __be64 frwr_mkey_mask(void) MLX5_MKEY_MASK_LW | MLX5_MKEY_MASK_RR | MLX5_MKEY_MASK_RW | - MLX5_MKEY_MASK_A | MLX5_MKEY_MASK_SMALL_FENCE | MLX5_MKEY_MASK_FREE; + if (atomic) + result |= MLX5_MKEY_MASK_A; + return cpu_to_be64(result); } @@ -4204,7 +4206,7 @@ static __be64 sig_mkey_mask(void) } static void set_reg_umr_seg(struct mlx5_wqe_umr_ctrl_seg *umr, - struct mlx5_ib_mr *mr, u8 flags) + struct mlx5_ib_mr *mr, u8 flags, bool atomic) { int size = (mr->ndescs + mr->meta_ndescs) * mr->desc_size; @@ -4212,7 +4214,7 @@ static void set_reg_umr_seg(struct mlx5_wqe_umr_ctrl_seg *umr, umr->flags = flags; umr->xlt_octowords = cpu_to_be16(get_xlt_octo(size)); - umr->mkey_mask = frwr_mkey_mask(); + umr->mkey_mask = frwr_mkey_mask(atomic); } static void set_linv_umr_seg(struct mlx5_wqe_umr_ctrl_seg *umr) @@ -4811,10 +4813,22 @@ static int set_reg_wr(struct mlx5_ib_qp *qp, { struct mlx5_ib_mr *mr = to_mmr(wr->mr); struct mlx5_ib_pd *pd = to_mpd(qp->ibqp.pd); + struct mlx5_ib_dev *dev = to_mdev(pd->ibpd.device); int mr_list_size = (mr->ndescs + mr->meta_ndescs) * mr->desc_size; bool umr_inline = mr_list_size <= MLX5_IB_SQ_UMR_INLINE_THRESHOLD; + bool atomic = wr->access & IB_ACCESS_REMOTE_ATOMIC; u8 flags = 0; + if (!mlx5_ib_can_use_umr(dev, atomic)) { + mlx5_ib_warn(to_mdev(qp->ibqp.device), + "Fast update of %s for MR is disabled\n", + (MLX5_CAP_GEN(dev->mdev, + umr_modify_entity_size_disabled)) ? + "entity size" : + "atomic access"); + return -EINVAL; + } + if (unlikely(wr->wr.send_flags & IB_SEND_INLINE)) { mlx5_ib_warn(to_mdev(qp->ibqp.device), "Invalid IB_SEND_INLINE send flag\n"); @@ -4826,7 +4840,7 @@ static int set_reg_wr(struct mlx5_ib_qp *qp, if (umr_inline) flags |= MLX5_UMR_INLINE; - set_reg_umr_seg(*seg, mr, flags); + set_reg_umr_seg(*seg, mr, flags, atomic); *seg += sizeof(struct mlx5_wqe_umr_ctrl_seg); *size += sizeof(struct mlx5_wqe_umr_ctrl_seg) / 16; handle_post_send_edge(&qp->sq, seg, *size, cur_edge); diff --git a/drivers/infiniband/sw/siw/Kconfig b/drivers/infiniband/sw/siw/Kconfig index dace276aea14..b622fc62f2cd 100644 --- a/drivers/infiniband/sw/siw/Kconfig +++ b/drivers/infiniband/sw/siw/Kconfig @@ -1,6 +1,6 @@ config RDMA_SIW tristate "Software RDMA over TCP/IP (iWARP) driver" - depends on INET && INFINIBAND && LIBCRC32C && 64BIT + depends on INET && INFINIBAND && LIBCRC32C select DMA_VIRT_OPS help This driver implements the iWARP RDMA transport over diff --git a/drivers/infiniband/sw/siw/siw.h b/drivers/infiniband/sw/siw/siw.h index 03fd7b2f595f..dba4535494ab 100644 --- a/drivers/infiniband/sw/siw/siw.h +++ b/drivers/infiniband/sw/siw/siw.h @@ -138,9 +138,9 @@ struct siw_umem { }; struct siw_pble { - u64 addr; /* Address of assigned user buffer */ - u64 size; /* Size of this entry */ - u64 pbl_off; /* Total offset from start of PBL */ + dma_addr_t addr; /* Address of assigned buffer */ + unsigned int size; /* Size of this entry */ + unsigned long pbl_off; /* Total offset from start of PBL */ }; struct siw_pbl { @@ -214,7 +214,7 @@ struct siw_wqe { struct siw_cq { struct ib_cq base_cq; spinlock_t lock; - u64 *notify; + struct siw_cq_ctrl *notify; struct siw_cqe *queue; u32 cq_put; u32 cq_get; @@ -734,7 +734,7 @@ static inline void siw_crc_skb(struct siw_rx_stream *srx, unsigned int len) "MEM[0x%08x] %s: " fmt, mem->stag, __func__, ##__VA_ARGS__) #define siw_dbg_cep(cep, fmt, ...) \ - ibdev_dbg(&cep->sdev->base_dev, "CEP[0x%p] %s: " fmt, \ + ibdev_dbg(&cep->sdev->base_dev, "CEP[0x%pK] %s: " fmt, \ cep, __func__, ##__VA_ARGS__) void siw_cq_flush(struct siw_cq *cq); diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index 9ce8a1b925d2..8c1931a57f4a 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -355,8 +355,8 @@ static int siw_cm_upcall(struct siw_cep *cep, enum iw_cm_event_type reason, getname_local(cep->sock, &event.local_addr); getname_peer(cep->sock, &event.remote_addr); } - siw_dbg_cep(cep, "[QP %u]: id 0x%p, reason=%d, status=%d\n", - cep->qp ? qp_id(cep->qp) : -1, id, reason, status); + siw_dbg_cep(cep, "[QP %u]: reason=%d, status=%d\n", + cep->qp ? qp_id(cep->qp) : UINT_MAX, reason, status); return id->event_handler(id, &event); } @@ -947,8 +947,6 @@ static void siw_accept_newconn(struct siw_cep *cep) siw_cep_get(new_cep); new_s->sk->sk_user_data = new_cep; - siw_dbg_cep(cep, "listen socket 0x%p, new 0x%p\n", s, new_s); - if (siw_tcp_nagle == false) { int val = 1; @@ -1011,7 +1009,8 @@ static void siw_cm_work_handler(struct work_struct *w) cep = work->cep; siw_dbg_cep(cep, "[QP %u]: work type: %d, state %d\n", - cep->qp ? qp_id(cep->qp) : -1, work->type, cep->state); + cep->qp ? qp_id(cep->qp) : UINT_MAX, + work->type, cep->state); siw_cep_set_inuse(cep); @@ -1145,9 +1144,9 @@ static void siw_cm_work_handler(struct work_struct *w) } if (release_cep) { siw_dbg_cep(cep, - "release: timer=%s, QP[%u], id 0x%p\n", + "release: timer=%s, QP[%u]\n", cep->mpa_timer ? "y" : "n", - cep->qp ? qp_id(cep->qp) : -1, cep->cm_id); + cep->qp ? qp_id(cep->qp) : UINT_MAX); siw_cancel_mpatimer(cep); @@ -1211,8 +1210,8 @@ int siw_cm_queue_work(struct siw_cep *cep, enum siw_work_type type) else delay = MPAREP_TIMEOUT; } - siw_dbg_cep(cep, "[QP %u]: work type: %d, work 0x%p, timeout %lu\n", - cep->qp ? qp_id(cep->qp) : -1, type, work, delay); + siw_dbg_cep(cep, "[QP %u]: work type: %d, timeout %lu\n", + cep->qp ? qp_id(cep->qp) : -1, type, delay); queue_delayed_work(siw_cm_wq, &work->work, delay); @@ -1376,16 +1375,16 @@ int siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) } if (v4) siw_dbg_qp(qp, - "id 0x%p, pd_len %d, laddr %pI4 %d, raddr %pI4 %d\n", - id, pd_len, + "pd_len %d, laddr %pI4 %d, raddr %pI4 %d\n", + pd_len, &((struct sockaddr_in *)(laddr))->sin_addr, ntohs(((struct sockaddr_in *)(laddr))->sin_port), &((struct sockaddr_in *)(raddr))->sin_addr, ntohs(((struct sockaddr_in *)(raddr))->sin_port)); else siw_dbg_qp(qp, - "id 0x%p, pd_len %d, laddr %pI6 %d, raddr %pI6 %d\n", - id, pd_len, + "pd_len %d, laddr %pI6 %d, raddr %pI6 %d\n", + pd_len, &((struct sockaddr_in6 *)(laddr))->sin6_addr, ntohs(((struct sockaddr_in6 *)(laddr))->sin6_port), &((struct sockaddr_in6 *)(raddr))->sin6_addr, @@ -1508,14 +1507,13 @@ int siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) if (rv >= 0) { rv = siw_cm_queue_work(cep, SIW_CM_WORK_MPATIMEOUT); if (!rv) { - siw_dbg_cep(cep, "id 0x%p, [QP %u]: exit\n", id, - qp_id(qp)); + siw_dbg_cep(cep, "[QP %u]: exit\n", qp_id(qp)); siw_cep_set_free(cep); return 0; } } error: - siw_dbg_qp(qp, "failed: %d\n", rv); + siw_dbg(id->device, "failed: %d\n", rv); if (cep) { siw_socket_disassoc(s); @@ -1540,7 +1538,8 @@ error: } else if (s) { sock_release(s); } - siw_qp_put(qp); + if (qp) + siw_qp_put(qp); return rv; } @@ -1580,7 +1579,7 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) siw_cancel_mpatimer(cep); if (cep->state != SIW_EPSTATE_RECVD_MPAREQ) { - siw_dbg_cep(cep, "id 0x%p: out of state\n", id); + siw_dbg_cep(cep, "out of state\n"); siw_cep_set_free(cep); siw_cep_put(cep); @@ -1601,7 +1600,7 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) up_write(&qp->state_lock); goto error; } - siw_dbg_cep(cep, "id 0x%p\n", id); + siw_dbg_cep(cep, "[QP %d]\n", params->qpn); if (try_gso && cep->mpa.hdr.params.bits & MPA_RR_FLAG_GSO_EXP) { siw_dbg_cep(cep, "peer allows GSO on TX\n"); @@ -1611,8 +1610,8 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) params->ird > sdev->attrs.max_ird) { siw_dbg_cep( cep, - "id 0x%p, [QP %u]: ord %d (max %d), ird %d (max %d)\n", - id, qp_id(qp), params->ord, sdev->attrs.max_ord, + "[QP %u]: ord %d (max %d), ird %d (max %d)\n", + qp_id(qp), params->ord, sdev->attrs.max_ord, params->ird, sdev->attrs.max_ird); rv = -EINVAL; up_write(&qp->state_lock); @@ -1624,8 +1623,8 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) if (params->private_data_len > max_priv_data) { siw_dbg_cep( cep, - "id 0x%p, [QP %u]: private data length: %d (max %d)\n", - id, qp_id(qp), params->private_data_len, max_priv_data); + "[QP %u]: private data length: %d (max %d)\n", + qp_id(qp), params->private_data_len, max_priv_data); rv = -EINVAL; up_write(&qp->state_lock); goto error; @@ -1679,7 +1678,7 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) qp_attrs.flags = SIW_MPA_CRC; qp_attrs.state = SIW_QP_STATE_RTS; - siw_dbg_cep(cep, "id 0x%p, [QP%u]: moving to rts\n", id, qp_id(qp)); + siw_dbg_cep(cep, "[QP%u]: moving to rts\n", qp_id(qp)); /* Associate QP with CEP */ siw_cep_get(cep); @@ -1700,8 +1699,8 @@ int siw_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) if (rv) goto error; - siw_dbg_cep(cep, "id 0x%p, [QP %u]: send mpa reply, %d byte pdata\n", - id, qp_id(qp), params->private_data_len); + siw_dbg_cep(cep, "[QP %u]: send mpa reply, %d byte pdata\n", + qp_id(qp), params->private_data_len); rv = siw_send_mpareqrep(cep, params->private_data, params->private_data_len); @@ -1759,14 +1758,14 @@ int siw_reject(struct iw_cm_id *id, const void *pdata, u8 pd_len) siw_cancel_mpatimer(cep); if (cep->state != SIW_EPSTATE_RECVD_MPAREQ) { - siw_dbg_cep(cep, "id 0x%p: out of state\n", id); + siw_dbg_cep(cep, "out of state\n"); siw_cep_set_free(cep); siw_cep_put(cep); /* put last reference */ return -ECONNRESET; } - siw_dbg_cep(cep, "id 0x%p, cep->state %d, pd_len %d\n", id, cep->state, + siw_dbg_cep(cep, "cep->state %d, pd_len %d\n", cep->state, pd_len); if (__mpa_rr_revision(cep->mpa.hdr.params.bits) >= MPA_REVISION_1) { @@ -1804,14 +1803,14 @@ static int siw_listen_address(struct iw_cm_id *id, int backlog, rv = kernel_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&s_val, sizeof(s_val)); if (rv) { - siw_dbg(id->device, "id 0x%p: setsockopt error: %d\n", id, rv); + siw_dbg(id->device, "setsockopt error: %d\n", rv); goto error; } rv = s->ops->bind(s, laddr, addr_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); if (rv) { - siw_dbg(id->device, "id 0x%p: socket bind error: %d\n", id, rv); + siw_dbg(id->device, "socket bind error: %d\n", rv); goto error; } cep = siw_cep_alloc(sdev); @@ -1824,13 +1823,13 @@ static int siw_listen_address(struct iw_cm_id *id, int backlog, rv = siw_cm_alloc_work(cep, backlog); if (rv) { siw_dbg(id->device, - "id 0x%p: alloc_work error %d, backlog %d\n", id, + "alloc_work error %d, backlog %d\n", rv, backlog); goto error; } rv = s->ops->listen(s, backlog); if (rv) { - siw_dbg(id->device, "id 0x%p: listen error %d\n", id, rv); + siw_dbg(id->device, "listen error %d\n", rv); goto error; } cep->cm_id = id; @@ -1914,8 +1913,7 @@ static void siw_drop_listeners(struct iw_cm_id *id) list_del(p); - siw_dbg_cep(cep, "id 0x%p: drop cep, state %d\n", id, - cep->state); + siw_dbg_cep(cep, "drop cep, state %d\n", cep->state); siw_cep_set_inuse(cep); @@ -1952,7 +1950,7 @@ int siw_create_listen(struct iw_cm_id *id, int backlog) struct net_device *dev = to_siw_dev(id->device)->netdev; int rv = 0, listeners = 0; - siw_dbg(id->device, "id 0x%p: backlog %d\n", id, backlog); + siw_dbg(id->device, "backlog %d\n", backlog); /* * For each attached address of the interface, create a @@ -1964,12 +1962,16 @@ int siw_create_listen(struct iw_cm_id *id, int backlog) struct sockaddr_in s_laddr, *s_raddr; const struct in_ifaddr *ifa; + if (!in_dev) { + rv = -ENODEV; + goto out; + } memcpy(&s_laddr, &id->local_addr, sizeof(s_laddr)); s_raddr = (struct sockaddr_in *)&id->remote_addr; siw_dbg(id->device, - "id 0x%p: laddr %pI4:%d, raddr %pI4:%d\n", - id, &s_laddr.sin_addr, ntohs(s_laddr.sin_port), + "laddr %pI4:%d, raddr %pI4:%d\n", + &s_laddr.sin_addr, ntohs(s_laddr.sin_port), &s_raddr->sin_addr, ntohs(s_raddr->sin_port)); rtnl_lock(); @@ -1993,22 +1995,27 @@ int siw_create_listen(struct iw_cm_id *id, int backlog) struct sockaddr_in6 *s_laddr = &to_sockaddr_in6(id->local_addr), *s_raddr = &to_sockaddr_in6(id->remote_addr); + if (!in6_dev) { + rv = -ENODEV; + goto out; + } siw_dbg(id->device, - "id 0x%p: laddr %pI6:%d, raddr %pI6:%d\n", - id, &s_laddr->sin6_addr, ntohs(s_laddr->sin6_port), + "laddr %pI6:%d, raddr %pI6:%d\n", + &s_laddr->sin6_addr, ntohs(s_laddr->sin6_port), &s_raddr->sin6_addr, ntohs(s_raddr->sin6_port)); - read_lock_bh(&in6_dev->lock); + rtnl_lock(); list_for_each_entry(ifp, &in6_dev->addr_list, if_list) { - struct sockaddr_in6 bind_addr; - + if (ifp->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)) + continue; if (ipv6_addr_any(&s_laddr->sin6_addr) || ipv6_addr_equal(&s_laddr->sin6_addr, &ifp->addr)) { - bind_addr.sin6_family = AF_INET6; - bind_addr.sin6_port = s_laddr->sin6_port; - bind_addr.sin6_flowinfo = 0; - bind_addr.sin6_addr = ifp->addr; - bind_addr.sin6_scope_id = dev->ifindex; + struct sockaddr_in6 bind_addr = { + .sin6_family = AF_INET6, + .sin6_port = s_laddr->sin6_port, + .sin6_flowinfo = 0, + .sin6_addr = ifp->addr, + .sin6_scope_id = dev->ifindex }; rv = siw_listen_address(id, backlog, (struct sockaddr *)&bind_addr, @@ -2017,28 +2024,26 @@ int siw_create_listen(struct iw_cm_id *id, int backlog) listeners++; } } - read_unlock_bh(&in6_dev->lock); - + rtnl_unlock(); in6_dev_put(in6_dev); } else { - return -EAFNOSUPPORT; + rv = -EAFNOSUPPORT; } +out: if (listeners) rv = 0; else if (!rv) rv = -EINVAL; - siw_dbg(id->device, "id 0x%p: %s\n", id, rv ? "FAIL" : "OK"); + siw_dbg(id->device, "%s\n", rv ? "FAIL" : "OK"); return rv; } int siw_destroy_listen(struct iw_cm_id *id) { - siw_dbg(id->device, "id 0x%p\n", id); - if (!id->provider_data) { - siw_dbg(id->device, "id 0x%p: no cep(s)\n", id); + siw_dbg(id->device, "no cep(s)\n"); return 0; } siw_drop_listeners(id); diff --git a/drivers/infiniband/sw/siw/siw_cq.c b/drivers/infiniband/sw/siw/siw_cq.c index e381ae9b7d62..d8db3bee9da7 100644 --- a/drivers/infiniband/sw/siw/siw_cq.c +++ b/drivers/infiniband/sw/siw/siw_cq.c @@ -71,9 +71,10 @@ int siw_reap_cqe(struct siw_cq *cq, struct ib_wc *wc) wc->wc_flags = IB_WC_WITH_INVALIDATE; } wc->qp = cqe->base_qp; - siw_dbg_cq(cq, "idx %u, type %d, flags %2x, id 0x%p\n", + siw_dbg_cq(cq, + "idx %u, type %d, flags %2x, id 0x%pK\n", cq->cq_get % cq->num_cqe, cqe->opcode, - cqe->flags, (void *)cqe->id); + cqe->flags, (void *)(uintptr_t)cqe->id); } WRITE_ONCE(cqe->flags, 0); cq->cq_get++; diff --git a/drivers/infiniband/sw/siw/siw_main.c b/drivers/infiniband/sw/siw/siw_main.c index d0f140daf659..05a92f997f60 100644 --- a/drivers/infiniband/sw/siw/siw_main.c +++ b/drivers/infiniband/sw/siw/siw_main.c @@ -160,10 +160,8 @@ static int siw_init_cpulist(void) out_err: siw_cpu_info.num_nodes = 0; - while (i) { + while (--i >= 0) kfree(siw_cpu_info.tx_valid_cpus[i]); - siw_cpu_info.tx_valid_cpus[i--] = NULL; - } kfree(siw_cpu_info.tx_valid_cpus); siw_cpu_info.tx_valid_cpus = NULL; diff --git a/drivers/infiniband/sw/siw/siw_mem.c b/drivers/infiniband/sw/siw/siw_mem.c index 67171c82b0c4..87a56039f0ef 100644 --- a/drivers/infiniband/sw/siw/siw_mem.c +++ b/drivers/infiniband/sw/siw/siw_mem.c @@ -197,12 +197,12 @@ int siw_check_mem(struct ib_pd *pd, struct siw_mem *mem, u64 addr, */ if (addr < mem->va || addr + len > mem->va + mem->len) { siw_dbg_pd(pd, "MEM interval len %d\n", len); - siw_dbg_pd(pd, "[0x%016llx, 0x%016llx] out of bounds\n", - (unsigned long long)addr, - (unsigned long long)(addr + len)); - siw_dbg_pd(pd, "[0x%016llx, 0x%016llx] STag=0x%08x\n", - (unsigned long long)mem->va, - (unsigned long long)(mem->va + mem->len), + siw_dbg_pd(pd, "[0x%pK, 0x%pK] out of bounds\n", + (void *)(uintptr_t)addr, + (void *)(uintptr_t)(addr + len)); + siw_dbg_pd(pd, "[0x%pK, 0x%pK] STag=0x%08x\n", + (void *)(uintptr_t)mem->va, + (void *)(uintptr_t)(mem->va + mem->len), mem->stag); return -E_BASE_BOUNDS; @@ -330,7 +330,7 @@ out: * Optionally, provides remaining len within current element, and * current PBL index for later resume at same element. */ -u64 siw_pbl_get_buffer(struct siw_pbl *pbl, u64 off, int *len, int *idx) +dma_addr_t siw_pbl_get_buffer(struct siw_pbl *pbl, u64 off, int *len, int *idx) { int i = idx ? *idx : 0; diff --git a/drivers/infiniband/sw/siw/siw_mem.h b/drivers/infiniband/sw/siw/siw_mem.h index f43daf280891..db138c8423da 100644 --- a/drivers/infiniband/sw/siw/siw_mem.h +++ b/drivers/infiniband/sw/siw/siw_mem.h @@ -9,7 +9,7 @@ struct siw_umem *siw_umem_get(u64 start, u64 len, bool writable); void siw_umem_release(struct siw_umem *umem, bool dirty); struct siw_pbl *siw_pbl_alloc(u32 num_buf); -u64 siw_pbl_get_buffer(struct siw_pbl *pbl, u64 off, int *len, int *idx); +dma_addr_t siw_pbl_get_buffer(struct siw_pbl *pbl, u64 off, int *len, int *idx); struct siw_mem *siw_mem_id2obj(struct siw_device *sdev, int stag_index); int siw_mem_add(struct siw_device *sdev, struct siw_mem *m); int siw_invalidate_stag(struct ib_pd *pd, u32 stag); diff --git a/drivers/infiniband/sw/siw/siw_qp.c b/drivers/infiniband/sw/siw/siw_qp.c index e27bd5b35b96..430314c8abd9 100644 --- a/drivers/infiniband/sw/siw/siw_qp.c +++ b/drivers/infiniband/sw/siw/siw_qp.c @@ -949,7 +949,7 @@ skip_irq: rv = -EINVAL; goto out; } - wqe->sqe.sge[0].laddr = (u64)&wqe->sqe.sge[1]; + wqe->sqe.sge[0].laddr = (uintptr_t)&wqe->sqe.sge[1]; wqe->sqe.sge[0].lkey = 0; wqe->sqe.num_sge = 1; } @@ -1013,18 +1013,24 @@ out: */ static bool siw_cq_notify_now(struct siw_cq *cq, u32 flags) { - u64 cq_notify; + u32 cq_notify; if (!cq->base_cq.comp_handler) return false; - cq_notify = READ_ONCE(*cq->notify); + /* Read application shared notification state */ + cq_notify = READ_ONCE(cq->notify->flags); if ((cq_notify & SIW_NOTIFY_NEXT_COMPLETION) || ((cq_notify & SIW_NOTIFY_SOLICITED) && (flags & SIW_WQE_SOLICITED))) { - /* dis-arm CQ */ - smp_store_mb(*cq->notify, SIW_NOTIFY_NOT); + /* + * CQ notification is one-shot: Since the + * current CQE causes user notification, + * the CQ gets dis-aremd and must be re-aremd + * by the user for a new notification. + */ + WRITE_ONCE(cq->notify->flags, SIW_NOTIFY_NOT); return true; } diff --git a/drivers/infiniband/sw/siw/siw_qp_rx.c b/drivers/infiniband/sw/siw/siw_qp_rx.c index f87657a11657..c0a887240325 100644 --- a/drivers/infiniband/sw/siw/siw_qp_rx.c +++ b/drivers/infiniband/sw/siw/siw_qp_rx.c @@ -38,9 +38,10 @@ static int siw_rx_umem(struct siw_rx_stream *srx, struct siw_umem *umem, p = siw_get_upage(umem, dest_addr); if (unlikely(!p)) { - pr_warn("siw: %s: [QP %u]: bogus addr: %p, %p\n", + pr_warn("siw: %s: [QP %u]: bogus addr: %pK, %pK\n", __func__, qp_id(rx_qp(srx)), - (void *)dest_addr, (void *)umem->fp_addr); + (void *)(uintptr_t)dest_addr, + (void *)(uintptr_t)umem->fp_addr); /* siw internal error */ srx->skb_copied += copied; srx->skb_new -= copied; @@ -50,7 +51,7 @@ static int siw_rx_umem(struct siw_rx_stream *srx, struct siw_umem *umem, pg_off = dest_addr & ~PAGE_MASK; bytes = min(len, (int)PAGE_SIZE - pg_off); - siw_dbg_qp(rx_qp(srx), "page %p, bytes=%u\n", p, bytes); + siw_dbg_qp(rx_qp(srx), "page %pK, bytes=%u\n", p, bytes); dest = kmap_atomic(p); rv = skb_copy_bits(srx->skb, srx->skb_offset, dest + pg_off, @@ -104,11 +105,11 @@ static int siw_rx_kva(struct siw_rx_stream *srx, void *kva, int len) { int rv; - siw_dbg_qp(rx_qp(srx), "kva: 0x%p, len: %u\n", kva, len); + siw_dbg_qp(rx_qp(srx), "kva: 0x%pK, len: %u\n", kva, len); rv = skb_copy_bits(srx->skb, srx->skb_offset, kva, len); if (unlikely(rv)) { - pr_warn("siw: [QP %u]: %s, len %d, kva 0x%p, rv %d\n", + pr_warn("siw: [QP %u]: %s, len %d, kva 0x%pK, rv %d\n", qp_id(rx_qp(srx)), __func__, len, kva, rv); return rv; @@ -132,7 +133,7 @@ static int siw_rx_pbl(struct siw_rx_stream *srx, int *pbl_idx, while (len) { int bytes; - u64 buf_addr = + dma_addr_t buf_addr = siw_pbl_get_buffer(pbl, offset, &bytes, pbl_idx); if (!buf_addr) break; @@ -485,8 +486,8 @@ int siw_proc_send(struct siw_qp *qp) mem_p = *mem; if (mem_p->mem_obj == NULL) rv = siw_rx_kva(srx, - (void *)(sge->laddr + frx->sge_off), - sge_bytes); + (void *)(uintptr_t)(sge->laddr + frx->sge_off), + sge_bytes); else if (!mem_p->is_pbl) rv = siw_rx_umem(srx, mem_p->umem, sge->laddr + frx->sge_off, sge_bytes); @@ -598,8 +599,8 @@ int siw_proc_write(struct siw_qp *qp) if (mem->mem_obj == NULL) rv = siw_rx_kva(srx, - (void *)(srx->ddp_to + srx->fpdu_part_rcvd), - bytes); + (void *)(uintptr_t)(srx->ddp_to + srx->fpdu_part_rcvd), + bytes); else if (!mem->is_pbl) rv = siw_rx_umem(srx, mem->umem, srx->ddp_to + srx->fpdu_part_rcvd, bytes); @@ -841,8 +842,9 @@ int siw_proc_rresp(struct siw_qp *qp) bytes = min(srx->fpdu_part_rem, srx->skb_new); if (mem_p->mem_obj == NULL) - rv = siw_rx_kva(srx, (void *)(sge->laddr + wqe->processed), - bytes); + rv = siw_rx_kva(srx, + (void *)(uintptr_t)(sge->laddr + wqe->processed), + bytes); else if (!mem_p->is_pbl) rv = siw_rx_umem(srx, mem_p->umem, sge->laddr + wqe->processed, bytes); diff --git a/drivers/infiniband/sw/siw/siw_qp_tx.c b/drivers/infiniband/sw/siw/siw_qp_tx.c index 43020d2040fc..438a2917a47c 100644 --- a/drivers/infiniband/sw/siw/siw_qp_tx.c +++ b/drivers/infiniband/sw/siw/siw_qp_tx.c @@ -26,7 +26,7 @@ static struct page *siw_get_pblpage(struct siw_mem *mem, u64 addr, int *idx) { struct siw_pbl *pbl = mem->pbl; u64 offset = addr - mem->va; - u64 paddr = siw_pbl_get_buffer(pbl, offset, NULL, idx); + dma_addr_t paddr = siw_pbl_get_buffer(pbl, offset, NULL, idx); if (paddr) return virt_to_page(paddr); @@ -37,7 +37,7 @@ static struct page *siw_get_pblpage(struct siw_mem *mem, u64 addr, int *idx) /* * Copy short payload at provided destination payload address */ -static int siw_try_1seg(struct siw_iwarp_tx *c_tx, u64 paddr) +static int siw_try_1seg(struct siw_iwarp_tx *c_tx, void *paddr) { struct siw_wqe *wqe = &c_tx->wqe_active; struct siw_sge *sge = &wqe->sqe.sge[0]; @@ -50,16 +50,16 @@ static int siw_try_1seg(struct siw_iwarp_tx *c_tx, u64 paddr) return 0; if (tx_flags(wqe) & SIW_WQE_INLINE) { - memcpy((void *)paddr, &wqe->sqe.sge[1], bytes); + memcpy(paddr, &wqe->sqe.sge[1], bytes); } else { struct siw_mem *mem = wqe->mem[0]; if (!mem->mem_obj) { /* Kernel client using kva */ - memcpy((void *)paddr, (void *)sge->laddr, bytes); + memcpy(paddr, + (const void *)(uintptr_t)sge->laddr, bytes); } else if (c_tx->in_syscall) { - if (copy_from_user((void *)paddr, - (const void __user *)sge->laddr, + if (copy_from_user(paddr, u64_to_user_ptr(sge->laddr), bytes)) return -EFAULT; } else { @@ -79,12 +79,12 @@ static int siw_try_1seg(struct siw_iwarp_tx *c_tx, u64 paddr) buffer = kmap_atomic(p); if (likely(PAGE_SIZE - off >= bytes)) { - memcpy((void *)paddr, buffer + off, bytes); + memcpy(paddr, buffer + off, bytes); kunmap_atomic(buffer); } else { unsigned long part = bytes - (PAGE_SIZE - off); - memcpy((void *)paddr, buffer + off, part); + memcpy(paddr, buffer + off, part); kunmap_atomic(buffer); if (!mem->is_pbl) @@ -98,7 +98,7 @@ static int siw_try_1seg(struct siw_iwarp_tx *c_tx, u64 paddr) return -EFAULT; buffer = kmap_atomic(p); - memcpy((void *)(paddr + part), buffer, + memcpy(paddr + part, buffer, bytes - part); kunmap_atomic(buffer); } @@ -166,7 +166,7 @@ static int siw_qp_prepare_tx(struct siw_iwarp_tx *c_tx) c_tx->ctrl_len = sizeof(struct iwarp_send); crc = (char *)&c_tx->pkt.send_pkt.crc; - data = siw_try_1seg(c_tx, (u64)crc); + data = siw_try_1seg(c_tx, crc); break; case SIW_OP_SEND_REMOTE_INV: @@ -189,7 +189,7 @@ static int siw_qp_prepare_tx(struct siw_iwarp_tx *c_tx) c_tx->ctrl_len = sizeof(struct iwarp_send_inv); crc = (char *)&c_tx->pkt.send_pkt.crc; - data = siw_try_1seg(c_tx, (u64)crc); + data = siw_try_1seg(c_tx, crc); break; case SIW_OP_WRITE: @@ -201,7 +201,7 @@ static int siw_qp_prepare_tx(struct siw_iwarp_tx *c_tx) c_tx->ctrl_len = sizeof(struct iwarp_rdma_write); crc = (char *)&c_tx->pkt.write_pkt.crc; - data = siw_try_1seg(c_tx, (u64)crc); + data = siw_try_1seg(c_tx, crc); break; case SIW_OP_READ_RESPONSE: @@ -216,7 +216,7 @@ static int siw_qp_prepare_tx(struct siw_iwarp_tx *c_tx) c_tx->ctrl_len = sizeof(struct iwarp_rdma_rresp); crc = (char *)&c_tx->pkt.write_pkt.crc; - data = siw_try_1seg(c_tx, (u64)crc); + data = siw_try_1seg(c_tx, crc); break; default: @@ -398,15 +398,13 @@ static int siw_0copy_tx(struct socket *s, struct page **page, #define MAX_TRAILER (MPA_CRC_SIZE + 4) -static void siw_unmap_pages(struct page **pages, int hdr_len, int num_maps) +static void siw_unmap_pages(struct page **pp, unsigned long kmap_mask) { - if (hdr_len) { - ++pages; - --num_maps; - } - while (num_maps-- > 0) { - kunmap(*pages); - pages++; + while (kmap_mask) { + if (kmap_mask & BIT(0)) + kunmap(*pp); + pp++; + kmap_mask >>= 1; } } @@ -437,6 +435,7 @@ static int siw_tx_hdt(struct siw_iwarp_tx *c_tx, struct socket *s) unsigned int data_len = c_tx->bytes_unsent, hdr_len = 0, trl_len = 0, sge_off = c_tx->sge_off, sge_idx = c_tx->sge_idx, pbl_idx = c_tx->pbl_idx; + unsigned long kmap_mask = 0L; if (c_tx->state == SIW_SEND_HDR) { if (c_tx->use_sendpage) { @@ -463,8 +462,7 @@ static int siw_tx_hdt(struct siw_iwarp_tx *c_tx, struct socket *s) if (!(tx_flags(wqe) & SIW_WQE_INLINE)) { mem = wqe->mem[sge_idx]; - if (!mem->mem_obj) - is_kva = 1; + is_kva = mem->mem_obj == NULL ? 1 : 0; } else { is_kva = 1; } @@ -473,7 +471,8 @@ static int siw_tx_hdt(struct siw_iwarp_tx *c_tx, struct socket *s) * tx from kernel virtual address: either inline data * or memory region with assigned kernel buffer */ - iov[seg].iov_base = (void *)(sge->laddr + sge_off); + iov[seg].iov_base = + (void *)(uintptr_t)(sge->laddr + sge_off); iov[seg].iov_len = sge_len; if (do_crc) @@ -500,12 +499,7 @@ static int siw_tx_hdt(struct siw_iwarp_tx *c_tx, struct socket *s) p = siw_get_upage(mem->umem, sge->laddr + sge_off); if (unlikely(!p)) { - if (hdr_len) - seg--; - if (!c_tx->use_sendpage && seg) { - siw_unmap_pages(page_array, - hdr_len, seg); - } + siw_unmap_pages(page_array, kmap_mask); wqe->processed -= c_tx->bytes_unsent; rv = -EFAULT; goto done_crc; @@ -515,6 +509,10 @@ static int siw_tx_hdt(struct siw_iwarp_tx *c_tx, struct socket *s) if (!c_tx->use_sendpage) { iov[seg].iov_base = kmap(p) + fp_off; iov[seg].iov_len = plen; + + /* Remember for later kunmap() */ + kmap_mask |= BIT(seg); + if (do_crc) crypto_shash_update( c_tx->mpa_crc_hd, @@ -526,13 +524,13 @@ static int siw_tx_hdt(struct siw_iwarp_tx *c_tx, struct socket *s) page_address(p) + fp_off, plen); } else { - u64 pa = ((sge->laddr + sge_off) & PAGE_MASK); + u64 va = sge->laddr + sge_off; - page_array[seg] = virt_to_page(pa); + page_array[seg] = virt_to_page(va & PAGE_MASK); if (do_crc) crypto_shash_update( c_tx->mpa_crc_hd, - (void *)(sge->laddr + sge_off), + (void *)(uintptr_t)va, plen); } @@ -543,10 +541,7 @@ static int siw_tx_hdt(struct siw_iwarp_tx *c_tx, struct socket *s) if (++seg > (int)MAX_ARRAY) { siw_dbg_qp(tx_qp(c_tx), "to many fragments\n"); - if (!is_kva && !c_tx->use_sendpage) { - siw_unmap_pages(page_array, hdr_len, - seg - 1); - } + siw_unmap_pages(page_array, kmap_mask); wqe->processed -= c_tx->bytes_unsent; rv = -EMSGSIZE; goto done_crc; @@ -597,8 +592,7 @@ sge_done: } else { rv = kernel_sendmsg(s, &msg, iov, seg + 1, hdr_len + data_len + trl_len); - if (!is_kva) - siw_unmap_pages(page_array, hdr_len, seg); + siw_unmap_pages(page_array, kmap_mask); } if (rv < (int)hdr_len) { /* Not even complete hdr pushed or negative rv */ @@ -829,7 +823,8 @@ static int siw_qp_sq_proc_tx(struct siw_qp *qp, struct siw_wqe *wqe) rv = -EINVAL; goto tx_error; } - wqe->sqe.sge[0].laddr = (u64)&wqe->sqe.sge[1]; + wqe->sqe.sge[0].laddr = + (u64)(uintptr_t)&wqe->sqe.sge[1]; } } wqe->wr_status = SIW_WR_INPROGRESS; @@ -924,7 +919,7 @@ tx_error: static int siw_fastreg_mr(struct ib_pd *pd, struct siw_sqe *sqe) { - struct ib_mr *base_mr = (struct ib_mr *)sqe->base_mr; + struct ib_mr *base_mr = (struct ib_mr *)(uintptr_t)sqe->base_mr; struct siw_device *sdev = to_siw_dev(pd->device); struct siw_mem *mem = siw_mem_id2obj(sdev, sqe->rkey >> 8); int rv = 0; @@ -954,8 +949,7 @@ static int siw_fastreg_mr(struct ib_pd *pd, struct siw_sqe *sqe) mem->stag = sqe->rkey; mem->perms = sqe->access; - siw_dbg_mem(mem, "STag now valid, MR va: 0x%016llx -> 0x%016llx\n", - mem->va, base_mr->iova); + siw_dbg_mem(mem, "STag 0x%08x now valid\n", sqe->rkey); mem->va = base_mr->iova; mem->stag_valid = 1; out: diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index 32dc79d0e898..da52c90e06d4 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -424,8 +424,7 @@ struct ib_qp *siw_create_qp(struct ib_pd *pd, */ qp->srq = to_siw_srq(attrs->srq); qp->attrs.rq_size = 0; - siw_dbg(base_dev, "QP [%u]: [SRQ 0x%p] attached\n", - qp->qp_num, qp->srq); + siw_dbg(base_dev, "QP [%u]: SRQ attached\n", qp->qp_num); } else if (num_rqe) { if (qp->kernel_verbs) qp->recvq = vzalloc(num_rqe * sizeof(struct siw_rqe)); @@ -610,7 +609,7 @@ int siw_destroy_qp(struct ib_qp *base_qp, struct ib_udata *udata) base_ucontext); struct siw_qp_attrs qp_attrs; - siw_dbg_qp(qp, "state %d, cep 0x%p\n", qp->attrs.state, qp->cep); + siw_dbg_qp(qp, "state %d\n", qp->attrs.state); /* * Mark QP as in process of destruction to prevent from @@ -662,7 +661,7 @@ static int siw_copy_inline_sgl(const struct ib_send_wr *core_wr, void *kbuf = &sqe->sge[1]; int num_sge = core_wr->num_sge, bytes = 0; - sqe->sge[0].laddr = (u64)kbuf; + sqe->sge[0].laddr = (uintptr_t)kbuf; sqe->sge[0].lkey = 0; while (num_sge--) { @@ -825,7 +824,7 @@ int siw_post_send(struct ib_qp *base_qp, const struct ib_send_wr *wr, break; case IB_WR_REG_MR: - sqe->base_mr = (uint64_t)reg_wr(wr)->mr; + sqe->base_mr = (uintptr_t)reg_wr(wr)->mr; sqe->rkey = reg_wr(wr)->key; sqe->access = reg_wr(wr)->access & IWARP_ACCESS_MASK; sqe->opcode = SIW_OP_REG_MR; @@ -842,8 +841,9 @@ int siw_post_send(struct ib_qp *base_qp, const struct ib_send_wr *wr, rv = -EINVAL; break; } - siw_dbg_qp(qp, "opcode %d, flags 0x%x, wr_id 0x%p\n", - sqe->opcode, sqe->flags, (void *)sqe->id); + siw_dbg_qp(qp, "opcode %d, flags 0x%x, wr_id 0x%pK\n", + sqe->opcode, sqe->flags, + (void *)(uintptr_t)sqe->id); if (unlikely(rv < 0)) break; @@ -1049,7 +1049,7 @@ int siw_create_cq(struct ib_cq *base_cq, const struct ib_cq_init_attr *attr, spin_lock_init(&cq->lock); - cq->notify = &((struct siw_cq_ctrl *)&cq->queue[size])->notify; + cq->notify = (struct siw_cq_ctrl *)&cq->queue[size]; if (udata) { struct siw_uresp_create_cq uresp = {}; @@ -1141,11 +1141,17 @@ int siw_req_notify_cq(struct ib_cq *base_cq, enum ib_cq_notify_flags flags) siw_dbg_cq(cq, "flags: 0x%02x\n", flags); if ((flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED) - /* CQ event for next solicited completion */ - smp_store_mb(*cq->notify, SIW_NOTIFY_SOLICITED); + /* + * Enable CQ event for next solicited completion. + * and make it visible to all associated producers. + */ + smp_store_mb(cq->notify->flags, SIW_NOTIFY_SOLICITED); else - /* CQ event for any signalled completion */ - smp_store_mb(*cq->notify, SIW_NOTIFY_ALL); + /* + * Enable CQ event for any signalled completion. + * and make it visible to all associated producers. + */ + smp_store_mb(cq->notify->flags, SIW_NOTIFY_ALL); if (flags & IB_CQ_REPORT_MISSED_EVENTS) return cq->cq_put - cq->cq_get; @@ -1199,8 +1205,8 @@ struct ib_mr *siw_reg_user_mr(struct ib_pd *pd, u64 start, u64 len, unsigned long mem_limit = rlimit(RLIMIT_MEMLOCK); int rv; - siw_dbg_pd(pd, "start: 0x%016llx, va: 0x%016llx, len: %llu\n", - (unsigned long long)start, (unsigned long long)rnic_va, + siw_dbg_pd(pd, "start: 0x%pK, va: 0x%pK, len: %llu\n", + (void *)(uintptr_t)start, (void *)(uintptr_t)rnic_va, (unsigned long long)len); if (atomic_inc_return(&sdev->num_mr) > SIW_MAX_MR) { @@ -1357,7 +1363,7 @@ int siw_map_mr_sg(struct ib_mr *base_mr, struct scatterlist *sl, int num_sle, struct siw_mem *mem = mr->mem; struct siw_pbl *pbl = mem->pbl; struct siw_pble *pble; - u64 pbl_size; + unsigned long pbl_size; int i, rv; if (!pbl) { @@ -1396,16 +1402,18 @@ int siw_map_mr_sg(struct ib_mr *base_mr, struct scatterlist *sl, int num_sle, pbl_size += sg_dma_len(slp); } siw_dbg_mem(mem, - "sge[%d], size %llu, addr 0x%016llx, total %llu\n", - i, pble->size, pble->addr, pbl_size); + "sge[%d], size %u, addr 0x%p, total %lu\n", + i, pble->size, (void *)(uintptr_t)pble->addr, + pbl_size); } rv = ib_sg_to_pages(base_mr, sl, num_sle, sg_off, siw_set_pbl_page); if (rv > 0) { mem->len = base_mr->length; mem->va = base_mr->iova; siw_dbg_mem(mem, - "%llu bytes, start 0x%016llx, %u SLE to %u entries\n", - mem->len, mem->va, num_sle, pbl->num_buf); + "%llu bytes, start 0x%pK, %u SLE to %u entries\n", + mem->len, (void *)(uintptr_t)mem->va, num_sle, + pbl->num_buf); } return rv; } @@ -1523,7 +1531,7 @@ int siw_create_srq(struct ib_srq *base_srq, } spin_lock_init(&srq->lock); - siw_dbg_pd(base_srq->pd, "[SRQ 0x%p]: success\n", srq); + siw_dbg_pd(base_srq->pd, "[SRQ]: success\n"); return 0; @@ -1644,8 +1652,7 @@ int siw_post_srq_recv(struct ib_srq *base_srq, const struct ib_recv_wr *wr, if (unlikely(!srq->kernel_verbs)) { siw_dbg_pd(base_srq->pd, - "[SRQ 0x%p]: no kernel post_recv for mapped srq\n", - srq); + "[SRQ]: no kernel post_recv for mapped srq\n"); rv = -EINVAL; goto out; } @@ -1667,8 +1674,7 @@ int siw_post_srq_recv(struct ib_srq *base_srq, const struct ib_recv_wr *wr, } if (unlikely(wr->num_sge > srq->max_sge)) { siw_dbg_pd(base_srq->pd, - "[SRQ 0x%p]: too many sge's: %d\n", srq, - wr->num_sge); + "[SRQ]: too many sge's: %d\n", wr->num_sge); rv = -EINVAL; break; } @@ -1687,7 +1693,7 @@ int siw_post_srq_recv(struct ib_srq *base_srq, const struct ib_recv_wr *wr, spin_unlock_irqrestore(&srq->lock, flags); out: if (unlikely(rv < 0)) { - siw_dbg_pd(base_srq->pd, "[SRQ 0x%p]: error %d\n", srq, rv); + siw_dbg_pd(base_srq->pd, "[SRQ]: error %d\n", rv); *bad_wr = wr; } return rv; diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 29abfeeef9a5..6c554c11a7ac 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -201,7 +201,12 @@ static int iforce_usb_probe(struct usb_interface *intf, return -ENODEV; epirq = &interface->endpoint[0].desc; + if (!usb_endpoint_is_int_in(epirq)) + return -ENODEV; + epout = &interface->endpoint[1].desc; + if (!usb_endpoint_is_int_out(epout)) + return -ENODEV; iforce_usb = kzalloc(sizeof(*iforce_usb), GFP_KERNEL); if (!iforce_usb) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 8e9c3ea9d5e7..90e8a7f2f07c 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -76,6 +76,8 @@ config KEYBOARD_APPLESPI depends on ACPI && EFI depends on SPI depends on X86 || COMPILE_TEST + depends on LEDS_CLASS + select CRC16 help Say Y here if you are running Linux on any Apple MacBook8,1 or later, or any MacBookPro13,* or MacBookPro14,*. diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c index 548737e7aeda..584289b67fb3 100644 --- a/drivers/input/keyboard/applespi.c +++ b/drivers/input/keyboard/applespi.c @@ -134,10 +134,10 @@ struct keyboard_protocol { * struct tp_finger - single trackpad finger structure, le16-aligned * * @origin: zero when switching track finger - * @abs_x: absolute x coodinate - * @abs_y: absolute y coodinate - * @rel_x: relative x coodinate - * @rel_y: relative y coodinate + * @abs_x: absolute x coordinate + * @abs_y: absolute y coordinate + * @rel_x: relative x coordinate + * @rel_y: relative y coordinate * @tool_major: tool area, major axis * @tool_minor: tool area, minor axis * @orientation: 16384 when point, else 15 bit angle @@ -944,10 +944,14 @@ static inline int le16_to_int(__le16 x) static void applespi_debug_update_dimensions(struct applespi_data *applespi, const struct tp_finger *f) { - applespi->tp_dim_min_x = min_t(int, applespi->tp_dim_min_x, f->abs_x); - applespi->tp_dim_max_x = max_t(int, applespi->tp_dim_max_x, f->abs_x); - applespi->tp_dim_min_y = min_t(int, applespi->tp_dim_min_y, f->abs_y); - applespi->tp_dim_max_y = max_t(int, applespi->tp_dim_max_y, f->abs_y); + applespi->tp_dim_min_x = min(applespi->tp_dim_min_x, + le16_to_int(f->abs_x)); + applespi->tp_dim_max_x = max(applespi->tp_dim_max_x, + le16_to_int(f->abs_x)); + applespi->tp_dim_min_y = min(applespi->tp_dim_min_y, + le16_to_int(f->abs_y)); + applespi->tp_dim_max_y = max(applespi->tp_dim_max_y, + le16_to_int(f->abs_y)); } static int applespi_tp_dim_open(struct inode *inode, struct file *file) @@ -1490,8 +1494,7 @@ static void applespi_got_data(struct applespi_data *applespi) size_t tp_len; tp = &message->touchpad; - tp_len = sizeof(*tp) + - tp->number_of_fingers * sizeof(tp->fingers[0]); + tp_len = struct_size(tp, fingers, tp->number_of_fingers); if (le16_to_cpu(message->length) + 2 != tp_len) { dev_warn_ratelimited(&applespi->spi->dev, @@ -1611,8 +1614,8 @@ static void applespi_save_bl_level(struct applespi_data *applespi, efi_attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; - sts = efivar_entry_set_safe(EFI_BL_LEVEL_NAME, efi_guid, efi_attr, true, - efi_data_len, &efi_data); + sts = efivar_entry_set_safe((efi_char16_t *)EFI_BL_LEVEL_NAME, efi_guid, + efi_attr, true, efi_data_len, &efi_data); if (sts) dev_warn(&applespi->spi->dev, "Error saving backlight level to EFI vars: %d\n", sts); @@ -1953,7 +1956,7 @@ static const struct acpi_device_id applespi_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, applespi_acpi_match); -const struct dev_pm_ops applespi_pm_ops = { +static const struct dev_pm_ops applespi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(applespi_suspend, applespi_resume) .poweroff_late = applespi_poweroff_late, }; diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 2d8434b7b623..04fe43440a3c 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1827,6 +1827,31 @@ static int elantech_create_smbus(struct psmouse *psmouse, leave_breadcrumbs); } +static bool elantech_use_host_notify(struct psmouse *psmouse, + struct elantech_device_info *info) +{ + if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version)) + return true; + + switch (info->bus) { + case ETP_BUS_PS2_ONLY: + /* expected case */ + break; + case ETP_BUS_SMB_HST_NTFY_ONLY: + case ETP_BUS_PS2_SMB_HST_NTFY: + /* SMbus implementation is stable since 2018 */ + if (dmi_get_bios_year() >= 2018) + return true; + /* fall through */ + default: + psmouse_dbg(psmouse, + "Ignoring SMBus bus provider %d\n", info->bus); + break; + } + + return false; +} + /** * elantech_setup_smbus - called once the PS/2 devices are enumerated * and decides to instantiate a SMBus InterTouch device. @@ -1846,7 +1871,7 @@ static int elantech_setup_smbus(struct psmouse *psmouse, * i2c_blacklist_pnp_ids. * Old ICs are up to the user to decide. */ - if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) || + if (!elantech_use_host_notify(psmouse, info) || psmouse_matches_pnp_id(psmouse, i2c_blacklist_pnp_ids)) return -ENXIO; } @@ -1866,34 +1891,6 @@ static int elantech_setup_smbus(struct psmouse *psmouse, return 0; } -static bool elantech_use_host_notify(struct psmouse *psmouse, - struct elantech_device_info *info) -{ - if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version)) - return true; - - switch (info->bus) { - case ETP_BUS_PS2_ONLY: - /* expected case */ - break; - case ETP_BUS_SMB_ALERT_ONLY: - /* fall-through */ - case ETP_BUS_PS2_SMB_ALERT: - psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n"); - break; - case ETP_BUS_SMB_HST_NTFY_ONLY: - /* fall-through */ - case ETP_BUS_PS2_SMB_HST_NTFY: - return true; - default: - psmouse_dbg(psmouse, - "Ignoring SMBus bus provider %d.\n", - info->bus); - } - - return false; -} - int elantech_init_smbus(struct psmouse *psmouse) { struct elantech_device_info info; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index b1956ed4c0dd..46bbe99d6511 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -182,6 +182,7 @@ static const char * const smbus_pnp_ids[] = { "LEN2055", /* E580 */ "SYN3052", /* HP EliteBook 840 G4 */ "SYN3221", /* HP 15-ay000 */ + "SYN323d", /* HP Spectre X360 13-w013dx */ NULL }; diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index 88ae7c2ac3c8..e486a8a74c40 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -237,40 +237,17 @@ static void hv_kbd_handle_received_packet(struct hv_device *hv_dev, static void hv_kbd_on_channel_callback(void *context) { + struct vmpacket_descriptor *desc; struct hv_device *hv_dev = context; - void *buffer; - int bufferlen = 0x100; /* Start with sensible size */ u32 bytes_recvd; u64 req_id; - int error; - buffer = kmalloc(bufferlen, GFP_ATOMIC); - if (!buffer) - return; - - while (1) { - error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen, - &bytes_recvd, &req_id); - switch (error) { - case 0: - if (bytes_recvd == 0) { - kfree(buffer); - return; - } - - hv_kbd_handle_received_packet(hv_dev, buffer, - bytes_recvd, req_id); - break; + foreach_vmbus_pkt(desc, hv_dev->channel) { + bytes_recvd = desc->len8 * 8; + req_id = desc->trans_id; - case -ENOBUFS: - kfree(buffer); - /* Handle large packet */ - bufferlen = bytes_recvd; - buffer = kmalloc(bytes_recvd, GFP_ATOMIC); - if (!buffer) - return; - break; - } + hv_kbd_handle_received_packet(hv_dev, desc, bytes_recvd, + req_id); } } diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c index 04b85571f41e..aa577898e952 100644 --- a/drivers/input/tablet/kbtab.c +++ b/drivers/input/tablet/kbtab.c @@ -117,6 +117,10 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i if (intf->cur_altsetting->desc.bNumEndpoints < 1) return -ENODEV; + endpoint = &intf->cur_altsetting->endpoint[0].desc; + if (!usb_endpoint_is_int_in(endpoint)) + return -ENODEV; + kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL); input_dev = input_allocate_device(); if (!kbtab || !input_dev) @@ -155,8 +159,6 @@ static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *i input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0); - endpoint = &intf->cur_altsetting->endpoint[0].desc; - usb_fill_int_urb(kbtab->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), kbtab->data, 8, diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index a2cec6cacf57..16d70201de4a 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -1659,6 +1659,8 @@ static int usbtouch_probe(struct usb_interface *intf, if (!usbtouch || !input_dev) goto out_free; + mutex_init(&usbtouch->pm_mutex); + type = &usbtouch_dev_info[id->driver_info]; usbtouch->type = type; if (!type->process_pkt) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index e15cdcd8cb3c..a4ddeade8ac4 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -182,6 +182,7 @@ config INTEL_IOMMU select IOMMU_IOVA select NEED_DMA_MAP_STATE select DMAR_TABLE + select SWIOTLB help DMA remapping (DMAR) devices support enables independent address translations for Direct Memory Access (DMA) from devices. diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index f13f36ae1af6..4f405f926e73 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -10,13 +10,14 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o obj-$(CONFIG_IOMMU_IOVA) += iova.o obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o -obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o +obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o amd_iommu_quirks.o obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd_iommu_debugfs.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o -obj-$(CONFIG_ARM_SMMU) += arm-smmu.o +obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-impl.o obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o obj-$(CONFIG_DMAR_TABLE) += dmar.o obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o intel-pasid.o +obj-$(CONFIG_INTEL_IOMMU) += intel-trace.o obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += intel-iommu-debugfs.o obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index b607a92791d3..1ed3b98324ba 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -436,7 +436,7 @@ static int iommu_init_device(struct device *dev) * invalid address), we ignore the capability for the device so * it'll be forced to go into translation mode. */ - if ((iommu_pass_through || !amd_iommu_force_isolation) && + if ((iommu_default_passthrough() || !amd_iommu_force_isolation) && dev_is_pci(dev) && pci_iommuv2_capable(to_pci_dev(dev))) { struct amd_iommu *iommu; @@ -1143,6 +1143,17 @@ static void amd_iommu_flush_tlb_all(struct amd_iommu *iommu) iommu_completion_wait(iommu); } +static void amd_iommu_flush_tlb_domid(struct amd_iommu *iommu, u32 dom_id) +{ + struct iommu_cmd cmd; + + build_inv_iommu_pages(&cmd, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, + dom_id, 1); + iommu_queue_command(iommu, &cmd); + + iommu_completion_wait(iommu); +} + static void amd_iommu_flush_all(struct amd_iommu *iommu) { struct iommu_cmd cmd; @@ -1424,18 +1435,21 @@ static void free_pagetable(struct protection_domain *domain) * another level increases the size of the address space by 9 bits to a size up * to 64 bits. */ -static bool increase_address_space(struct protection_domain *domain, +static void increase_address_space(struct protection_domain *domain, gfp_t gfp) { + unsigned long flags; u64 *pte; - if (domain->mode == PAGE_MODE_6_LEVEL) + spin_lock_irqsave(&domain->lock, flags); + + if (WARN_ON_ONCE(domain->mode == PAGE_MODE_6_LEVEL)) /* address space already 64 bit large */ - return false; + goto out; pte = (void *)get_zeroed_page(gfp); if (!pte) - return false; + goto out; *pte = PM_LEVEL_PDE(domain->mode, iommu_virt_to_phys(domain->pt_root)); @@ -1443,7 +1457,10 @@ static bool increase_address_space(struct protection_domain *domain, domain->mode += 1; domain->updated = true; - return true; +out: + spin_unlock_irqrestore(&domain->lock, flags); + + return; } static u64 *alloc_pte(struct protection_domain *domain, @@ -1873,6 +1890,7 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, { u64 pte_root = 0; u64 flags = 0; + u32 old_domid; if (domain->mode != PAGE_MODE_NONE) pte_root = iommu_virt_to_phys(domain->pt_root); @@ -1922,8 +1940,20 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, flags &= ~DEV_DOMID_MASK; flags |= domain->id; + old_domid = amd_iommu_dev_table[devid].data[1] & DEV_DOMID_MASK; amd_iommu_dev_table[devid].data[1] = flags; amd_iommu_dev_table[devid].data[0] = pte_root; + + /* + * A kdump kernel might be replacing a domain ID that was copied from + * the previous kernel--if so, it needs to flush the translation cache + * entries for the old domain ID that is being overwritten + */ + if (old_domid) { + struct amd_iommu *iommu = amd_iommu_rlookup_table[devid]; + + amd_iommu_flush_tlb_domid(iommu, old_domid); + } } static void clear_dte_entry(u16 devid) @@ -2226,7 +2256,7 @@ static int amd_iommu_add_device(struct device *dev) BUG_ON(!dev_data); - if (iommu_pass_through || dev_data->iommu_v2) + if (dev_data->iommu_v2) iommu_request_dm_for_dev(dev); /* Domains are initialized for this device - have a look what we ended up with */ @@ -2547,7 +2577,9 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, bus_addr = address + s->dma_address + (j << PAGE_SHIFT); phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT); - ret = iommu_map_page(domain, bus_addr, phys_addr, PAGE_SIZE, prot, GFP_ATOMIC); + ret = iommu_map_page(domain, bus_addr, phys_addr, + PAGE_SIZE, prot, + GFP_ATOMIC | __GFP_NOWARN); if (ret) goto out_unmap; @@ -2805,7 +2837,7 @@ int __init amd_iommu_init_api(void) int __init amd_iommu_init_dma_ops(void) { - swiotlb = (iommu_pass_through || sme_me_mask) ? 1 : 0; + swiotlb = (iommu_default_passthrough() || sme_me_mask) ? 1 : 0; iommu_detected = 1; if (amd_iommu_unmap_flush) @@ -3055,7 +3087,8 @@ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova, } static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, - size_t page_size) + size_t page_size, + struct iommu_iotlb_gather *gather) { struct protection_domain *domain = to_pdomain(dom); size_t unmap_size; @@ -3196,9 +3229,10 @@ static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain) domain_flush_complete(dom); } -static void amd_iommu_iotlb_range_add(struct iommu_domain *domain, - unsigned long iova, size_t size) +static void amd_iommu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) { + amd_iommu_flush_iotlb_all(domain); } const struct iommu_ops amd_iommu_ops = { @@ -3219,8 +3253,7 @@ const struct iommu_ops amd_iommu_ops = { .is_attach_deferred = amd_iommu_is_attach_deferred, .pgsize_bitmap = AMD_IOMMU_PGSIZES, .flush_iotlb_all = amd_iommu_flush_iotlb_all, - .iotlb_range_add = amd_iommu_iotlb_range_add, - .iotlb_sync = amd_iommu_flush_iotlb_all, + .iotlb_sync = amd_iommu_iotlb_sync, }; /***************************************************************************** @@ -4313,13 +4346,62 @@ static const struct irq_domain_ops amd_ir_domain_ops = { .deactivate = irq_remapping_deactivate, }; +int amd_iommu_activate_guest_mode(void *data) +{ + struct amd_ir_data *ir_data = (struct amd_ir_data *)data; + struct irte_ga *entry = (struct irte_ga *) ir_data->entry; + + if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) || + !entry || entry->lo.fields_vapic.guest_mode) + return 0; + + entry->lo.val = 0; + entry->hi.val = 0; + + entry->lo.fields_vapic.guest_mode = 1; + entry->lo.fields_vapic.ga_log_intr = 1; + entry->hi.fields.ga_root_ptr = ir_data->ga_root_ptr; + entry->hi.fields.vector = ir_data->ga_vector; + entry->lo.fields_vapic.ga_tag = ir_data->ga_tag; + + return modify_irte_ga(ir_data->irq_2_irte.devid, + ir_data->irq_2_irte.index, entry, NULL); +} +EXPORT_SYMBOL(amd_iommu_activate_guest_mode); + +int amd_iommu_deactivate_guest_mode(void *data) +{ + struct amd_ir_data *ir_data = (struct amd_ir_data *)data; + struct irte_ga *entry = (struct irte_ga *) ir_data->entry; + struct irq_cfg *cfg = ir_data->cfg; + + if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) || + !entry || !entry->lo.fields_vapic.guest_mode) + return 0; + + entry->lo.val = 0; + entry->hi.val = 0; + + entry->lo.fields_remap.dm = apic->irq_dest_mode; + entry->lo.fields_remap.int_type = apic->irq_delivery_mode; + entry->hi.fields.vector = cfg->vector; + entry->lo.fields_remap.destination = + APICID_TO_IRTE_DEST_LO(cfg->dest_apicid); + entry->hi.fields.destination = + APICID_TO_IRTE_DEST_HI(cfg->dest_apicid); + + return modify_irte_ga(ir_data->irq_2_irte.devid, + ir_data->irq_2_irte.index, entry, NULL); +} +EXPORT_SYMBOL(amd_iommu_deactivate_guest_mode); + static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info) { + int ret; struct amd_iommu *iommu; struct amd_iommu_pi_data *pi_data = vcpu_info; struct vcpu_data *vcpu_pi_info = pi_data->vcpu_data; struct amd_ir_data *ir_data = data->chip_data; - struct irte_ga *irte = (struct irte_ga *) ir_data->entry; struct irq_2_irte *irte_info = &ir_data->irq_2_irte; struct iommu_dev_data *dev_data = search_dev_data(irte_info->devid); @@ -4330,6 +4412,7 @@ static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info) if (!dev_data || !dev_data->use_vapic) return 0; + ir_data->cfg = irqd_cfg(data); pi_data->ir_data = ir_data; /* Note: @@ -4348,37 +4431,24 @@ static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info) pi_data->prev_ga_tag = ir_data->cached_ga_tag; if (pi_data->is_guest_mode) { - /* Setting */ - irte->hi.fields.ga_root_ptr = (pi_data->base >> 12); - irte->hi.fields.vector = vcpu_pi_info->vector; - irte->lo.fields_vapic.ga_log_intr = 1; - irte->lo.fields_vapic.guest_mode = 1; - irte->lo.fields_vapic.ga_tag = pi_data->ga_tag; - - ir_data->cached_ga_tag = pi_data->ga_tag; + ir_data->ga_root_ptr = (pi_data->base >> 12); + ir_data->ga_vector = vcpu_pi_info->vector; + ir_data->ga_tag = pi_data->ga_tag; + ret = amd_iommu_activate_guest_mode(ir_data); + if (!ret) + ir_data->cached_ga_tag = pi_data->ga_tag; } else { - /* Un-Setting */ - struct irq_cfg *cfg = irqd_cfg(data); - - irte->hi.val = 0; - irte->lo.val = 0; - irte->hi.fields.vector = cfg->vector; - irte->lo.fields_remap.guest_mode = 0; - irte->lo.fields_remap.destination = - APICID_TO_IRTE_DEST_LO(cfg->dest_apicid); - irte->hi.fields.destination = - APICID_TO_IRTE_DEST_HI(cfg->dest_apicid); - irte->lo.fields_remap.int_type = apic->irq_delivery_mode; - irte->lo.fields_remap.dm = apic->irq_dest_mode; + ret = amd_iommu_deactivate_guest_mode(ir_data); /* * This communicates the ga_tag back to the caller * so that it can do all the necessary clean up. */ - ir_data->cached_ga_tag = 0; + if (!ret) + ir_data->cached_ga_tag = 0; } - return modify_irte_ga(irte_info->devid, irte_info->index, irte, ir_data); + return ret; } diff --git a/drivers/iommu/amd_iommu.h b/drivers/iommu/amd_iommu.h new file mode 100644 index 000000000000..12d540d9b59b --- /dev/null +++ b/drivers/iommu/amd_iommu.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef AMD_IOMMU_H +#define AMD_IOMMU_H + +int __init add_special_device(u8 type, u8 id, u16 *devid, bool cmd_line); + +#ifdef CONFIG_DMI +void amd_iommu_apply_ivrs_quirks(void); +#else +static void amd_iommu_apply_ivrs_quirks(void) { } +#endif + +#endif diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 4413aa67000e..568c52317757 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -32,6 +32,7 @@ #include <asm/irq_remapping.h> #include <linux/crash_dump.h> +#include "amd_iommu.h" #include "amd_iommu_proto.h" #include "amd_iommu_types.h" #include "irq_remapping.h" @@ -1002,7 +1003,7 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu, set_iommu_for_device(iommu, devid); } -static int __init add_special_device(u8 type, u8 id, u16 *devid, bool cmd_line) +int __init add_special_device(u8 type, u8 id, u16 *devid, bool cmd_line) { struct devid_map *entry; struct list_head *list; @@ -1153,6 +1154,8 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu, if (ret) return ret; + amd_iommu_apply_ivrs_quirks(); + /* * First save the recommended feature enable bits from ACPI */ diff --git a/drivers/iommu/amd_iommu_quirks.c b/drivers/iommu/amd_iommu_quirks.c new file mode 100644 index 000000000000..c235f79b7a20 --- /dev/null +++ b/drivers/iommu/amd_iommu_quirks.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Quirks for AMD IOMMU + * + * Copyright (C) 2019 Kai-Heng Feng <kai.heng.feng@canonical.com> + */ + +#ifdef CONFIG_DMI +#include <linux/dmi.h> + +#include "amd_iommu.h" + +#define IVHD_SPECIAL_IOAPIC 1 + +struct ivrs_quirk_entry { + u8 id; + u16 devid; +}; + +enum { + DELL_INSPIRON_7375 = 0, + DELL_LATITUDE_5495, + LENOVO_IDEAPAD_330S_15ARR, +}; + +static const struct ivrs_quirk_entry ivrs_ioapic_quirks[][3] __initconst = { + /* ivrs_ioapic[4]=00:14.0 ivrs_ioapic[5]=00:00.2 */ + [DELL_INSPIRON_7375] = { + { .id = 4, .devid = 0xa0 }, + { .id = 5, .devid = 0x2 }, + {} + }, + /* ivrs_ioapic[4]=00:14.0 */ + [DELL_LATITUDE_5495] = { + { .id = 4, .devid = 0xa0 }, + {} + }, + /* ivrs_ioapic[32]=00:14.0 */ + [LENOVO_IDEAPAD_330S_15ARR] = { + { .id = 32, .devid = 0xa0 }, + {} + }, + {} +}; + +static int __init ivrs_ioapic_quirk_cb(const struct dmi_system_id *d) +{ + const struct ivrs_quirk_entry *i; + + for (i = d->driver_data; i->id != 0 && i->devid != 0; i++) + add_special_device(IVHD_SPECIAL_IOAPIC, i->id, (u16 *)&i->devid, 0); + + return 0; +} + +static const struct dmi_system_id ivrs_quirks[] __initconst = { + { + .callback = ivrs_ioapic_quirk_cb, + .ident = "Dell Inspiron 7375", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7375"), + }, + .driver_data = (void *)&ivrs_ioapic_quirks[DELL_INSPIRON_7375], + }, + { + .callback = ivrs_ioapic_quirk_cb, + .ident = "Dell Latitude 5495", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 5495"), + }, + .driver_data = (void *)&ivrs_ioapic_quirks[DELL_LATITUDE_5495], + }, + { + .callback = ivrs_ioapic_quirk_cb, + .ident = "Lenovo ideapad 330S-15ARR", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "81FB"), + }, + .driver_data = (void *)&ivrs_ioapic_quirks[LENOVO_IDEAPAD_330S_15ARR], + }, + {} +}; + +void __init amd_iommu_apply_ivrs_quirks(void) +{ + dmi_check_system(ivrs_quirks); +} +#endif diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index 64edd5a9694c..9ac229e92b07 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -873,6 +873,15 @@ struct amd_ir_data { struct msi_msg msi_entry; void *entry; /* Pointer to union irte or struct irte_ga */ void *ref; /* Pointer to the actual irte */ + + /** + * Store information for activate/de-activate + * Guest virtual APIC mode during runtime. + */ + struct irq_cfg *cfg; + int ga_vector; + int ga_root_ptr; + int ga_tag; }; struct amd_irte_ops { diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c new file mode 100644 index 000000000000..5c87a38620c4 --- /dev/null +++ b/drivers/iommu/arm-smmu-impl.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Miscellaneous Arm SMMU implementation and integration quirks +// Copyright (C) 2019 Arm Limited + +#define pr_fmt(fmt) "arm-smmu: " fmt + +#include <linux/bitfield.h> +#include <linux/of.h> + +#include "arm-smmu.h" + + +static int arm_smmu_gr0_ns(int offset) +{ + switch(offset) { + case ARM_SMMU_GR0_sCR0: + case ARM_SMMU_GR0_sACR: + case ARM_SMMU_GR0_sGFSR: + case ARM_SMMU_GR0_sGFSYNR0: + case ARM_SMMU_GR0_sGFSYNR1: + case ARM_SMMU_GR0_sGFSYNR2: + return offset + 0x400; + default: + return offset; + } +} + +static u32 arm_smmu_read_ns(struct arm_smmu_device *smmu, int page, + int offset) +{ + if (page == ARM_SMMU_GR0) + offset = arm_smmu_gr0_ns(offset); + return readl_relaxed(arm_smmu_page(smmu, page) + offset); +} + +static void arm_smmu_write_ns(struct arm_smmu_device *smmu, int page, + int offset, u32 val) +{ + if (page == ARM_SMMU_GR0) + offset = arm_smmu_gr0_ns(offset); + writel_relaxed(val, arm_smmu_page(smmu, page) + offset); +} + +/* Since we don't care for sGFAR, we can do without 64-bit accessors */ +static const struct arm_smmu_impl calxeda_impl = { + .read_reg = arm_smmu_read_ns, + .write_reg = arm_smmu_write_ns, +}; + + +struct cavium_smmu { + struct arm_smmu_device smmu; + u32 id_base; +}; + +static int cavium_cfg_probe(struct arm_smmu_device *smmu) +{ + static atomic_t context_count = ATOMIC_INIT(0); + struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu); + /* + * Cavium CN88xx erratum #27704. + * Ensure ASID and VMID allocation is unique across all SMMUs in + * the system. + */ + cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count); + dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); + + return 0; +} + +static int cavium_init_context(struct arm_smmu_domain *smmu_domain) +{ + struct cavium_smmu *cs = container_of(smmu_domain->smmu, + struct cavium_smmu, smmu); + + if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) + smmu_domain->cfg.vmid += cs->id_base; + else + smmu_domain->cfg.asid += cs->id_base; + + return 0; +} + +static const struct arm_smmu_impl cavium_impl = { + .cfg_probe = cavium_cfg_probe, + .init_context = cavium_init_context, +}; + +static struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu) +{ + struct cavium_smmu *cs; + + cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL); + if (!cs) + return ERR_PTR(-ENOMEM); + + cs->smmu = *smmu; + cs->smmu.impl = &cavium_impl; + + devm_kfree(smmu->dev, smmu); + + return &cs->smmu; +} + + +#define ARM_MMU500_ACTLR_CPRE (1 << 1) + +#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) +#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) +#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) + +static int arm_mmu500_reset(struct arm_smmu_device *smmu) +{ + u32 reg, major; + int i; + /* + * On MMU-500 r2p0 onwards we need to clear ACR.CACHE_LOCK before + * writes to the context bank ACTLRs will stick. And we just hope that + * Secure has also cleared SACR.CACHE_LOCK for this to take effect... + */ + reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7); + major = FIELD_GET(ID7_MAJOR, reg); + reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR); + if (major >= 2) + reg &= ~ARM_MMU500_ACR_CACHE_LOCK; + /* + * Allow unmatched Stream IDs to allocate bypass + * TLB entries for reduced latency. + */ + reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN; + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg); + + /* + * Disable MMU-500's not-particularly-beneficial next-page + * prefetcher for the sake of errata #841119 and #826419. + */ + for (i = 0; i < smmu->num_context_banks; ++i) { + reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); + reg &= ~ARM_MMU500_ACTLR_CPRE; + arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg); + } + + return 0; +} + +static const struct arm_smmu_impl arm_mmu500_impl = { + .reset = arm_mmu500_reset, +}; + + +struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) +{ + /* + * We will inevitably have to combine model-specific implementation + * quirks with platform-specific integration quirks, but everything + * we currently support happens to work out as straightforward + * mutually-exclusive assignments. + */ + switch (smmu->model) { + case ARM_MMU500: + smmu->impl = &arm_mmu500_impl; + break; + case CAVIUM_SMMUV2: + return cavium_smmu_impl_init(smmu); + default: + break; + } + + if (of_property_read_bool(smmu->dev->of_node, + "calxeda,smmu-secure-config-access")) + smmu->impl = &calxeda_impl; + + return smmu; +} diff --git a/drivers/iommu/arm-smmu-regs.h b/drivers/iommu/arm-smmu-regs.h deleted file mode 100644 index 1c278f7ae888..000000000000 --- a/drivers/iommu/arm-smmu-regs.h +++ /dev/null @@ -1,210 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * IOMMU API for ARM architected SMMU implementations. - * - * Copyright (C) 2013 ARM Limited - * - * Author: Will Deacon <will.deacon@arm.com> - */ - -#ifndef _ARM_SMMU_REGS_H -#define _ARM_SMMU_REGS_H - -/* Configuration registers */ -#define ARM_SMMU_GR0_sCR0 0x0 -#define sCR0_CLIENTPD (1 << 0) -#define sCR0_GFRE (1 << 1) -#define sCR0_GFIE (1 << 2) -#define sCR0_EXIDENABLE (1 << 3) -#define sCR0_GCFGFRE (1 << 4) -#define sCR0_GCFGFIE (1 << 5) -#define sCR0_USFCFG (1 << 10) -#define sCR0_VMIDPNE (1 << 11) -#define sCR0_PTM (1 << 12) -#define sCR0_FB (1 << 13) -#define sCR0_VMID16EN (1 << 31) -#define sCR0_BSU_SHIFT 14 -#define sCR0_BSU_MASK 0x3 - -/* Auxiliary Configuration register */ -#define ARM_SMMU_GR0_sACR 0x10 - -/* Identification registers */ -#define ARM_SMMU_GR0_ID0 0x20 -#define ARM_SMMU_GR0_ID1 0x24 -#define ARM_SMMU_GR0_ID2 0x28 -#define ARM_SMMU_GR0_ID3 0x2c -#define ARM_SMMU_GR0_ID4 0x30 -#define ARM_SMMU_GR0_ID5 0x34 -#define ARM_SMMU_GR0_ID6 0x38 -#define ARM_SMMU_GR0_ID7 0x3c -#define ARM_SMMU_GR0_sGFSR 0x48 -#define ARM_SMMU_GR0_sGFSYNR0 0x50 -#define ARM_SMMU_GR0_sGFSYNR1 0x54 -#define ARM_SMMU_GR0_sGFSYNR2 0x58 - -#define ID0_S1TS (1 << 30) -#define ID0_S2TS (1 << 29) -#define ID0_NTS (1 << 28) -#define ID0_SMS (1 << 27) -#define ID0_ATOSNS (1 << 26) -#define ID0_PTFS_NO_AARCH32 (1 << 25) -#define ID0_PTFS_NO_AARCH32S (1 << 24) -#define ID0_CTTW (1 << 14) -#define ID0_NUMIRPT_SHIFT 16 -#define ID0_NUMIRPT_MASK 0xff -#define ID0_NUMSIDB_SHIFT 9 -#define ID0_NUMSIDB_MASK 0xf -#define ID0_EXIDS (1 << 8) -#define ID0_NUMSMRG_SHIFT 0 -#define ID0_NUMSMRG_MASK 0xff - -#define ID1_PAGESIZE (1 << 31) -#define ID1_NUMPAGENDXB_SHIFT 28 -#define ID1_NUMPAGENDXB_MASK 7 -#define ID1_NUMS2CB_SHIFT 16 -#define ID1_NUMS2CB_MASK 0xff -#define ID1_NUMCB_SHIFT 0 -#define ID1_NUMCB_MASK 0xff - -#define ID2_OAS_SHIFT 4 -#define ID2_OAS_MASK 0xf -#define ID2_IAS_SHIFT 0 -#define ID2_IAS_MASK 0xf -#define ID2_UBS_SHIFT 8 -#define ID2_UBS_MASK 0xf -#define ID2_PTFS_4K (1 << 12) -#define ID2_PTFS_16K (1 << 13) -#define ID2_PTFS_64K (1 << 14) -#define ID2_VMID16 (1 << 15) - -#define ID7_MAJOR_SHIFT 4 -#define ID7_MAJOR_MASK 0xf - -/* Global TLB invalidation */ -#define ARM_SMMU_GR0_TLBIVMID 0x64 -#define ARM_SMMU_GR0_TLBIALLNSNH 0x68 -#define ARM_SMMU_GR0_TLBIALLH 0x6c -#define ARM_SMMU_GR0_sTLBGSYNC 0x70 -#define ARM_SMMU_GR0_sTLBGSTATUS 0x74 -#define sTLBGSTATUS_GSACTIVE (1 << 0) - -/* Stream mapping registers */ -#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2)) -#define SMR_VALID (1 << 31) -#define SMR_MASK_SHIFT 16 -#define SMR_ID_SHIFT 0 - -#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2)) -#define S2CR_CBNDX_SHIFT 0 -#define S2CR_CBNDX_MASK 0xff -#define S2CR_EXIDVALID (1 << 10) -#define S2CR_TYPE_SHIFT 16 -#define S2CR_TYPE_MASK 0x3 -enum arm_smmu_s2cr_type { - S2CR_TYPE_TRANS, - S2CR_TYPE_BYPASS, - S2CR_TYPE_FAULT, -}; - -#define S2CR_PRIVCFG_SHIFT 24 -#define S2CR_PRIVCFG_MASK 0x3 -enum arm_smmu_s2cr_privcfg { - S2CR_PRIVCFG_DEFAULT, - S2CR_PRIVCFG_DIPAN, - S2CR_PRIVCFG_UNPRIV, - S2CR_PRIVCFG_PRIV, -}; - -/* Context bank attribute registers */ -#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2)) -#define CBAR_VMID_SHIFT 0 -#define CBAR_VMID_MASK 0xff -#define CBAR_S1_BPSHCFG_SHIFT 8 -#define CBAR_S1_BPSHCFG_MASK 3 -#define CBAR_S1_BPSHCFG_NSH 3 -#define CBAR_S1_MEMATTR_SHIFT 12 -#define CBAR_S1_MEMATTR_MASK 0xf -#define CBAR_S1_MEMATTR_WB 0xf -#define CBAR_TYPE_SHIFT 16 -#define CBAR_TYPE_MASK 0x3 -#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT) -#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT) -#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT) -#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT) -#define CBAR_IRPTNDX_SHIFT 24 -#define CBAR_IRPTNDX_MASK 0xff - -#define ARM_SMMU_GR1_CBFRSYNRA(n) (0x400 + ((n) << 2)) - -#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2)) -#define CBA2R_RW64_32BIT (0 << 0) -#define CBA2R_RW64_64BIT (1 << 0) -#define CBA2R_VMID_SHIFT 16 -#define CBA2R_VMID_MASK 0xffff - -#define ARM_SMMU_CB_SCTLR 0x0 -#define ARM_SMMU_CB_ACTLR 0x4 -#define ARM_SMMU_CB_RESUME 0x8 -#define ARM_SMMU_CB_TTBCR2 0x10 -#define ARM_SMMU_CB_TTBR0 0x20 -#define ARM_SMMU_CB_TTBR1 0x28 -#define ARM_SMMU_CB_TTBCR 0x30 -#define ARM_SMMU_CB_CONTEXTIDR 0x34 -#define ARM_SMMU_CB_S1_MAIR0 0x38 -#define ARM_SMMU_CB_S1_MAIR1 0x3c -#define ARM_SMMU_CB_PAR 0x50 -#define ARM_SMMU_CB_FSR 0x58 -#define ARM_SMMU_CB_FAR 0x60 -#define ARM_SMMU_CB_FSYNR0 0x68 -#define ARM_SMMU_CB_S1_TLBIVA 0x600 -#define ARM_SMMU_CB_S1_TLBIASID 0x610 -#define ARM_SMMU_CB_S1_TLBIVAL 0x620 -#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630 -#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638 -#define ARM_SMMU_CB_TLBSYNC 0x7f0 -#define ARM_SMMU_CB_TLBSTATUS 0x7f4 -#define ARM_SMMU_CB_ATS1PR 0x800 -#define ARM_SMMU_CB_ATSR 0x8f0 - -#define SCTLR_S1_ASIDPNE (1 << 12) -#define SCTLR_CFCFG (1 << 7) -#define SCTLR_CFIE (1 << 6) -#define SCTLR_CFRE (1 << 5) -#define SCTLR_E (1 << 4) -#define SCTLR_AFE (1 << 2) -#define SCTLR_TRE (1 << 1) -#define SCTLR_M (1 << 0) - -#define CB_PAR_F (1 << 0) - -#define ATSR_ACTIVE (1 << 0) - -#define RESUME_RETRY (0 << 0) -#define RESUME_TERMINATE (1 << 0) - -#define TTBCR2_SEP_SHIFT 15 -#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT) -#define TTBCR2_AS (1 << 4) - -#define TTBRn_ASID_SHIFT 48 - -#define FSR_MULTI (1 << 31) -#define FSR_SS (1 << 30) -#define FSR_UUT (1 << 8) -#define FSR_ASF (1 << 7) -#define FSR_TLBLKF (1 << 6) -#define FSR_TLBMCF (1 << 5) -#define FSR_EF (1 << 4) -#define FSR_PF (1 << 3) -#define FSR_AFF (1 << 2) -#define FSR_TF (1 << 1) - -#define FSR_IGN (FSR_AFF | FSR_ASF | \ - FSR_TLBMCF | FSR_TLBLKF) -#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \ - FSR_EF | FSR_PF | FSR_TF | FSR_IGN) - -#define FSYNR0_WNR (1 << 4) - -#endif /* _ARM_SMMU_REGS_H */ diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index a9a9fabd3968..4aa414843557 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -181,12 +181,13 @@ #define ARM_SMMU_MEMATTR_DEVICE_nGnRE 0x1 #define ARM_SMMU_MEMATTR_OIWB 0xf -#define Q_IDX(q, p) ((p) & ((1 << (q)->max_n_shift) - 1)) -#define Q_WRP(q, p) ((p) & (1 << (q)->max_n_shift)) -#define Q_OVERFLOW_FLAG (1 << 31) -#define Q_OVF(q, p) ((p) & Q_OVERFLOW_FLAG) +#define Q_IDX(llq, p) ((p) & ((1 << (llq)->max_n_shift) - 1)) +#define Q_WRP(llq, p) ((p) & (1 << (llq)->max_n_shift)) +#define Q_OVERFLOW_FLAG (1U << 31) +#define Q_OVF(p) ((p) & Q_OVERFLOW_FLAG) #define Q_ENT(q, p) ((q)->base + \ - Q_IDX(q, p) * (q)->ent_dwords) + Q_IDX(&((q)->llq), p) * \ + (q)->ent_dwords) #define Q_BASE_RWA (1UL << 62) #define Q_BASE_ADDR_MASK GENMASK_ULL(51, 5) @@ -306,6 +307,15 @@ #define CMDQ_ERR_CERROR_ABT_IDX 2 #define CMDQ_ERR_CERROR_ATC_INV_IDX 3 +#define CMDQ_PROD_OWNED_FLAG Q_OVERFLOW_FLAG + +/* + * This is used to size the command queue and therefore must be at least + * BITS_PER_LONG so that the valid_map works correctly (it relies on the + * total number of queue entries being a multiple of BITS_PER_LONG). + */ +#define CMDQ_BATCH_ENTRIES BITS_PER_LONG + #define CMDQ_0_OP GENMASK_ULL(7, 0) #define CMDQ_0_SSV (1UL << 11) @@ -368,9 +378,8 @@ #define PRIQ_1_ADDR_MASK GENMASK_ULL(63, 12) /* High-level queue structures */ -#define ARM_SMMU_POLL_TIMEOUT_US 100 -#define ARM_SMMU_CMDQ_SYNC_TIMEOUT_US 1000000 /* 1s! */ -#define ARM_SMMU_CMDQ_SYNC_SPIN_COUNT 10 +#define ARM_SMMU_POLL_TIMEOUT_US 1000000 /* 1s! */ +#define ARM_SMMU_POLL_SPIN_COUNT 10 #define MSI_IOVA_BASE 0x8000000 #define MSI_IOVA_LENGTH 0x100000 @@ -472,13 +481,29 @@ struct arm_smmu_cmdq_ent { #define CMDQ_OP_CMD_SYNC 0x46 struct { - u32 msidata; u64 msiaddr; } sync; }; }; +struct arm_smmu_ll_queue { + union { + u64 val; + struct { + u32 prod; + u32 cons; + }; + struct { + atomic_t prod; + atomic_t cons; + } atomic; + u8 __pad[SMP_CACHE_BYTES]; + } ____cacheline_aligned_in_smp; + u32 max_n_shift; +}; + struct arm_smmu_queue { + struct arm_smmu_ll_queue llq; int irq; /* Wired interrupt */ __le64 *base; @@ -486,17 +511,23 @@ struct arm_smmu_queue { u64 q_base; size_t ent_dwords; - u32 max_n_shift; - u32 prod; - u32 cons; u32 __iomem *prod_reg; u32 __iomem *cons_reg; }; +struct arm_smmu_queue_poll { + ktime_t timeout; + unsigned int delay; + unsigned int spin_cnt; + bool wfe; +}; + struct arm_smmu_cmdq { struct arm_smmu_queue q; - spinlock_t lock; + atomic_long_t *valid_map; + atomic_t owner_prod; + atomic_t lock; }; struct arm_smmu_evtq { @@ -576,8 +607,6 @@ struct arm_smmu_device { int gerr_irq; int combined_irq; - u32 sync_nr; - u8 prev_cmd_opcode; unsigned long ias; /* IPA */ unsigned long oas; /* PA */ @@ -596,12 +625,6 @@ struct arm_smmu_device { struct arm_smmu_strtab_cfg strtab_cfg; - /* Hi16xx adds an extra 32 bits of goodness to its MSI payload */ - union { - u32 sync_count; - u64 padding; - }; - /* IOMMU core code handle */ struct iommu_device iommu; }; @@ -614,7 +637,7 @@ struct arm_smmu_master { struct list_head domain_head; u32 *sids; unsigned int num_sids; - bool ats_enabled :1; + bool ats_enabled; }; /* SMMU private data for an IOMMU domain */ @@ -631,6 +654,7 @@ struct arm_smmu_domain { struct io_pgtable_ops *pgtbl_ops; bool non_strict; + atomic_t nr_ats_masters; enum arm_smmu_domain_stage stage; union { @@ -685,85 +709,97 @@ static void parse_driver_options(struct arm_smmu_device *smmu) } /* Low-level queue manipulation functions */ -static bool queue_full(struct arm_smmu_queue *q) +static bool queue_has_space(struct arm_smmu_ll_queue *q, u32 n) +{ + u32 space, prod, cons; + + prod = Q_IDX(q, q->prod); + cons = Q_IDX(q, q->cons); + + if (Q_WRP(q, q->prod) == Q_WRP(q, q->cons)) + space = (1 << q->max_n_shift) - (prod - cons); + else + space = cons - prod; + + return space >= n; +} + +static bool queue_full(struct arm_smmu_ll_queue *q) { return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) && Q_WRP(q, q->prod) != Q_WRP(q, q->cons); } -static bool queue_empty(struct arm_smmu_queue *q) +static bool queue_empty(struct arm_smmu_ll_queue *q) { return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) && Q_WRP(q, q->prod) == Q_WRP(q, q->cons); } -static void queue_sync_cons(struct arm_smmu_queue *q) +static bool queue_consumed(struct arm_smmu_ll_queue *q, u32 prod) { - q->cons = readl_relaxed(q->cons_reg); + return ((Q_WRP(q, q->cons) == Q_WRP(q, prod)) && + (Q_IDX(q, q->cons) > Q_IDX(q, prod))) || + ((Q_WRP(q, q->cons) != Q_WRP(q, prod)) && + (Q_IDX(q, q->cons) <= Q_IDX(q, prod))); } -static void queue_inc_cons(struct arm_smmu_queue *q) +static void queue_sync_cons_out(struct arm_smmu_queue *q) { - u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1; - - q->cons = Q_OVF(q, q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons); - /* * Ensure that all CPU accesses (reads and writes) to the queue * are complete before we update the cons pointer. */ mb(); - writel_relaxed(q->cons, q->cons_reg); + writel_relaxed(q->llq.cons, q->cons_reg); +} + +static void queue_inc_cons(struct arm_smmu_ll_queue *q) +{ + u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1; + q->cons = Q_OVF(q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons); } -static int queue_sync_prod(struct arm_smmu_queue *q) +static int queue_sync_prod_in(struct arm_smmu_queue *q) { int ret = 0; u32 prod = readl_relaxed(q->prod_reg); - if (Q_OVF(q, prod) != Q_OVF(q, q->prod)) + if (Q_OVF(prod) != Q_OVF(q->llq.prod)) ret = -EOVERFLOW; - q->prod = prod; + q->llq.prod = prod; return ret; } -static void queue_inc_prod(struct arm_smmu_queue *q) +static u32 queue_inc_prod_n(struct arm_smmu_ll_queue *q, int n) { - u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + 1; - - q->prod = Q_OVF(q, q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod); - writel(q->prod, q->prod_reg); + u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + n; + return Q_OVF(q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod); } -/* - * Wait for the SMMU to consume items. If sync is true, wait until the queue - * is empty. Otherwise, wait until there is at least one free slot. - */ -static int queue_poll_cons(struct arm_smmu_queue *q, bool sync, bool wfe) +static void queue_poll_init(struct arm_smmu_device *smmu, + struct arm_smmu_queue_poll *qp) { - ktime_t timeout; - unsigned int delay = 1, spin_cnt = 0; - - /* Wait longer if it's a CMD_SYNC */ - timeout = ktime_add_us(ktime_get(), sync ? - ARM_SMMU_CMDQ_SYNC_TIMEOUT_US : - ARM_SMMU_POLL_TIMEOUT_US); + qp->delay = 1; + qp->spin_cnt = 0; + qp->wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV); + qp->timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US); +} - while (queue_sync_cons(q), (sync ? !queue_empty(q) : queue_full(q))) { - if (ktime_compare(ktime_get(), timeout) > 0) - return -ETIMEDOUT; +static int queue_poll(struct arm_smmu_queue_poll *qp) +{ + if (ktime_compare(ktime_get(), qp->timeout) > 0) + return -ETIMEDOUT; - if (wfe) { - wfe(); - } else if (++spin_cnt < ARM_SMMU_CMDQ_SYNC_SPIN_COUNT) { - cpu_relax(); - continue; - } else { - udelay(delay); - delay *= 2; - spin_cnt = 0; - } + if (qp->wfe) { + wfe(); + } else if (++qp->spin_cnt < ARM_SMMU_POLL_SPIN_COUNT) { + cpu_relax(); + } else { + udelay(qp->delay); + qp->delay *= 2; + qp->spin_cnt = 0; } return 0; @@ -777,16 +813,6 @@ static void queue_write(__le64 *dst, u64 *src, size_t n_dwords) *dst++ = cpu_to_le64(*src++); } -static int queue_insert_raw(struct arm_smmu_queue *q, u64 *ent) -{ - if (queue_full(q)) - return -ENOSPC; - - queue_write(Q_ENT(q, q->prod), ent, q->ent_dwords); - queue_inc_prod(q); - return 0; -} - static void queue_read(__le64 *dst, u64 *src, size_t n_dwords) { int i; @@ -797,11 +823,12 @@ static void queue_read(__le64 *dst, u64 *src, size_t n_dwords) static int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent) { - if (queue_empty(q)) + if (queue_empty(&q->llq)) return -EAGAIN; - queue_read(ent, Q_ENT(q, q->cons), q->ent_dwords); - queue_inc_cons(q); + queue_read(ent, Q_ENT(q, q->llq.cons), q->ent_dwords); + queue_inc_cons(&q->llq); + queue_sync_cons_out(q); return 0; } @@ -868,20 +895,14 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) cmd[1] |= FIELD_PREP(CMDQ_PRI_1_RESP, ent->pri.resp); break; case CMDQ_OP_CMD_SYNC: - if (ent->sync.msiaddr) + if (ent->sync.msiaddr) { cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_IRQ); - else + cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK; + } else { cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_SEV); + } cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSH, ARM_SMMU_SH_ISH); cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIATTR, ARM_SMMU_MEMATTR_OIWB); - /* - * Commands are written little-endian, but we want the SMMU to - * receive MSIData, and thus write it back to memory, in CPU - * byte order, so big-endian needs an extra byteswap here. - */ - cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIDATA, - cpu_to_le32(ent->sync.msidata)); - cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK; break; default: return -ENOENT; @@ -890,6 +911,27 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) return 0; } +static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu, + u32 prod) +{ + struct arm_smmu_queue *q = &smmu->cmdq.q; + struct arm_smmu_cmdq_ent ent = { + .opcode = CMDQ_OP_CMD_SYNC, + }; + + /* + * Beware that Hi16xx adds an extra 32 bits of goodness to its MSI + * payload, so the write will zero the entire command on that platform. + */ + if (smmu->features & ARM_SMMU_FEAT_MSI && + smmu->features & ARM_SMMU_FEAT_COHERENCY) { + ent.sync.msiaddr = q->base_dma + Q_IDX(&q->llq, prod) * + q->ent_dwords * 8; + } + + arm_smmu_cmdq_build_cmd(cmd, &ent); +} + static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu) { static const char *cerror_str[] = { @@ -948,109 +990,456 @@ static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu) queue_write(Q_ENT(q, cons), cmd, q->ent_dwords); } -static void arm_smmu_cmdq_insert_cmd(struct arm_smmu_device *smmu, u64 *cmd) +/* + * Command queue locking. + * This is a form of bastardised rwlock with the following major changes: + * + * - The only LOCK routines are exclusive_trylock() and shared_lock(). + * Neither have barrier semantics, and instead provide only a control + * dependency. + * + * - The UNLOCK routines are supplemented with shared_tryunlock(), which + * fails if the caller appears to be the last lock holder (yes, this is + * racy). All successful UNLOCK routines have RELEASE semantics. + */ +static void arm_smmu_cmdq_shared_lock(struct arm_smmu_cmdq *cmdq) { - struct arm_smmu_queue *q = &smmu->cmdq.q; - bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV); + int val; + + /* + * We can try to avoid the cmpxchg() loop by simply incrementing the + * lock counter. When held in exclusive state, the lock counter is set + * to INT_MIN so these increments won't hurt as the value will remain + * negative. + */ + if (atomic_fetch_inc_relaxed(&cmdq->lock) >= 0) + return; + + do { + val = atomic_cond_read_relaxed(&cmdq->lock, VAL >= 0); + } while (atomic_cmpxchg_relaxed(&cmdq->lock, val, val + 1) != val); +} + +static void arm_smmu_cmdq_shared_unlock(struct arm_smmu_cmdq *cmdq) +{ + (void)atomic_dec_return_release(&cmdq->lock); +} - smmu->prev_cmd_opcode = FIELD_GET(CMDQ_0_OP, cmd[0]); +static bool arm_smmu_cmdq_shared_tryunlock(struct arm_smmu_cmdq *cmdq) +{ + if (atomic_read(&cmdq->lock) == 1) + return false; - while (queue_insert_raw(q, cmd) == -ENOSPC) { - if (queue_poll_cons(q, false, wfe)) - dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); + arm_smmu_cmdq_shared_unlock(cmdq); + return true; +} + +#define arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags) \ +({ \ + bool __ret; \ + local_irq_save(flags); \ + __ret = !atomic_cmpxchg_relaxed(&cmdq->lock, 0, INT_MIN); \ + if (!__ret) \ + local_irq_restore(flags); \ + __ret; \ +}) + +#define arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags) \ +({ \ + atomic_set_release(&cmdq->lock, 0); \ + local_irq_restore(flags); \ +}) + + +/* + * Command queue insertion. + * This is made fiddly by our attempts to achieve some sort of scalability + * since there is one queue shared amongst all of the CPUs in the system. If + * you like mixed-size concurrency, dependency ordering and relaxed atomics, + * then you'll *love* this monstrosity. + * + * The basic idea is to split the queue up into ranges of commands that are + * owned by a given CPU; the owner may not have written all of the commands + * itself, but is responsible for advancing the hardware prod pointer when + * the time comes. The algorithm is roughly: + * + * 1. Allocate some space in the queue. At this point we also discover + * whether the head of the queue is currently owned by another CPU, + * or whether we are the owner. + * + * 2. Write our commands into our allocated slots in the queue. + * + * 3. Mark our slots as valid in arm_smmu_cmdq.valid_map. + * + * 4. If we are an owner: + * a. Wait for the previous owner to finish. + * b. Mark the queue head as unowned, which tells us the range + * that we are responsible for publishing. + * c. Wait for all commands in our owned range to become valid. + * d. Advance the hardware prod pointer. + * e. Tell the next owner we've finished. + * + * 5. If we are inserting a CMD_SYNC (we may or may not have been an + * owner), then we need to stick around until it has completed: + * a. If we have MSIs, the SMMU can write back into the CMD_SYNC + * to clear the first 4 bytes. + * b. Otherwise, we spin waiting for the hardware cons pointer to + * advance past our command. + * + * The devil is in the details, particularly the use of locking for handling + * SYNC completion and freeing up space in the queue before we think that it is + * full. + */ +static void __arm_smmu_cmdq_poll_set_valid_map(struct arm_smmu_cmdq *cmdq, + u32 sprod, u32 eprod, bool set) +{ + u32 swidx, sbidx, ewidx, ebidx; + struct arm_smmu_ll_queue llq = { + .max_n_shift = cmdq->q.llq.max_n_shift, + .prod = sprod, + }; + + ewidx = BIT_WORD(Q_IDX(&llq, eprod)); + ebidx = Q_IDX(&llq, eprod) % BITS_PER_LONG; + + while (llq.prod != eprod) { + unsigned long mask; + atomic_long_t *ptr; + u32 limit = BITS_PER_LONG; + + swidx = BIT_WORD(Q_IDX(&llq, llq.prod)); + sbidx = Q_IDX(&llq, llq.prod) % BITS_PER_LONG; + + ptr = &cmdq->valid_map[swidx]; + + if ((swidx == ewidx) && (sbidx < ebidx)) + limit = ebidx; + + mask = GENMASK(limit - 1, sbidx); + + /* + * The valid bit is the inverse of the wrap bit. This means + * that a zero-initialised queue is invalid and, after marking + * all entries as valid, they become invalid again when we + * wrap. + */ + if (set) { + atomic_long_xor(mask, ptr); + } else { /* Poll */ + unsigned long valid; + + valid = (ULONG_MAX + !!Q_WRP(&llq, llq.prod)) & mask; + atomic_long_cond_read_relaxed(ptr, (VAL & mask) == valid); + } + + llq.prod = queue_inc_prod_n(&llq, limit - sbidx); } } -static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, - struct arm_smmu_cmdq_ent *ent) +/* Mark all entries in the range [sprod, eprod) as valid */ +static void arm_smmu_cmdq_set_valid_map(struct arm_smmu_cmdq *cmdq, + u32 sprod, u32 eprod) +{ + __arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, true); +} + +/* Wait for all entries in the range [sprod, eprod) to become valid */ +static void arm_smmu_cmdq_poll_valid_map(struct arm_smmu_cmdq *cmdq, + u32 sprod, u32 eprod) +{ + __arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, false); +} + +/* Wait for the command queue to become non-full */ +static int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu, + struct arm_smmu_ll_queue *llq) { - u64 cmd[CMDQ_ENT_DWORDS]; unsigned long flags; + struct arm_smmu_queue_poll qp; + struct arm_smmu_cmdq *cmdq = &smmu->cmdq; + int ret = 0; - if (arm_smmu_cmdq_build_cmd(cmd, ent)) { - dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n", - ent->opcode); - return; + /* + * Try to update our copy of cons by grabbing exclusive cmdq access. If + * that fails, spin until somebody else updates it for us. + */ + if (arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags)) { + WRITE_ONCE(cmdq->q.llq.cons, readl_relaxed(cmdq->q.cons_reg)); + arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags); + llq->val = READ_ONCE(cmdq->q.llq.val); + return 0; } - spin_lock_irqsave(&smmu->cmdq.lock, flags); - arm_smmu_cmdq_insert_cmd(smmu, cmd); - spin_unlock_irqrestore(&smmu->cmdq.lock, flags); + queue_poll_init(smmu, &qp); + do { + llq->val = READ_ONCE(smmu->cmdq.q.llq.val); + if (!queue_full(llq)) + break; + + ret = queue_poll(&qp); + } while (!ret); + + return ret; } /* - * The difference between val and sync_idx is bounded by the maximum size of - * a queue at 2^20 entries, so 32 bits is plenty for wrap-safe arithmetic. + * Wait until the SMMU signals a CMD_SYNC completion MSI. + * Must be called with the cmdq lock held in some capacity. */ -static int __arm_smmu_sync_poll_msi(struct arm_smmu_device *smmu, u32 sync_idx) +static int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu, + struct arm_smmu_ll_queue *llq) { - ktime_t timeout; - u32 val; + int ret = 0; + struct arm_smmu_queue_poll qp; + struct arm_smmu_cmdq *cmdq = &smmu->cmdq; + u32 *cmd = (u32 *)(Q_ENT(&cmdq->q, llq->prod)); - timeout = ktime_add_us(ktime_get(), ARM_SMMU_CMDQ_SYNC_TIMEOUT_US); - val = smp_cond_load_acquire(&smmu->sync_count, - (int)(VAL - sync_idx) >= 0 || - !ktime_before(ktime_get(), timeout)); + queue_poll_init(smmu, &qp); - return (int)(val - sync_idx) < 0 ? -ETIMEDOUT : 0; + /* + * The MSI won't generate an event, since it's being written back + * into the command queue. + */ + qp.wfe = false; + smp_cond_load_relaxed(cmd, !VAL || (ret = queue_poll(&qp))); + llq->cons = ret ? llq->prod : queue_inc_prod_n(llq, 1); + return ret; } -static int __arm_smmu_cmdq_issue_sync_msi(struct arm_smmu_device *smmu) +/* + * Wait until the SMMU cons index passes llq->prod. + * Must be called with the cmdq lock held in some capacity. + */ +static int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu, + struct arm_smmu_ll_queue *llq) { - u64 cmd[CMDQ_ENT_DWORDS]; - unsigned long flags; - struct arm_smmu_cmdq_ent ent = { - .opcode = CMDQ_OP_CMD_SYNC, - .sync = { - .msiaddr = virt_to_phys(&smmu->sync_count), - }, - }; + struct arm_smmu_queue_poll qp; + struct arm_smmu_cmdq *cmdq = &smmu->cmdq; + u32 prod = llq->prod; + int ret = 0; + + queue_poll_init(smmu, &qp); + llq->val = READ_ONCE(smmu->cmdq.q.llq.val); + do { + if (queue_consumed(llq, prod)) + break; - spin_lock_irqsave(&smmu->cmdq.lock, flags); + ret = queue_poll(&qp); - /* Piggy-back on the previous command if it's a SYNC */ - if (smmu->prev_cmd_opcode == CMDQ_OP_CMD_SYNC) { - ent.sync.msidata = smmu->sync_nr; - } else { - ent.sync.msidata = ++smmu->sync_nr; - arm_smmu_cmdq_build_cmd(cmd, &ent); - arm_smmu_cmdq_insert_cmd(smmu, cmd); - } + /* + * This needs to be a readl() so that our subsequent call + * to arm_smmu_cmdq_shared_tryunlock() can fail accurately. + * + * Specifically, we need to ensure that we observe all + * shared_lock()s by other CMD_SYNCs that share our owner, + * so that a failing call to tryunlock() means that we're + * the last one out and therefore we can safely advance + * cmdq->q.llq.cons. Roughly speaking: + * + * CPU 0 CPU1 CPU2 (us) + * + * if (sync) + * shared_lock(); + * + * dma_wmb(); + * set_valid_map(); + * + * if (owner) { + * poll_valid_map(); + * <control dependency> + * writel(prod_reg); + * + * readl(cons_reg); + * tryunlock(); + * + * Requires us to see CPU 0's shared_lock() acquisition. + */ + llq->cons = readl(cmdq->q.cons_reg); + } while (!ret); - spin_unlock_irqrestore(&smmu->cmdq.lock, flags); + return ret; +} + +static int arm_smmu_cmdq_poll_until_sync(struct arm_smmu_device *smmu, + struct arm_smmu_ll_queue *llq) +{ + if (smmu->features & ARM_SMMU_FEAT_MSI && + smmu->features & ARM_SMMU_FEAT_COHERENCY) + return __arm_smmu_cmdq_poll_until_msi(smmu, llq); - return __arm_smmu_sync_poll_msi(smmu, ent.sync.msidata); + return __arm_smmu_cmdq_poll_until_consumed(smmu, llq); } -static int __arm_smmu_cmdq_issue_sync(struct arm_smmu_device *smmu) +static void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds, + u32 prod, int n) { - u64 cmd[CMDQ_ENT_DWORDS]; + int i; + struct arm_smmu_ll_queue llq = { + .max_n_shift = cmdq->q.llq.max_n_shift, + .prod = prod, + }; + + for (i = 0; i < n; ++i) { + u64 *cmd = &cmds[i * CMDQ_ENT_DWORDS]; + + prod = queue_inc_prod_n(&llq, i); + queue_write(Q_ENT(&cmdq->q, prod), cmd, CMDQ_ENT_DWORDS); + } +} + +/* + * This is the actual insertion function, and provides the following + * ordering guarantees to callers: + * + * - There is a dma_wmb() before publishing any commands to the queue. + * This can be relied upon to order prior writes to data structures + * in memory (such as a CD or an STE) before the command. + * + * - On completion of a CMD_SYNC, there is a control dependency. + * This can be relied upon to order subsequent writes to memory (e.g. + * freeing an IOVA) after completion of the CMD_SYNC. + * + * - Command insertion is totally ordered, so if two CPUs each race to + * insert their own list of commands then all of the commands from one + * CPU will appear before any of the commands from the other CPU. + */ +static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, + u64 *cmds, int n, bool sync) +{ + u64 cmd_sync[CMDQ_ENT_DWORDS]; + u32 prod; unsigned long flags; - bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV); - struct arm_smmu_cmdq_ent ent = { .opcode = CMDQ_OP_CMD_SYNC }; - int ret; + bool owner; + struct arm_smmu_cmdq *cmdq = &smmu->cmdq; + struct arm_smmu_ll_queue llq = { + .max_n_shift = cmdq->q.llq.max_n_shift, + }, head = llq; + int ret = 0; - arm_smmu_cmdq_build_cmd(cmd, &ent); + /* 1. Allocate some space in the queue */ + local_irq_save(flags); + llq.val = READ_ONCE(cmdq->q.llq.val); + do { + u64 old; + + while (!queue_has_space(&llq, n + sync)) { + local_irq_restore(flags); + if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq)) + dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); + local_irq_save(flags); + } + + head.cons = llq.cons; + head.prod = queue_inc_prod_n(&llq, n + sync) | + CMDQ_PROD_OWNED_FLAG; - spin_lock_irqsave(&smmu->cmdq.lock, flags); - arm_smmu_cmdq_insert_cmd(smmu, cmd); - ret = queue_poll_cons(&smmu->cmdq.q, true, wfe); - spin_unlock_irqrestore(&smmu->cmdq.lock, flags); + old = cmpxchg_relaxed(&cmdq->q.llq.val, llq.val, head.val); + if (old == llq.val) + break; + + llq.val = old; + } while (1); + owner = !(llq.prod & CMDQ_PROD_OWNED_FLAG); + head.prod &= ~CMDQ_PROD_OWNED_FLAG; + llq.prod &= ~CMDQ_PROD_OWNED_FLAG; + + /* + * 2. Write our commands into the queue + * Dependency ordering from the cmpxchg() loop above. + */ + arm_smmu_cmdq_write_entries(cmdq, cmds, llq.prod, n); + if (sync) { + prod = queue_inc_prod_n(&llq, n); + arm_smmu_cmdq_build_sync_cmd(cmd_sync, smmu, prod); + queue_write(Q_ENT(&cmdq->q, prod), cmd_sync, CMDQ_ENT_DWORDS); + + /* + * In order to determine completion of our CMD_SYNC, we must + * ensure that the queue can't wrap twice without us noticing. + * We achieve that by taking the cmdq lock as shared before + * marking our slot as valid. + */ + arm_smmu_cmdq_shared_lock(cmdq); + } + + /* 3. Mark our slots as valid, ensuring commands are visible first */ + dma_wmb(); + arm_smmu_cmdq_set_valid_map(cmdq, llq.prod, head.prod); + + /* 4. If we are the owner, take control of the SMMU hardware */ + if (owner) { + /* a. Wait for previous owner to finish */ + atomic_cond_read_relaxed(&cmdq->owner_prod, VAL == llq.prod); + + /* b. Stop gathering work by clearing the owned flag */ + prod = atomic_fetch_andnot_relaxed(CMDQ_PROD_OWNED_FLAG, + &cmdq->q.llq.atomic.prod); + prod &= ~CMDQ_PROD_OWNED_FLAG; + + /* + * c. Wait for any gathered work to be written to the queue. + * Note that we read our own entries so that we have the control + * dependency required by (d). + */ + arm_smmu_cmdq_poll_valid_map(cmdq, llq.prod, prod); + + /* + * d. Advance the hardware prod pointer + * Control dependency ordering from the entries becoming valid. + */ + writel_relaxed(prod, cmdq->q.prod_reg); + + /* + * e. Tell the next owner we're done + * Make sure we've updated the hardware first, so that we don't + * race to update prod and potentially move it backwards. + */ + atomic_set_release(&cmdq->owner_prod, prod); + } + + /* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */ + if (sync) { + llq.prod = queue_inc_prod_n(&llq, n); + ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq); + if (ret) { + dev_err_ratelimited(smmu->dev, + "CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n", + llq.prod, + readl_relaxed(cmdq->q.prod_reg), + readl_relaxed(cmdq->q.cons_reg)); + } + + /* + * Try to unlock the cmq lock. This will fail if we're the last + * reader, in which case we can safely update cmdq->q.llq.cons + */ + if (!arm_smmu_cmdq_shared_tryunlock(cmdq)) { + WRITE_ONCE(cmdq->q.llq.cons, llq.cons); + arm_smmu_cmdq_shared_unlock(cmdq); + } + } + local_irq_restore(flags); return ret; } -static int arm_smmu_cmdq_issue_sync(struct arm_smmu_device *smmu) +static int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq_ent *ent) { - int ret; - bool msi = (smmu->features & ARM_SMMU_FEAT_MSI) && - (smmu->features & ARM_SMMU_FEAT_COHERENCY); + u64 cmd[CMDQ_ENT_DWORDS]; - ret = msi ? __arm_smmu_cmdq_issue_sync_msi(smmu) - : __arm_smmu_cmdq_issue_sync(smmu); - if (ret) - dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n"); - return ret; + if (arm_smmu_cmdq_build_cmd(cmd, ent)) { + dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n", + ent->opcode); + return -EINVAL; + } + + return arm_smmu_cmdq_issue_cmdlist(smmu, cmd, 1, false); +} + +static int arm_smmu_cmdq_issue_sync(struct arm_smmu_device *smmu) +{ + return arm_smmu_cmdq_issue_cmdlist(smmu, NULL, 0, true); } /* Context descriptor manipulation functions */ @@ -1186,8 +1575,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, ste_live = true; break; case STRTAB_STE_0_CFG_ABORT: - if (disable_bypass) - break; + BUG_ON(!disable_bypass); + break; default: BUG(); /* STE corruption */ } @@ -1305,6 +1694,7 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) int i; struct arm_smmu_device *smmu = dev; struct arm_smmu_queue *q = &smmu->evtq.q; + struct arm_smmu_ll_queue *llq = &q->llq; u64 evt[EVTQ_ENT_DWORDS]; do { @@ -1322,12 +1712,13 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) * Not much we can do on overflow, so scream and pretend we're * trying harder. */ - if (queue_sync_prod(q) == -EOVERFLOW) + if (queue_sync_prod_in(q) == -EOVERFLOW) dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n"); - } while (!queue_empty(q)); + } while (!queue_empty(llq)); /* Sync our overflow flag, as we believe we're up to speed */ - q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons); + llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) | + Q_IDX(llq, llq->cons); return IRQ_HANDLED; } @@ -1373,19 +1764,21 @@ static irqreturn_t arm_smmu_priq_thread(int irq, void *dev) { struct arm_smmu_device *smmu = dev; struct arm_smmu_queue *q = &smmu->priq.q; + struct arm_smmu_ll_queue *llq = &q->llq; u64 evt[PRIQ_ENT_DWORDS]; do { while (!queue_remove_raw(q, evt)) arm_smmu_handle_ppr(smmu, evt); - if (queue_sync_prod(q) == -EOVERFLOW) + if (queue_sync_prod_in(q) == -EOVERFLOW) dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n"); - } while (!queue_empty(q)); + } while (!queue_empty(llq)); /* Sync our overflow flag, as we believe we're up to speed */ - q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons); - writel(q->cons, q->cons_reg); + llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) | + Q_IDX(llq, llq->cons); + queue_sync_cons_out(q); return IRQ_HANDLED; } @@ -1534,6 +1927,23 @@ static int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS)) return 0; + /* + * Ensure that we've completed prior invalidation of the main TLBs + * before we read 'nr_ats_masters' in case of a concurrent call to + * arm_smmu_enable_ats(): + * + * // unmap() // arm_smmu_enable_ats() + * TLBI+SYNC atomic_inc(&nr_ats_masters); + * smp_mb(); [...] + * atomic_read(&nr_ats_masters); pci_enable_ats() // writel() + * + * Ensures that we always see the incremented 'nr_ats_masters' count if + * ATS was enabled at the PCI device before completion of the TLBI. + */ + smp_mb(); + if (!atomic_read(&smmu_domain->nr_ats_masters)) + return 0; + arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd); spin_lock_irqsave(&smmu_domain->devices_lock, flags); @@ -1545,13 +1955,6 @@ static int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, } /* IO_PGTABLE API */ -static void arm_smmu_tlb_sync(void *cookie) -{ - struct arm_smmu_domain *smmu_domain = cookie; - - arm_smmu_cmdq_issue_sync(smmu_domain->smmu); -} - static void arm_smmu_tlb_inv_context(void *cookie) { struct arm_smmu_domain *smmu_domain = cookie; @@ -1570,25 +1973,32 @@ static void arm_smmu_tlb_inv_context(void *cookie) /* * NOTE: when io-pgtable is in non-strict mode, we may get here with * PTEs previously cleared by unmaps on the current CPU not yet visible - * to the SMMU. We are relying on the DSB implicit in queue_inc_prod() - * to guarantee those are observed before the TLBI. Do be careful, 007. + * to the SMMU. We are relying on the dma_wmb() implicit during cmd + * insertion to guarantee those are observed before the TLBI. Do be + * careful, 007. */ arm_smmu_cmdq_issue_cmd(smmu, &cmd); arm_smmu_cmdq_issue_sync(smmu); + arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0); } -static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size, - size_t granule, bool leaf, void *cookie) +static void arm_smmu_tlb_inv_range(unsigned long iova, size_t size, + size_t granule, bool leaf, + struct arm_smmu_domain *smmu_domain) { - struct arm_smmu_domain *smmu_domain = cookie; + u64 cmds[CMDQ_BATCH_ENTRIES * CMDQ_ENT_DWORDS]; struct arm_smmu_device *smmu = smmu_domain->smmu; + unsigned long start = iova, end = iova + size; + int i = 0; struct arm_smmu_cmdq_ent cmd = { .tlbi = { .leaf = leaf, - .addr = iova, }, }; + if (!size) + return; + if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { cmd.opcode = CMDQ_OP_TLBI_NH_VA; cmd.tlbi.asid = smmu_domain->s1_cfg.cd.asid; @@ -1597,16 +2007,54 @@ static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size, cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid; } - do { - arm_smmu_cmdq_issue_cmd(smmu, &cmd); - cmd.tlbi.addr += granule; - } while (size -= granule); + while (iova < end) { + if (i == CMDQ_BATCH_ENTRIES) { + arm_smmu_cmdq_issue_cmdlist(smmu, cmds, i, false); + i = 0; + } + + cmd.tlbi.addr = iova; + arm_smmu_cmdq_build_cmd(&cmds[i * CMDQ_ENT_DWORDS], &cmd); + iova += granule; + i++; + } + + arm_smmu_cmdq_issue_cmdlist(smmu, cmds, i, true); + + /* + * Unfortunately, this can't be leaf-only since we may have + * zapped an entire table. + */ + arm_smmu_atc_inv_domain(smmu_domain, 0, start, size); +} + +static void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, + void *cookie) +{ + struct arm_smmu_domain *smmu_domain = cookie; + struct iommu_domain *domain = &smmu_domain->domain; + + iommu_iotlb_gather_add_page(domain, gather, iova, granule); } -static const struct iommu_gather_ops arm_smmu_gather_ops = { +static void arm_smmu_tlb_inv_walk(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + arm_smmu_tlb_inv_range(iova, size, granule, false, cookie); +} + +static void arm_smmu_tlb_inv_leaf(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + arm_smmu_tlb_inv_range(iova, size, granule, true, cookie); +} + +static const struct iommu_flush_ops arm_smmu_flush_ops = { .tlb_flush_all = arm_smmu_tlb_inv_context, - .tlb_add_flush = arm_smmu_tlb_inv_range_nosync, - .tlb_sync = arm_smmu_tlb_sync, + .tlb_flush_walk = arm_smmu_tlb_inv_walk, + .tlb_flush_leaf = arm_smmu_tlb_inv_leaf, + .tlb_add_page = arm_smmu_tlb_inv_page_nosync, }; /* IOMMU API */ @@ -1796,7 +2244,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain) .ias = ias, .oas = oas, .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY, - .tlb = &arm_smmu_gather_ops, + .tlb = &arm_smmu_flush_ops, .iommu_dev = smmu->dev, }; @@ -1863,44 +2311,65 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master) } } -static int arm_smmu_enable_ats(struct arm_smmu_master *master) +#ifdef CONFIG_PCI_ATS +static bool arm_smmu_ats_supported(struct arm_smmu_master *master) { - int ret; - size_t stu; struct pci_dev *pdev; struct arm_smmu_device *smmu = master->smmu; struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev); if (!(smmu->features & ARM_SMMU_FEAT_ATS) || !dev_is_pci(master->dev) || !(fwspec->flags & IOMMU_FWSPEC_PCI_RC_ATS) || pci_ats_disabled()) - return -ENXIO; + return false; pdev = to_pci_dev(master->dev); - if (pdev->untrusted) - return -EPERM; + return !pdev->untrusted && pdev->ats_cap; +} +#else +static bool arm_smmu_ats_supported(struct arm_smmu_master *master) +{ + return false; +} +#endif + +static void arm_smmu_enable_ats(struct arm_smmu_master *master) +{ + size_t stu; + struct pci_dev *pdev; + struct arm_smmu_device *smmu = master->smmu; + struct arm_smmu_domain *smmu_domain = master->domain; + + /* Don't enable ATS at the endpoint if it's not enabled in the STE */ + if (!master->ats_enabled) + return; /* Smallest Translation Unit: log2 of the smallest supported granule */ stu = __ffs(smmu->pgsize_bitmap); + pdev = to_pci_dev(master->dev); - ret = pci_enable_ats(pdev, stu); - if (ret) - return ret; - - master->ats_enabled = true; - return 0; + atomic_inc(&smmu_domain->nr_ats_masters); + arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0); + if (pci_enable_ats(pdev, stu)) + dev_err(master->dev, "Failed to enable ATS (STU %zu)\n", stu); } static void arm_smmu_disable_ats(struct arm_smmu_master *master) { struct arm_smmu_cmdq_ent cmd; + struct arm_smmu_domain *smmu_domain = master->domain; - if (!master->ats_enabled || !dev_is_pci(master->dev)) + if (!master->ats_enabled) return; + pci_disable_ats(to_pci_dev(master->dev)); + /* + * Ensure ATS is disabled at the endpoint before we issue the + * ATC invalidation via the SMMU. + */ + wmb(); arm_smmu_atc_inv_to_cmd(0, 0, 0, &cmd); arm_smmu_atc_inv_master(master, &cmd); - pci_disable_ats(to_pci_dev(master->dev)); - master->ats_enabled = false; + atomic_dec(&smmu_domain->nr_ats_masters); } static void arm_smmu_detach_dev(struct arm_smmu_master *master) @@ -1911,14 +2380,15 @@ static void arm_smmu_detach_dev(struct arm_smmu_master *master) if (!smmu_domain) return; + arm_smmu_disable_ats(master); + spin_lock_irqsave(&smmu_domain->devices_lock, flags); list_del(&master->domain_head); spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); master->domain = NULL; + master->ats_enabled = false; arm_smmu_install_ste_for_dev(master); - - arm_smmu_disable_ats(master); } static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) @@ -1958,17 +2428,20 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) master->domain = smmu_domain; - spin_lock_irqsave(&smmu_domain->devices_lock, flags); - list_add(&master->domain_head, &smmu_domain->devices); - spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); - if (smmu_domain->stage != ARM_SMMU_DOMAIN_BYPASS) - arm_smmu_enable_ats(master); + master->ats_enabled = arm_smmu_ats_supported(master); if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) arm_smmu_write_ctx_desc(smmu, &smmu_domain->s1_cfg); arm_smmu_install_ste_for_dev(master); + + spin_lock_irqsave(&smmu_domain->devices_lock, flags); + list_add(&master->domain_head, &smmu_domain->devices); + spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); + + arm_smmu_enable_ats(master); + out_unlock: mutex_unlock(&smmu_domain->init_mutex); return ret; @@ -1985,21 +2458,16 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, return ops->map(ops, iova, paddr, size, prot); } -static size_t -arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) +static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, + size_t size, struct iommu_iotlb_gather *gather) { - int ret; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; if (!ops) return 0; - ret = ops->unmap(ops, iova, size); - if (ret && arm_smmu_atc_inv_domain(smmu_domain, 0, iova, size)) - return 0; - - return ret; + return ops->unmap(ops, iova, size, gather); } static void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) @@ -2010,12 +2478,13 @@ static void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) arm_smmu_tlb_inv_context(smmu_domain); } -static void arm_smmu_iotlb_sync(struct iommu_domain *domain) +static void arm_smmu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) { - struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu; + struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - if (smmu) - arm_smmu_cmdq_issue_sync(smmu); + arm_smmu_tlb_inv_range(gather->start, gather->end - gather->start, + gather->pgsize, true, smmu_domain); } static phys_addr_t @@ -2286,13 +2755,13 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu, size_t qsz; do { - qsz = ((1 << q->max_n_shift) * dwords) << 3; + qsz = ((1 << q->llq.max_n_shift) * dwords) << 3; q->base = dmam_alloc_coherent(smmu->dev, qsz, &q->base_dma, GFP_KERNEL); if (q->base || qsz < PAGE_SIZE) break; - q->max_n_shift--; + q->llq.max_n_shift--; } while (1); if (!q->base) { @@ -2304,7 +2773,7 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu, if (!WARN_ON(q->base_dma & (qsz - 1))) { dev_info(smmu->dev, "allocated %u entries for %s\n", - 1 << q->max_n_shift, name); + 1 << q->llq.max_n_shift, name); } q->prod_reg = arm_smmu_page1_fixup(prod_off, smmu); @@ -2313,24 +2782,55 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu, q->q_base = Q_BASE_RWA; q->q_base |= q->base_dma & Q_BASE_ADDR_MASK; - q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->max_n_shift); + q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->llq.max_n_shift); - q->prod = q->cons = 0; + q->llq.prod = q->llq.cons = 0; return 0; } +static void arm_smmu_cmdq_free_bitmap(void *data) +{ + unsigned long *bitmap = data; + bitmap_free(bitmap); +} + +static int arm_smmu_cmdq_init(struct arm_smmu_device *smmu) +{ + int ret = 0; + struct arm_smmu_cmdq *cmdq = &smmu->cmdq; + unsigned int nents = 1 << cmdq->q.llq.max_n_shift; + atomic_long_t *bitmap; + + atomic_set(&cmdq->owner_prod, 0); + atomic_set(&cmdq->lock, 0); + + bitmap = (atomic_long_t *)bitmap_zalloc(nents, GFP_KERNEL); + if (!bitmap) { + dev_err(smmu->dev, "failed to allocate cmdq bitmap\n"); + ret = -ENOMEM; + } else { + cmdq->valid_map = bitmap; + devm_add_action(smmu->dev, arm_smmu_cmdq_free_bitmap, bitmap); + } + + return ret; +} + static int arm_smmu_init_queues(struct arm_smmu_device *smmu) { int ret; /* cmdq */ - spin_lock_init(&smmu->cmdq.lock); ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD, ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS, "cmdq"); if (ret) return ret; + ret = arm_smmu_cmdq_init(smmu); + if (ret) + return ret; + /* evtq */ ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD, ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS, @@ -2708,8 +3208,8 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass) /* Command queue */ writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE); - writel_relaxed(smmu->cmdq.q.prod, smmu->base + ARM_SMMU_CMDQ_PROD); - writel_relaxed(smmu->cmdq.q.cons, smmu->base + ARM_SMMU_CMDQ_CONS); + writel_relaxed(smmu->cmdq.q.llq.prod, smmu->base + ARM_SMMU_CMDQ_PROD); + writel_relaxed(smmu->cmdq.q.llq.cons, smmu->base + ARM_SMMU_CMDQ_CONS); enables = CR0_CMDQEN; ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, @@ -2736,9 +3236,9 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass) /* Event queue */ writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE); - writel_relaxed(smmu->evtq.q.prod, + writel_relaxed(smmu->evtq.q.llq.prod, arm_smmu_page1_fixup(ARM_SMMU_EVTQ_PROD, smmu)); - writel_relaxed(smmu->evtq.q.cons, + writel_relaxed(smmu->evtq.q.llq.cons, arm_smmu_page1_fixup(ARM_SMMU_EVTQ_CONS, smmu)); enables |= CR0_EVTQEN; @@ -2753,9 +3253,9 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass) if (smmu->features & ARM_SMMU_FEAT_PRI) { writeq_relaxed(smmu->priq.q.q_base, smmu->base + ARM_SMMU_PRIQ_BASE); - writel_relaxed(smmu->priq.q.prod, + writel_relaxed(smmu->priq.q.llq.prod, arm_smmu_page1_fixup(ARM_SMMU_PRIQ_PROD, smmu)); - writel_relaxed(smmu->priq.q.cons, + writel_relaxed(smmu->priq.q.llq.cons, arm_smmu_page1_fixup(ARM_SMMU_PRIQ_CONS, smmu)); enables |= CR0_PRIQEN; @@ -2909,18 +3409,24 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) } /* Queue sizes, capped to ensure natural alignment */ - smmu->cmdq.q.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT, - FIELD_GET(IDR1_CMDQS, reg)); - if (!smmu->cmdq.q.max_n_shift) { - /* Odd alignment restrictions on the base, so ignore for now */ - dev_err(smmu->dev, "unit-length command queue not supported\n"); + smmu->cmdq.q.llq.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT, + FIELD_GET(IDR1_CMDQS, reg)); + if (smmu->cmdq.q.llq.max_n_shift <= ilog2(CMDQ_BATCH_ENTRIES)) { + /* + * We don't support splitting up batches, so one batch of + * commands plus an extra sync needs to fit inside the command + * queue. There's also no way we can handle the weird alignment + * restrictions on the base pointer for a unit-length queue. + */ + dev_err(smmu->dev, "command queue size <= %d entries not supported\n", + CMDQ_BATCH_ENTRIES); return -ENXIO; } - smmu->evtq.q.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT, - FIELD_GET(IDR1_EVTQS, reg)); - smmu->priq.q.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT, - FIELD_GET(IDR1_PRIQS, reg)); + smmu->evtq.q.llq.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT, + FIELD_GET(IDR1_EVTQS, reg)); + smmu->priq.q.llq.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT, + FIELD_GET(IDR1_PRIQS, reg)); /* SID/SSID sizes */ smmu->ssid_bits = FIELD_GET(IDR1_SSIDSIZE, reg); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 64977c131ee6..5b93c79371e9 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -19,16 +19,13 @@ #include <linux/acpi.h> #include <linux/acpi_iort.h> -#include <linux/atomic.h> +#include <linux/bitfield.h> #include <linux/delay.h> #include <linux/dma-iommu.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/io.h> -#include <linux/io-64-nonatomic-hi-lo.h> -#include <linux/io-pgtable.h> -#include <linux/iommu.h> #include <linux/iopoll.h> #include <linux/init.h> #include <linux/moduleparam.h> @@ -40,12 +37,11 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> -#include <linux/spinlock.h> #include <linux/amba/bus.h> #include <linux/fsl/mc.h> -#include "arm-smmu-regs.h" +#include "arm-smmu.h" /* * Apparently, some Qualcomm arm64 platforms which appear to expose their SMMU @@ -56,46 +52,9 @@ */ #define QCOM_DUMMY_VAL -1 -#define ARM_MMU500_ACTLR_CPRE (1 << 1) - -#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) -#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) -#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) - #define TLB_LOOP_TIMEOUT 1000000 /* 1s! */ #define TLB_SPIN_COUNT 10 -/* Maximum number of context banks per SMMU */ -#define ARM_SMMU_MAX_CBS 128 - -/* SMMU global address space */ -#define ARM_SMMU_GR0(smmu) ((smmu)->base) -#define ARM_SMMU_GR1(smmu) ((smmu)->base + (1 << (smmu)->pgshift)) - -/* - * SMMU global address space with conditional offset to access secure - * aliases of non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448, - * nsGFSYNR0: 0x450) - */ -#define ARM_SMMU_GR0_NS(smmu) \ - ((smmu)->base + \ - ((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \ - ? 0x400 : 0)) - -/* - * Some 64-bit registers only make sense to write atomically, but in such - * cases all the data relevant to AArch32 formats lies within the lower word, - * therefore this actually makes more sense than it might first appear. - */ -#ifdef CONFIG_64BIT -#define smmu_write_atomic_lq writeq_relaxed -#else -#define smmu_write_atomic_lq writel_relaxed -#endif - -/* Translation context bank */ -#define ARM_SMMU_CB(smmu, n) ((smmu)->cb_base + ((n) << (smmu)->pgshift)) - #define MSI_IOVA_BASE 0x8000000 #define MSI_IOVA_LENGTH 0x100000 @@ -113,19 +72,6 @@ module_param(disable_bypass, bool, S_IRUGO); MODULE_PARM_DESC(disable_bypass, "Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU."); -enum arm_smmu_arch_version { - ARM_SMMU_V1, - ARM_SMMU_V1_64K, - ARM_SMMU_V2, -}; - -enum arm_smmu_implementation { - GENERIC_SMMU, - ARM_MMU500, - CAVIUM_SMMUV2, - QCOM_SMMUV2, -}; - struct arm_smmu_s2cr { struct iommu_group *group; int count; @@ -163,117 +109,8 @@ struct arm_smmu_master_cfg { #define for_each_cfg_sme(fw, i, idx) \ for (i = 0; idx = fwspec_smendx(fw, i), i < fw->num_ids; ++i) -struct arm_smmu_device { - struct device *dev; - - void __iomem *base; - void __iomem *cb_base; - unsigned long pgshift; - -#define ARM_SMMU_FEAT_COHERENT_WALK (1 << 0) -#define ARM_SMMU_FEAT_STREAM_MATCH (1 << 1) -#define ARM_SMMU_FEAT_TRANS_S1 (1 << 2) -#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3) -#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4) -#define ARM_SMMU_FEAT_TRANS_OPS (1 << 5) -#define ARM_SMMU_FEAT_VMID16 (1 << 6) -#define ARM_SMMU_FEAT_FMT_AARCH64_4K (1 << 7) -#define ARM_SMMU_FEAT_FMT_AARCH64_16K (1 << 8) -#define ARM_SMMU_FEAT_FMT_AARCH64_64K (1 << 9) -#define ARM_SMMU_FEAT_FMT_AARCH32_L (1 << 10) -#define ARM_SMMU_FEAT_FMT_AARCH32_S (1 << 11) -#define ARM_SMMU_FEAT_EXIDS (1 << 12) - u32 features; - -#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0) - u32 options; - enum arm_smmu_arch_version version; - enum arm_smmu_implementation model; - - u32 num_context_banks; - u32 num_s2_context_banks; - DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS); - struct arm_smmu_cb *cbs; - atomic_t irptndx; - - u32 num_mapping_groups; - u16 streamid_mask; - u16 smr_mask_mask; - struct arm_smmu_smr *smrs; - struct arm_smmu_s2cr *s2crs; - struct mutex stream_map_mutex; - - unsigned long va_size; - unsigned long ipa_size; - unsigned long pa_size; - unsigned long pgsize_bitmap; - - u32 num_global_irqs; - u32 num_context_irqs; - unsigned int *irqs; - struct clk_bulk_data *clks; - int num_clks; - - u32 cavium_id_base; /* Specific to Cavium */ - - spinlock_t global_sync_lock; - - /* IOMMU core code handle */ - struct iommu_device iommu; -}; - -enum arm_smmu_context_fmt { - ARM_SMMU_CTX_FMT_NONE, - ARM_SMMU_CTX_FMT_AARCH64, - ARM_SMMU_CTX_FMT_AARCH32_L, - ARM_SMMU_CTX_FMT_AARCH32_S, -}; - -struct arm_smmu_cfg { - u8 cbndx; - u8 irptndx; - union { - u16 asid; - u16 vmid; - }; - u32 cbar; - enum arm_smmu_context_fmt fmt; -}; -#define INVALID_IRPTNDX 0xff - -enum arm_smmu_domain_stage { - ARM_SMMU_DOMAIN_S1 = 0, - ARM_SMMU_DOMAIN_S2, - ARM_SMMU_DOMAIN_NESTED, - ARM_SMMU_DOMAIN_BYPASS, -}; - -struct arm_smmu_domain { - struct arm_smmu_device *smmu; - struct io_pgtable_ops *pgtbl_ops; - const struct iommu_gather_ops *tlb_ops; - struct arm_smmu_cfg cfg; - enum arm_smmu_domain_stage stage; - bool non_strict; - struct mutex init_mutex; /* Protects smmu pointer */ - spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */ - struct iommu_domain domain; -}; - -struct arm_smmu_option_prop { - u32 opt; - const char *prop; -}; - -static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); - static bool using_legacy_binding, using_generic_binding; -static struct arm_smmu_option_prop arm_smmu_options[] = { - { ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" }, - { 0, NULL}, -}; - static inline int arm_smmu_rpm_get(struct arm_smmu_device *smmu) { if (pm_runtime_enabled(smmu->dev)) @@ -293,20 +130,6 @@ static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) return container_of(dom, struct arm_smmu_domain, domain); } -static void parse_driver_options(struct arm_smmu_device *smmu) -{ - int i = 0; - - do { - if (of_property_read_bool(smmu->dev->of_node, - arm_smmu_options[i].prop)) { - smmu->options |= arm_smmu_options[i].opt; - dev_notice(smmu->dev, "option %s\n", - arm_smmu_options[i].prop); - } - } while (arm_smmu_options[++i].opt); -} - static struct device_node *dev_get_dev_node(struct device *dev) { if (dev_is_pci(dev)) { @@ -415,15 +238,17 @@ static void __arm_smmu_free_bitmap(unsigned long *map, int idx) } /* Wait for any pending TLB invalidations to complete */ -static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, - void __iomem *sync, void __iomem *status) +static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, int page, + int sync, int status) { unsigned int spin_cnt, delay; + u32 reg; - writel_relaxed(QCOM_DUMMY_VAL, sync); + arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL); for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) { for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) { - if (!(readl_relaxed(status) & sTLBGSTATUS_GSACTIVE)) + reg = arm_smmu_readl(smmu, page, status); + if (!(reg & sTLBGSTATUS_GSACTIVE)) return; cpu_relax(); } @@ -435,12 +260,11 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu) { - void __iomem *base = ARM_SMMU_GR0(smmu); unsigned long flags; spin_lock_irqsave(&smmu->global_sync_lock, flags); - __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_GR0_sTLBGSYNC, - base + ARM_SMMU_GR0_sTLBGSTATUS); + __arm_smmu_tlb_sync(smmu, ARM_SMMU_GR0, ARM_SMMU_GR0_sTLBGSYNC, + ARM_SMMU_GR0_sTLBGSTATUS); spin_unlock_irqrestore(&smmu->global_sync_lock, flags); } @@ -448,12 +272,11 @@ static void arm_smmu_tlb_sync_context(void *cookie) { struct arm_smmu_domain *smmu_domain = cookie; struct arm_smmu_device *smmu = smmu_domain->smmu; - void __iomem *base = ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx); unsigned long flags; spin_lock_irqsave(&smmu_domain->cb_lock, flags); - __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_CB_TLBSYNC, - base + ARM_SMMU_CB_TLBSTATUS); + __arm_smmu_tlb_sync(smmu, ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx), + ARM_SMMU_CB_TLBSYNC, ARM_SMMU_CB_TLBSTATUS); spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); } @@ -467,14 +290,13 @@ static void arm_smmu_tlb_sync_vmid(void *cookie) static void arm_smmu_tlb_inv_context_s1(void *cookie) { struct arm_smmu_domain *smmu_domain = cookie; - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - void __iomem *base = ARM_SMMU_CB(smmu_domain->smmu, cfg->cbndx); - /* - * NOTE: this is not a relaxed write; it needs to guarantee that PTEs - * cleared by the current CPU are visible to the SMMU before the TLBI. + * The TLBI write may be relaxed, so ensure that PTEs cleared by the + * current CPU are visible beforehand. */ - writel(cfg->asid, base + ARM_SMMU_CB_S1_TLBIASID); + wmb(); + arm_smmu_cb_write(smmu_domain->smmu, smmu_domain->cfg.cbndx, + ARM_SMMU_CB_S1_TLBIASID, smmu_domain->cfg.asid); arm_smmu_tlb_sync_context(cookie); } @@ -482,87 +304,143 @@ static void arm_smmu_tlb_inv_context_s2(void *cookie) { struct arm_smmu_domain *smmu_domain = cookie; struct arm_smmu_device *smmu = smmu_domain->smmu; - void __iomem *base = ARM_SMMU_GR0(smmu); - /* NOTE: see above */ - writel(smmu_domain->cfg.vmid, base + ARM_SMMU_GR0_TLBIVMID); + /* See above */ + wmb(); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIVMID, smmu_domain->cfg.vmid); arm_smmu_tlb_sync_global(smmu); } -static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size, - size_t granule, bool leaf, void *cookie) +static void arm_smmu_tlb_inv_range_s1(unsigned long iova, size_t size, + size_t granule, bool leaf, void *cookie) { struct arm_smmu_domain *smmu_domain = cookie; + struct arm_smmu_device *smmu = smmu_domain->smmu; struct arm_smmu_cfg *cfg = &smmu_domain->cfg; - bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; - void __iomem *reg = ARM_SMMU_CB(smmu_domain->smmu, cfg->cbndx); + int reg, idx = cfg->cbndx; - if (smmu_domain->smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) + if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) wmb(); - if (stage1) { - reg += leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA; - - if (cfg->fmt != ARM_SMMU_CTX_FMT_AARCH64) { - iova &= ~12UL; - iova |= cfg->asid; - do { - writel_relaxed(iova, reg); - iova += granule; - } while (size -= granule); - } else { - iova >>= 12; - iova |= (u64)cfg->asid << 48; - do { - writeq_relaxed(iova, reg); - iova += granule >> 12; - } while (size -= granule); - } + reg = leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA; + + if (cfg->fmt != ARM_SMMU_CTX_FMT_AARCH64) { + iova = (iova >> 12) << 12; + iova |= cfg->asid; + do { + arm_smmu_cb_write(smmu, idx, reg, iova); + iova += granule; + } while (size -= granule); } else { - reg += leaf ? ARM_SMMU_CB_S2_TLBIIPAS2L : - ARM_SMMU_CB_S2_TLBIIPAS2; iova >>= 12; + iova |= (u64)cfg->asid << 48; do { - smmu_write_atomic_lq(iova, reg); + arm_smmu_cb_writeq(smmu, idx, reg, iova); iova += granule >> 12; } while (size -= granule); } } +static void arm_smmu_tlb_inv_range_s2(unsigned long iova, size_t size, + size_t granule, bool leaf, void *cookie) +{ + struct arm_smmu_domain *smmu_domain = cookie; + struct arm_smmu_device *smmu = smmu_domain->smmu; + int reg, idx = smmu_domain->cfg.cbndx; + + if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) + wmb(); + + reg = leaf ? ARM_SMMU_CB_S2_TLBIIPAS2L : ARM_SMMU_CB_S2_TLBIIPAS2; + iova >>= 12; + do { + if (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH64) + arm_smmu_cb_writeq(smmu, idx, reg, iova); + else + arm_smmu_cb_write(smmu, idx, reg, iova); + iova += granule >> 12; + } while (size -= granule); +} + /* * On MMU-401 at least, the cost of firing off multiple TLBIVMIDs appears * almost negligible, but the benefit of getting the first one in as far ahead * of the sync as possible is significant, hence we don't just make this a - * no-op and set .tlb_sync to arm_smmu_inv_context_s2() as you might think. + * no-op and set .tlb_sync to arm_smmu_tlb_inv_context_s2() as you might think. */ static void arm_smmu_tlb_inv_vmid_nosync(unsigned long iova, size_t size, size_t granule, bool leaf, void *cookie) { struct arm_smmu_domain *smmu_domain = cookie; - void __iomem *base = ARM_SMMU_GR0(smmu_domain->smmu); + struct arm_smmu_device *smmu = smmu_domain->smmu; - if (smmu_domain->smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) + if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) wmb(); - writel_relaxed(smmu_domain->cfg.vmid, base + ARM_SMMU_GR0_TLBIVMID); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIVMID, smmu_domain->cfg.vmid); +} + +static void arm_smmu_tlb_inv_walk(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + struct arm_smmu_domain *smmu_domain = cookie; + const struct arm_smmu_flush_ops *ops = smmu_domain->flush_ops; + + ops->tlb_inv_range(iova, size, granule, false, cookie); + ops->tlb_sync(cookie); +} + +static void arm_smmu_tlb_inv_leaf(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + struct arm_smmu_domain *smmu_domain = cookie; + const struct arm_smmu_flush_ops *ops = smmu_domain->flush_ops; + + ops->tlb_inv_range(iova, size, granule, true, cookie); + ops->tlb_sync(cookie); +} + +static void arm_smmu_tlb_add_page(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, + void *cookie) +{ + struct arm_smmu_domain *smmu_domain = cookie; + const struct arm_smmu_flush_ops *ops = smmu_domain->flush_ops; + + ops->tlb_inv_range(iova, granule, granule, true, cookie); } -static const struct iommu_gather_ops arm_smmu_s1_tlb_ops = { - .tlb_flush_all = arm_smmu_tlb_inv_context_s1, - .tlb_add_flush = arm_smmu_tlb_inv_range_nosync, - .tlb_sync = arm_smmu_tlb_sync_context, +static const struct arm_smmu_flush_ops arm_smmu_s1_tlb_ops = { + .tlb = { + .tlb_flush_all = arm_smmu_tlb_inv_context_s1, + .tlb_flush_walk = arm_smmu_tlb_inv_walk, + .tlb_flush_leaf = arm_smmu_tlb_inv_leaf, + .tlb_add_page = arm_smmu_tlb_add_page, + }, + .tlb_inv_range = arm_smmu_tlb_inv_range_s1, + .tlb_sync = arm_smmu_tlb_sync_context, }; -static const struct iommu_gather_ops arm_smmu_s2_tlb_ops_v2 = { - .tlb_flush_all = arm_smmu_tlb_inv_context_s2, - .tlb_add_flush = arm_smmu_tlb_inv_range_nosync, - .tlb_sync = arm_smmu_tlb_sync_context, +static const struct arm_smmu_flush_ops arm_smmu_s2_tlb_ops_v2 = { + .tlb = { + .tlb_flush_all = arm_smmu_tlb_inv_context_s2, + .tlb_flush_walk = arm_smmu_tlb_inv_walk, + .tlb_flush_leaf = arm_smmu_tlb_inv_leaf, + .tlb_add_page = arm_smmu_tlb_add_page, + }, + .tlb_inv_range = arm_smmu_tlb_inv_range_s2, + .tlb_sync = arm_smmu_tlb_sync_context, }; -static const struct iommu_gather_ops arm_smmu_s2_tlb_ops_v1 = { - .tlb_flush_all = arm_smmu_tlb_inv_context_s2, - .tlb_add_flush = arm_smmu_tlb_inv_vmid_nosync, - .tlb_sync = arm_smmu_tlb_sync_vmid, +static const struct arm_smmu_flush_ops arm_smmu_s2_tlb_ops_v1 = { + .tlb = { + .tlb_flush_all = arm_smmu_tlb_inv_context_s2, + .tlb_flush_walk = arm_smmu_tlb_inv_walk, + .tlb_flush_leaf = arm_smmu_tlb_inv_leaf, + .tlb_add_page = arm_smmu_tlb_add_page, + }, + .tlb_inv_range = arm_smmu_tlb_inv_vmid_nosync, + .tlb_sync = arm_smmu_tlb_sync_vmid, }; static irqreturn_t arm_smmu_context_fault(int irq, void *dev) @@ -571,26 +449,22 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev) unsigned long iova; struct iommu_domain *domain = dev; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); - struct arm_smmu_cfg *cfg = &smmu_domain->cfg; struct arm_smmu_device *smmu = smmu_domain->smmu; - void __iomem *gr1_base = ARM_SMMU_GR1(smmu); - void __iomem *cb_base; - - cb_base = ARM_SMMU_CB(smmu, cfg->cbndx); - fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR); + int idx = smmu_domain->cfg.cbndx; + fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR); if (!(fsr & FSR_FAULT)) return IRQ_NONE; - fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0); - iova = readq_relaxed(cb_base + ARM_SMMU_CB_FAR); - cbfrsynra = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx)); + fsynr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSYNR0); + iova = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_FAR); + cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(idx)); dev_err_ratelimited(smmu->dev, "Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, cbfrsynra=0x%x, cb=%d\n", - fsr, iova, fsynr, cbfrsynra, cfg->cbndx); + fsr, iova, fsynr, cbfrsynra, idx); - writel(fsr, cb_base + ARM_SMMU_CB_FSR); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr); return IRQ_HANDLED; } @@ -598,12 +472,11 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev) { u32 gfsr, gfsynr0, gfsynr1, gfsynr2; struct arm_smmu_device *smmu = dev; - void __iomem *gr0_base = ARM_SMMU_GR0_NS(smmu); - gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR); - gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0); - gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1); - gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2); + gfsr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSR); + gfsynr0 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR0); + gfsynr1 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR1); + gfsynr2 = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSYNR2); if (!gfsr) return IRQ_NONE; @@ -614,7 +487,7 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev) "\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n", gfsr, gfsynr0, gfsynr1, gfsynr2); - writel(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sGFSR, gfsr); return IRQ_HANDLED; } @@ -627,16 +500,16 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, cb->cfg = cfg; - /* TTBCR */ + /* TCR */ if (stage1) { if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr; } else { cb->tcr[0] = pgtbl_cfg->arm_lpae_s1_cfg.tcr; cb->tcr[1] = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32; - cb->tcr[1] |= TTBCR2_SEP_UPSTREAM; + cb->tcr[1] |= FIELD_PREP(TCR2_SEP, TCR2_SEP_UPSTREAM); if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) - cb->tcr[1] |= TTBCR2_AS; + cb->tcr[1] |= TCR2_AS; } } else { cb->tcr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vtcr; @@ -649,9 +522,9 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain, cb->ttbr[1] = pgtbl_cfg->arm_v7s_cfg.ttbr[1]; } else { cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0]; - cb->ttbr[0] |= (u64)cfg->asid << TTBRn_ASID_SHIFT; + cb->ttbr[0] |= FIELD_PREP(TTBRn_ASID, cfg->asid); cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1]; - cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT; + cb->ttbr[1] |= FIELD_PREP(TTBRn_ASID, cfg->asid); } } else { cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; @@ -675,74 +548,71 @@ static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx) bool stage1; struct arm_smmu_cb *cb = &smmu->cbs[idx]; struct arm_smmu_cfg *cfg = cb->cfg; - void __iomem *cb_base, *gr1_base; - - cb_base = ARM_SMMU_CB(smmu, idx); /* Unassigned context banks only need disabling */ if (!cfg) { - writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, 0); return; } - gr1_base = ARM_SMMU_GR1(smmu); stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS; /* CBA2R */ if (smmu->version > ARM_SMMU_V1) { if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) - reg = CBA2R_RW64_64BIT; + reg = CBA2R_VA64; else - reg = CBA2R_RW64_32BIT; + reg = 0; /* 16-bit VMIDs live in CBA2R */ if (smmu->features & ARM_SMMU_FEAT_VMID16) - reg |= cfg->vmid << CBA2R_VMID_SHIFT; + reg |= FIELD_PREP(CBA2R_VMID16, cfg->vmid); - writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(idx)); + arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBA2R(idx), reg); } /* CBAR */ - reg = cfg->cbar; + reg = FIELD_PREP(CBAR_TYPE, cfg->cbar); if (smmu->version < ARM_SMMU_V2) - reg |= cfg->irptndx << CBAR_IRPTNDX_SHIFT; + reg |= FIELD_PREP(CBAR_IRPTNDX, cfg->irptndx); /* * Use the weakest shareability/memory types, so they are * overridden by the ttbcr/pte. */ if (stage1) { - reg |= (CBAR_S1_BPSHCFG_NSH << CBAR_S1_BPSHCFG_SHIFT) | - (CBAR_S1_MEMATTR_WB << CBAR_S1_MEMATTR_SHIFT); + reg |= FIELD_PREP(CBAR_S1_BPSHCFG, CBAR_S1_BPSHCFG_NSH) | + FIELD_PREP(CBAR_S1_MEMATTR, CBAR_S1_MEMATTR_WB); } else if (!(smmu->features & ARM_SMMU_FEAT_VMID16)) { /* 8-bit VMIDs live in CBAR */ - reg |= cfg->vmid << CBAR_VMID_SHIFT; + reg |= FIELD_PREP(CBAR_VMID, cfg->vmid); } - writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(idx)); + arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(idx), reg); /* - * TTBCR + * TCR * We must write this before the TTBRs, since it determines the * access behaviour of some fields (in particular, ASID[15:8]). */ if (stage1 && smmu->version > ARM_SMMU_V1) - writel_relaxed(cb->tcr[1], cb_base + ARM_SMMU_CB_TTBCR2); - writel_relaxed(cb->tcr[0], cb_base + ARM_SMMU_CB_TTBCR); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TCR2, cb->tcr[1]); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TCR, cb->tcr[0]); /* TTBRs */ if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) { - writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR); - writel_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0); - writel_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_CONTEXTIDR, cfg->asid); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TTBR0, cb->ttbr[0]); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_TTBR1, cb->ttbr[1]); } else { - writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0); + arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_TTBR0, cb->ttbr[0]); if (stage1) - writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1); + arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_TTBR1, + cb->ttbr[1]); } /* MAIRs (stage-1 only) */ if (stage1) { - writel_relaxed(cb->mair[0], cb_base + ARM_SMMU_CB_S1_MAIR0); - writel_relaxed(cb->mair[1], cb_base + ARM_SMMU_CB_S1_MAIR1); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_S1_MAIR0, cb->mair[0]); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_S1_MAIR1, cb->mair[1]); } /* SCTLR */ @@ -752,7 +622,7 @@ static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx) if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) reg |= SCTLR_E; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR); + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, reg); } static int arm_smmu_init_domain_context(struct iommu_domain *domain, @@ -842,7 +712,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, ias = min(ias, 32UL); oas = min(oas, 32UL); } - smmu_domain->tlb_ops = &arm_smmu_s1_tlb_ops; + smmu_domain->flush_ops = &arm_smmu_s1_tlb_ops; break; case ARM_SMMU_DOMAIN_NESTED: /* @@ -862,9 +732,9 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, oas = min(oas, 40UL); } if (smmu->version == ARM_SMMU_V2) - smmu_domain->tlb_ops = &arm_smmu_s2_tlb_ops_v2; + smmu_domain->flush_ops = &arm_smmu_s2_tlb_ops_v2; else - smmu_domain->tlb_ops = &arm_smmu_s2_tlb_ops_v1; + smmu_domain->flush_ops = &arm_smmu_s2_tlb_ops_v1; break; default: ret = -EINVAL; @@ -884,23 +754,29 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, } if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) - cfg->vmid = cfg->cbndx + 1 + smmu->cavium_id_base; + cfg->vmid = cfg->cbndx + 1; else - cfg->asid = cfg->cbndx + smmu->cavium_id_base; + cfg->asid = cfg->cbndx; + + smmu_domain->smmu = smmu; + if (smmu->impl && smmu->impl->init_context) { + ret = smmu->impl->init_context(smmu_domain); + if (ret) + goto out_unlock; + } pgtbl_cfg = (struct io_pgtable_cfg) { .pgsize_bitmap = smmu->pgsize_bitmap, .ias = ias, .oas = oas, .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK, - .tlb = smmu_domain->tlb_ops, + .tlb = &smmu_domain->flush_ops->tlb, .iommu_dev = smmu->dev, }; if (smmu_domain->non_strict) pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT; - smmu_domain->smmu = smmu; pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); if (!pgtbl_ops) { ret = -ENOMEM; @@ -1019,24 +895,24 @@ static void arm_smmu_domain_free(struct iommu_domain *domain) static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx) { struct arm_smmu_smr *smr = smmu->smrs + idx; - u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT; + u32 reg = FIELD_PREP(SMR_ID, smr->id) | FIELD_PREP(SMR_MASK, smr->mask); if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid) reg |= SMR_VALID; - writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx)); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), reg); } static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx) { struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx; - u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT | - (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT | - (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT; + u32 reg = FIELD_PREP(S2CR_TYPE, s2cr->type) | + FIELD_PREP(S2CR_CBNDX, s2cr->cbndx) | + FIELD_PREP(S2CR_PRIVCFG, s2cr->privcfg); if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs && smmu->smrs[idx].valid) reg |= S2CR_EXIDVALID; - writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx)); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg); } static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx) @@ -1052,7 +928,6 @@ static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx) */ static void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu) { - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); u32 smr; if (!smmu->smrs) @@ -1063,15 +938,15 @@ static void arm_smmu_test_smr_masks(struct arm_smmu_device *smmu) * bits are set, so check each one separately. We can reject * masters later if they try to claim IDs outside these masks. */ - smr = smmu->streamid_mask << SMR_ID_SHIFT; - writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0)); - smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0)); - smmu->streamid_mask = smr >> SMR_ID_SHIFT; + smr = FIELD_PREP(SMR_ID, smmu->streamid_mask); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(0), smr); + smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(0)); + smmu->streamid_mask = FIELD_GET(SMR_ID, smr); - smr = smmu->streamid_mask << SMR_MASK_SHIFT; - writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0)); - smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0)); - smmu->smr_mask_mask = smr >> SMR_MASK_SHIFT; + smr = FIELD_PREP(SMR_MASK, smmu->streamid_mask); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(0), smr); + smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(0)); + smmu->smr_mask_mask = FIELD_GET(SMR_MASK, smr); } static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask) @@ -1140,8 +1015,8 @@ static int arm_smmu_master_alloc_smes(struct device *dev) mutex_lock(&smmu->stream_map_mutex); /* Figure out a viable stream map entry allocation */ for_each_cfg_sme(fwspec, i, idx) { - u16 sid = fwspec->ids[i]; - u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT; + u16 sid = FIELD_GET(SMR_ID, fwspec->ids[i]); + u16 mask = FIELD_GET(SMR_MASK, fwspec->ids[i]); if (idx != INVALID_SMENDX) { ret = -EEXIST; @@ -1301,7 +1176,7 @@ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, } static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu; @@ -1311,7 +1186,7 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, return 0; arm_smmu_rpm_get(smmu); - ret = ops->unmap(ops, iova, size); + ret = ops->unmap(ops, iova, size, gather); arm_smmu_rpm_put(smmu); return ret; @@ -1322,21 +1197,22 @@ static void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_device *smmu = smmu_domain->smmu; - if (smmu_domain->tlb_ops) { + if (smmu_domain->flush_ops) { arm_smmu_rpm_get(smmu); - smmu_domain->tlb_ops->tlb_flush_all(smmu_domain); + smmu_domain->flush_ops->tlb.tlb_flush_all(smmu_domain); arm_smmu_rpm_put(smmu); } } -static void arm_smmu_iotlb_sync(struct iommu_domain *domain) +static void arm_smmu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) { struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_device *smmu = smmu_domain->smmu; - if (smmu_domain->tlb_ops) { + if (smmu_domain->flush_ops) { arm_smmu_rpm_get(smmu); - smmu_domain->tlb_ops->tlb_sync(smmu_domain); + smmu_domain->flush_ops->tlb_sync(smmu_domain); arm_smmu_rpm_put(smmu); } } @@ -1349,28 +1225,25 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, struct arm_smmu_cfg *cfg = &smmu_domain->cfg; struct io_pgtable_ops *ops= smmu_domain->pgtbl_ops; struct device *dev = smmu->dev; - void __iomem *cb_base; + void __iomem *reg; u32 tmp; u64 phys; unsigned long va, flags; - int ret; + int ret, idx = cfg->cbndx; ret = arm_smmu_rpm_get(smmu); if (ret < 0) return 0; - cb_base = ARM_SMMU_CB(smmu, cfg->cbndx); - spin_lock_irqsave(&smmu_domain->cb_lock, flags); - /* ATS1 registers can only be written atomically */ va = iova & ~0xfffUL; - if (smmu->version == ARM_SMMU_V2) - smmu_write_atomic_lq(va, cb_base + ARM_SMMU_CB_ATS1PR); - else /* Register is only 32-bit in v1 */ - writel_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR); + if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64) + arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_ATS1PR, va); + else + arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_ATS1PR, va); - if (readl_poll_timeout_atomic(cb_base + ARM_SMMU_CB_ATSR, tmp, - !(tmp & ATSR_ACTIVE), 5, 50)) { + reg = arm_smmu_page(smmu, ARM_SMMU_CB(smmu, idx)) + ARM_SMMU_CB_ATSR; + if (readl_poll_timeout_atomic(reg, tmp, !(tmp & ATSR_ACTIVE), 5, 50)) { spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); dev_err(dev, "iova to phys timed out on %pad. Falling back to software table walk.\n", @@ -1378,7 +1251,7 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, return ops->iova_to_phys(ops, iova); } - phys = readq_relaxed(cb_base + ARM_SMMU_CB_PAR); + phys = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_PAR); spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); if (phys & CB_PAR_F) { dev_err(dev, "translation fault!\n"); @@ -1466,8 +1339,8 @@ static int arm_smmu_add_device(struct device *dev) ret = -EINVAL; for (i = 0; i < fwspec->num_ids; i++) { - u16 sid = fwspec->ids[i]; - u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT; + u16 sid = FIELD_GET(SMR_ID, fwspec->ids[i]); + u16 mask = FIELD_GET(SMR_MASK, fwspec->ids[i]); if (sid & ~smmu->streamid_mask) { dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n", @@ -1648,12 +1521,12 @@ static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) u32 mask, fwid = 0; if (args->args_count > 0) - fwid |= (u16)args->args[0]; + fwid |= FIELD_PREP(SMR_ID, args->args[0]); if (args->args_count > 1) - fwid |= (u16)args->args[1] << SMR_MASK_SHIFT; + fwid |= FIELD_PREP(SMR_MASK, args->args[1]); else if (!of_property_read_u32(args->np, "stream-match-mask", &mask)) - fwid |= (u16)mask << SMR_MASK_SHIFT; + fwid |= FIELD_PREP(SMR_MASK, mask); return iommu_fwspec_add_ids(dev, &fwid, 1); } @@ -1706,13 +1579,12 @@ static struct iommu_ops arm_smmu_ops = { static void arm_smmu_device_reset(struct arm_smmu_device *smmu) { - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); int i; - u32 reg, major; + u32 reg; /* clear global FSR */ - reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR); - writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR); + reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sGFSR); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sGFSR, reg); /* * Reset stream mapping groups: Initial values mark all SMRn as @@ -1721,47 +1593,17 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) for (i = 0; i < smmu->num_mapping_groups; ++i) arm_smmu_write_sme(smmu, i); - if (smmu->model == ARM_MMU500) { - /* - * Before clearing ARM_MMU500_ACTLR_CPRE, need to - * clear CACHE_LOCK bit of ACR first. And, CACHE_LOCK - * bit is only present in MMU-500r2 onwards. - */ - reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID7); - major = (reg >> ID7_MAJOR_SHIFT) & ID7_MAJOR_MASK; - reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sACR); - if (major >= 2) - reg &= ~ARM_MMU500_ACR_CACHE_LOCK; - /* - * Allow unmatched Stream IDs to allocate bypass - * TLB entries for reduced latency. - */ - reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN; - writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_sACR); - } - /* Make sure all context banks are disabled and clear CB_FSR */ for (i = 0; i < smmu->num_context_banks; ++i) { - void __iomem *cb_base = ARM_SMMU_CB(smmu, i); - arm_smmu_write_context_bank(smmu, i); - writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR); - /* - * Disable MMU-500's not-particularly-beneficial next-page - * prefetcher for the sake of errata #841119 and #826419. - */ - if (smmu->model == ARM_MMU500) { - reg = readl_relaxed(cb_base + ARM_SMMU_CB_ACTLR); - reg &= ~ARM_MMU500_ACTLR_CPRE; - writel_relaxed(reg, cb_base + ARM_SMMU_CB_ACTLR); - } + arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_FSR, FSR_FAULT); } /* Invalidate the TLB, just in case */ - writel_relaxed(QCOM_DUMMY_VAL, gr0_base + ARM_SMMU_GR0_TLBIALLH); - writel_relaxed(QCOM_DUMMY_VAL, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIALLH, QCOM_DUMMY_VAL); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_TLBIALLNSNH, QCOM_DUMMY_VAL); - reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); + reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sCR0); /* Enable fault reporting */ reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE); @@ -1780,7 +1622,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) reg &= ~sCR0_FB; /* Don't upgrade barriers */ - reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT); + reg &= ~(sCR0_BSU); if (smmu->features & ARM_SMMU_FEAT_VMID16) reg |= sCR0_VMID16EN; @@ -1788,9 +1630,12 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) if (smmu->features & ARM_SMMU_FEAT_EXIDS) reg |= sCR0_EXIDENABLE; + if (smmu->impl && smmu->impl->reset) + smmu->impl->reset(smmu); + /* Push the button */ arm_smmu_tlb_sync_global(smmu); - writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, reg); } static int arm_smmu_id_size_to_bits(int size) @@ -1814,8 +1659,7 @@ static int arm_smmu_id_size_to_bits(int size) static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) { - unsigned long size; - void __iomem *gr0_base = ARM_SMMU_GR0(smmu); + unsigned int size; u32 id; bool cttw_reg, cttw_fw = smmu->features & ARM_SMMU_FEAT_COHERENT_WALK; int i; @@ -1825,7 +1669,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) smmu->version == ARM_SMMU_V2 ? 2 : 1); /* ID0 */ - id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID0); + id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID0); /* Restrict available stages based on module parameter */ if (force_stage == 1) @@ -1879,12 +1723,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) smmu->features |= ARM_SMMU_FEAT_EXIDS; size = 1 << 16; } else { - size = 1 << ((id >> ID0_NUMSIDB_SHIFT) & ID0_NUMSIDB_MASK); + size = 1 << FIELD_GET(ID0_NUMSIDB, id); } smmu->streamid_mask = size - 1; if (id & ID0_SMS) { smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH; - size = (id >> ID0_NUMSMRG_SHIFT) & ID0_NUMSMRG_MASK; + size = FIELD_GET(ID0_NUMSMRG, id); if (size == 0) { dev_err(smmu->dev, "stream-matching supported, but no SMRs present!\n"); @@ -1898,7 +1742,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) return -ENOMEM; dev_notice(smmu->dev, - "\tstream matching with %lu register groups", size); + "\tstream matching with %u register groups", size); } /* s2cr->type == 0 means translation, so initialise explicitly */ smmu->s2crs = devm_kmalloc_array(smmu->dev, size, sizeof(*smmu->s2crs), @@ -1919,49 +1763,38 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) } /* ID1 */ - id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID1); + id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID1); smmu->pgshift = (id & ID1_PAGESIZE) ? 16 : 12; /* Check for size mismatch of SMMU address space from mapped region */ - size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1); - size <<= smmu->pgshift; - if (smmu->cb_base != gr0_base + size) + size = 1 << (FIELD_GET(ID1_NUMPAGENDXB, id) + 1); + if (smmu->numpage != 2 * size << smmu->pgshift) dev_warn(smmu->dev, - "SMMU address space size (0x%lx) differs from mapped region size (0x%tx)!\n", - size * 2, (smmu->cb_base - gr0_base) * 2); + "SMMU address space size (0x%x) differs from mapped region size (0x%x)!\n", + 2 * size << smmu->pgshift, smmu->numpage); + /* Now properly encode NUMPAGE to subsequently derive SMMU_CB_BASE */ + smmu->numpage = size; - smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) & ID1_NUMS2CB_MASK; - smmu->num_context_banks = (id >> ID1_NUMCB_SHIFT) & ID1_NUMCB_MASK; + smmu->num_s2_context_banks = FIELD_GET(ID1_NUMS2CB, id); + smmu->num_context_banks = FIELD_GET(ID1_NUMCB, id); if (smmu->num_s2_context_banks > smmu->num_context_banks) { dev_err(smmu->dev, "impossible number of S2 context banks!\n"); return -ENODEV; } dev_notice(smmu->dev, "\t%u context banks (%u stage-2 only)\n", smmu->num_context_banks, smmu->num_s2_context_banks); - /* - * Cavium CN88xx erratum #27704. - * Ensure ASID and VMID allocation is unique across all SMMUs in - * the system. - */ - if (smmu->model == CAVIUM_SMMUV2) { - smmu->cavium_id_base = - atomic_add_return(smmu->num_context_banks, - &cavium_smmu_context_count); - smmu->cavium_id_base -= smmu->num_context_banks; - dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); - } smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks, sizeof(*smmu->cbs), GFP_KERNEL); if (!smmu->cbs) return -ENOMEM; /* ID2 */ - id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2); - size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK); + id = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID2); + size = arm_smmu_id_size_to_bits(FIELD_GET(ID2_IAS, id)); smmu->ipa_size = size; /* The output mask is also applied for bypass */ - size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK); + size = arm_smmu_id_size_to_bits(FIELD_GET(ID2_OAS, id)); smmu->pa_size = size; if (id & ID2_VMID16) @@ -1981,7 +1814,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) if (smmu->version == ARM_SMMU_V1_64K) smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_64K; } else { - size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK; + size = FIELD_GET(ID2_UBS, id); smmu->va_size = arm_smmu_id_size_to_bits(size); if (id & ID2_PTFS_4K) smmu->features |= ARM_SMMU_FEAT_FMT_AARCH64_4K; @@ -2018,6 +1851,9 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n", smmu->ipa_size, smmu->pa_size); + if (smmu->impl && smmu->impl->cfg_probe) + return smmu->impl->cfg_probe(smmu); + return 0; } @@ -2130,8 +1966,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev, smmu->version = data->version; smmu->model = data->model; - parse_driver_options(smmu); - legacy_binding = of_find_property(dev->of_node, "mmu-masters", NULL); if (legacy_binding && !using_generic_binding) { if (!using_legacy_binding) @@ -2194,12 +2028,20 @@ static int arm_smmu_device_probe(struct platform_device *pdev) if (err) return err; + smmu = arm_smmu_impl_init(smmu); + if (IS_ERR(smmu)) + return PTR_ERR(smmu); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ioaddr = res->start; smmu->base = devm_ioremap_resource(dev, res); if (IS_ERR(smmu->base)) return PTR_ERR(smmu->base); - smmu->cb_base = smmu->base + resource_size(res) / 2; + /* + * The resource size should effectively match the value of SMMU_TOP; + * stash that temporarily until we know PAGESIZE to validate it with. + */ + smmu->numpage = resource_size(res); num_irqs = 0; while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) { @@ -2339,7 +2181,7 @@ static void arm_smmu_device_shutdown(struct platform_device *pdev) arm_smmu_rpm_get(smmu); /* Turn the thing off */ - writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, sCR0_CLIENTPD); arm_smmu_rpm_put(smmu); if (pm_runtime_enabled(smmu->dev)) diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h new file mode 100644 index 000000000000..b19b6cae9b5e --- /dev/null +++ b/drivers/iommu/arm-smmu.h @@ -0,0 +1,402 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * IOMMU API for ARM architected SMMU implementations. + * + * Copyright (C) 2013 ARM Limited + * + * Author: Will Deacon <will.deacon@arm.com> + */ + +#ifndef _ARM_SMMU_H +#define _ARM_SMMU_H + +#include <linux/atomic.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io-64-nonatomic-hi-lo.h> +#include <linux/io-pgtable.h> +#include <linux/iommu.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +/* Configuration registers */ +#define ARM_SMMU_GR0_sCR0 0x0 +#define sCR0_VMID16EN BIT(31) +#define sCR0_BSU GENMASK(15, 14) +#define sCR0_FB BIT(13) +#define sCR0_PTM BIT(12) +#define sCR0_VMIDPNE BIT(11) +#define sCR0_USFCFG BIT(10) +#define sCR0_GCFGFIE BIT(5) +#define sCR0_GCFGFRE BIT(4) +#define sCR0_EXIDENABLE BIT(3) +#define sCR0_GFIE BIT(2) +#define sCR0_GFRE BIT(1) +#define sCR0_CLIENTPD BIT(0) + +/* Auxiliary Configuration register */ +#define ARM_SMMU_GR0_sACR 0x10 + +/* Identification registers */ +#define ARM_SMMU_GR0_ID0 0x20 +#define ID0_S1TS BIT(30) +#define ID0_S2TS BIT(29) +#define ID0_NTS BIT(28) +#define ID0_SMS BIT(27) +#define ID0_ATOSNS BIT(26) +#define ID0_PTFS_NO_AARCH32 BIT(25) +#define ID0_PTFS_NO_AARCH32S BIT(24) +#define ID0_NUMIRPT GENMASK(23, 16) +#define ID0_CTTW BIT(14) +#define ID0_NUMSIDB GENMASK(12, 9) +#define ID0_EXIDS BIT(8) +#define ID0_NUMSMRG GENMASK(7, 0) + +#define ARM_SMMU_GR0_ID1 0x24 +#define ID1_PAGESIZE BIT(31) +#define ID1_NUMPAGENDXB GENMASK(30, 28) +#define ID1_NUMS2CB GENMASK(23, 16) +#define ID1_NUMCB GENMASK(7, 0) + +#define ARM_SMMU_GR0_ID2 0x28 +#define ID2_VMID16 BIT(15) +#define ID2_PTFS_64K BIT(14) +#define ID2_PTFS_16K BIT(13) +#define ID2_PTFS_4K BIT(12) +#define ID2_UBS GENMASK(11, 8) +#define ID2_OAS GENMASK(7, 4) +#define ID2_IAS GENMASK(3, 0) + +#define ARM_SMMU_GR0_ID3 0x2c +#define ARM_SMMU_GR0_ID4 0x30 +#define ARM_SMMU_GR0_ID5 0x34 +#define ARM_SMMU_GR0_ID6 0x38 + +#define ARM_SMMU_GR0_ID7 0x3c +#define ID7_MAJOR GENMASK(7, 4) +#define ID7_MINOR GENMASK(3, 0) + +#define ARM_SMMU_GR0_sGFSR 0x48 +#define ARM_SMMU_GR0_sGFSYNR0 0x50 +#define ARM_SMMU_GR0_sGFSYNR1 0x54 +#define ARM_SMMU_GR0_sGFSYNR2 0x58 + +/* Global TLB invalidation */ +#define ARM_SMMU_GR0_TLBIVMID 0x64 +#define ARM_SMMU_GR0_TLBIALLNSNH 0x68 +#define ARM_SMMU_GR0_TLBIALLH 0x6c +#define ARM_SMMU_GR0_sTLBGSYNC 0x70 + +#define ARM_SMMU_GR0_sTLBGSTATUS 0x74 +#define sTLBGSTATUS_GSACTIVE BIT(0) + +/* Stream mapping registers */ +#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2)) +#define SMR_VALID BIT(31) +#define SMR_MASK GENMASK(31, 16) +#define SMR_ID GENMASK(15, 0) + +#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2)) +#define S2CR_PRIVCFG GENMASK(25, 24) +enum arm_smmu_s2cr_privcfg { + S2CR_PRIVCFG_DEFAULT, + S2CR_PRIVCFG_DIPAN, + S2CR_PRIVCFG_UNPRIV, + S2CR_PRIVCFG_PRIV, +}; +#define S2CR_TYPE GENMASK(17, 16) +enum arm_smmu_s2cr_type { + S2CR_TYPE_TRANS, + S2CR_TYPE_BYPASS, + S2CR_TYPE_FAULT, +}; +#define S2CR_EXIDVALID BIT(10) +#define S2CR_CBNDX GENMASK(7, 0) + +/* Context bank attribute registers */ +#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2)) +#define CBAR_IRPTNDX GENMASK(31, 24) +#define CBAR_TYPE GENMASK(17, 16) +enum arm_smmu_cbar_type { + CBAR_TYPE_S2_TRANS, + CBAR_TYPE_S1_TRANS_S2_BYPASS, + CBAR_TYPE_S1_TRANS_S2_FAULT, + CBAR_TYPE_S1_TRANS_S2_TRANS, +}; +#define CBAR_S1_MEMATTR GENMASK(15, 12) +#define CBAR_S1_MEMATTR_WB 0xf +#define CBAR_S1_BPSHCFG GENMASK(9, 8) +#define CBAR_S1_BPSHCFG_NSH 3 +#define CBAR_VMID GENMASK(7, 0) + +#define ARM_SMMU_GR1_CBFRSYNRA(n) (0x400 + ((n) << 2)) + +#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2)) +#define CBA2R_VMID16 GENMASK(31, 16) +#define CBA2R_VA64 BIT(0) + +#define ARM_SMMU_CB_SCTLR 0x0 +#define SCTLR_S1_ASIDPNE BIT(12) +#define SCTLR_CFCFG BIT(7) +#define SCTLR_CFIE BIT(6) +#define SCTLR_CFRE BIT(5) +#define SCTLR_E BIT(4) +#define SCTLR_AFE BIT(2) +#define SCTLR_TRE BIT(1) +#define SCTLR_M BIT(0) + +#define ARM_SMMU_CB_ACTLR 0x4 + +#define ARM_SMMU_CB_RESUME 0x8 +#define RESUME_TERMINATE BIT(0) + +#define ARM_SMMU_CB_TCR2 0x10 +#define TCR2_SEP GENMASK(17, 15) +#define TCR2_SEP_UPSTREAM 0x7 +#define TCR2_AS BIT(4) + +#define ARM_SMMU_CB_TTBR0 0x20 +#define ARM_SMMU_CB_TTBR1 0x28 +#define TTBRn_ASID GENMASK_ULL(63, 48) + +#define ARM_SMMU_CB_TCR 0x30 +#define ARM_SMMU_CB_CONTEXTIDR 0x34 +#define ARM_SMMU_CB_S1_MAIR0 0x38 +#define ARM_SMMU_CB_S1_MAIR1 0x3c + +#define ARM_SMMU_CB_PAR 0x50 +#define CB_PAR_F BIT(0) + +#define ARM_SMMU_CB_FSR 0x58 +#define FSR_MULTI BIT(31) +#define FSR_SS BIT(30) +#define FSR_UUT BIT(8) +#define FSR_ASF BIT(7) +#define FSR_TLBLKF BIT(6) +#define FSR_TLBMCF BIT(5) +#define FSR_EF BIT(4) +#define FSR_PF BIT(3) +#define FSR_AFF BIT(2) +#define FSR_TF BIT(1) + +#define FSR_IGN (FSR_AFF | FSR_ASF | \ + FSR_TLBMCF | FSR_TLBLKF) +#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \ + FSR_EF | FSR_PF | FSR_TF | FSR_IGN) + +#define ARM_SMMU_CB_FAR 0x60 + +#define ARM_SMMU_CB_FSYNR0 0x68 +#define FSYNR0_WNR BIT(4) + +#define ARM_SMMU_CB_S1_TLBIVA 0x600 +#define ARM_SMMU_CB_S1_TLBIASID 0x610 +#define ARM_SMMU_CB_S1_TLBIVAL 0x620 +#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630 +#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638 +#define ARM_SMMU_CB_TLBSYNC 0x7f0 +#define ARM_SMMU_CB_TLBSTATUS 0x7f4 +#define ARM_SMMU_CB_ATS1PR 0x800 + +#define ARM_SMMU_CB_ATSR 0x8f0 +#define ATSR_ACTIVE BIT(0) + + +/* Maximum number of context banks per SMMU */ +#define ARM_SMMU_MAX_CBS 128 + + +/* Shared driver definitions */ +enum arm_smmu_arch_version { + ARM_SMMU_V1, + ARM_SMMU_V1_64K, + ARM_SMMU_V2, +}; + +enum arm_smmu_implementation { + GENERIC_SMMU, + ARM_MMU500, + CAVIUM_SMMUV2, + QCOM_SMMUV2, +}; + +struct arm_smmu_device { + struct device *dev; + + void __iomem *base; + unsigned int numpage; + unsigned int pgshift; + +#define ARM_SMMU_FEAT_COHERENT_WALK (1 << 0) +#define ARM_SMMU_FEAT_STREAM_MATCH (1 << 1) +#define ARM_SMMU_FEAT_TRANS_S1 (1 << 2) +#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3) +#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4) +#define ARM_SMMU_FEAT_TRANS_OPS (1 << 5) +#define ARM_SMMU_FEAT_VMID16 (1 << 6) +#define ARM_SMMU_FEAT_FMT_AARCH64_4K (1 << 7) +#define ARM_SMMU_FEAT_FMT_AARCH64_16K (1 << 8) +#define ARM_SMMU_FEAT_FMT_AARCH64_64K (1 << 9) +#define ARM_SMMU_FEAT_FMT_AARCH32_L (1 << 10) +#define ARM_SMMU_FEAT_FMT_AARCH32_S (1 << 11) +#define ARM_SMMU_FEAT_EXIDS (1 << 12) + u32 features; + + enum arm_smmu_arch_version version; + enum arm_smmu_implementation model; + const struct arm_smmu_impl *impl; + + u32 num_context_banks; + u32 num_s2_context_banks; + DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS); + struct arm_smmu_cb *cbs; + atomic_t irptndx; + + u32 num_mapping_groups; + u16 streamid_mask; + u16 smr_mask_mask; + struct arm_smmu_smr *smrs; + struct arm_smmu_s2cr *s2crs; + struct mutex stream_map_mutex; + + unsigned long va_size; + unsigned long ipa_size; + unsigned long pa_size; + unsigned long pgsize_bitmap; + + u32 num_global_irqs; + u32 num_context_irqs; + unsigned int *irqs; + struct clk_bulk_data *clks; + int num_clks; + + spinlock_t global_sync_lock; + + /* IOMMU core code handle */ + struct iommu_device iommu; +}; + +enum arm_smmu_context_fmt { + ARM_SMMU_CTX_FMT_NONE, + ARM_SMMU_CTX_FMT_AARCH64, + ARM_SMMU_CTX_FMT_AARCH32_L, + ARM_SMMU_CTX_FMT_AARCH32_S, +}; + +struct arm_smmu_cfg { + u8 cbndx; + u8 irptndx; + union { + u16 asid; + u16 vmid; + }; + enum arm_smmu_cbar_type cbar; + enum arm_smmu_context_fmt fmt; +}; +#define INVALID_IRPTNDX 0xff + +enum arm_smmu_domain_stage { + ARM_SMMU_DOMAIN_S1 = 0, + ARM_SMMU_DOMAIN_S2, + ARM_SMMU_DOMAIN_NESTED, + ARM_SMMU_DOMAIN_BYPASS, +}; + +struct arm_smmu_flush_ops { + struct iommu_flush_ops tlb; + void (*tlb_inv_range)(unsigned long iova, size_t size, size_t granule, + bool leaf, void *cookie); + void (*tlb_sync)(void *cookie); +}; + +struct arm_smmu_domain { + struct arm_smmu_device *smmu; + struct io_pgtable_ops *pgtbl_ops; + const struct arm_smmu_flush_ops *flush_ops; + struct arm_smmu_cfg cfg; + enum arm_smmu_domain_stage stage; + bool non_strict; + struct mutex init_mutex; /* Protects smmu pointer */ + spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */ + struct iommu_domain domain; +}; + + +/* Implementation details, yay! */ +struct arm_smmu_impl { + u32 (*read_reg)(struct arm_smmu_device *smmu, int page, int offset); + void (*write_reg)(struct arm_smmu_device *smmu, int page, int offset, + u32 val); + u64 (*read_reg64)(struct arm_smmu_device *smmu, int page, int offset); + void (*write_reg64)(struct arm_smmu_device *smmu, int page, int offset, + u64 val); + int (*cfg_probe)(struct arm_smmu_device *smmu); + int (*reset)(struct arm_smmu_device *smmu); + int (*init_context)(struct arm_smmu_domain *smmu_domain); +}; + +static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n) +{ + return smmu->base + (n << smmu->pgshift); +} + +static inline u32 arm_smmu_readl(struct arm_smmu_device *smmu, int page, int offset) +{ + if (smmu->impl && unlikely(smmu->impl->read_reg)) + return smmu->impl->read_reg(smmu, page, offset); + return readl_relaxed(arm_smmu_page(smmu, page) + offset); +} + +static inline void arm_smmu_writel(struct arm_smmu_device *smmu, int page, + int offset, u32 val) +{ + if (smmu->impl && unlikely(smmu->impl->write_reg)) + smmu->impl->write_reg(smmu, page, offset, val); + else + writel_relaxed(val, arm_smmu_page(smmu, page) + offset); +} + +static inline u64 arm_smmu_readq(struct arm_smmu_device *smmu, int page, int offset) +{ + if (smmu->impl && unlikely(smmu->impl->read_reg64)) + return smmu->impl->read_reg64(smmu, page, offset); + return readq_relaxed(arm_smmu_page(smmu, page) + offset); +} + +static inline void arm_smmu_writeq(struct arm_smmu_device *smmu, int page, + int offset, u64 val) +{ + if (smmu->impl && unlikely(smmu->impl->write_reg64)) + smmu->impl->write_reg64(smmu, page, offset, val); + else + writeq_relaxed(val, arm_smmu_page(smmu, page) + offset); +} + +#define ARM_SMMU_GR0 0 +#define ARM_SMMU_GR1 1 +#define ARM_SMMU_CB(s, n) ((s)->numpage + (n)) + +#define arm_smmu_gr0_read(s, o) \ + arm_smmu_readl((s), ARM_SMMU_GR0, (o)) +#define arm_smmu_gr0_write(s, o, v) \ + arm_smmu_writel((s), ARM_SMMU_GR0, (o), (v)) + +#define arm_smmu_gr1_read(s, o) \ + arm_smmu_readl((s), ARM_SMMU_GR1, (o)) +#define arm_smmu_gr1_write(s, o, v) \ + arm_smmu_writel((s), ARM_SMMU_GR1, (o), (v)) + +#define arm_smmu_cb_read(s, n, o) \ + arm_smmu_readl((s), ARM_SMMU_CB((s), (n)), (o)) +#define arm_smmu_cb_write(s, n, o, v) \ + arm_smmu_writel((s), ARM_SMMU_CB((s), (n)), (o), (v)) +#define arm_smmu_cb_readq(s, n, o) \ + arm_smmu_readq((s), ARM_SMMU_CB((s), (n)), (o)) +#define arm_smmu_cb_writeq(s, n, o, v) \ + arm_smmu_writeq((s), ARM_SMMU_CB((s), (n)), (o), (v)) + +struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu); + +#endif /* _ARM_SMMU_H */ diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index a7f9c3edbcb2..8f412af84247 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -303,13 +303,15 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size, struct device *dev) { struct iommu_dma_cookie *cookie = domain->iova_cookie; - struct iova_domain *iovad = &cookie->iovad; unsigned long order, base_pfn; + struct iova_domain *iovad; int attr; if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE) return -EINVAL; + iovad = &cookie->iovad; + /* Use the smallest supported page size for IOVA granularity */ order = __ffs(domain->pgsize_bitmap); base_pfn = max_t(unsigned long, 1, base >> order); @@ -444,13 +446,18 @@ static void __iommu_dma_unmap(struct device *dev, dma_addr_t dma_addr, struct iommu_dma_cookie *cookie = domain->iova_cookie; struct iova_domain *iovad = &cookie->iovad; size_t iova_off = iova_offset(iovad, dma_addr); + struct iommu_iotlb_gather iotlb_gather; + size_t unmapped; dma_addr -= iova_off; size = iova_align(iovad, size + iova_off); + iommu_iotlb_gather_init(&iotlb_gather); + + unmapped = iommu_unmap_fast(domain, dma_addr, size, &iotlb_gather); + WARN_ON(unmapped != size); - WARN_ON(iommu_unmap_fast(domain, dma_addr, size) != size); if (!cookie->fq_domain) - iommu_tlb_sync(domain); + iommu_tlb_sync(domain, &iotlb_gather); iommu_dma_free_iova(cookie, dma_addr, size); } @@ -459,13 +466,11 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys, { struct iommu_domain *domain = iommu_get_dma_domain(dev); struct iommu_dma_cookie *cookie = domain->iova_cookie; - size_t iova_off = 0; + struct iova_domain *iovad = &cookie->iovad; + size_t iova_off = iova_offset(iovad, phys); dma_addr_t iova; - if (cookie->type == IOMMU_DMA_IOVA_COOKIE) { - iova_off = iova_offset(&cookie->iovad, phys); - size = iova_align(&cookie->iovad, size + iova_off); - } + size = iova_align(iovad, size + iova_off); iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev); if (!iova) @@ -574,7 +579,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, size_t size, struct iova_domain *iovad = &cookie->iovad; bool coherent = dev_is_dma_coherent(dev); int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs); - pgprot_t prot = arch_dma_mmap_pgprot(dev, PAGE_KERNEL, attrs); + pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs); unsigned int count, min_size, alloc_sizes = domain->pgsize_bitmap; struct page **pages; struct sg_table sgt; @@ -764,7 +769,7 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents, * - and wouldn't make the resulting output segment too long */ if (cur_len && !s_iova_off && (dma_addr & seg_mask) && - (cur_len + s_length <= max_len)) { + (max_len - cur_len >= s_length)) { /* ...then concatenate it with the previous one */ cur_len += s_length; } else { @@ -967,15 +972,18 @@ static void *iommu_dma_alloc_pages(struct device *dev, size_t size, { bool coherent = dev_is_dma_coherent(dev); size_t alloc_size = PAGE_ALIGN(size); + int node = dev_to_node(dev); struct page *page = NULL; void *cpu_addr; page = dma_alloc_contiguous(dev, alloc_size, gfp); if (!page) + page = alloc_pages_node(node, gfp, get_order(alloc_size)); + if (!page) return NULL; if (IS_ENABLED(CONFIG_DMA_REMAP) && (!coherent || PageHighMem(page))) { - pgprot_t prot = arch_dma_mmap_pgprot(dev, PAGE_KERNEL, attrs); + pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs); cpu_addr = dma_common_contiguous_remap(page, alloc_size, VM_USERMAP, prot, __builtin_return_address(0)); @@ -1035,7 +1043,7 @@ static int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma, unsigned long pfn, off = vma->vm_pgoff; int ret; - vma->vm_page_prot = arch_dma_mmap_pgprot(dev, vma->vm_page_prot, attrs); + vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs); if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret)) return ret; @@ -1147,16 +1155,21 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev, if (!msi_page) return NULL; - iova = __iommu_dma_map(dev, msi_addr, size, prot); - if (iova == DMA_MAPPING_ERROR) + iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev); + if (!iova) goto out_free_page; + if (iommu_map(domain, iova, msi_addr, size, prot)) + goto out_free_iova; + INIT_LIST_HEAD(&msi_page->list); msi_page->phys = msi_addr; msi_page->iova = iova; list_add(&msi_page->list, &cookie->msi_page_list); return msi_page; +out_free_iova: + iommu_dma_free_iova(cookie, iova, size); out_free_page: kfree(msi_page); return NULL; diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 5d0754ed5fa0..eecd6a421667 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1519,6 +1519,64 @@ static const char *dma_remap_fault_reasons[] = "PCE for translation request specifies blocking", }; +static const char * const dma_remap_sm_fault_reasons[] = { + "SM: Invalid Root Table Address", + "SM: TTM 0 for request with PASID", + "SM: TTM 0 for page group request", + "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x33-0x37 */ + "SM: Error attempting to access Root Entry", + "SM: Present bit in Root Entry is clear", + "SM: Non-zero reserved field set in Root Entry", + "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x3B-0x3F */ + "SM: Error attempting to access Context Entry", + "SM: Present bit in Context Entry is clear", + "SM: Non-zero reserved field set in the Context Entry", + "SM: Invalid Context Entry", + "SM: DTE field in Context Entry is clear", + "SM: PASID Enable field in Context Entry is clear", + "SM: PASID is larger than the max in Context Entry", + "SM: PRE field in Context-Entry is clear", + "SM: RID_PASID field error in Context-Entry", + "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x49-0x4F */ + "SM: Error attempting to access the PASID Directory Entry", + "SM: Present bit in Directory Entry is clear", + "SM: Non-zero reserved field set in PASID Directory Entry", + "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x53-0x57 */ + "SM: Error attempting to access PASID Table Entry", + "SM: Present bit in PASID Table Entry is clear", + "SM: Non-zero reserved field set in PASID Table Entry", + "SM: Invalid Scalable-Mode PASID Table Entry", + "SM: ERE field is clear in PASID Table Entry", + "SM: SRE field is clear in PASID Table Entry", + "Unknown", "Unknown",/* 0x5E-0x5F */ + "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x60-0x67 */ + "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x68-0x6F */ + "SM: Error attempting to access first-level paging entry", + "SM: Present bit in first-level paging entry is clear", + "SM: Non-zero reserved field set in first-level paging entry", + "SM: Error attempting to access FL-PML4 entry", + "SM: First-level entry address beyond MGAW in Nested translation", + "SM: Read permission error in FL-PML4 entry in Nested translation", + "SM: Read permission error in first-level paging entry in Nested translation", + "SM: Write permission error in first-level paging entry in Nested translation", + "SM: Error attempting to access second-level paging entry", + "SM: Read/Write permission error in second-level paging entry", + "SM: Non-zero reserved field set in second-level paging entry", + "SM: Invalid second-level page table pointer", + "SM: A/D bit update needed in second-level entry when set up in no snoop", + "Unknown", "Unknown", "Unknown", /* 0x7D-0x7F */ + "SM: Address in first-level translation is not canonical", + "SM: U/S set 0 for first-level translation with user privilege", + "SM: No execute permission for request with PASID and ER=1", + "SM: Address beyond the DMA hardware max", + "SM: Second-level entry address beyond the max", + "SM: No write permission for Write/AtomicOp request", + "SM: No read permission for Read/AtomicOp request", + "SM: Invalid address-interrupt address", + "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x88-0x8F */ + "SM: A/D bit update needed in first-level entry when set up in no snoop", +}; + static const char *irq_remap_fault_reasons[] = { "Detected reserved fields in the decoded interrupt-remapped request", @@ -1536,6 +1594,10 @@ static const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type) ARRAY_SIZE(irq_remap_fault_reasons))) { *fault_type = INTR_REMAP; return irq_remap_fault_reasons[fault_reason - 0x20]; + } else if (fault_reason >= 0x30 && (fault_reason - 0x30 < + ARRAY_SIZE(dma_remap_sm_fault_reasons))) { + *fault_type = DMA_REMAP; + return dma_remap_sm_fault_reasons[fault_reason - 0x30]; } else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) { *fault_type = DMA_REMAP; return dma_remap_fault_reasons[fault_reason]; @@ -1611,7 +1673,8 @@ void dmar_msi_read(int irq, struct msi_msg *msg) } static int dmar_fault_do_one(struct intel_iommu *iommu, int type, - u8 fault_reason, u16 source_id, unsigned long long addr) + u8 fault_reason, int pasid, u16 source_id, + unsigned long long addr) { const char *reason; int fault_type; @@ -1624,10 +1687,11 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type, PCI_FUNC(source_id & 0xFF), addr >> 48, fault_reason, reason); else - pr_err("[%s] Request device [%02x:%02x.%d] fault addr %llx [fault reason %02d] %s\n", + pr_err("[%s] Request device [%02x:%02x.%d] PASID %x fault addr %llx [fault reason %02d] %s\n", type ? "DMA Read" : "DMA Write", source_id >> 8, PCI_SLOT(source_id & 0xFF), - PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason); + PCI_FUNC(source_id & 0xFF), pasid, addr, + fault_reason, reason); return 0; } @@ -1659,8 +1723,9 @@ irqreturn_t dmar_fault(int irq, void *dev_id) u8 fault_reason; u16 source_id; u64 guest_addr; - int type; + int type, pasid; u32 data; + bool pasid_present; /* highest 32 bits */ data = readl(iommu->reg + reg + @@ -1672,10 +1737,12 @@ irqreturn_t dmar_fault(int irq, void *dev_id) fault_reason = dma_frcd_fault_reason(data); type = dma_frcd_type(data); + pasid = dma_frcd_pasid_value(data); data = readl(iommu->reg + reg + fault_index * PRIMARY_FAULT_REG_LEN + 8); source_id = dma_frcd_source_id(data); + pasid_present = dma_frcd_pasid_present(data); guest_addr = dmar_readq(iommu->reg + reg + fault_index * PRIMARY_FAULT_REG_LEN); guest_addr = dma_frcd_page_addr(guest_addr); @@ -1688,7 +1755,9 @@ irqreturn_t dmar_fault(int irq, void *dev_id) raw_spin_unlock_irqrestore(&iommu->register_lock, flag); if (!ratelimited) + /* Using pasid -1 if pasid is not present */ dmar_fault_do_one(iommu, type, fault_reason, + pasid_present ? pasid : -1, source_id, guest_addr); fault_index++; diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b0c1e5f9daae..9c94e16fb127 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -566,7 +566,7 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, static const struct iommu_ops exynos_iommu_ops; -static int __init exynos_sysmmu_probe(struct platform_device *pdev) +static int exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret; struct device *dev = &pdev->dev; @@ -583,10 +583,8 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) return PTR_ERR(data->sfrbase); irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "Unable to find IRQ resource\n"); + if (irq <= 0) return irq; - } ret = devm_request_irq(dev, irq, exynos_sysmmu_irq, 0, dev_name(dev), data); @@ -1130,7 +1128,8 @@ static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *domain } static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain, - unsigned long l_iova, size_t size) + unsigned long l_iova, size_t size, + struct iommu_iotlb_gather *gather) { struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain); sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; diff --git a/drivers/iommu/intel-iommu-debugfs.c b/drivers/iommu/intel-iommu-debugfs.c index 2b25d9c59336..471f05d452e0 100644 --- a/drivers/iommu/intel-iommu-debugfs.c +++ b/drivers/iommu/intel-iommu-debugfs.c @@ -235,7 +235,7 @@ static void ctx_tbl_walk(struct seq_file *m, struct intel_iommu *iommu, u16 bus) tbl_wlk.ctx_entry = context; m->private = &tbl_wlk; - if (pasid_supported(iommu) && is_pasid_enabled(context)) { + if (dmar_readq(iommu->reg + DMAR_RTADDR_REG) & DMA_RTADDR_SMT) { pasid_dir_ptr = context->lo & VTD_PAGE_MASK; pasid_dir_size = get_pasid_dir_size(context); pasid_dir_walk(m, pasid_dir_ptr, pasid_dir_size); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index bdaed2da8a55..87de0b975672 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -41,9 +41,11 @@ #include <linux/dma-direct.h> #include <linux/crash_dump.h> #include <linux/numa.h> +#include <linux/swiotlb.h> #include <asm/irq_remapping.h> #include <asm/cacheflush.h> #include <asm/iommu.h> +#include <trace/events/intel_iommu.h> #include "irq_remapping.h" #include "intel-pasid.h" @@ -339,11 +341,15 @@ static void domain_exit(struct dmar_domain *domain); static void domain_remove_dev_info(struct dmar_domain *domain); static void dmar_remove_one_dev_info(struct device *dev); static void __dmar_remove_one_dev_info(struct device_domain_info *info); +static void domain_context_clear(struct intel_iommu *iommu, + struct device *dev); static int domain_detach_iommu(struct dmar_domain *domain, struct intel_iommu *iommu); static bool device_is_rmrr_locked(struct device *dev); static int intel_iommu_attach_device(struct iommu_domain *domain, struct device *dev); +static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova); #ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON int dmar_disabled = 0; @@ -360,6 +366,7 @@ static int dmar_forcedac; static int intel_iommu_strict; static int intel_iommu_superpage = 1; static int iommu_identity_mapping; +static int intel_no_bounce; #define IDENTMAP_ALL 1 #define IDENTMAP_GFX 2 @@ -373,6 +380,9 @@ EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped); static DEFINE_SPINLOCK(device_domain_lock); static LIST_HEAD(device_domain_list); +#define device_needs_bounce(d) (!intel_no_bounce && dev_is_pci(d) && \ + to_pci_dev(d)->untrusted) + /* * Iterate over elements in device_domain_list and call the specified * callback @fn against each element. @@ -455,6 +465,9 @@ static int __init intel_iommu_setup(char *str) printk(KERN_INFO "Intel-IOMMU: not forcing on after tboot. This could expose security risk for tboot\n"); intel_iommu_tboot_noforce = 1; + } else if (!strncmp(str, "nobounce", 8)) { + pr_info("Intel-IOMMU: No bounce buffer. This could expose security risks of DMA attacks\n"); + intel_no_bounce = 1; } str += strcspn(str, ","); @@ -2105,9 +2118,26 @@ out_unlock: return ret; } +struct domain_context_mapping_data { + struct dmar_domain *domain; + struct intel_iommu *iommu; + struct pasid_table *table; +}; + +static int domain_context_mapping_cb(struct pci_dev *pdev, + u16 alias, void *opaque) +{ + struct domain_context_mapping_data *data = opaque; + + return domain_context_mapping_one(data->domain, data->iommu, + data->table, PCI_BUS_NUM(alias), + alias & 0xff); +} + static int domain_context_mapping(struct dmar_domain *domain, struct device *dev) { + struct domain_context_mapping_data data; struct pasid_table *table; struct intel_iommu *iommu; u8 bus, devfn; @@ -2117,7 +2147,17 @@ domain_context_mapping(struct dmar_domain *domain, struct device *dev) return -ENODEV; table = intel_pasid_get_table(dev); - return domain_context_mapping_one(domain, iommu, table, bus, devfn); + + if (!dev_is_pci(dev)) + return domain_context_mapping_one(domain, iommu, table, + bus, devfn); + + data.domain = domain; + data.iommu = iommu; + data.table = table; + + return pci_for_each_dma_alias(to_pci_dev(dev), + &domain_context_mapping_cb, &data); } static int domain_context_mapped_cb(struct pci_dev *pdev, @@ -3267,7 +3307,7 @@ static int __init init_dmars(void) iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); } - if (iommu_pass_through) + if (iommu_default_passthrough()) iommu_identity_mapping |= IDENTMAP_ALL; #ifdef CONFIG_INTEL_IOMMU_BROKEN_GFX_WA @@ -3449,6 +3489,7 @@ static bool iommu_need_mapping(struct device *dev) dmar_domain = to_dmar_domain(domain); dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN; } + dmar_remove_one_dev_info(dev); get_private_domain_for_dev(dev); } @@ -3504,6 +3545,9 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, start_paddr = (phys_addr_t)iova_pfn << PAGE_SHIFT; start_paddr += paddr & ~PAGE_MASK; + + trace_map_single(dev, start_paddr, paddr, size << VTD_PAGE_SHIFT); + return start_paddr; error: @@ -3559,10 +3603,7 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) if (dev_is_pci(dev)) pdev = to_pci_dev(dev); - dev_dbg(dev, "Device unmapping: pfn %lx-%lx\n", start_pfn, last_pfn); - freelist = domain_unmap(domain, start_pfn, last_pfn); - if (intel_iommu_strict || (pdev && pdev->untrusted) || !has_iova_flush_queue(&domain->iovad)) { iommu_flush_iotlb_psi(iommu, domain, start_pfn, @@ -3578,6 +3619,8 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) * cpu used up by the iotlb flush operation... */ } + + trace_unmap_single(dev, dev_addr, size); } static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr, @@ -3668,6 +3711,8 @@ static void intel_unmap_sg(struct device *dev, struct scatterlist *sglist, } intel_unmap(dev, startaddr, nrpages << VTD_PAGE_SHIFT); + + trace_unmap_sg(dev, startaddr, nrpages << VTD_PAGE_SHIFT); } static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nelems, @@ -3724,6 +3769,9 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele return 0; } + trace_map_sg(dev, iova_pfn << PAGE_SHIFT, + sg_phys(sglist), size << VTD_PAGE_SHIFT); + return nelems; } @@ -3739,6 +3787,252 @@ static const struct dma_map_ops intel_dma_ops = { .dma_supported = dma_direct_supported, }; +static void +bounce_sync_single(struct device *dev, dma_addr_t addr, size_t size, + enum dma_data_direction dir, enum dma_sync_target target) +{ + struct dmar_domain *domain; + phys_addr_t tlb_addr; + + domain = find_domain(dev); + if (WARN_ON(!domain)) + return; + + tlb_addr = intel_iommu_iova_to_phys(&domain->domain, addr); + if (is_swiotlb_buffer(tlb_addr)) + swiotlb_tbl_sync_single(dev, tlb_addr, size, dir, target); +} + +static dma_addr_t +bounce_map_single(struct device *dev, phys_addr_t paddr, size_t size, + enum dma_data_direction dir, unsigned long attrs, + u64 dma_mask) +{ + size_t aligned_size = ALIGN(size, VTD_PAGE_SIZE); + struct dmar_domain *domain; + struct intel_iommu *iommu; + unsigned long iova_pfn; + unsigned long nrpages; + phys_addr_t tlb_addr; + int prot = 0; + int ret; + + domain = find_domain(dev); + if (WARN_ON(dir == DMA_NONE || !domain)) + return DMA_MAPPING_ERROR; + + iommu = domain_get_iommu(domain); + if (WARN_ON(!iommu)) + return DMA_MAPPING_ERROR; + + nrpages = aligned_nrpages(0, size); + iova_pfn = intel_alloc_iova(dev, domain, + dma_to_mm_pfn(nrpages), dma_mask); + if (!iova_pfn) + return DMA_MAPPING_ERROR; + + /* + * Check if DMAR supports zero-length reads on write only + * mappings.. + */ + if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL || + !cap_zlr(iommu->cap)) + prot |= DMA_PTE_READ; + if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) + prot |= DMA_PTE_WRITE; + + /* + * If both the physical buffer start address and size are + * page aligned, we don't need to use a bounce page. + */ + if (!IS_ALIGNED(paddr | size, VTD_PAGE_SIZE)) { + tlb_addr = swiotlb_tbl_map_single(dev, + __phys_to_dma(dev, io_tlb_start), + paddr, size, aligned_size, dir, attrs); + if (tlb_addr == DMA_MAPPING_ERROR) { + goto swiotlb_error; + } else { + /* Cleanup the padding area. */ + void *padding_start = phys_to_virt(tlb_addr); + size_t padding_size = aligned_size; + + if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && + (dir == DMA_TO_DEVICE || + dir == DMA_BIDIRECTIONAL)) { + padding_start += size; + padding_size -= size; + } + + memset(padding_start, 0, padding_size); + } + } else { + tlb_addr = paddr; + } + + ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova_pfn), + tlb_addr >> VTD_PAGE_SHIFT, nrpages, prot); + if (ret) + goto mapping_error; + + trace_bounce_map_single(dev, iova_pfn << PAGE_SHIFT, paddr, size); + + return (phys_addr_t)iova_pfn << PAGE_SHIFT; + +mapping_error: + if (is_swiotlb_buffer(tlb_addr)) + swiotlb_tbl_unmap_single(dev, tlb_addr, size, + aligned_size, dir, attrs); +swiotlb_error: + free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(nrpages)); + dev_err(dev, "Device bounce map: %zx@%llx dir %d --- failed\n", + size, (unsigned long long)paddr, dir); + + return DMA_MAPPING_ERROR; +} + +static void +bounce_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + size_t aligned_size = ALIGN(size, VTD_PAGE_SIZE); + struct dmar_domain *domain; + phys_addr_t tlb_addr; + + domain = find_domain(dev); + if (WARN_ON(!domain)) + return; + + tlb_addr = intel_iommu_iova_to_phys(&domain->domain, dev_addr); + if (WARN_ON(!tlb_addr)) + return; + + intel_unmap(dev, dev_addr, size); + if (is_swiotlb_buffer(tlb_addr)) + swiotlb_tbl_unmap_single(dev, tlb_addr, size, + aligned_size, dir, attrs); + + trace_bounce_unmap_single(dev, dev_addr, size); +} + +static dma_addr_t +bounce_map_page(struct device *dev, struct page *page, unsigned long offset, + size_t size, enum dma_data_direction dir, unsigned long attrs) +{ + return bounce_map_single(dev, page_to_phys(page) + offset, + size, dir, attrs, *dev->dma_mask); +} + +static dma_addr_t +bounce_map_resource(struct device *dev, phys_addr_t phys_addr, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + return bounce_map_single(dev, phys_addr, size, + dir, attrs, *dev->dma_mask); +} + +static void +bounce_unmap_page(struct device *dev, dma_addr_t dev_addr, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + bounce_unmap_single(dev, dev_addr, size, dir, attrs); +} + +static void +bounce_unmap_resource(struct device *dev, dma_addr_t dev_addr, size_t size, + enum dma_data_direction dir, unsigned long attrs) +{ + bounce_unmap_single(dev, dev_addr, size, dir, attrs); +} + +static void +bounce_unmap_sg(struct device *dev, struct scatterlist *sglist, int nelems, + enum dma_data_direction dir, unsigned long attrs) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sglist, sg, nelems, i) + bounce_unmap_page(dev, sg->dma_address, + sg_dma_len(sg), dir, attrs); +} + +static int +bounce_map_sg(struct device *dev, struct scatterlist *sglist, int nelems, + enum dma_data_direction dir, unsigned long attrs) +{ + int i; + struct scatterlist *sg; + + for_each_sg(sglist, sg, nelems, i) { + sg->dma_address = bounce_map_page(dev, sg_page(sg), + sg->offset, sg->length, + dir, attrs); + if (sg->dma_address == DMA_MAPPING_ERROR) + goto out_unmap; + sg_dma_len(sg) = sg->length; + } + + return nelems; + +out_unmap: + bounce_unmap_sg(dev, sglist, i, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC); + return 0; +} + +static void +bounce_sync_single_for_cpu(struct device *dev, dma_addr_t addr, + size_t size, enum dma_data_direction dir) +{ + bounce_sync_single(dev, addr, size, dir, SYNC_FOR_CPU); +} + +static void +bounce_sync_single_for_device(struct device *dev, dma_addr_t addr, + size_t size, enum dma_data_direction dir) +{ + bounce_sync_single(dev, addr, size, dir, SYNC_FOR_DEVICE); +} + +static void +bounce_sync_sg_for_cpu(struct device *dev, struct scatterlist *sglist, + int nelems, enum dma_data_direction dir) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sglist, sg, nelems, i) + bounce_sync_single(dev, sg_dma_address(sg), + sg_dma_len(sg), dir, SYNC_FOR_CPU); +} + +static void +bounce_sync_sg_for_device(struct device *dev, struct scatterlist *sglist, + int nelems, enum dma_data_direction dir) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sglist, sg, nelems, i) + bounce_sync_single(dev, sg_dma_address(sg), + sg_dma_len(sg), dir, SYNC_FOR_DEVICE); +} + +static const struct dma_map_ops bounce_dma_ops = { + .alloc = intel_alloc_coherent, + .free = intel_free_coherent, + .map_sg = bounce_map_sg, + .unmap_sg = bounce_unmap_sg, + .map_page = bounce_map_page, + .unmap_page = bounce_unmap_page, + .sync_single_for_cpu = bounce_sync_single_for_cpu, + .sync_single_for_device = bounce_sync_single_for_device, + .sync_sg_for_cpu = bounce_sync_sg_for_cpu, + .sync_sg_for_device = bounce_sync_sg_for_device, + .map_resource = bounce_map_resource, + .unmap_resource = bounce_unmap_resource, + .dma_supported = dma_direct_supported, +}; + static inline int iommu_domain_cache_init(void) { int ret = 0; @@ -4539,22 +4833,20 @@ const struct attribute_group *intel_iommu_groups[] = { NULL, }; -static int __init platform_optin_force_iommu(void) +static inline bool has_untrusted_dev(void) { struct pci_dev *pdev = NULL; - bool has_untrusted_dev = false; - if (!dmar_platform_optin() || no_platform_optin) - return 0; + for_each_pci_dev(pdev) + if (pdev->untrusted) + return true; - for_each_pci_dev(pdev) { - if (pdev->untrusted) { - has_untrusted_dev = true; - break; - } - } + return false; +} - if (!has_untrusted_dev) +static int __init platform_optin_force_iommu(void) +{ + if (!dmar_platform_optin() || no_platform_optin || !has_untrusted_dev()) return 0; if (no_iommu || dmar_disabled) @@ -4568,9 +4860,6 @@ static int __init platform_optin_force_iommu(void) iommu_identity_mapping |= IDENTMAP_ALL; dmar_disabled = 0; -#if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB) - swiotlb = 0; -#endif no_iommu = 0; return 1; @@ -4710,7 +4999,14 @@ int __init intel_iommu_init(void) up_write(&dmar_global_lock); #if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB) - swiotlb = 0; + /* + * If the system has no untrusted device or the user has decided + * to disable the bounce page mechanisms, we don't need swiotlb. + * Mark this and the pre-allocated bounce pages will be released + * later. + */ + if (!has_untrusted_dev() || intel_no_bounce) + swiotlb = 0; #endif dma_ops = &intel_dma_ops; @@ -4758,6 +5054,28 @@ out_free_dmar: return ret; } +static int domain_context_clear_one_cb(struct pci_dev *pdev, u16 alias, void *opaque) +{ + struct intel_iommu *iommu = opaque; + + domain_context_clear_one(iommu, PCI_BUS_NUM(alias), alias & 0xff); + return 0; +} + +/* + * NB - intel-iommu lacks any sort of reference counting for the users of + * dependent devices. If multiple endpoints have intersecting dependent + * devices, unbinding the driver from any one of them will possibly leave + * the others unable to operate. + */ +static void domain_context_clear(struct intel_iommu *iommu, struct device *dev) +{ + if (!iommu || !dev || !dev_is_pci(dev)) + return; + + pci_for_each_dma_alias(to_pci_dev(dev), &domain_context_clear_one_cb, iommu); +} + static void __dmar_remove_one_dev_info(struct device_domain_info *info) { struct dmar_domain *domain; @@ -4778,7 +5096,7 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info) PASID_RID2PASID); iommu_disable_dev_iotlb(info); - domain_context_clear_one(iommu, info->bus, info->devfn); + domain_context_clear(iommu, info->dev); intel_pasid_free_table(info->dev); } @@ -4790,7 +5108,8 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info) /* free the private domain */ if (domain->flags & DOMAIN_FLAG_LOSE_CHILDREN && - !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY)) + !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) && + list_empty(&domain->devices)) domain_exit(info->domain); free_devinfo_mem(info); @@ -4803,7 +5122,8 @@ static void dmar_remove_one_dev_info(struct device *dev) spin_lock_irqsave(&device_domain_lock, flags); info = dev->archdata.iommu; - __dmar_remove_one_dev_info(info); + if (info) + __dmar_remove_one_dev_info(info); spin_unlock_irqrestore(&device_domain_lock, flags); } @@ -5150,7 +5470,8 @@ static int intel_iommu_map(struct iommu_domain *domain, } static size_t intel_iommu_unmap(struct iommu_domain *domain, - unsigned long iova, size_t size) + unsigned long iova, size_t size, + struct iommu_iotlb_gather *gather) { struct dmar_domain *dmar_domain = to_dmar_domain(domain); struct page *freelist = NULL; @@ -5281,6 +5602,7 @@ static int intel_iommu_add_device(struct device *dev) if (device_def_domain_type(dev) == IOMMU_DOMAIN_IDENTITY) { ret = iommu_request_dm_for_dev(dev); if (ret) { + dmar_remove_one_dev_info(dev); dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN; domain_add_dev_info(si_domain, dev); dev_info(dev, @@ -5291,6 +5613,7 @@ static int intel_iommu_add_device(struct device *dev) if (device_def_domain_type(dev) == IOMMU_DOMAIN_DMA) { ret = iommu_request_dma_domain_for_dev(dev); if (ret) { + dmar_remove_one_dev_info(dev); dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN; if (!get_private_domain_for_dev(dev)) { dev_warn(dev, @@ -5304,6 +5627,11 @@ static int intel_iommu_add_device(struct device *dev) } } + if (device_needs_bounce(dev)) { + dev_info(dev, "Use Intel IOMMU bounce page dma_ops\n"); + set_dma_ops(dev, &bounce_dma_ops); + } + return 0; } @@ -5316,9 +5644,14 @@ static void intel_iommu_remove_device(struct device *dev) if (!iommu) return; + dmar_remove_one_dev_info(dev); + iommu_group_remove_device(dev); iommu_device_unlink(&iommu->iommu, dev); + + if (device_needs_bounce(dev)) + set_dma_ops(dev, NULL); } static void intel_iommu_get_resv_regions(struct device *device, @@ -5632,20 +5965,46 @@ const struct iommu_ops intel_iommu_ops = { .pgsize_bitmap = INTEL_IOMMU_PGSIZES, }; -static void quirk_iommu_g4x_gfx(struct pci_dev *dev) +static void quirk_iommu_igfx(struct pci_dev *dev) { - /* G4x/GM45 integrated gfx dmar support is totally busted. */ pci_info(dev, "Disabling IOMMU for graphics on this chipset\n"); dmar_map_gfx = 0; } -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_g4x_gfx); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_g4x_gfx); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e10, quirk_iommu_g4x_gfx); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e20, quirk_iommu_g4x_gfx); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e30, quirk_iommu_g4x_gfx); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e40, quirk_iommu_g4x_gfx); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e90, quirk_iommu_g4x_gfx); +/* G4x/GM45 integrated gfx dmar support is totally busted. */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e10, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e20, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e30, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e40, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e90, quirk_iommu_igfx); + +/* Broadwell igfx malfunctions with dmar */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1606, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1602, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1616, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1612, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1626, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1622, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1636, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); static void quirk_iommu_rwbf(struct pci_dev *dev) { diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index 780de0caafe8..9b159132405d 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -100,24 +100,19 @@ int intel_svm_finish_prq(struct intel_iommu *iommu) } static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_dev *sdev, - unsigned long address, unsigned long pages, int ih, int gl) + unsigned long address, unsigned long pages, int ih) { struct qi_desc desc; - if (pages == -1) { - /* For global kernel pages we have to flush them in *all* PASIDs - * because that's the only option the hardware gives us. Despite - * the fact that they are actually only accessible through one. */ - if (gl) - desc.qw0 = QI_EIOTLB_PASID(svm->pasid) | - QI_EIOTLB_DID(sdev->did) | - QI_EIOTLB_GRAN(QI_GRAN_ALL_ALL) | - QI_EIOTLB_TYPE; - else - desc.qw0 = QI_EIOTLB_PASID(svm->pasid) | - QI_EIOTLB_DID(sdev->did) | - QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) | - QI_EIOTLB_TYPE; + /* + * Do PASID granu IOTLB invalidation if page selective capability is + * not available. + */ + if (pages == -1 || !cap_pgsel_inv(svm->iommu->cap)) { + desc.qw0 = QI_EIOTLB_PASID(svm->pasid) | + QI_EIOTLB_DID(sdev->did) | + QI_EIOTLB_GRAN(QI_GRAN_NONG_PASID) | + QI_EIOTLB_TYPE; desc.qw1 = 0; } else { int mask = ilog2(__roundup_pow_of_two(pages)); @@ -127,7 +122,6 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d QI_EIOTLB_GRAN(QI_GRAN_PSI_PASID) | QI_EIOTLB_TYPE; desc.qw1 = QI_EIOTLB_ADDR(address) | - QI_EIOTLB_GL(gl) | QI_EIOTLB_IH(ih) | QI_EIOTLB_AM(mask); } @@ -162,13 +156,13 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d } static void intel_flush_svm_range(struct intel_svm *svm, unsigned long address, - unsigned long pages, int ih, int gl) + unsigned long pages, int ih) { struct intel_svm_dev *sdev; rcu_read_lock(); list_for_each_entry_rcu(sdev, &svm->devs, list) - intel_flush_svm_range_dev(svm, sdev, address, pages, ih, gl); + intel_flush_svm_range_dev(svm, sdev, address, pages, ih); rcu_read_unlock(); } @@ -180,7 +174,7 @@ static void intel_invalidate_range(struct mmu_notifier *mn, struct intel_svm *svm = container_of(mn, struct intel_svm, notifier); intel_flush_svm_range(svm, start, - (end - start + PAGE_SIZE - 1) >> VTD_PAGE_SHIFT, 0, 0); + (end - start + PAGE_SIZE - 1) >> VTD_PAGE_SHIFT, 0); } static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) @@ -203,7 +197,7 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) rcu_read_lock(); list_for_each_entry_rcu(sdev, &svm->devs, list) { intel_pasid_tear_down_entry(svm->iommu, sdev->dev, svm->pasid); - intel_flush_svm_range_dev(svm, sdev, 0, -1, 0, !svm->mm); + intel_flush_svm_range_dev(svm, sdev, 0, -1, 0); } rcu_read_unlock(); @@ -425,7 +419,7 @@ int intel_svm_unbind_mm(struct device *dev, int pasid) * large and has to be physically contiguous. So it's * hard to be as defensive as we might like. */ intel_pasid_tear_down_entry(iommu, dev, svm->pasid); - intel_flush_svm_range_dev(svm, sdev, 0, -1, 0, !svm->mm); + intel_flush_svm_range_dev(svm, sdev, 0, -1, 0); kfree_rcu(sdev, rcu); if (list_empty(&svm->devs)) { diff --git a/drivers/iommu/intel-trace.c b/drivers/iommu/intel-trace.c new file mode 100644 index 000000000000..bfb6a6e37a88 --- /dev/null +++ b/drivers/iommu/intel-trace.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel IOMMU trace support + * + * Copyright (C) 2019 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ + +#include <linux/string.h> +#include <linux/types.h> + +#define CREATE_TRACE_POINTS +#include <trace/events/intel_iommu.h> diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 4786ca061e31..81e43c1df7ec 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -376,13 +376,13 @@ static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque) { struct set_msi_sid_data *data = opaque; + if (data->count == 0 || PCI_BUS_NUM(alias) == PCI_BUS_NUM(data->alias)) + data->busmatch_count++; + data->pdev = pdev; data->alias = alias; data->count++; - if (PCI_BUS_NUM(alias) == pdev->bus->number) - data->busmatch_count++; - return 0; } diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c index 0fc8dfab2abf..4cb394937700 100644 --- a/drivers/iommu/io-pgtable-arm-v7s.c +++ b/drivers/iommu/io-pgtable-arm-v7s.c @@ -112,7 +112,9 @@ #define ARM_V7S_TEX_MASK 0x7 #define ARM_V7S_ATTR_TEX(val) (((val) & ARM_V7S_TEX_MASK) << ARM_V7S_TEX_SHIFT) -#define ARM_V7S_ATTR_MTK_4GB BIT(9) /* MTK extend it for 4GB mode */ +/* MediaTek extend the two bits for PA 32bit/33bit */ +#define ARM_V7S_ATTR_MTK_PA_BIT32 BIT(9) +#define ARM_V7S_ATTR_MTK_PA_BIT33 BIT(4) /* *well, except for TEX on level 2 large pages, of course :( */ #define ARM_V7S_CONT_PAGE_TEX_SHIFT 6 @@ -169,18 +171,62 @@ struct arm_v7s_io_pgtable { spinlock_t split_lock; }; +static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl); + static dma_addr_t __arm_v7s_dma_addr(void *pages) { return (dma_addr_t)virt_to_phys(pages); } -static arm_v7s_iopte *iopte_deref(arm_v7s_iopte pte, int lvl) +static bool arm_v7s_is_mtk_enabled(struct io_pgtable_cfg *cfg) +{ + return IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT) && + (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT); +} + +static arm_v7s_iopte paddr_to_iopte(phys_addr_t paddr, int lvl, + struct io_pgtable_cfg *cfg) +{ + arm_v7s_iopte pte = paddr & ARM_V7S_LVL_MASK(lvl); + + if (!arm_v7s_is_mtk_enabled(cfg)) + return pte; + + if (paddr & BIT_ULL(32)) + pte |= ARM_V7S_ATTR_MTK_PA_BIT32; + if (paddr & BIT_ULL(33)) + pte |= ARM_V7S_ATTR_MTK_PA_BIT33; + return pte; +} + +static phys_addr_t iopte_to_paddr(arm_v7s_iopte pte, int lvl, + struct io_pgtable_cfg *cfg) { + arm_v7s_iopte mask; + phys_addr_t paddr; + if (ARM_V7S_PTE_IS_TABLE(pte, lvl)) - pte &= ARM_V7S_TABLE_MASK; + mask = ARM_V7S_TABLE_MASK; + else if (arm_v7s_pte_is_cont(pte, lvl)) + mask = ARM_V7S_LVL_MASK(lvl) * ARM_V7S_CONT_PAGES; else - pte &= ARM_V7S_LVL_MASK(lvl); - return phys_to_virt(pte); + mask = ARM_V7S_LVL_MASK(lvl); + + paddr = pte & mask; + if (!arm_v7s_is_mtk_enabled(cfg)) + return paddr; + + if (pte & ARM_V7S_ATTR_MTK_PA_BIT32) + paddr |= BIT_ULL(32); + if (pte & ARM_V7S_ATTR_MTK_PA_BIT33) + paddr |= BIT_ULL(33); + return paddr; +} + +static arm_v7s_iopte *iopte_deref(arm_v7s_iopte pte, int lvl, + struct arm_v7s_io_pgtable *data) +{ + return phys_to_virt(iopte_to_paddr(pte, lvl, &data->iop.cfg)); } static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp, @@ -295,9 +341,6 @@ static arm_v7s_iopte arm_v7s_prot_to_pte(int prot, int lvl, if (lvl == 1 && (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)) pte |= ARM_V7S_ATTR_NS_SECTION; - if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_4GB) - pte |= ARM_V7S_ATTR_MTK_4GB; - return pte; } @@ -362,7 +405,8 @@ static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl) return false; } -static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *, unsigned long, +static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *, + struct iommu_iotlb_gather *, unsigned long, size_t, int, arm_v7s_iopte *); static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data, @@ -383,7 +427,7 @@ static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data, size_t sz = ARM_V7S_BLOCK_SIZE(lvl); tblp = ptep - ARM_V7S_LVL_IDX(iova, lvl); - if (WARN_ON(__arm_v7s_unmap(data, iova + i * sz, + if (WARN_ON(__arm_v7s_unmap(data, NULL, iova + i * sz, sz, lvl, tblp) != sz)) return -EINVAL; } else if (ptep[i]) { @@ -396,7 +440,7 @@ static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data, if (num_entries > 1) pte = arm_v7s_pte_to_cont(pte, lvl); - pte |= paddr & ARM_V7S_LVL_MASK(lvl); + pte |= paddr_to_iopte(paddr, lvl, cfg); __arm_v7s_set_pte(ptep, pte, num_entries, cfg); return 0; @@ -462,7 +506,7 @@ static int __arm_v7s_map(struct arm_v7s_io_pgtable *data, unsigned long iova, } if (ARM_V7S_PTE_IS_TABLE(pte, lvl)) { - cptep = iopte_deref(pte, lvl); + cptep = iopte_deref(pte, lvl, data); } else if (pte) { /* We require an unmap first */ WARN_ON(!selftest_running); @@ -484,7 +528,8 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova, if (!(prot & (IOMMU_READ | IOMMU_WRITE))) return 0; - if (WARN_ON(upper_32_bits(iova) || upper_32_bits(paddr))) + if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) || + paddr >= (1ULL << data->iop.cfg.oas))) return -ERANGE; ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd); @@ -493,9 +538,8 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova, * a chance for anything to kick off a table walk for the new iova. */ if (iop->cfg.quirks & IO_PGTABLE_QUIRK_TLBI_ON_MAP) { - io_pgtable_tlb_add_flush(iop, iova, size, - ARM_V7S_BLOCK_SIZE(2), false); - io_pgtable_tlb_sync(iop); + io_pgtable_tlb_flush_walk(iop, iova, size, + ARM_V7S_BLOCK_SIZE(2)); } else { wmb(); } @@ -512,7 +556,8 @@ static void arm_v7s_free_pgtable(struct io_pgtable *iop) arm_v7s_iopte pte = data->pgd[i]; if (ARM_V7S_PTE_IS_TABLE(pte, 1)) - __arm_v7s_free_table(iopte_deref(pte, 1), 2, data); + __arm_v7s_free_table(iopte_deref(pte, 1, data), + 2, data); } __arm_v7s_free_table(data->pgd, 1, data); kmem_cache_destroy(data->l2_tables); @@ -541,12 +586,12 @@ static arm_v7s_iopte arm_v7s_split_cont(struct arm_v7s_io_pgtable *data, __arm_v7s_pte_sync(ptep, ARM_V7S_CONT_PAGES, &iop->cfg); size *= ARM_V7S_CONT_PAGES; - io_pgtable_tlb_add_flush(iop, iova, size, size, true); - io_pgtable_tlb_sync(iop); + io_pgtable_tlb_flush_leaf(iop, iova, size, size); return pte; } static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data, + struct iommu_iotlb_gather *gather, unsigned long iova, size_t size, arm_v7s_iopte blk_pte, arm_v7s_iopte *ptep) @@ -582,16 +627,16 @@ static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data, if (!ARM_V7S_PTE_IS_TABLE(pte, 1)) return 0; - tablep = iopte_deref(pte, 1); - return __arm_v7s_unmap(data, iova, size, 2, tablep); + tablep = iopte_deref(pte, 1, data); + return __arm_v7s_unmap(data, gather, iova, size, 2, tablep); } - io_pgtable_tlb_add_flush(&data->iop, iova, size, size, true); - io_pgtable_tlb_sync(&data->iop); + io_pgtable_tlb_add_page(&data->iop, gather, iova, size); return size; } static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, + struct iommu_iotlb_gather *gather, unsigned long iova, size_t size, int lvl, arm_v7s_iopte *ptep) { @@ -638,10 +683,9 @@ static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, for (i = 0; i < num_entries; i++) { if (ARM_V7S_PTE_IS_TABLE(pte[i], lvl)) { /* Also flush any partial walks */ - io_pgtable_tlb_add_flush(iop, iova, blk_size, - ARM_V7S_BLOCK_SIZE(lvl + 1), false); - io_pgtable_tlb_sync(iop); - ptep = iopte_deref(pte[i], lvl); + io_pgtable_tlb_flush_walk(iop, iova, blk_size, + ARM_V7S_BLOCK_SIZE(lvl + 1)); + ptep = iopte_deref(pte[i], lvl, data); __arm_v7s_free_table(ptep, lvl + 1, data); } else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) { /* @@ -651,8 +695,7 @@ static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, */ smp_wmb(); } else { - io_pgtable_tlb_add_flush(iop, iova, blk_size, - blk_size, true); + io_pgtable_tlb_add_page(iop, gather, iova, blk_size); } iova += blk_size; } @@ -662,23 +705,24 @@ static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data, * Insert a table at the next level to map the old region, * minus the part we want to unmap */ - return arm_v7s_split_blk_unmap(data, iova, size, pte[0], ptep); + return arm_v7s_split_blk_unmap(data, gather, iova, size, pte[0], + ptep); } /* Keep on walkin' */ - ptep = iopte_deref(pte[0], lvl); - return __arm_v7s_unmap(data, iova, size, lvl + 1, ptep); + ptep = iopte_deref(pte[0], lvl, data); + return __arm_v7s_unmap(data, gather, iova, size, lvl + 1, ptep); } static size_t arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops); if (WARN_ON(upper_32_bits(iova))) return 0; - return __arm_v7s_unmap(data, iova, size, 1, data->pgd); + return __arm_v7s_unmap(data, gather, iova, size, 1, data->pgd); } static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops, @@ -692,7 +736,7 @@ static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops, do { ptep += ARM_V7S_LVL_IDX(iova, ++lvl); pte = READ_ONCE(*ptep); - ptep = iopte_deref(pte, lvl); + ptep = iopte_deref(pte, lvl, data); } while (ARM_V7S_PTE_IS_TABLE(pte, lvl)); if (!ARM_V7S_PTE_IS_VALID(pte)) @@ -701,7 +745,7 @@ static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops, mask = ARM_V7S_LVL_MASK(lvl); if (arm_v7s_pte_is_cont(pte, lvl)) mask *= ARM_V7S_CONT_PAGES; - return (pte & mask) | (iova & ~mask); + return iopte_to_paddr(pte, lvl, &data->iop.cfg) | (iova & ~mask); } static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg, @@ -709,18 +753,21 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg, { struct arm_v7s_io_pgtable *data; - if (cfg->ias > ARM_V7S_ADDR_BITS || cfg->oas > ARM_V7S_ADDR_BITS) + if (cfg->ias > ARM_V7S_ADDR_BITS) + return NULL; + + if (cfg->oas > (arm_v7s_is_mtk_enabled(cfg) ? 34 : ARM_V7S_ADDR_BITS)) return NULL; if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS | IO_PGTABLE_QUIRK_NO_PERMS | IO_PGTABLE_QUIRK_TLBI_ON_MAP | - IO_PGTABLE_QUIRK_ARM_MTK_4GB | + IO_PGTABLE_QUIRK_ARM_MTK_EXT | IO_PGTABLE_QUIRK_NON_STRICT)) return NULL; /* If ARM_MTK_4GB is enabled, the NO_PERMS is also expected. */ - if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_4GB && + if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT && !(cfg->quirks & IO_PGTABLE_QUIRK_NO_PERMS)) return NULL; @@ -806,22 +853,24 @@ static void dummy_tlb_flush_all(void *cookie) WARN_ON(cookie != cfg_cookie); } -static void dummy_tlb_add_flush(unsigned long iova, size_t size, - size_t granule, bool leaf, void *cookie) +static void dummy_tlb_flush(unsigned long iova, size_t size, size_t granule, + void *cookie) { WARN_ON(cookie != cfg_cookie); WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); } -static void dummy_tlb_sync(void *cookie) +static void dummy_tlb_add_page(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, void *cookie) { - WARN_ON(cookie != cfg_cookie); + dummy_tlb_flush(iova, granule, granule, cookie); } -static const struct iommu_gather_ops dummy_tlb_ops = { +static const struct iommu_flush_ops dummy_tlb_ops = { .tlb_flush_all = dummy_tlb_flush_all, - .tlb_add_flush = dummy_tlb_add_flush, - .tlb_sync = dummy_tlb_sync, + .tlb_flush_walk = dummy_tlb_flush, + .tlb_flush_leaf = dummy_tlb_flush, + .tlb_add_page = dummy_tlb_add_page, }; #define __FAIL(ops) ({ \ @@ -896,7 +945,7 @@ static int __init arm_v7s_do_selftests(void) size = 1UL << __ffs(cfg.pgsize_bitmap); while (i < loopnr) { iova_start = i * SZ_16M; - if (ops->unmap(ops, iova_start + size, size) != size) + if (ops->unmap(ops, iova_start + size, size, NULL) != size) return __FAIL(ops); /* Remap of partial unmap */ @@ -914,7 +963,7 @@ static int __init arm_v7s_do_selftests(void) for_each_set_bit(i, &cfg.pgsize_bitmap, BITS_PER_LONG) { size = 1UL << i; - if (ops->unmap(ops, iova, size) != size) + if (ops->unmap(ops, iova, size, NULL) != size) return __FAIL(ops); if (ops->iova_to_phys(ops, iova + 42)) diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 161a7d56264d..4c91359057c5 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -12,7 +12,6 @@ #include <linux/atomic.h> #include <linux/bitops.h> #include <linux/io-pgtable.h> -#include <linux/iommu.h> #include <linux/kernel.h> #include <linux/sizes.h> #include <linux/slab.h> @@ -290,6 +289,7 @@ static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte, } static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, + struct iommu_iotlb_gather *gather, unsigned long iova, size_t size, int lvl, arm_lpae_iopte *ptep); @@ -335,8 +335,10 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data); tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data); - if (WARN_ON(__arm_lpae_unmap(data, iova, sz, lvl, tblp) != sz)) + if (__arm_lpae_unmap(data, NULL, iova, sz, lvl, tblp) != sz) { + WARN_ON(1); return -EINVAL; + } } __arm_lpae_init_pte(data, paddr, prot, lvl, ptep); @@ -537,6 +539,7 @@ static void arm_lpae_free_pgtable(struct io_pgtable *iop) } static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, + struct iommu_iotlb_gather *gather, unsigned long iova, size_t size, arm_lpae_iopte blk_pte, int lvl, arm_lpae_iopte *ptep) @@ -582,15 +585,15 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data, tablep = iopte_deref(pte, data); } else if (unmap_idx >= 0) { - io_pgtable_tlb_add_flush(&data->iop, iova, size, size, true); - io_pgtable_tlb_sync(&data->iop); + io_pgtable_tlb_add_page(&data->iop, gather, iova, size); return size; } - return __arm_lpae_unmap(data, iova, size, lvl, tablep); + return __arm_lpae_unmap(data, gather, iova, size, lvl, tablep); } static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, + struct iommu_iotlb_gather *gather, unsigned long iova, size_t size, int lvl, arm_lpae_iopte *ptep) { @@ -612,9 +615,8 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, if (!iopte_leaf(pte, lvl, iop->fmt)) { /* Also flush any partial walks */ - io_pgtable_tlb_add_flush(iop, iova, size, - ARM_LPAE_GRANULE(data), false); - io_pgtable_tlb_sync(iop); + io_pgtable_tlb_flush_walk(iop, iova, size, + ARM_LPAE_GRANULE(data)); ptep = iopte_deref(pte, data); __arm_lpae_free_pgtable(data, lvl + 1, ptep); } else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) { @@ -625,7 +627,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, */ smp_wmb(); } else { - io_pgtable_tlb_add_flush(iop, iova, size, size, true); + io_pgtable_tlb_add_page(iop, gather, iova, size); } return size; @@ -634,17 +636,17 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, * Insert a table at the next level to map the old region, * minus the part we want to unmap */ - return arm_lpae_split_blk_unmap(data, iova, size, pte, + return arm_lpae_split_blk_unmap(data, gather, iova, size, pte, lvl + 1, ptep); } /* Keep on walkin' */ ptep = iopte_deref(pte, data); - return __arm_lpae_unmap(data, iova, size, lvl + 1, ptep); + return __arm_lpae_unmap(data, gather, iova, size, lvl + 1, ptep); } static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops); arm_lpae_iopte *ptep = data->pgd; @@ -653,7 +655,7 @@ static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias))) return 0; - return __arm_lpae_unmap(data, iova, size, lvl, ptep); + return __arm_lpae_unmap(data, gather, iova, size, lvl, ptep); } static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, @@ -1070,22 +1072,24 @@ static void dummy_tlb_flush_all(void *cookie) WARN_ON(cookie != cfg_cookie); } -static void dummy_tlb_add_flush(unsigned long iova, size_t size, - size_t granule, bool leaf, void *cookie) +static void dummy_tlb_flush(unsigned long iova, size_t size, size_t granule, + void *cookie) { WARN_ON(cookie != cfg_cookie); WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); } -static void dummy_tlb_sync(void *cookie) +static void dummy_tlb_add_page(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, void *cookie) { - WARN_ON(cookie != cfg_cookie); + dummy_tlb_flush(iova, granule, granule, cookie); } -static const struct iommu_gather_ops dummy_tlb_ops __initconst = { +static const struct iommu_flush_ops dummy_tlb_ops __initconst = { .tlb_flush_all = dummy_tlb_flush_all, - .tlb_add_flush = dummy_tlb_add_flush, - .tlb_sync = dummy_tlb_sync, + .tlb_flush_walk = dummy_tlb_flush, + .tlb_flush_leaf = dummy_tlb_flush, + .tlb_add_page = dummy_tlb_add_page, }; static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops) @@ -1168,7 +1172,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg) /* Partial unmap */ size = 1UL << __ffs(cfg->pgsize_bitmap); - if (ops->unmap(ops, SZ_1G + size, size) != size) + if (ops->unmap(ops, SZ_1G + size, size, NULL) != size) return __FAIL(ops, i); /* Remap of partial unmap */ @@ -1183,7 +1187,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg) for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) { size = 1UL << j; - if (ops->unmap(ops, iova, size) != size) + if (ops->unmap(ops, iova, size, NULL) != size) return __FAIL(ops, i); if (ops->iova_to_phys(ops, iova + 42)) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 0c674d80c37f..d658c7c6a2ab 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -26,12 +26,10 @@ static struct kset *iommu_group_kset; static DEFINE_IDA(iommu_group_ida); -#ifdef CONFIG_IOMMU_DEFAULT_PASSTHROUGH -static unsigned int iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY; -#else -static unsigned int iommu_def_domain_type = IOMMU_DOMAIN_DMA; -#endif + +static unsigned int iommu_def_domain_type __read_mostly; static bool iommu_dma_strict __read_mostly = true; +static u32 iommu_cmd_line __read_mostly; struct iommu_group { struct kobject kobj; @@ -68,6 +66,18 @@ static const char * const iommu_group_resv_type_string[] = { [IOMMU_RESV_SW_MSI] = "msi", }; +#define IOMMU_CMD_LINE_DMA_API BIT(0) + +static void iommu_set_cmd_line_dma_api(void) +{ + iommu_cmd_line |= IOMMU_CMD_LINE_DMA_API; +} + +static bool iommu_cmd_line_dma_api(void) +{ + return !!(iommu_cmd_line & IOMMU_CMD_LINE_DMA_API); +} + #define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \ struct iommu_group_attribute iommu_group_attr_##_name = \ __ATTR(_name, _mode, _show, _store) @@ -80,12 +90,55 @@ struct iommu_group_attribute iommu_group_attr_##_name = \ static LIST_HEAD(iommu_device_list); static DEFINE_SPINLOCK(iommu_device_lock); +/* + * Use a function instead of an array here because the domain-type is a + * bit-field, so an array would waste memory. + */ +static const char *iommu_domain_type_str(unsigned int t) +{ + switch (t) { + case IOMMU_DOMAIN_BLOCKED: + return "Blocked"; + case IOMMU_DOMAIN_IDENTITY: + return "Passthrough"; + case IOMMU_DOMAIN_UNMANAGED: + return "Unmanaged"; + case IOMMU_DOMAIN_DMA: + return "Translated"; + default: + return "Unknown"; + } +} + +static int __init iommu_subsys_init(void) +{ + bool cmd_line = iommu_cmd_line_dma_api(); + + if (!cmd_line) { + if (IS_ENABLED(CONFIG_IOMMU_DEFAULT_PASSTHROUGH)) + iommu_set_default_passthrough(false); + else + iommu_set_default_translated(false); + + if (iommu_default_passthrough() && mem_encrypt_active()) { + pr_info("Memory encryption detected - Disabling default IOMMU Passthrough\n"); + iommu_set_default_translated(false); + } + } + + pr_info("Default domain type: %s %s\n", + iommu_domain_type_str(iommu_def_domain_type), + cmd_line ? "(set via kernel command line)" : ""); + + return 0; +} +subsys_initcall(iommu_subsys_init); + int iommu_device_register(struct iommu_device *iommu) { spin_lock(&iommu_device_lock); list_add_tail(&iommu->list, &iommu_device_list); spin_unlock(&iommu_device_lock); - return 0; } @@ -165,7 +218,11 @@ static int __init iommu_set_def_domain_type(char *str) if (ret) return ret; - iommu_def_domain_type = pt ? IOMMU_DOMAIN_IDENTITY : IOMMU_DOMAIN_DMA; + if (pt) + iommu_set_default_passthrough(true); + else + iommu_set_default_translated(true); + return 0; } early_param("iommu.passthrough", iommu_set_def_domain_type); @@ -229,60 +286,58 @@ static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf) * @new: new region to insert * @regions: list of regions * - * The new element is sorted by address with respect to the other - * regions of the same type. In case it overlaps with another - * region of the same type, regions are merged. In case it - * overlaps with another region of different type, regions are - * not merged. + * Elements are sorted by start address and overlapping segments + * of the same type are merged. */ -static int iommu_insert_resv_region(struct iommu_resv_region *new, - struct list_head *regions) +int iommu_insert_resv_region(struct iommu_resv_region *new, + struct list_head *regions) { - struct iommu_resv_region *region; - phys_addr_t start = new->start; - phys_addr_t end = new->start + new->length - 1; - struct list_head *pos = regions->next; - - while (pos != regions) { - struct iommu_resv_region *entry = - list_entry(pos, struct iommu_resv_region, list); - phys_addr_t a = entry->start; - phys_addr_t b = entry->start + entry->length - 1; - int type = entry->type; - - if (end < a) { - goto insert; - } else if (start > b) { - pos = pos->next; - } else if ((start >= a) && (end <= b)) { - if (new->type == type) - return 0; - else - pos = pos->next; + struct iommu_resv_region *iter, *tmp, *nr, *top; + LIST_HEAD(stack); + + nr = iommu_alloc_resv_region(new->start, new->length, + new->prot, new->type); + if (!nr) + return -ENOMEM; + + /* First add the new element based on start address sorting */ + list_for_each_entry(iter, regions, list) { + if (nr->start < iter->start || + (nr->start == iter->start && nr->type <= iter->type)) + break; + } + list_add_tail(&nr->list, &iter->list); + + /* Merge overlapping segments of type nr->type in @regions, if any */ + list_for_each_entry_safe(iter, tmp, regions, list) { + phys_addr_t top_end, iter_end = iter->start + iter->length - 1; + + /* no merge needed on elements of different types than @nr */ + if (iter->type != nr->type) { + list_move_tail(&iter->list, &stack); + continue; + } + + /* look for the last stack element of same type as @iter */ + list_for_each_entry_reverse(top, &stack, list) + if (top->type == iter->type) + goto check_overlap; + + list_move_tail(&iter->list, &stack); + continue; + +check_overlap: + top_end = top->start + top->length - 1; + + if (iter->start > top_end + 1) { + list_move_tail(&iter->list, &stack); } else { - if (new->type == type) { - phys_addr_t new_start = min(a, start); - phys_addr_t new_end = max(b, end); - int ret; - - list_del(&entry->list); - entry->start = new_start; - entry->length = new_end - new_start + 1; - ret = iommu_insert_resv_region(entry, regions); - kfree(entry); - return ret; - } else { - pos = pos->next; - } + top->length = max(top_end, iter_end) - top->start + 1; + list_del(&iter->list); + kfree(iter); } } -insert: - region = iommu_alloc_resv_region(new->start, new->length, - new->prot, new->type); - if (!region) - return -ENOMEM; - - list_add_tail(®ion->list, pos); + list_splice(&stack, regions); return 0; } @@ -1862,7 +1917,7 @@ EXPORT_SYMBOL_GPL(iommu_map); static size_t __iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size, - bool sync) + struct iommu_iotlb_gather *iotlb_gather) { const struct iommu_ops *ops = domain->ops; size_t unmapped_page, unmapped = 0; @@ -1899,13 +1954,10 @@ static size_t __iommu_unmap(struct iommu_domain *domain, while (unmapped < size) { size_t pgsize = iommu_pgsize(domain, iova, size - unmapped); - unmapped_page = ops->unmap(domain, iova, pgsize); + unmapped_page = ops->unmap(domain, iova, pgsize, iotlb_gather); if (!unmapped_page) break; - if (sync && ops->iotlb_range_add) - ops->iotlb_range_add(domain, iova, pgsize); - pr_debug("unmapped: iova 0x%lx size 0x%zx\n", iova, unmapped_page); @@ -1913,9 +1965,6 @@ static size_t __iommu_unmap(struct iommu_domain *domain, unmapped += unmapped_page; } - if (sync && ops->iotlb_sync) - ops->iotlb_sync(domain); - trace_unmap(orig_iova, size, unmapped); return unmapped; } @@ -1923,14 +1972,22 @@ static size_t __iommu_unmap(struct iommu_domain *domain, size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) { - return __iommu_unmap(domain, iova, size, true); + struct iommu_iotlb_gather iotlb_gather; + size_t ret; + + iommu_iotlb_gather_init(&iotlb_gather); + ret = __iommu_unmap(domain, iova, size, &iotlb_gather); + iommu_tlb_sync(domain, &iotlb_gather); + + return ret; } EXPORT_SYMBOL_GPL(iommu_unmap); size_t iommu_unmap_fast(struct iommu_domain *domain, - unsigned long iova, size_t size) + unsigned long iova, size_t size, + struct iommu_iotlb_gather *iotlb_gather) { - return __iommu_unmap(domain, iova, size, false); + return __iommu_unmap(domain, iova, size, iotlb_gather); } EXPORT_SYMBOL_GPL(iommu_unmap_fast); @@ -2143,7 +2200,6 @@ request_default_domain_for_dev(struct device *dev, unsigned long type) mutex_lock(&group->mutex); - /* Check if the default domain is already direct mapped */ ret = 0; if (group->default_domain && group->default_domain->type == type) goto out; @@ -2153,7 +2209,6 @@ request_default_domain_for_dev(struct device *dev, unsigned long type) if (iommu_group_device_count(group) != 1) goto out; - /* Allocate a direct mapped domain */ ret = -ENOMEM; domain = __iommu_domain_alloc(dev->bus, type); if (!domain) @@ -2168,7 +2223,7 @@ request_default_domain_for_dev(struct device *dev, unsigned long type) iommu_group_create_direct_mappings(group, dev); - /* Make the direct mapped domain the default for this group */ + /* Make the domain the default for this group */ if (group->default_domain) iommu_domain_free(group->default_domain); group->default_domain = domain; @@ -2196,6 +2251,28 @@ int iommu_request_dma_domain_for_dev(struct device *dev) return request_default_domain_for_dev(dev, IOMMU_DOMAIN_DMA); } +void iommu_set_default_passthrough(bool cmd_line) +{ + if (cmd_line) + iommu_set_cmd_line_dma_api(); + + iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY; +} + +void iommu_set_default_translated(bool cmd_line) +{ + if (cmd_line) + iommu_set_cmd_line_dma_api(); + + iommu_def_domain_type = IOMMU_DOMAIN_DMA; +} + +bool iommu_default_passthrough(void) +{ + return iommu_def_domain_type == IOMMU_DOMAIN_IDENTITY; +} +EXPORT_SYMBOL_GPL(iommu_default_passthrough); + const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode) { const struct iommu_ops *ops = NULL; diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 3e1a8a675572..41c605b0058f 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -577,7 +577,9 @@ void queue_iova(struct iova_domain *iovad, spin_unlock_irqrestore(&fq->lock, flags); - if (atomic_cmpxchg(&iovad->fq_timer_on, 0, 1) == 0) + /* Avoid false sharing as much as possible. */ + if (!atomic_read(&iovad->fq_timer_on) && + !atomic_cmpxchg(&iovad->fq_timer_on, 0, 1)) mod_timer(&iovad->fq_timer, jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT)); } diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index ad0098c0c87c..9da8309f7170 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -49,6 +49,7 @@ struct ipmmu_features { bool setup_imbuscr; bool twobit_imttbcr_sl0; bool reserved_context; + bool cache_snoop; }; struct ipmmu_vmsa_device { @@ -115,45 +116,44 @@ static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev) #define IMTTBCR 0x0008 #define IMTTBCR_EAE (1 << 31) #define IMTTBCR_PMB (1 << 30) -#define IMTTBCR_SH1_NON_SHAREABLE (0 << 28) -#define IMTTBCR_SH1_OUTER_SHAREABLE (2 << 28) -#define IMTTBCR_SH1_INNER_SHAREABLE (3 << 28) -#define IMTTBCR_SH1_MASK (3 << 28) -#define IMTTBCR_ORGN1_NC (0 << 26) -#define IMTTBCR_ORGN1_WB_WA (1 << 26) -#define IMTTBCR_ORGN1_WT (2 << 26) -#define IMTTBCR_ORGN1_WB (3 << 26) -#define IMTTBCR_ORGN1_MASK (3 << 26) -#define IMTTBCR_IRGN1_NC (0 << 24) -#define IMTTBCR_IRGN1_WB_WA (1 << 24) -#define IMTTBCR_IRGN1_WT (2 << 24) -#define IMTTBCR_IRGN1_WB (3 << 24) -#define IMTTBCR_IRGN1_MASK (3 << 24) +#define IMTTBCR_SH1_NON_SHAREABLE (0 << 28) /* R-Car Gen2 only */ +#define IMTTBCR_SH1_OUTER_SHAREABLE (2 << 28) /* R-Car Gen2 only */ +#define IMTTBCR_SH1_INNER_SHAREABLE (3 << 28) /* R-Car Gen2 only */ +#define IMTTBCR_SH1_MASK (3 << 28) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN1_NC (0 << 26) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN1_WB_WA (1 << 26) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN1_WT (2 << 26) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN1_WB (3 << 26) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN1_MASK (3 << 26) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN1_NC (0 << 24) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN1_WB_WA (1 << 24) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN1_WT (2 << 24) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN1_WB (3 << 24) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN1_MASK (3 << 24) /* R-Car Gen2 only */ #define IMTTBCR_TSZ1_MASK (7 << 16) #define IMTTBCR_TSZ1_SHIFT 16 -#define IMTTBCR_SH0_NON_SHAREABLE (0 << 12) -#define IMTTBCR_SH0_OUTER_SHAREABLE (2 << 12) -#define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) -#define IMTTBCR_SH0_MASK (3 << 12) -#define IMTTBCR_ORGN0_NC (0 << 10) -#define IMTTBCR_ORGN0_WB_WA (1 << 10) -#define IMTTBCR_ORGN0_WT (2 << 10) -#define IMTTBCR_ORGN0_WB (3 << 10) -#define IMTTBCR_ORGN0_MASK (3 << 10) -#define IMTTBCR_IRGN0_NC (0 << 8) -#define IMTTBCR_IRGN0_WB_WA (1 << 8) -#define IMTTBCR_IRGN0_WT (2 << 8) -#define IMTTBCR_IRGN0_WB (3 << 8) -#define IMTTBCR_IRGN0_MASK (3 << 8) +#define IMTTBCR_SH0_NON_SHAREABLE (0 << 12) /* R-Car Gen2 only */ +#define IMTTBCR_SH0_OUTER_SHAREABLE (2 << 12) /* R-Car Gen2 only */ +#define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) /* R-Car Gen2 only */ +#define IMTTBCR_SH0_MASK (3 << 12) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN0_NC (0 << 10) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN0_WB_WA (1 << 10) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN0_WT (2 << 10) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN0_WB (3 << 10) /* R-Car Gen2 only */ +#define IMTTBCR_ORGN0_MASK (3 << 10) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN0_NC (0 << 8) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN0_WB_WA (1 << 8) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN0_WT (2 << 8) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN0_WB (3 << 8) /* R-Car Gen2 only */ +#define IMTTBCR_IRGN0_MASK (3 << 8) /* R-Car Gen2 only */ +#define IMTTBCR_SL0_TWOBIT_LVL_3 (0 << 6) /* R-Car Gen3 only */ +#define IMTTBCR_SL0_TWOBIT_LVL_2 (1 << 6) /* R-Car Gen3 only */ +#define IMTTBCR_SL0_TWOBIT_LVL_1 (2 << 6) /* R-Car Gen3 only */ #define IMTTBCR_SL0_LVL_2 (0 << 4) #define IMTTBCR_SL0_LVL_1 (1 << 4) #define IMTTBCR_TSZ0_MASK (7 << 0) #define IMTTBCR_TSZ0_SHIFT O -#define IMTTBCR_SL0_TWOBIT_LVL_3 (0 << 6) -#define IMTTBCR_SL0_TWOBIT_LVL_2 (1 << 6) -#define IMTTBCR_SL0_TWOBIT_LVL_1 (2 << 6) - #define IMBUSCR 0x000c #define IMBUSCR_DVM (1 << 2) #define IMBUSCR_BUSSEL_SYS (0 << 0) @@ -361,16 +361,16 @@ static void ipmmu_tlb_flush_all(void *cookie) ipmmu_tlb_invalidate(domain); } -static void ipmmu_tlb_add_flush(unsigned long iova, size_t size, - size_t granule, bool leaf, void *cookie) +static void ipmmu_tlb_flush(unsigned long iova, size_t size, + size_t granule, void *cookie) { - /* The hardware doesn't support selective TLB flush. */ + ipmmu_tlb_flush_all(cookie); } -static const struct iommu_gather_ops ipmmu_gather_ops = { +static const struct iommu_flush_ops ipmmu_flush_ops = { .tlb_flush_all = ipmmu_tlb_flush_all, - .tlb_add_flush = ipmmu_tlb_add_flush, - .tlb_sync = ipmmu_tlb_flush_all, + .tlb_flush_walk = ipmmu_tlb_flush, + .tlb_flush_leaf = ipmmu_tlb_flush, }; /* ----------------------------------------------------------------------------- @@ -422,17 +422,19 @@ static void ipmmu_domain_setup_context(struct ipmmu_vmsa_domain *domain) /* * TTBCR - * We use long descriptors with inner-shareable WBWA tables and allocate - * the whole 32-bit VA space to TTBR0. + * We use long descriptors and allocate the whole 32-bit VA space to + * TTBR0. */ if (domain->mmu->features->twobit_imttbcr_sl0) tmp = IMTTBCR_SL0_TWOBIT_LVL_1; else tmp = IMTTBCR_SL0_LVL_1; - ipmmu_ctx_write_root(domain, IMTTBCR, IMTTBCR_EAE | - IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA | - IMTTBCR_IRGN0_WB_WA | tmp); + if (domain->mmu->features->cache_snoop) + tmp |= IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA | + IMTTBCR_IRGN0_WB_WA; + + ipmmu_ctx_write_root(domain, IMTTBCR, IMTTBCR_EAE | tmp); /* MAIR0 */ ipmmu_ctx_write_root(domain, IMMAIR0, @@ -480,7 +482,7 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) domain->cfg.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K; domain->cfg.ias = 32; domain->cfg.oas = 40; - domain->cfg.tlb = &ipmmu_gather_ops; + domain->cfg.tlb = &ipmmu_flush_ops; domain->io_domain.geometry.aperture_end = DMA_BIT_MASK(32); domain->io_domain.geometry.force_aperture = true; /* @@ -733,14 +735,14 @@ static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova, } static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); - return domain->iop->unmap(domain->iop, iova, size); + return domain->iop->unmap(domain->iop, iova, size, gather); } -static void ipmmu_iotlb_sync(struct iommu_domain *io_domain) +static void ipmmu_flush_iotlb_all(struct iommu_domain *io_domain) { struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); @@ -748,6 +750,12 @@ static void ipmmu_iotlb_sync(struct iommu_domain *io_domain) ipmmu_tlb_flush_all(domain); } +static void ipmmu_iotlb_sync(struct iommu_domain *io_domain, + struct iommu_iotlb_gather *gather) +{ + ipmmu_flush_iotlb_all(io_domain); +} + static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, dma_addr_t iova) { @@ -957,7 +965,7 @@ static const struct iommu_ops ipmmu_ops = { .detach_dev = ipmmu_detach_device, .map = ipmmu_map, .unmap = ipmmu_unmap, - .flush_iotlb_all = ipmmu_iotlb_sync, + .flush_iotlb_all = ipmmu_flush_iotlb_all, .iotlb_sync = ipmmu_iotlb_sync, .iova_to_phys = ipmmu_iova_to_phys, .add_device = ipmmu_add_device, @@ -988,6 +996,7 @@ static const struct ipmmu_features ipmmu_features_default = { .setup_imbuscr = true, .twobit_imttbcr_sl0 = false, .reserved_context = false, + .cache_snoop = true, }; static const struct ipmmu_features ipmmu_features_rcar_gen3 = { @@ -998,6 +1007,7 @@ static const struct ipmmu_features ipmmu_features_rcar_gen3 = { .setup_imbuscr = false, .twobit_imttbcr_sl0 = true, .reserved_context = true, + .cache_snoop = false, }; static const struct of_device_id ipmmu_of_ids[] = { diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c index b25e2eb9e038..be99d408cf35 100644 --- a/drivers/iommu/msm_iommu.c +++ b/drivers/iommu/msm_iommu.c @@ -168,20 +168,29 @@ fail: return; } -static void __flush_iotlb_sync(void *cookie) +static void __flush_iotlb_walk(unsigned long iova, size_t size, + size_t granule, void *cookie) { - /* - * Nothing is needed here, the barrier to guarantee - * completion of the tlb sync operation is implicitly - * taken care when the iommu client does a writel before - * kick starting the other master. - */ + __flush_iotlb_range(iova, size, granule, false, cookie); +} + +static void __flush_iotlb_leaf(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + __flush_iotlb_range(iova, size, granule, true, cookie); } -static const struct iommu_gather_ops msm_iommu_gather_ops = { +static void __flush_iotlb_page(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, void *cookie) +{ + __flush_iotlb_range(iova, granule, granule, true, cookie); +} + +static const struct iommu_flush_ops msm_iommu_flush_ops = { .tlb_flush_all = __flush_iotlb, - .tlb_add_flush = __flush_iotlb_range, - .tlb_sync = __flush_iotlb_sync, + .tlb_flush_walk = __flush_iotlb_walk, + .tlb_flush_leaf = __flush_iotlb_leaf, + .tlb_add_page = __flush_iotlb_page, }; static int msm_iommu_alloc_ctx(unsigned long *map, int start, int end) @@ -345,7 +354,7 @@ static int msm_iommu_domain_config(struct msm_priv *priv) .pgsize_bitmap = msm_iommu_ops.pgsize_bitmap, .ias = 32, .oas = 32, - .tlb = &msm_iommu_gather_ops, + .tlb = &msm_iommu_flush_ops, .iommu_dev = priv->dev, }; @@ -509,13 +518,13 @@ static int msm_iommu_map(struct iommu_domain *domain, unsigned long iova, } static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t len) + size_t len, struct iommu_iotlb_gather *gather) { struct msm_priv *priv = to_msm_priv(domain); unsigned long flags; spin_lock_irqsave(&priv->pgtlock, flags); - len = priv->iop->unmap(priv->iop, iova, len); + len = priv->iop->unmap(priv->iop, iova, len, gather); spin_unlock_irqrestore(&priv->pgtlock, flags); return len; @@ -691,6 +700,13 @@ static struct iommu_ops msm_iommu_ops = { .detach_dev = msm_iommu_detach_dev, .map = msm_iommu_map, .unmap = msm_iommu_unmap, + /* + * Nothing is needed here, the barrier to guarantee + * completion of the tlb sync operation is implicitly + * taken care when the iommu client does a writel before + * kick starting the other master. + */ + .iotlb_sync = NULL, .iova_to_phys = msm_iommu_iova_to_phys, .add_device = msm_iommu_add_device, .remove_device = msm_iommu_remove_device, @@ -750,7 +766,6 @@ static int msm_iommu_probe(struct platform_device *pdev) iommu->irq = platform_get_irq(pdev, 0); if (iommu->irq < 0) { - dev_err(iommu->dev, "could not get iommu irq\n"); ret = -ENODEV; goto fail; } diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index 82e4be4dfdaf..67a483c1a935 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -28,6 +28,7 @@ #include "mtk_iommu.h" #define REG_MMU_PT_BASE_ADDR 0x000 +#define MMU_PT_ADDR_MASK GENMASK(31, 7) #define REG_MMU_INVALIDATE 0x020 #define F_ALL_INVLD 0x2 @@ -44,12 +45,9 @@ #define REG_MMU_DCM_DIS 0x050 #define REG_MMU_CTRL_REG 0x110 +#define F_MMU_TF_PROT_TO_PROGRAM_ADDR (2 << 4) #define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4) -#define F_MMU_TF_PROTECT_SEL_SHIFT(data) \ - ((data)->m4u_plat == M4U_MT2712 ? 4 : 5) -/* It's named by F_MMU_TF_PROT_SEL in mt2712. */ -#define F_MMU_TF_PROTECT_SEL(prot, data) \ - (((prot) & 0x3) << F_MMU_TF_PROTECT_SEL_SHIFT(data)) +#define F_MMU_TF_PROT_TO_PROGRAM_ADDR_MT8173 (2 << 5) #define REG_MMU_IVRP_PADDR 0x114 @@ -66,26 +64,32 @@ #define F_INT_CLR_BIT BIT(12) #define REG_MMU_INT_MAIN_CONTROL 0x124 -#define F_INT_TRANSLATION_FAULT BIT(0) -#define F_INT_MAIN_MULTI_HIT_FAULT BIT(1) -#define F_INT_INVALID_PA_FAULT BIT(2) -#define F_INT_ENTRY_REPLACEMENT_FAULT BIT(3) -#define F_INT_TLB_MISS_FAULT BIT(4) -#define F_INT_MISS_TRANSACTION_FIFO_FAULT BIT(5) -#define F_INT_PRETETCH_TRANSATION_FIFO_FAULT BIT(6) + /* mmu0 | mmu1 */ +#define F_INT_TRANSLATION_FAULT (BIT(0) | BIT(7)) +#define F_INT_MAIN_MULTI_HIT_FAULT (BIT(1) | BIT(8)) +#define F_INT_INVALID_PA_FAULT (BIT(2) | BIT(9)) +#define F_INT_ENTRY_REPLACEMENT_FAULT (BIT(3) | BIT(10)) +#define F_INT_TLB_MISS_FAULT (BIT(4) | BIT(11)) +#define F_INT_MISS_TRANSACTION_FIFO_FAULT (BIT(5) | BIT(12)) +#define F_INT_PRETETCH_TRANSATION_FIFO_FAULT (BIT(6) | BIT(13)) #define REG_MMU_CPE_DONE 0x12C #define REG_MMU_FAULT_ST1 0x134 +#define F_REG_MMU0_FAULT_MASK GENMASK(6, 0) +#define F_REG_MMU1_FAULT_MASK GENMASK(13, 7) -#define REG_MMU_FAULT_VA 0x13c +#define REG_MMU0_FAULT_VA 0x13c #define F_MMU_FAULT_VA_WRITE_BIT BIT(1) #define F_MMU_FAULT_VA_LAYER_BIT BIT(0) -#define REG_MMU_INVLD_PA 0x140 -#define REG_MMU_INT_ID 0x150 -#define F_MMU0_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7) -#define F_MMU0_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f) +#define REG_MMU0_INVLD_PA 0x140 +#define REG_MMU1_FAULT_VA 0x144 +#define REG_MMU1_INVLD_PA 0x148 +#define REG_MMU0_INT_ID 0x150 +#define REG_MMU1_INT_ID 0x154 +#define F_MMU_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7) +#define F_MMU_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f) #define MTK_PROTECT_PA_ALIGN 128 @@ -107,6 +111,30 @@ struct mtk_iommu_domain { static const struct iommu_ops mtk_iommu_ops; +/* + * In M4U 4GB mode, the physical address is remapped as below: + * + * CPU Physical address: + * ==================== + * + * 0 1G 2G 3G 4G 5G + * |---A---|---B---|---C---|---D---|---E---| + * +--I/O--+------------Memory-------------+ + * + * IOMMU output physical address: + * ============================= + * + * 4G 5G 6G 7G 8G + * |---E---|---B---|---C---|---D---| + * +------------Memory-------------+ + * + * The Region 'A'(I/O) can NOT be mapped by M4U; For Region 'B'/'C'/'D', the + * bit32 of the CPU physical address always is needed to set, and for Region + * 'E', the CPU physical address keep as is. + * Additionally, The iommu consumers always use the CPU phyiscal address. + */ +#define MTK_IOMMU_4GB_MODE_REMAP_BASE 0x140000000UL + static LIST_HEAD(m4ulist); /* List all the M4U HWs */ #define for_each_m4u(data) list_for_each_entry(data, &m4ulist, list) @@ -188,10 +216,32 @@ static void mtk_iommu_tlb_sync(void *cookie) } } -static const struct iommu_gather_ops mtk_iommu_gather_ops = { +static void mtk_iommu_tlb_flush_walk(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + mtk_iommu_tlb_add_flush_nosync(iova, size, granule, false, cookie); + mtk_iommu_tlb_sync(cookie); +} + +static void mtk_iommu_tlb_flush_leaf(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + mtk_iommu_tlb_add_flush_nosync(iova, size, granule, true, cookie); + mtk_iommu_tlb_sync(cookie); +} + +static void mtk_iommu_tlb_flush_page_nosync(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, + void *cookie) +{ + mtk_iommu_tlb_add_flush_nosync(iova, granule, granule, true, cookie); +} + +static const struct iommu_flush_ops mtk_iommu_flush_ops = { .tlb_flush_all = mtk_iommu_tlb_flush_all, - .tlb_add_flush = mtk_iommu_tlb_add_flush_nosync, - .tlb_sync = mtk_iommu_tlb_sync, + .tlb_flush_walk = mtk_iommu_tlb_flush_walk, + .tlb_flush_leaf = mtk_iommu_tlb_flush_leaf, + .tlb_add_page = mtk_iommu_tlb_flush_page_nosync, }; static irqreturn_t mtk_iommu_isr(int irq, void *dev_id) @@ -204,13 +254,21 @@ static irqreturn_t mtk_iommu_isr(int irq, void *dev_id) /* Read error info from registers */ int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST1); - fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA); + if (int_state & F_REG_MMU0_FAULT_MASK) { + regval = readl_relaxed(data->base + REG_MMU0_INT_ID); + fault_iova = readl_relaxed(data->base + REG_MMU0_FAULT_VA); + fault_pa = readl_relaxed(data->base + REG_MMU0_INVLD_PA); + } else { + regval = readl_relaxed(data->base + REG_MMU1_INT_ID); + fault_iova = readl_relaxed(data->base + REG_MMU1_FAULT_VA); + fault_pa = readl_relaxed(data->base + REG_MMU1_INVLD_PA); + } layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT; write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT; - fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA); - regval = readl_relaxed(data->base + REG_MMU_INT_ID); - fault_larb = F_MMU0_INT_ID_LARB_ID(regval); - fault_port = F_MMU0_INT_ID_PORT_ID(regval); + fault_larb = F_MMU_INT_ID_LARB_ID(regval); + fault_port = F_MMU_INT_ID_PORT_ID(regval); + + fault_larb = data->plat_data->larbid_remap[fault_larb]; if (report_iommu_fault(&dom->domain, data->dev, fault_iova, write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) { @@ -242,7 +300,7 @@ static void mtk_iommu_config(struct mtk_iommu_data *data, for (i = 0; i < fwspec->num_ids; ++i) { larbid = MTK_M4U_TO_LARB(fwspec->ids[i]); portid = MTK_M4U_TO_PORT(fwspec->ids[i]); - larb_mmu = &data->smi_imu.larb_imu[larbid]; + larb_mmu = &data->larb_imu[larbid]; dev_dbg(dev, "%s iommu port: %d\n", enable ? "enable" : "disable", portid); @@ -263,17 +321,15 @@ static int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom) dom->cfg = (struct io_pgtable_cfg) { .quirks = IO_PGTABLE_QUIRK_ARM_NS | IO_PGTABLE_QUIRK_NO_PERMS | - IO_PGTABLE_QUIRK_TLBI_ON_MAP, + IO_PGTABLE_QUIRK_TLBI_ON_MAP | + IO_PGTABLE_QUIRK_ARM_MTK_EXT, .pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap, .ias = 32, - .oas = 32, - .tlb = &mtk_iommu_gather_ops, + .oas = 34, + .tlb = &mtk_iommu_flush_ops, .iommu_dev = data->dev, }; - if (data->enable_4GB) - dom->cfg.quirks |= IO_PGTABLE_QUIRK_ARM_MTK_4GB; - dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data); if (!dom->iop) { dev_err(data->dev, "Failed to alloc io pgtable\n"); @@ -336,7 +392,7 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain, /* Update the pgtable base address register of the M4U HW */ if (!data->m4u_dom) { data->m4u_dom = dom; - writel(dom->cfg.arm_v7s_cfg.ttbr[0], + writel(dom->cfg.arm_v7s_cfg.ttbr[0] & MMU_PT_ADDR_MASK, data->base + REG_MMU_PT_BASE_ADDR); } @@ -359,32 +415,43 @@ static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, int prot) { struct mtk_iommu_domain *dom = to_mtk_domain(domain); + struct mtk_iommu_data *data = mtk_iommu_get_m4u_data(); unsigned long flags; int ret; + /* The "4GB mode" M4U physically can not use the lower remap of Dram. */ + if (data->enable_4GB) + paddr |= BIT_ULL(32); + spin_lock_irqsave(&dom->pgtlock, flags); - ret = dom->iop->map(dom->iop, iova, paddr & DMA_BIT_MASK(32), - size, prot); + ret = dom->iop->map(dom->iop, iova, paddr, size, prot); spin_unlock_irqrestore(&dom->pgtlock, flags); return ret; } static size_t mtk_iommu_unmap(struct iommu_domain *domain, - unsigned long iova, size_t size) + unsigned long iova, size_t size, + struct iommu_iotlb_gather *gather) { struct mtk_iommu_domain *dom = to_mtk_domain(domain); unsigned long flags; size_t unmapsz; spin_lock_irqsave(&dom->pgtlock, flags); - unmapsz = dom->iop->unmap(dom->iop, iova, size); + unmapsz = dom->iop->unmap(dom->iop, iova, size, gather); spin_unlock_irqrestore(&dom->pgtlock, flags); return unmapsz; } -static void mtk_iommu_iotlb_sync(struct iommu_domain *domain) +static void mtk_iommu_flush_iotlb_all(struct iommu_domain *domain) +{ + mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data()); +} + +static void mtk_iommu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) { mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data()); } @@ -401,8 +468,8 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, pa = dom->iop->iova_to_phys(dom->iop, iova); spin_unlock_irqrestore(&dom->pgtlock, flags); - if (data->enable_4GB) - pa |= BIT_ULL(32); + if (data->enable_4GB && pa >= MTK_IOMMU_4GB_MODE_REMAP_BASE) + pa &= ~BIT_ULL(32); return pa; } @@ -490,7 +557,7 @@ static const struct iommu_ops mtk_iommu_ops = { .detach_dev = mtk_iommu_detach_device, .map = mtk_iommu_map, .unmap = mtk_iommu_unmap, - .flush_iotlb_all = mtk_iommu_iotlb_sync, + .flush_iotlb_all = mtk_iommu_flush_iotlb_all, .iotlb_sync = mtk_iommu_iotlb_sync, .iova_to_phys = mtk_iommu_iova_to_phys, .add_device = mtk_iommu_add_device, @@ -511,9 +578,11 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) return ret; } - regval = F_MMU_TF_PROTECT_SEL(2, data); - if (data->m4u_plat == M4U_MT8173) - regval |= F_MMU_PREFETCH_RT_REPLACE_MOD; + if (data->plat_data->m4u_plat == M4U_MT8173) + regval = F_MMU_PREFETCH_RT_REPLACE_MOD | + F_MMU_TF_PROT_TO_PROGRAM_ADDR_MT8173; + else + regval = F_MMU_TF_PROT_TO_PROGRAM_ADDR; writel_relaxed(regval, data->base + REG_MMU_CTRL_REG); regval = F_L2_MULIT_HIT_EN | @@ -533,14 +602,14 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) F_INT_PRETETCH_TRANSATION_FIFO_FAULT; writel_relaxed(regval, data->base + REG_MMU_INT_MAIN_CONTROL); - if (data->m4u_plat == M4U_MT8173) + if (data->plat_data->m4u_plat == M4U_MT8173) regval = (data->protect_base >> 1) | (data->enable_4GB << 31); else regval = lower_32_bits(data->protect_base) | upper_32_bits(data->protect_base); writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR); - if (data->enable_4GB && data->m4u_plat != M4U_MT8173) { + if (data->enable_4GB && data->plat_data->has_vld_pa_rng) { /* * If 4GB mode is enabled, the validate PA range is from * 0x1_0000_0000 to 0x1_ffff_ffff. here record bit[32:30]. @@ -550,8 +619,7 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) } writel_relaxed(0, data->base + REG_MMU_DCM_DIS); - /* It's MISC control register whose default value is ok except mt8173.*/ - if (data->m4u_plat == M4U_MT8173) + if (data->plat_data->reset_axi) writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE); if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0, @@ -584,7 +652,7 @@ static int mtk_iommu_probe(struct platform_device *pdev) if (!data) return -ENOMEM; data->dev = dev; - data->m4u_plat = (enum mtk_iommu_plat)of_device_get_match_data(dev); + data->plat_data = of_device_get_match_data(dev); /* Protect memory. HW will access here while translation fault.*/ protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL); @@ -594,6 +662,8 @@ static int mtk_iommu_probe(struct platform_device *pdev) /* Whether the current dram is over 4GB */ data->enable_4GB = !!(max_pfn > (BIT_ULL(32) >> PAGE_SHIFT)); + if (!data->plat_data->has_4gb_mode) + data->enable_4GB = false; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->base = devm_ioremap_resource(dev, res); @@ -605,15 +675,16 @@ static int mtk_iommu_probe(struct platform_device *pdev) if (data->irq < 0) return data->irq; - data->bclk = devm_clk_get(dev, "bclk"); - if (IS_ERR(data->bclk)) - return PTR_ERR(data->bclk); + if (data->plat_data->has_bclk) { + data->bclk = devm_clk_get(dev, "bclk"); + if (IS_ERR(data->bclk)) + return PTR_ERR(data->bclk); + } larb_nr = of_count_phandle_with_args(dev->of_node, "mediatek,larbs", NULL); if (larb_nr < 0) return larb_nr; - data->smi_imu.larb_nr = larb_nr; for (i = 0; i < larb_nr; i++) { struct device_node *larbnode; @@ -638,7 +709,7 @@ static int mtk_iommu_probe(struct platform_device *pdev) of_node_put(larbnode); return -EPROBE_DEFER; } - data->smi_imu.larb_imu[id].dev = &plarbdev->dev; + data->larb_imu[id].dev = &plarbdev->dev; component_match_add_release(dev, &match, release_of, compare_of, larbnode); @@ -699,6 +770,7 @@ static int __maybe_unused mtk_iommu_suspend(struct device *dev) reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0); reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL); reg->ivrp_paddr = readl_relaxed(base + REG_MMU_IVRP_PADDR); + reg->vld_pa_rng = readl_relaxed(base + REG_MMU_VLD_PA_RNG); clk_disable_unprepare(data->bclk); return 0; } @@ -707,6 +779,7 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev) { struct mtk_iommu_data *data = dev_get_drvdata(dev); struct mtk_iommu_suspend_reg *reg = &data->reg; + struct mtk_iommu_domain *m4u_dom = data->m4u_dom; void __iomem *base = data->base; int ret; @@ -722,8 +795,9 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev) writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0); writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL); writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR); - if (data->m4u_dom) - writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0], + writel_relaxed(reg->vld_pa_rng, base + REG_MMU_VLD_PA_RNG); + if (m4u_dom) + writel(m4u_dom->cfg.arm_v7s_cfg.ttbr[0] & MMU_PT_ADDR_MASK, base + REG_MMU_PT_BASE_ADDR); return 0; } @@ -732,9 +806,32 @@ static const struct dev_pm_ops mtk_iommu_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume) }; +static const struct mtk_iommu_plat_data mt2712_data = { + .m4u_plat = M4U_MT2712, + .has_4gb_mode = true, + .has_bclk = true, + .has_vld_pa_rng = true, + .larbid_remap = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, +}; + +static const struct mtk_iommu_plat_data mt8173_data = { + .m4u_plat = M4U_MT8173, + .has_4gb_mode = true, + .has_bclk = true, + .reset_axi = true, + .larbid_remap = {0, 1, 2, 3, 4, 5}, /* Linear mapping. */ +}; + +static const struct mtk_iommu_plat_data mt8183_data = { + .m4u_plat = M4U_MT8183, + .reset_axi = true, + .larbid_remap = {0, 4, 5, 6, 7, 2, 3, 1}, +}; + static const struct of_device_id mtk_iommu_of_ids[] = { - { .compatible = "mediatek,mt2712-m4u", .data = (void *)M4U_MT2712}, - { .compatible = "mediatek,mt8173-m4u", .data = (void *)M4U_MT8173}, + { .compatible = "mediatek,mt2712-m4u", .data = &mt2712_data}, + { .compatible = "mediatek,mt8173-m4u", .data = &mt8173_data}, + { .compatible = "mediatek,mt8183-m4u", .data = &mt8183_data}, {} }; diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h index 59337323db58..fc0f16eabacd 100644 --- a/drivers/iommu/mtk_iommu.h +++ b/drivers/iommu/mtk_iommu.h @@ -24,12 +24,25 @@ struct mtk_iommu_suspend_reg { u32 int_control0; u32 int_main_control; u32 ivrp_paddr; + u32 vld_pa_rng; }; enum mtk_iommu_plat { M4U_MT2701, M4U_MT2712, M4U_MT8173, + M4U_MT8183, +}; + +struct mtk_iommu_plat_data { + enum mtk_iommu_plat m4u_plat; + bool has_4gb_mode; + + /* HW will use the EMI clock if there isn't the "bclk". */ + bool has_bclk; + bool has_vld_pa_rng; + bool reset_axi; + unsigned char larbid_remap[MTK_LARB_NR_MAX]; }; struct mtk_iommu_domain; @@ -43,14 +56,14 @@ struct mtk_iommu_data { struct mtk_iommu_suspend_reg reg; struct mtk_iommu_domain *m4u_dom; struct iommu_group *m4u_group; - struct mtk_smi_iommu smi_imu; /* SMI larb iommu info */ bool enable_4GB; bool tlb_flush_active; struct iommu_device iommu; - enum mtk_iommu_plat m4u_plat; + const struct mtk_iommu_plat_data *plat_data; struct list_head list; + struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX]; }; static inline int compare_of(struct device *dev, void *data) @@ -67,14 +80,14 @@ static inline int mtk_iommu_bind(struct device *dev) { struct mtk_iommu_data *data = dev_get_drvdata(dev); - return component_bind_all(dev, &data->smi_imu); + return component_bind_all(dev, &data->larb_imu); } static inline void mtk_iommu_unbind(struct device *dev) { struct mtk_iommu_data *data = dev_get_drvdata(dev); - component_unbind_all(dev, &data->smi_imu); + component_unbind_all(dev, &data->larb_imu); } #endif diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c index abeeac488372..210b1c7c0bda 100644 --- a/drivers/iommu/mtk_iommu_v1.c +++ b/drivers/iommu/mtk_iommu_v1.c @@ -206,7 +206,7 @@ static void mtk_iommu_config(struct mtk_iommu_data *data, for (i = 0; i < fwspec->num_ids; ++i) { larbid = mt2701_m4u_to_larb(fwspec->ids[i]); portid = mt2701_m4u_to_port(fwspec->ids[i]); - larb_mmu = &data->smi_imu.larb_imu[larbid]; + larb_mmu = &data->larb_imu[larbid]; dev_dbg(dev, "%s iommu port: %d\n", enable ? "enable" : "disable", portid); @@ -324,7 +324,8 @@ static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova, } static size_t mtk_iommu_unmap(struct iommu_domain *domain, - unsigned long iova, size_t size) + unsigned long iova, size_t size, + struct iommu_iotlb_gather *gather) { struct mtk_iommu_domain *dom = to_mtk_domain(domain); unsigned long flags; @@ -610,14 +611,12 @@ static int mtk_iommu_probe(struct platform_device *pdev) } } - data->smi_imu.larb_imu[larb_nr].dev = &plarbdev->dev; + data->larb_imu[larb_nr].dev = &plarbdev->dev; component_match_add_release(dev, &match, release_of, compare_of, larb_spec.np); larb_nr++; } - data->smi_imu.larb_nr = larb_nr; - platform_set_drvdata(pdev, data); ret = mtk_iommu_hw_init(data); diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index dfb961d8c21b..09c6e1c680db 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -35,6 +35,15 @@ static const struct iommu_ops omap_iommu_ops; +struct orphan_dev { + struct device *dev; + struct list_head node; +}; + +static LIST_HEAD(orphan_dev_list); + +static DEFINE_SPINLOCK(orphan_lock); + #define to_iommu(dev) ((struct omap_iommu *)dev_get_drvdata(dev)) /* bitmap of the page sizes currently supported */ @@ -53,6 +62,8 @@ static const struct iommu_ops omap_iommu_ops; static struct platform_driver omap_iommu_driver; static struct kmem_cache *iopte_cachep; +static int _omap_iommu_add_device(struct device *dev); + /** * to_omap_domain - Get struct omap_iommu_domain from generic iommu_domain * @dom: generic iommu domain handle @@ -65,6 +76,9 @@ static struct omap_iommu_domain *to_omap_domain(struct iommu_domain *dom) /** * omap_iommu_save_ctx - Save registers for pm off-mode support * @dev: client device + * + * This should be treated as an deprecated API. It is preserved only + * to maintain existing functionality for OMAP3 ISP driver. **/ void omap_iommu_save_ctx(struct device *dev) { @@ -92,6 +106,9 @@ EXPORT_SYMBOL_GPL(omap_iommu_save_ctx); /** * omap_iommu_restore_ctx - Restore registers for pm off-mode support * @dev: client device + * + * This should be treated as an deprecated API. It is preserved only + * to maintain existing functionality for OMAP3 ISP driver. **/ void omap_iommu_restore_ctx(struct device *dev) { @@ -186,36 +203,18 @@ static void omap2_iommu_disable(struct omap_iommu *obj) static int iommu_enable(struct omap_iommu *obj) { - int err; - struct platform_device *pdev = to_platform_device(obj->dev); - struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev); - - if (pdata && pdata->deassert_reset) { - err = pdata->deassert_reset(pdev, pdata->reset_name); - if (err) { - dev_err(obj->dev, "deassert_reset failed: %d\n", err); - return err; - } - } - - pm_runtime_get_sync(obj->dev); + int ret; - err = omap2_iommu_enable(obj); + ret = pm_runtime_get_sync(obj->dev); + if (ret < 0) + pm_runtime_put_noidle(obj->dev); - return err; + return ret < 0 ? ret : 0; } static void iommu_disable(struct omap_iommu *obj) { - struct platform_device *pdev = to_platform_device(obj->dev); - struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev); - - omap2_iommu_disable(obj); - pm_runtime_put_sync(obj->dev); - - if (pdata && pdata->assert_reset) - pdata->assert_reset(pdev, pdata->reset_name); } /* @@ -901,15 +900,219 @@ static void omap_iommu_detach(struct omap_iommu *obj) dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE, DMA_TO_DEVICE); - iommu_disable(obj); obj->pd_dma = 0; obj->iopgd = NULL; + iommu_disable(obj); spin_unlock(&obj->iommu_lock); dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name); } +static void omap_iommu_save_tlb_entries(struct omap_iommu *obj) +{ + struct iotlb_lock lock; + struct cr_regs cr; + struct cr_regs *tmp; + int i; + + /* check if there are any locked tlbs to save */ + iotlb_lock_get(obj, &lock); + obj->num_cr_ctx = lock.base; + if (!obj->num_cr_ctx) + return; + + tmp = obj->cr_ctx; + for_each_iotlb_cr(obj, obj->num_cr_ctx, i, cr) + * tmp++ = cr; +} + +static void omap_iommu_restore_tlb_entries(struct omap_iommu *obj) +{ + struct iotlb_lock l; + struct cr_regs *tmp; + int i; + + /* no locked tlbs to restore */ + if (!obj->num_cr_ctx) + return; + + l.base = 0; + tmp = obj->cr_ctx; + for (i = 0; i < obj->num_cr_ctx; i++, tmp++) { + l.vict = i; + iotlb_lock_set(obj, &l); + iotlb_load_cr(obj, tmp); + } + l.base = obj->num_cr_ctx; + l.vict = i; + iotlb_lock_set(obj, &l); +} + +/** + * omap_iommu_domain_deactivate - deactivate attached iommu devices + * @domain: iommu domain attached to the target iommu device + * + * This API allows the client devices of IOMMU devices to suspend + * the IOMMUs they control at runtime, after they are idled and + * suspended all activity. System Suspend will leverage the PM + * driver late callbacks. + **/ +int omap_iommu_domain_deactivate(struct iommu_domain *domain) +{ + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); + struct omap_iommu_device *iommu; + struct omap_iommu *oiommu; + int i; + + if (!omap_domain->dev) + return 0; + + iommu = omap_domain->iommus; + iommu += (omap_domain->num_iommus - 1); + for (i = 0; i < omap_domain->num_iommus; i++, iommu--) { + oiommu = iommu->iommu_dev; + pm_runtime_put_sync(oiommu->dev); + } + + return 0; +} +EXPORT_SYMBOL_GPL(omap_iommu_domain_deactivate); + +/** + * omap_iommu_domain_activate - activate attached iommu devices + * @domain: iommu domain attached to the target iommu device + * + * This API allows the client devices of IOMMU devices to resume the + * IOMMUs they control at runtime, before they can resume operations. + * System Resume will leverage the PM driver late callbacks. + **/ +int omap_iommu_domain_activate(struct iommu_domain *domain) +{ + struct omap_iommu_domain *omap_domain = to_omap_domain(domain); + struct omap_iommu_device *iommu; + struct omap_iommu *oiommu; + int i; + + if (!omap_domain->dev) + return 0; + + iommu = omap_domain->iommus; + for (i = 0; i < omap_domain->num_iommus; i++, iommu++) { + oiommu = iommu->iommu_dev; + pm_runtime_get_sync(oiommu->dev); + } + + return 0; +} +EXPORT_SYMBOL_GPL(omap_iommu_domain_activate); + +/** + * omap_iommu_runtime_suspend - disable an iommu device + * @dev: iommu device + * + * This function performs all that is necessary to disable an + * IOMMU device, either during final detachment from a client + * device, or during system/runtime suspend of the device. This + * includes programming all the appropriate IOMMU registers, and + * managing the associated omap_hwmod's state and the device's + * reset line. This function also saves the context of any + * locked TLBs if suspending. + **/ +static __maybe_unused int omap_iommu_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iommu_platform_data *pdata = dev_get_platdata(dev); + struct omap_iommu *obj = to_iommu(dev); + int ret; + + /* save the TLBs only during suspend, and not for power down */ + if (obj->domain && obj->iopgd) + omap_iommu_save_tlb_entries(obj); + + omap2_iommu_disable(obj); + + if (pdata && pdata->device_idle) + pdata->device_idle(pdev); + + if (pdata && pdata->assert_reset) + pdata->assert_reset(pdev, pdata->reset_name); + + if (pdata && pdata->set_pwrdm_constraint) { + ret = pdata->set_pwrdm_constraint(pdev, false, &obj->pwrst); + if (ret) { + dev_warn(obj->dev, "pwrdm_constraint failed to be reset, status = %d\n", + ret); + } + } + + return 0; +} + +/** + * omap_iommu_runtime_resume - enable an iommu device + * @dev: iommu device + * + * This function performs all that is necessary to enable an + * IOMMU device, either during initial attachment to a client + * device, or during system/runtime resume of the device. This + * includes programming all the appropriate IOMMU registers, and + * managing the associated omap_hwmod's state and the device's + * reset line. The function also restores any locked TLBs if + * resuming after a suspend. + **/ +static __maybe_unused int omap_iommu_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iommu_platform_data *pdata = dev_get_platdata(dev); + struct omap_iommu *obj = to_iommu(dev); + int ret = 0; + + if (pdata && pdata->set_pwrdm_constraint) { + ret = pdata->set_pwrdm_constraint(pdev, true, &obj->pwrst); + if (ret) { + dev_warn(obj->dev, "pwrdm_constraint failed to be set, status = %d\n", + ret); + } + } + + if (pdata && pdata->deassert_reset) { + ret = pdata->deassert_reset(pdev, pdata->reset_name); + if (ret) { + dev_err(dev, "deassert_reset failed: %d\n", ret); + return ret; + } + } + + if (pdata && pdata->device_enable) + pdata->device_enable(pdev); + + /* restore the TLBs only during resume, and not for power up */ + if (obj->domain) + omap_iommu_restore_tlb_entries(obj); + + ret = omap2_iommu_enable(obj); + + return ret; +} + +/** + * omap_iommu_suspend_prepare - prepare() dev_pm_ops implementation + * @dev: iommu device + * + * This function performs the necessary checks to determine if the IOMMU + * device needs suspending or not. The function checks if the runtime_pm + * status of the device is suspended, and returns 1 in that case. This + * results in the PM core to skip invoking any of the Sleep PM callbacks + * (suspend, suspend_late, resume, resume_early etc). + */ +static int omap_iommu_prepare(struct device *dev) +{ + if (pm_runtime_status_suspended(dev)) + return 1; + return 0; +} + static bool omap_iommu_can_register(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -974,6 +1177,7 @@ static int omap_iommu_probe(struct platform_device *pdev) struct omap_iommu *obj; struct resource *res; struct device_node *of = pdev->dev.of_node; + struct orphan_dev *orphan_dev, *tmp; if (!of) { pr_err("%s: only DT-based devices are supported\n", __func__); @@ -984,6 +1188,15 @@ static int omap_iommu_probe(struct platform_device *pdev) if (!obj) return -ENOMEM; + /* + * self-manage the ordering dependencies between omap_device_enable/idle + * and omap_device_assert/deassert_hardreset API + */ + if (pdev->dev.pm_domain) { + dev_dbg(&pdev->dev, "device pm_domain is being reset\n"); + pdev->dev.pm_domain = NULL; + } + obj->name = dev_name(&pdev->dev); obj->nr_tlb_entries = 32; err = of_property_read_u32(of, "ti,#tlb-entries", &obj->nr_tlb_entries); @@ -996,6 +1209,11 @@ static int omap_iommu_probe(struct platform_device *pdev) obj->dev = &pdev->dev; obj->ctx = (void *)obj + sizeof(*obj); + obj->cr_ctx = devm_kzalloc(&pdev->dev, + sizeof(*obj->cr_ctx) * obj->nr_tlb_entries, + GFP_KERNEL); + if (!obj->cr_ctx) + return -ENOMEM; spin_lock_init(&obj->iommu_lock); spin_lock_init(&obj->page_table_lock); @@ -1036,13 +1254,20 @@ static int omap_iommu_probe(struct platform_device *pdev) goto out_sysfs; } - pm_runtime_irq_safe(obj->dev); pm_runtime_enable(obj->dev); omap_iommu_debugfs_add(obj); dev_info(&pdev->dev, "%s registered\n", obj->name); + list_for_each_entry_safe(orphan_dev, tmp, &orphan_dev_list, node) { + err = _omap_iommu_add_device(orphan_dev->dev); + if (!err) { + list_del(&orphan_dev->node); + kfree(orphan_dev); + } + } + return 0; out_sysfs: @@ -1072,6 +1297,14 @@ static int omap_iommu_remove(struct platform_device *pdev) return 0; } +static const struct dev_pm_ops omap_iommu_pm_ops = { + .prepare = omap_iommu_prepare, + SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(omap_iommu_runtime_suspend, + omap_iommu_runtime_resume, NULL) +}; + static const struct of_device_id omap_iommu_of_match[] = { { .compatible = "ti,omap2-iommu" }, { .compatible = "ti,omap4-iommu" }, @@ -1085,6 +1318,7 @@ static struct platform_driver omap_iommu_driver = { .remove = omap_iommu_remove, .driver = { .name = "omap-iommu", + .pm = &omap_iommu_pm_ops, .of_match_table = of_match_ptr(omap_iommu_of_match), }, }; @@ -1149,7 +1383,7 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da, } static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { struct omap_iommu_domain *omap_domain = to_omap_domain(domain); struct device *dev = omap_domain->dev; @@ -1423,7 +1657,7 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain, return ret; } -static int omap_iommu_add_device(struct device *dev) +static int _omap_iommu_add_device(struct device *dev) { struct omap_iommu_arch_data *arch_data, *tmp; struct omap_iommu *oiommu; @@ -1432,6 +1666,8 @@ static int omap_iommu_add_device(struct device *dev) struct platform_device *pdev; int num_iommus, i; int ret; + struct orphan_dev *orphan_dev; + unsigned long flags; /* * Allocate the archdata iommu structure for DT-based devices. @@ -1463,10 +1699,26 @@ static int omap_iommu_add_device(struct device *dev) } pdev = of_find_device_by_node(np); - if (WARN_ON(!pdev)) { + if (!pdev) { of_node_put(np); kfree(arch_data); - return -EINVAL; + spin_lock_irqsave(&orphan_lock, flags); + list_for_each_entry(orphan_dev, &orphan_dev_list, + node) { + if (orphan_dev->dev == dev) + break; + } + spin_unlock_irqrestore(&orphan_lock, flags); + + if (orphan_dev && orphan_dev->dev == dev) + return -EPROBE_DEFER; + + orphan_dev = kzalloc(sizeof(*orphan_dev), GFP_KERNEL); + orphan_dev->dev = dev; + spin_lock_irqsave(&orphan_lock, flags); + list_add(&orphan_dev->node, &orphan_dev_list); + spin_unlock_irqrestore(&orphan_lock, flags); + return -EPROBE_DEFER; } oiommu = platform_get_drvdata(pdev); @@ -1477,6 +1729,7 @@ static int omap_iommu_add_device(struct device *dev) } tmp->iommu_dev = oiommu; + tmp->dev = &pdev->dev; of_node_put(np); } @@ -1511,6 +1764,17 @@ static int omap_iommu_add_device(struct device *dev) return 0; } +static int omap_iommu_add_device(struct device *dev) +{ + int ret; + + ret = _omap_iommu_add_device(dev); + if (ret == -EPROBE_DEFER) + return 0; + + return ret; +} + static void omap_iommu_remove_device(struct device *dev) { struct omap_iommu_arch_data *arch_data = dev->archdata.iommu; @@ -1554,7 +1818,7 @@ static const struct iommu_ops omap_iommu_ops = { static int __init omap_iommu_init(void) { struct kmem_cache *p; - const unsigned long flags = SLAB_HWCACHE_ALIGN; + const slab_flags_t flags = SLAB_HWCACHE_ALIGN; size_t align = 1 << 10; /* L2 pagetable alignement */ struct device_node *np; int ret; diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h index 09968a02d291..18ee713ede78 100644 --- a/drivers/iommu/omap-iommu.h +++ b/drivers/iommu/omap-iommu.h @@ -73,16 +73,22 @@ struct omap_iommu { void *ctx; /* iommu context: registres saved area */ + struct cr_regs *cr_ctx; + u32 num_cr_ctx; + int has_bus_err_back; u32 id; struct iommu_device iommu; struct iommu_group *group; + + u8 pwrst; }; /** * struct omap_iommu_arch_data - omap iommu private data - * @iommu_dev: handle of the iommu device + * @iommu_dev: handle of the OMAP iommu device + * @dev: handle of the iommu device * * This is an omap iommu private data object, which binds an iommu user * to its iommu device. This object should be placed at the iommu user's @@ -91,6 +97,7 @@ struct omap_iommu { */ struct omap_iommu_arch_data { struct omap_iommu *iommu_dev; + struct device *dev; }; struct cr_regs { diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c index 34d0b9783b3e..c31e7bc4ccbe 100644 --- a/drivers/iommu/qcom_iommu.c +++ b/drivers/iommu/qcom_iommu.c @@ -7,6 +7,7 @@ */ #include <linux/atomic.h> +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/dma-iommu.h> @@ -32,7 +33,7 @@ #include <linux/slab.h> #include <linux/spinlock.h> -#include "arm-smmu-regs.h" +#include "arm-smmu.h" #define SMMU_INTR_SEL_NS 0x2000 @@ -155,7 +156,7 @@ static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size, struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]); size_t s = size; - iova &= ~12UL; + iova = (iova >> 12) << 12; iova |= ctx->asid; do { iommu_writel(ctx, reg, iova); @@ -164,10 +165,32 @@ static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size, } } -static const struct iommu_gather_ops qcom_gather_ops = { +static void qcom_iommu_tlb_flush_walk(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + qcom_iommu_tlb_inv_range_nosync(iova, size, granule, false, cookie); + qcom_iommu_tlb_sync(cookie); +} + +static void qcom_iommu_tlb_flush_leaf(unsigned long iova, size_t size, + size_t granule, void *cookie) +{ + qcom_iommu_tlb_inv_range_nosync(iova, size, granule, true, cookie); + qcom_iommu_tlb_sync(cookie); +} + +static void qcom_iommu_tlb_add_page(struct iommu_iotlb_gather *gather, + unsigned long iova, size_t granule, + void *cookie) +{ + qcom_iommu_tlb_inv_range_nosync(iova, granule, granule, true, cookie); +} + +static const struct iommu_flush_ops qcom_flush_ops = { .tlb_flush_all = qcom_iommu_tlb_inv_context, - .tlb_add_flush = qcom_iommu_tlb_inv_range_nosync, - .tlb_sync = qcom_iommu_tlb_sync, + .tlb_flush_walk = qcom_iommu_tlb_flush_walk, + .tlb_flush_leaf = qcom_iommu_tlb_flush_leaf, + .tlb_add_page = qcom_iommu_tlb_add_page, }; static irqreturn_t qcom_iommu_fault(int irq, void *dev) @@ -215,7 +238,7 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain, .pgsize_bitmap = qcom_iommu_ops.pgsize_bitmap, .ias = 32, .oas = 40, - .tlb = &qcom_gather_ops, + .tlb = &qcom_flush_ops, .iommu_dev = qcom_iommu->dev, }; @@ -247,16 +270,16 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain, /* TTBRs */ iommu_writeq(ctx, ARM_SMMU_CB_TTBR0, pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] | - ((u64)ctx->asid << TTBRn_ASID_SHIFT)); + FIELD_PREP(TTBRn_ASID, ctx->asid)); iommu_writeq(ctx, ARM_SMMU_CB_TTBR1, pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] | - ((u64)ctx->asid << TTBRn_ASID_SHIFT)); + FIELD_PREP(TTBRn_ASID, ctx->asid)); - /* TTBCR */ - iommu_writel(ctx, ARM_SMMU_CB_TTBCR2, + /* TCR */ + iommu_writel(ctx, ARM_SMMU_CB_TCR2, (pgtbl_cfg.arm_lpae_s1_cfg.tcr >> 32) | - TTBCR2_SEP_UPSTREAM); - iommu_writel(ctx, ARM_SMMU_CB_TTBCR, + FIELD_PREP(TCR2_SEP, TCR2_SEP_UPSTREAM)); + iommu_writel(ctx, ARM_SMMU_CB_TCR, pgtbl_cfg.arm_lpae_s1_cfg.tcr); /* MAIRs (stage-1 only) */ @@ -417,7 +440,7 @@ static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova, } static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { size_t ret; unsigned long flags; @@ -434,14 +457,14 @@ static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova, */ pm_runtime_get_sync(qcom_domain->iommu->dev); spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags); - ret = ops->unmap(ops, iova, size); + ret = ops->unmap(ops, iova, size, gather); spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags); pm_runtime_put_sync(qcom_domain->iommu->dev); return ret; } -static void qcom_iommu_iotlb_sync(struct iommu_domain *domain) +static void qcom_iommu_flush_iotlb_all(struct iommu_domain *domain) { struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain); struct io_pgtable *pgtable = container_of(qcom_domain->pgtbl_ops, @@ -454,6 +477,12 @@ static void qcom_iommu_iotlb_sync(struct iommu_domain *domain) pm_runtime_put_sync(qcom_domain->iommu->dev); } +static void qcom_iommu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) +{ + qcom_iommu_flush_iotlb_all(domain); +} + static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) { @@ -581,7 +610,7 @@ static const struct iommu_ops qcom_iommu_ops = { .detach_dev = qcom_iommu_detach_dev, .map = qcom_iommu_map, .unmap = qcom_iommu_unmap, - .flush_iotlb_all = qcom_iommu_iotlb_sync, + .flush_iotlb_all = qcom_iommu_flush_iotlb_all, .iotlb_sync = qcom_iommu_iotlb_sync, .iova_to_phys = qcom_iommu_iova_to_phys, .add_device = qcom_iommu_add_device, @@ -696,10 +725,8 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev) return PTR_ERR(ctx->base); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to get irq\n"); + if (irq < 0) return -ENODEV; - } /* clear IRQs before registering fault handler, just in case the * boot-loader left us a surprise: @@ -775,7 +802,7 @@ static int qcom_iommu_device_probe(struct platform_device *pdev) struct qcom_iommu_dev *qcom_iommu; struct device *dev = &pdev->dev; struct resource *res; - int ret, sz, max_asid = 0; + int ret, max_asid = 0; /* find the max asid (which is 1:1 to ctx bank idx), so we know how * many child ctx devices we have: @@ -783,9 +810,8 @@ static int qcom_iommu_device_probe(struct platform_device *pdev) for_each_child_of_node(dev->of_node, child) max_asid = max(max_asid, get_asid(child)); - sz = sizeof(*qcom_iommu) + (max_asid * sizeof(qcom_iommu->ctxs[0])); - - qcom_iommu = devm_kzalloc(dev, sz, GFP_KERNEL); + qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid), + GFP_KERNEL); if (!qcom_iommu) return -ENOMEM; qcom_iommu->num_ctxs = max_asid; diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index dc26d74d79c2..26290f310f90 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -794,7 +794,7 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova, } static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { struct rk_iommu_domain *rk_domain = to_rk_domain(domain); unsigned long flags; diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c index 22d4db302c1c..3b0b18e23187 100644 --- a/drivers/iommu/s390-iommu.c +++ b/drivers/iommu/s390-iommu.c @@ -314,7 +314,8 @@ static phys_addr_t s390_iommu_iova_to_phys(struct iommu_domain *domain, } static size_t s390_iommu_unmap(struct iommu_domain *domain, - unsigned long iova, size_t size) + unsigned long iova, size_t size, + struct iommu_iotlb_gather *gather) { struct s390_domain *s390_domain = to_s390_domain(domain); int flags = ZPCI_PTE_INVALID; diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 6d40bc1b38bf..3924f7c05544 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -207,7 +207,7 @@ static inline int __gart_iommu_unmap(struct gart_device *gart, } static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t bytes) + size_t bytes, struct iommu_iotlb_gather *gather) { struct gart_device *gart = gart_handle; int err; @@ -273,11 +273,17 @@ static int gart_iommu_of_xlate(struct device *dev, return 0; } -static void gart_iommu_sync(struct iommu_domain *domain) +static void gart_iommu_sync_map(struct iommu_domain *domain) { FLUSH_GART_REGS(gart_handle); } +static void gart_iommu_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) +{ + gart_iommu_sync_map(domain); +} + static const struct iommu_ops gart_iommu_ops = { .capable = gart_iommu_capable, .domain_alloc = gart_iommu_domain_alloc, @@ -292,7 +298,7 @@ static const struct iommu_ops gart_iommu_ops = { .iova_to_phys = gart_iommu_iova_to_phys, .pgsize_bitmap = GART_IOMMU_PGSIZES, .of_xlate = gart_iommu_of_xlate, - .iotlb_sync_map = gart_iommu_sync, + .iotlb_sync_map = gart_iommu_sync_map, .iotlb_sync = gart_iommu_sync, }; diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index c4a652b227f8..7293fc3f796d 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -680,7 +680,7 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova, } static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { struct tegra_smmu_as *as = to_smmu_as(domain); dma_addr_t pte_dma; diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c index 80a740df0737..3ea9d7682999 100644 --- a/drivers/iommu/virtio-iommu.c +++ b/drivers/iommu/virtio-iommu.c @@ -751,7 +751,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova, } static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova, - size_t size) + size_t size, struct iommu_iotlb_gather *gather) { int ret = 0; size_t unmapped; @@ -797,7 +797,8 @@ static phys_addr_t viommu_iova_to_phys(struct iommu_domain *domain, return paddr; } -static void viommu_iotlb_sync(struct iommu_domain *domain) +static void viommu_iotlb_sync(struct iommu_domain *domain, + struct iommu_iotlb_gather *gather) { struct viommu_domain *vdomain = to_viommu_domain(domain); diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 3c3ad42f22bf..c92b405b7646 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -688,6 +688,9 @@ capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos if (!cdev->ap.applid) return -ENODEV; + if (count < CAPIMSG_BASELEN) + return -EINVAL; + skb = alloc_skb(count, GFP_USER); if (!skb) return -ENOMEM; @@ -698,7 +701,8 @@ capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos } mlen = CAPIMSG_LEN(skb->data); if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { - if ((size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) { + if (count < CAPI_DATA_B3_REQ_LEN || + (size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) { kfree_skb(skb); return -EINVAL; } @@ -711,6 +715,10 @@ capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos CAPIMSG_SETAPPID(skb->data, cdev->ap.applid); if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) { + if (count < CAPI_DISCONNECT_B3_RESP_LEN) { + kfree_skb(skb); + return -EINVAL; + } mutex_lock(&cdev->lock); capincci_free(cdev, CAPIMSG_NCCI(skb->data)); mutex_unlock(&cdev->lock); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c index 0e224232f746..008a74a1ed44 100644 --- a/drivers/isdn/hardware/mISDN/hfcsusb.c +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -1394,6 +1394,7 @@ start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb, printk(KERN_DEBUG "%s: %s: alloc urb for fifo %i failed", hw->name, __func__, fifo->fifonum); + continue; } fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; fifo->iso[i].indx = i; @@ -1692,13 +1693,23 @@ hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel) static int setup_hfcsusb(struct hfcsusb *hw) { + void *dmabuf = kmalloc(sizeof(u_char), GFP_KERNEL); u_char b; + int ret; if (debug & DBG_HFC_CALL_TRACE) printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + if (!dmabuf) + return -ENOMEM; + + ret = read_reg_atomic(hw, HFCUSB_CHIP_ID, dmabuf); + + memcpy(&b, dmabuf, sizeof(u_char)); + kfree(dmabuf); + /* check the chip id */ - if (read_reg_atomic(hw, HFCUSB_CHIP_ID, &b) != 1) { + if (ret != 1) { printk(KERN_DEBUG "%s: %s: cannot read chip id\n", hw->name, __func__); return 1; diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index 9f0826712845..e2059af90791 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -23,24 +23,28 @@ static const char * const bch_cache_modes[] = { "writethrough", "writeback", "writearound", - "none" + "none", + NULL }; /* Default is 0 ("auto") */ static const char * const bch_stop_on_failure_modes[] = { "auto", - "always" + "always", + NULL }; static const char * const cache_replacement_policies[] = { "lru", "fifo", - "random" + "random", + NULL }; static const char * const error_actions[] = { "unregister", - "panic" + "panic", + NULL }; write_attribute(attach); @@ -338,7 +342,7 @@ STORE(__cached_dev) } if (attr == &sysfs_cache_mode) { - v = sysfs_match_string(bch_cache_modes, buf); + v = __sysfs_match_string(bch_cache_modes, -1, buf); if (v < 0) return v; @@ -349,7 +353,7 @@ STORE(__cached_dev) } if (attr == &sysfs_stop_when_cache_set_failed) { - v = sysfs_match_string(bch_stop_on_failure_modes, buf); + v = __sysfs_match_string(bch_stop_on_failure_modes, -1, buf); if (v < 0) return v; @@ -816,7 +820,7 @@ STORE(__bch_cache_set) 0, UINT_MAX); if (attr == &sysfs_errors) { - v = sysfs_match_string(error_actions, buf); + v = __sysfs_match_string(error_actions, -1, buf); if (v < 0) return v; @@ -1088,7 +1092,7 @@ STORE(__bch_cache) } if (attr == &sysfs_cache_replacement_policy) { - v = sysfs_match_string(cache_replacement_policies, buf); + v = __sysfs_match_string(cache_replacement_policies, -1, buf); if (v < 0) return v; diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index b6b5acc92ca2..2a48ea3f1b30 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -1599,7 +1599,9 @@ dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) unsigned long freed; c = container_of(shrink, struct dm_bufio_client, shrinker); - if (!dm_bufio_trylock(c)) + if (sc->gfp_mask & __GFP_FS) + dm_bufio_lock(c); + else if (!dm_bufio_trylock(c)) return SHRINK_STOP; freed = __scan(c, sc->nr_to_scan, sc->gfp_mask); diff --git a/drivers/md/dm-dust.c b/drivers/md/dm-dust.c index 845f376a72d9..8288887b7f94 100644 --- a/drivers/md/dm-dust.c +++ b/drivers/md/dm-dust.c @@ -25,6 +25,7 @@ struct dust_device { unsigned long long badblock_count; spinlock_t dust_lock; unsigned int blksz; + int sect_per_block_shift; unsigned int sect_per_block; sector_t start; bool fail_read_on_bb:1; @@ -79,7 +80,7 @@ static int dust_remove_block(struct dust_device *dd, unsigned long long block) unsigned long flags; spin_lock_irqsave(&dd->dust_lock, flags); - bblock = dust_rb_search(&dd->badblocklist, block * dd->sect_per_block); + bblock = dust_rb_search(&dd->badblocklist, block); if (bblock == NULL) { if (!dd->quiet_mode) { @@ -113,7 +114,7 @@ static int dust_add_block(struct dust_device *dd, unsigned long long block) } spin_lock_irqsave(&dd->dust_lock, flags); - bblock->bb = block * dd->sect_per_block; + bblock->bb = block; if (!dust_rb_insert(&dd->badblocklist, bblock)) { if (!dd->quiet_mode) { DMERR("%s: block %llu already in badblocklist", @@ -138,7 +139,7 @@ static int dust_query_block(struct dust_device *dd, unsigned long long block) unsigned long flags; spin_lock_irqsave(&dd->dust_lock, flags); - bblock = dust_rb_search(&dd->badblocklist, block * dd->sect_per_block); + bblock = dust_rb_search(&dd->badblocklist, block); if (bblock != NULL) DMINFO("%s: block %llu found in badblocklist", __func__, block); else @@ -165,6 +166,7 @@ static int dust_map_read(struct dust_device *dd, sector_t thisblock, int ret = DM_MAPIO_REMAPPED; if (fail_read_on_bb) { + thisblock >>= dd->sect_per_block_shift; spin_lock_irqsave(&dd->dust_lock, flags); ret = __dust_map_read(dd, thisblock); spin_unlock_irqrestore(&dd->dust_lock, flags); @@ -195,6 +197,7 @@ static int dust_map_write(struct dust_device *dd, sector_t thisblock, unsigned long flags; if (fail_read_on_bb) { + thisblock >>= dd->sect_per_block_shift; spin_lock_irqsave(&dd->dust_lock, flags); __dust_map_write(dd, thisblock); spin_unlock_irqrestore(&dd->dust_lock, flags); @@ -331,6 +334,8 @@ static int dust_ctr(struct dm_target *ti, unsigned int argc, char **argv) dd->blksz = blksz; dd->start = tmp; + dd->sect_per_block_shift = __ffs(sect_per_block); + /* * Whether to fail a read on a "bad" block. * Defaults to false; enabled later by message. diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index b1b0de402dfc..9118ab85cb3a 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -1943,7 +1943,22 @@ offload_to_thread: queue_work(ic->wait_wq, &dio->work); return; } + if (journal_read_pos != NOT_FOUND) + dio->range.n_sectors = ic->sectors_per_block; wait_and_add_new_range(ic, &dio->range); + /* + * wait_and_add_new_range drops the spinlock, so the journal + * may have been changed arbitrarily. We need to recheck. + * To simplify the code, we restrict I/O size to just one block. + */ + if (journal_read_pos != NOT_FOUND) { + sector_t next_sector; + unsigned new_pos = find_journal_node(ic, dio->range.logical_sector, &next_sector); + if (unlikely(new_pos != journal_read_pos)) { + remove_range_unlocked(ic, &dio->range); + goto retry; + } + } } spin_unlock_irq(&ic->endio_wait.lock); diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index df2011de7be2..1bbe4a34ef4c 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -566,8 +566,10 @@ static int run_io_job(struct kcopyd_job *job) * no point in continuing. */ if (test_bit(DM_KCOPYD_WRITE_SEQ, &job->flags) && - job->master_job->write_err) + job->master_job->write_err) { + job->write_err = job->master_job->write_err; return -EIO; + } io_job_start(job->kc->throttle); @@ -619,6 +621,7 @@ static int process_jobs(struct list_head *jobs, struct dm_kcopyd_client *kc, else job->read_err = 1; push(&kc->complete_jobs, job); + wake(kc); break; } diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 8a60a4a070ac..1f933dd197cd 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -3194,7 +3194,7 @@ static int raid_ctr(struct dm_target *ti, unsigned int argc, char **argv) */ r = rs_prepare_reshape(rs); if (r) - return r; + goto bad; /* Reshaping ain't recovery, so disable recovery */ rs_setup_recovery(rs, MaxSector); diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 7b6c3ee9e755..8820931ec7d2 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1342,7 +1342,7 @@ void dm_table_event(struct dm_table *t) } EXPORT_SYMBOL(dm_table_event); -sector_t dm_table_get_size(struct dm_table *t) +inline sector_t dm_table_get_size(struct dm_table *t) { return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0; } @@ -1367,6 +1367,9 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector) unsigned int l, n = 0, k = 0; sector_t *node; + if (unlikely(sector >= dm_table_get_size(t))) + return &t->targets[t->num_targets]; + for (l = 0; l < t->depth; l++) { n = get_child(n, k); node = get_node(t, l, n); diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index 8545dcee9fd0..595a73110e17 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 Western Digital Corporation or its affiliates. * @@ -34,7 +35,7 @@ * (1) Super block (1 block) * (2) Chunk mapping table (nr_map_blocks) * (3) Bitmap blocks (nr_bitmap_blocks) - * All metadata blocks are stored in conventional zones, starting from the + * All metadata blocks are stored in conventional zones, starting from * the first conventional zone found on disk. */ struct dmz_super { @@ -233,7 +234,7 @@ void dmz_unlock_map(struct dmz_metadata *zmd) * Lock/unlock metadata access. This is a "read" lock on a semaphore * that prevents metadata flush from running while metadata are being * modified. The actual metadata write mutual exclusion is achieved with - * the map lock and zone styate management (active and reclaim state are + * the map lock and zone state management (active and reclaim state are * mutually exclusive). */ void dmz_lock_metadata(struct dmz_metadata *zmd) @@ -402,15 +403,18 @@ static struct dmz_mblock *dmz_get_mblock_slow(struct dmz_metadata *zmd, sector_t block = zmd->sb[zmd->mblk_primary].block + mblk_no; struct bio *bio; + if (dmz_bdev_is_dying(zmd->dev)) + return ERR_PTR(-EIO); + /* Get a new block and a BIO to read it */ mblk = dmz_alloc_mblock(zmd, mblk_no); if (!mblk) - return NULL; + return ERR_PTR(-ENOMEM); bio = bio_alloc(GFP_NOIO, 1); if (!bio) { dmz_free_mblock(zmd, mblk); - return NULL; + return ERR_PTR(-ENOMEM); } spin_lock(&zmd->mblk_lock); @@ -541,8 +545,8 @@ static struct dmz_mblock *dmz_get_mblock(struct dmz_metadata *zmd, if (!mblk) { /* Cache miss: read the block from disk */ mblk = dmz_get_mblock_slow(zmd, mblk_no); - if (!mblk) - return ERR_PTR(-ENOMEM); + if (IS_ERR(mblk)) + return mblk; } /* Wait for on-going read I/O and check for error */ @@ -570,16 +574,19 @@ static void dmz_dirty_mblock(struct dmz_metadata *zmd, struct dmz_mblock *mblk) /* * Issue a metadata block write BIO. */ -static void dmz_write_mblock(struct dmz_metadata *zmd, struct dmz_mblock *mblk, - unsigned int set) +static int dmz_write_mblock(struct dmz_metadata *zmd, struct dmz_mblock *mblk, + unsigned int set) { sector_t block = zmd->sb[set].block + mblk->no; struct bio *bio; + if (dmz_bdev_is_dying(zmd->dev)) + return -EIO; + bio = bio_alloc(GFP_NOIO, 1); if (!bio) { set_bit(DMZ_META_ERROR, &mblk->state); - return; + return -ENOMEM; } set_bit(DMZ_META_WRITING, &mblk->state); @@ -591,6 +598,8 @@ static void dmz_write_mblock(struct dmz_metadata *zmd, struct dmz_mblock *mblk, bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_META | REQ_PRIO); bio_add_page(bio, mblk->page, DMZ_BLOCK_SIZE, 0); submit_bio(bio); + + return 0; } /* @@ -602,6 +611,9 @@ static int dmz_rdwr_block(struct dmz_metadata *zmd, int op, sector_t block, struct bio *bio; int ret; + if (dmz_bdev_is_dying(zmd->dev)) + return -EIO; + bio = bio_alloc(GFP_NOIO, 1); if (!bio) return -ENOMEM; @@ -659,22 +671,29 @@ static int dmz_write_dirty_mblocks(struct dmz_metadata *zmd, { struct dmz_mblock *mblk; struct blk_plug plug; - int ret = 0; + int ret = 0, nr_mblks_submitted = 0; /* Issue writes */ blk_start_plug(&plug); - list_for_each_entry(mblk, write_list, link) - dmz_write_mblock(zmd, mblk, set); + list_for_each_entry(mblk, write_list, link) { + ret = dmz_write_mblock(zmd, mblk, set); + if (ret) + break; + nr_mblks_submitted++; + } blk_finish_plug(&plug); /* Wait for completion */ list_for_each_entry(mblk, write_list, link) { + if (!nr_mblks_submitted) + break; wait_on_bit_io(&mblk->state, DMZ_META_WRITING, TASK_UNINTERRUPTIBLE); if (test_bit(DMZ_META_ERROR, &mblk->state)) { clear_bit(DMZ_META_ERROR, &mblk->state); ret = -EIO; } + nr_mblks_submitted--; } /* Flush drive cache (this will also sync data) */ @@ -736,6 +755,11 @@ int dmz_flush_metadata(struct dmz_metadata *zmd) */ dmz_lock_flush(zmd); + if (dmz_bdev_is_dying(zmd->dev)) { + ret = -EIO; + goto out; + } + /* Get dirty blocks */ spin_lock(&zmd->mblk_lock); list_splice_init(&zmd->mblk_dirty_list, &write_list); @@ -1542,7 +1566,7 @@ static struct dm_zone *dmz_get_rnd_zone_for_reclaim(struct dmz_metadata *zmd) struct dm_zone *zone; if (list_empty(&zmd->map_rnd_list)) - return NULL; + return ERR_PTR(-EBUSY); list_for_each_entry(zone, &zmd->map_rnd_list, link) { if (dmz_is_buf(zone)) @@ -1553,7 +1577,7 @@ static struct dm_zone *dmz_get_rnd_zone_for_reclaim(struct dmz_metadata *zmd) return dzone; } - return NULL; + return ERR_PTR(-EBUSY); } /* @@ -1564,7 +1588,7 @@ static struct dm_zone *dmz_get_seq_zone_for_reclaim(struct dmz_metadata *zmd) struct dm_zone *zone; if (list_empty(&zmd->map_seq_list)) - return NULL; + return ERR_PTR(-EBUSY); list_for_each_entry(zone, &zmd->map_seq_list, link) { if (!zone->bzone) @@ -1573,7 +1597,7 @@ static struct dm_zone *dmz_get_seq_zone_for_reclaim(struct dmz_metadata *zmd) return zone; } - return NULL; + return ERR_PTR(-EBUSY); } /* @@ -1628,9 +1652,13 @@ again: if (op != REQ_OP_WRITE) goto out; - /* Alloate a random zone */ + /* Allocate a random zone */ dzone = dmz_alloc_zone(zmd, DMZ_ALLOC_RND); if (!dzone) { + if (dmz_bdev_is_dying(zmd->dev)) { + dzone = ERR_PTR(-EIO); + goto out; + } dmz_wait_for_free_zones(zmd); goto again; } @@ -1725,9 +1753,13 @@ again: if (bzone) goto out; - /* Alloate a random zone */ + /* Allocate a random zone */ bzone = dmz_alloc_zone(zmd, DMZ_ALLOC_RND); if (!bzone) { + if (dmz_bdev_is_dying(zmd->dev)) { + bzone = ERR_PTR(-EIO); + goto out; + } dmz_wait_for_free_zones(zmd); goto again; } diff --git a/drivers/md/dm-zoned-reclaim.c b/drivers/md/dm-zoned-reclaim.c index edf4b95eb075..d240d7ca8a8a 100644 --- a/drivers/md/dm-zoned-reclaim.c +++ b/drivers/md/dm-zoned-reclaim.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 Western Digital Corporation or its affiliates. * @@ -37,7 +38,7 @@ enum { /* * Number of seconds of target BIO inactivity to consider the target idle. */ -#define DMZ_IDLE_PERIOD (10UL * HZ) +#define DMZ_IDLE_PERIOD (10UL * HZ) /* * Percentage of unmapped (free) random zones below which reclaim starts @@ -134,6 +135,9 @@ static int dmz_reclaim_copy(struct dmz_reclaim *zrc, set_bit(DM_KCOPYD_WRITE_SEQ, &flags); while (block < end_block) { + if (dev->flags & DMZ_BDEV_DYING) + return -EIO; + /* Get a valid region from the source zone */ ret = dmz_first_valid_block(zmd, src_zone, &block); if (ret <= 0) @@ -215,7 +219,7 @@ static int dmz_reclaim_buf(struct dmz_reclaim *zrc, struct dm_zone *dzone) dmz_unlock_flush(zmd); - return 0; + return ret; } /* @@ -259,7 +263,7 @@ static int dmz_reclaim_seq_data(struct dmz_reclaim *zrc, struct dm_zone *dzone) dmz_unlock_flush(zmd); - return 0; + return ret; } /* @@ -312,7 +316,7 @@ static int dmz_reclaim_rnd_data(struct dmz_reclaim *zrc, struct dm_zone *dzone) dmz_unlock_flush(zmd); - return 0; + return ret; } /* @@ -334,7 +338,7 @@ static void dmz_reclaim_empty(struct dmz_reclaim *zrc, struct dm_zone *dzone) /* * Find a candidate zone for reclaim and process it. */ -static void dmz_reclaim(struct dmz_reclaim *zrc) +static int dmz_do_reclaim(struct dmz_reclaim *zrc) { struct dmz_metadata *zmd = zrc->metadata; struct dm_zone *dzone; @@ -344,8 +348,8 @@ static void dmz_reclaim(struct dmz_reclaim *zrc) /* Get a data zone */ dzone = dmz_get_zone_for_reclaim(zmd); - if (!dzone) - return; + if (IS_ERR(dzone)) + return PTR_ERR(dzone); start = jiffies; @@ -391,13 +395,20 @@ static void dmz_reclaim(struct dmz_reclaim *zrc) out: if (ret) { dmz_unlock_zone_reclaim(dzone); - return; + return ret; } - (void) dmz_flush_metadata(zrc->metadata); + ret = dmz_flush_metadata(zrc->metadata); + if (ret) { + dmz_dev_debug(zrc->dev, + "Metadata flush for zone %u failed, err %d\n", + dmz_id(zmd, rzone), ret); + return ret; + } dmz_dev_debug(zrc->dev, "Reclaimed zone %u in %u ms", dmz_id(zmd, rzone), jiffies_to_msecs(jiffies - start)); + return 0; } /* @@ -427,7 +438,7 @@ static bool dmz_should_reclaim(struct dmz_reclaim *zrc) return false; /* - * If the percentage of unmappped random zones is low, + * If the percentage of unmapped random zones is low, * reclaim even if the target is busy. */ return p_unmap_rnd <= DMZ_RECLAIM_LOW_UNMAP_RND; @@ -442,6 +453,10 @@ static void dmz_reclaim_work(struct work_struct *work) struct dmz_metadata *zmd = zrc->metadata; unsigned int nr_rnd, nr_unmap_rnd; unsigned int p_unmap_rnd; + int ret; + + if (dmz_bdev_is_dying(zrc->dev)) + return; if (!dmz_should_reclaim(zrc)) { mod_delayed_work(zrc->wq, &zrc->work, DMZ_IDLE_PERIOD); @@ -471,7 +486,17 @@ static void dmz_reclaim_work(struct work_struct *work) (dmz_target_idle(zrc) ? "Idle" : "Busy"), p_unmap_rnd, nr_unmap_rnd, nr_rnd); - dmz_reclaim(zrc); + ret = dmz_do_reclaim(zrc); + if (ret) { + dmz_dev_debug(zrc->dev, "Reclaim error %d\n", ret); + if (ret == -EIO) + /* + * LLD might be performing some error handling sequence + * at the underlying device. To not interfere, do not + * attempt to schedule the next reclaim run immediately. + */ + return; + } dmz_schedule_reclaim(zrc); } diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c index 51d029bbb740..31478fef6032 100644 --- a/drivers/md/dm-zoned-target.c +++ b/drivers/md/dm-zoned-target.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 Western Digital Corporation or its affiliates. * @@ -133,6 +134,8 @@ static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone, refcount_inc(&bioctx->ref); generic_make_request(clone); + if (clone->bi_status == BLK_STS_IOERR) + return -EIO; if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone)) zone->wp_block += nr_blocks; @@ -277,8 +280,8 @@ static int dmz_handle_buffered_write(struct dmz_target *dmz, /* Get the buffer zone. One will be allocated if needed */ bzone = dmz_get_chunk_buffer(zmd, zone); - if (!bzone) - return -ENOSPC; + if (IS_ERR(bzone)) + return PTR_ERR(bzone); if (dmz_is_readonly(bzone)) return -EROFS; @@ -389,6 +392,11 @@ static void dmz_handle_bio(struct dmz_target *dmz, struct dm_chunk_work *cw, dmz_lock_metadata(zmd); + if (dmz->dev->flags & DMZ_BDEV_DYING) { + ret = -EIO; + goto out; + } + /* * Get the data zone mapping the chunk. There may be no * mapping for read and discard. If a mapping is obtained, @@ -493,6 +501,8 @@ static void dmz_flush_work(struct work_struct *work) /* Flush dirty metadata blocks */ ret = dmz_flush_metadata(dmz->metadata); + if (ret) + dmz_dev_debug(dmz->dev, "Metadata flush failed, rc=%d\n", ret); /* Process queued flush requests */ while (1) { @@ -513,22 +523,24 @@ static void dmz_flush_work(struct work_struct *work) * Get a chunk work and start it to process a new BIO. * If the BIO chunk has no work yet, create one. */ -static void dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio) +static int dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio) { unsigned int chunk = dmz_bio_chunk(dmz->dev, bio); struct dm_chunk_work *cw; + int ret = 0; mutex_lock(&dmz->chunk_lock); /* Get the BIO chunk work. If one is not active yet, create one */ cw = radix_tree_lookup(&dmz->chunk_rxtree, chunk); if (!cw) { - int ret; /* Create a new chunk work */ cw = kmalloc(sizeof(struct dm_chunk_work), GFP_NOIO); - if (!cw) + if (unlikely(!cw)) { + ret = -ENOMEM; goto out; + } INIT_WORK(&cw->work, dmz_chunk_work); refcount_set(&cw->refcount, 0); @@ -539,7 +551,6 @@ static void dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio) ret = radix_tree_insert(&dmz->chunk_rxtree, chunk, cw); if (unlikely(ret)) { kfree(cw); - cw = NULL; goto out; } } @@ -547,10 +558,38 @@ static void dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio) bio_list_add(&cw->bio_list, bio); dmz_get_chunk_work(cw); + dmz_reclaim_bio_acc(dmz->reclaim); if (queue_work(dmz->chunk_wq, &cw->work)) dmz_get_chunk_work(cw); out: mutex_unlock(&dmz->chunk_lock); + return ret; +} + +/* + * Check the backing device availability. If it's on the way out, + * start failing I/O. Reclaim and metadata components also call this + * function to cleanly abort operation in the event of such failure. + */ +bool dmz_bdev_is_dying(struct dmz_dev *dmz_dev) +{ + struct gendisk *disk; + + if (!(dmz_dev->flags & DMZ_BDEV_DYING)) { + disk = dmz_dev->bdev->bd_disk; + if (blk_queue_dying(bdev_get_queue(dmz_dev->bdev))) { + dmz_dev_warn(dmz_dev, "Backing device queue dying"); + dmz_dev->flags |= DMZ_BDEV_DYING; + } else if (disk->fops->check_events) { + if (disk->fops->check_events(disk, 0) & + DISK_EVENT_MEDIA_CHANGE) { + dmz_dev_warn(dmz_dev, "Backing device offline"); + dmz_dev->flags |= DMZ_BDEV_DYING; + } + } + } + + return dmz_dev->flags & DMZ_BDEV_DYING; } /* @@ -564,6 +603,10 @@ static int dmz_map(struct dm_target *ti, struct bio *bio) sector_t sector = bio->bi_iter.bi_sector; unsigned int nr_sectors = bio_sectors(bio); sector_t chunk_sector; + int ret; + + if (dmz_bdev_is_dying(dmz->dev)) + return DM_MAPIO_KILL; dmz_dev_debug(dev, "BIO op %d sector %llu + %u => chunk %llu, block %llu, %u blocks", bio_op(bio), (unsigned long long)sector, nr_sectors, @@ -601,8 +644,14 @@ static int dmz_map(struct dm_target *ti, struct bio *bio) dm_accept_partial_bio(bio, dev->zone_nr_sectors - chunk_sector); /* Now ready to handle this BIO */ - dmz_reclaim_bio_acc(dmz->reclaim); - dmz_queue_chunk_work(dmz, bio); + ret = dmz_queue_chunk_work(dmz, bio); + if (ret) { + dmz_dev_debug(dmz->dev, + "BIO op %d, can't process chunk %llu, err %i\n", + bio_op(bio), (u64)dmz_bio_chunk(dmz->dev, bio), + ret); + return DM_MAPIO_REQUEUE; + } return DM_MAPIO_SUBMITTED; } @@ -855,6 +904,9 @@ static int dmz_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct dmz_target *dmz = ti->private; + if (dmz_bdev_is_dying(dmz->dev)) + return -ENODEV; + *bdev = dmz->dev->bdev; return 0; diff --git a/drivers/md/dm-zoned.h b/drivers/md/dm-zoned.h index ed8de49c9a08..d8e70b0ade35 100644 --- a/drivers/md/dm-zoned.h +++ b/drivers/md/dm-zoned.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2017 Western Digital Corporation or its affiliates. * @@ -56,6 +57,8 @@ struct dmz_dev { unsigned int nr_zones; + unsigned int flags; + sector_t zone_nr_sectors; unsigned int zone_nr_sectors_shift; @@ -67,6 +70,9 @@ struct dmz_dev { (dev)->zone_nr_sectors_shift) #define dmz_chunk_block(dev, b) ((b) & ((dev)->zone_nr_blocks - 1)) +/* Device flags. */ +#define DMZ_BDEV_DYING (1 << 0) + /* * Zone descriptor. */ @@ -245,4 +251,9 @@ void dmz_resume_reclaim(struct dmz_reclaim *zrc); void dmz_reclaim_bio_acc(struct dmz_reclaim *zrc); void dmz_schedule_reclaim(struct dmz_reclaim *zrc); +/* + * Functions defined in dm-zoned-target.c + */ +bool dmz_bdev_is_dying(struct dmz_dev *dmz_dev); + #endif /* DM_ZONED_H */ diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index 58b319757b1e..8aae0624a297 100644 --- a/drivers/md/persistent-data/dm-btree.c +++ b/drivers/md/persistent-data/dm-btree.c @@ -628,39 +628,40 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key) new_parent = shadow_current(s); + pn = dm_block_data(new_parent); + size = le32_to_cpu(pn->header.flags) & INTERNAL_NODE ? + sizeof(__le64) : s->info->value_type.size; + + /* create & init the left block */ r = new_block(s->info, &left); if (r < 0) return r; + ln = dm_block_data(left); + nr_left = le32_to_cpu(pn->header.nr_entries) / 2; + + ln->header.flags = pn->header.flags; + ln->header.nr_entries = cpu_to_le32(nr_left); + ln->header.max_entries = pn->header.max_entries; + ln->header.value_size = pn->header.value_size; + memcpy(ln->keys, pn->keys, nr_left * sizeof(pn->keys[0])); + memcpy(value_ptr(ln, 0), value_ptr(pn, 0), nr_left * size); + + /* create & init the right block */ r = new_block(s->info, &right); if (r < 0) { unlock_block(s->info, left); return r; } - pn = dm_block_data(new_parent); - ln = dm_block_data(left); rn = dm_block_data(right); - - nr_left = le32_to_cpu(pn->header.nr_entries) / 2; nr_right = le32_to_cpu(pn->header.nr_entries) - nr_left; - ln->header.flags = pn->header.flags; - ln->header.nr_entries = cpu_to_le32(nr_left); - ln->header.max_entries = pn->header.max_entries; - ln->header.value_size = pn->header.value_size; - rn->header.flags = pn->header.flags; rn->header.nr_entries = cpu_to_le32(nr_right); rn->header.max_entries = pn->header.max_entries; rn->header.value_size = pn->header.value_size; - - memcpy(ln->keys, pn->keys, nr_left * sizeof(pn->keys[0])); memcpy(rn->keys, pn->keys + nr_left, nr_right * sizeof(pn->keys[0])); - - size = le32_to_cpu(pn->header.flags) & INTERNAL_NODE ? - sizeof(__le64) : s->info->value_type.size; - memcpy(value_ptr(ln, 0), value_ptr(pn, 0), nr_left * size); memcpy(value_ptr(rn, 0), value_ptr(pn, nr_left), nr_right * size); diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index aec449243966..25328582cc48 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c @@ -249,7 +249,7 @@ static int out(struct sm_metadata *smm) } if (smm->recursion_count == 1) - apply_bops(smm); + r = apply_bops(smm); smm->recursion_count--; diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index 29e3f5da59c1..11ec048929e8 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -253,8 +253,7 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout, */ pixsize = vout->bpp * vout->vrfb_bpp; - dst_icg = ((MAX_PIXELS_PER_LINE * pixsize) - - (vout->pix.width * vout->bpp)) + 1; + dst_icg = MAX_PIXELS_PER_LINE * pixsize - vout->pix.width * vout->bpp; xt->src_start = vout->buf_phy_addr[vb->i]; xt->dst_start = vout->vrfb_context[vb->i].paddr[0]; diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index bc2a176937a4..d535aac68ce1 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -1099,6 +1099,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* start creating the vb2 queues */ if (dev->has_vid_cap) { + snprintf(dev->vid_cap_dev.name, sizeof(dev->vid_cap_dev.name), + "vivid-%03d-vid-cap", inst); /* initialize vid_cap queue */ q = &dev->vb_vid_cap_q; q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : @@ -1122,6 +1124,8 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } if (dev->has_vid_out) { + snprintf(dev->vid_out_dev.name, sizeof(dev->vid_out_dev.name), + "vivid-%03d-vid-out", inst); /* initialize vid_out queue */ q = &dev->vb_vid_out_q; q->type = dev->multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : @@ -1265,8 +1269,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* finally start creating the device nodes */ if (dev->has_vid_cap) { vfd = &dev->vid_cap_dev; - snprintf(vfd->name, sizeof(vfd->name), - "vivid-%03d-vid-cap", inst); vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; vfd->device_caps = dev->vid_cap_caps; @@ -1312,8 +1314,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (dev->has_vid_out) { vfd = &dev->vid_out_dev; - snprintf(vfd->name, sizeof(vfd->name), - "vivid-%03d-vid-out", inst); vfd->vfl_dir = VFL_DIR_TX; vfd->fops = &vivid_fops; vfd->ioctl_ops = &vivid_ioctl_ops; diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c index 42ab43affbff..439d7d886873 100644 --- a/drivers/memory/mtk-smi.c +++ b/drivers/memory/mtk-smi.c @@ -41,17 +41,40 @@ #define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4)) #define F_MMU_EN BIT(0) +/* SMI COMMON */ +#define SMI_BUS_SEL 0x220 +#define SMI_BUS_LARB_SHIFT(larbid) ((larbid) << 1) +/* All are MMU0 defaultly. Only specialize mmu1 here. */ +#define F_MMU1_LARB(larbid) (0x1 << SMI_BUS_LARB_SHIFT(larbid)) + +enum mtk_smi_gen { + MTK_SMI_GEN1, + MTK_SMI_GEN2 +}; + +struct mtk_smi_common_plat { + enum mtk_smi_gen gen; + bool has_gals; + u32 bus_sel; /* Balance some larbs to enter mmu0 or mmu1 */ +}; + struct mtk_smi_larb_gen { - bool need_larbid; int port_in_larb[MTK_LARB_NR_MAX + 1]; void (*config_port)(struct device *); + unsigned int larb_direct_to_common_mask; + bool has_gals; }; struct mtk_smi { struct device *dev; struct clk *clk_apb, *clk_smi; + struct clk *clk_gals0, *clk_gals1; struct clk *clk_async; /*only needed by mt2701*/ - void __iomem *smi_ao_base; + union { + void __iomem *smi_ao_base; /* only for gen1 */ + void __iomem *base; /* only for gen2 */ + }; + const struct mtk_smi_common_plat *plat; }; struct mtk_smi_larb { /* larb: local arbiter */ @@ -63,82 +86,56 @@ struct mtk_smi_larb { /* larb: local arbiter */ u32 *mmu; }; -enum mtk_smi_gen { - MTK_SMI_GEN1, - MTK_SMI_GEN2 -}; - -static int mtk_smi_enable(const struct mtk_smi *smi) +static int mtk_smi_clk_enable(const struct mtk_smi *smi) { int ret; - ret = pm_runtime_get_sync(smi->dev); - if (ret < 0) - return ret; - ret = clk_prepare_enable(smi->clk_apb); if (ret) - goto err_put_pm; + return ret; ret = clk_prepare_enable(smi->clk_smi); if (ret) goto err_disable_apb; + ret = clk_prepare_enable(smi->clk_gals0); + if (ret) + goto err_disable_smi; + + ret = clk_prepare_enable(smi->clk_gals1); + if (ret) + goto err_disable_gals0; + return 0; +err_disable_gals0: + clk_disable_unprepare(smi->clk_gals0); +err_disable_smi: + clk_disable_unprepare(smi->clk_smi); err_disable_apb: clk_disable_unprepare(smi->clk_apb); -err_put_pm: - pm_runtime_put_sync(smi->dev); return ret; } -static void mtk_smi_disable(const struct mtk_smi *smi) +static void mtk_smi_clk_disable(const struct mtk_smi *smi) { + clk_disable_unprepare(smi->clk_gals1); + clk_disable_unprepare(smi->clk_gals0); clk_disable_unprepare(smi->clk_smi); clk_disable_unprepare(smi->clk_apb); - pm_runtime_put_sync(smi->dev); } int mtk_smi_larb_get(struct device *larbdev) { - struct mtk_smi_larb *larb = dev_get_drvdata(larbdev); - const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen; - struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); - int ret; - - /* Enable the smi-common's power and clocks */ - ret = mtk_smi_enable(common); - if (ret) - return ret; + int ret = pm_runtime_get_sync(larbdev); - /* Enable the larb's power and clocks */ - ret = mtk_smi_enable(&larb->smi); - if (ret) { - mtk_smi_disable(common); - return ret; - } - - /* Configure the iommu info for this larb */ - larb_gen->config_port(larbdev); - - return 0; + return (ret < 0) ? ret : 0; } EXPORT_SYMBOL_GPL(mtk_smi_larb_get); void mtk_smi_larb_put(struct device *larbdev) { - struct mtk_smi_larb *larb = dev_get_drvdata(larbdev); - struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); - - /* - * Don't de-configure the iommu info for this larb since there may be - * several modules in this larb. - * The iommu info will be reset after power off. - */ - - mtk_smi_disable(&larb->smi); - mtk_smi_disable(common); + pm_runtime_put_sync(larbdev); } EXPORT_SYMBOL_GPL(mtk_smi_larb_put); @@ -146,39 +143,26 @@ static int mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) { struct mtk_smi_larb *larb = dev_get_drvdata(dev); - struct mtk_smi_iommu *smi_iommu = data; + struct mtk_smi_larb_iommu *larb_mmu = data; unsigned int i; - if (larb->larb_gen->need_larbid) { - larb->mmu = &smi_iommu->larb_imu[larb->larbid].mmu; - return 0; - } - - /* - * If there is no larbid property, Loop to find the corresponding - * iommu information. - */ - for (i = 0; i < smi_iommu->larb_nr; i++) { - if (dev == smi_iommu->larb_imu[i].dev) { - /* The 'mmu' may be updated in iommu-attach/detach. */ - larb->mmu = &smi_iommu->larb_imu[i].mmu; + for (i = 0; i < MTK_LARB_NR_MAX; i++) { + if (dev == larb_mmu[i].dev) { + larb->larbid = i; + larb->mmu = &larb_mmu[i].mmu; return 0; } } return -ENODEV; } -static void mtk_smi_larb_config_port_mt2712(struct device *dev) +static void mtk_smi_larb_config_port_gen2_general(struct device *dev) { struct mtk_smi_larb *larb = dev_get_drvdata(dev); u32 reg; int i; - /* - * larb 8/9 is the bdpsys larb, the iommu_en is enabled defaultly. - * Don't need to set it again. - */ - if (larb->larbid == 8 || larb->larbid == 9) + if (BIT(larb->larbid) & larb->larb_gen->larb_direct_to_common_mask) return; for_each_set_bit(i, (unsigned long *)larb->mmu, 32) { @@ -243,7 +227,6 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = { }; static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = { - .need_larbid = true, .port_in_larb = { LARB0_PORT_OFFSET, LARB1_PORT_OFFSET, LARB2_PORT_OFFSET, LARB3_PORT_OFFSET @@ -252,8 +235,15 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = { }; static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = { - .need_larbid = true, - .config_port = mtk_smi_larb_config_port_mt2712, + .config_port = mtk_smi_larb_config_port_gen2_general, + .larb_direct_to_common_mask = BIT(8) | BIT(9), /* bdpsys */ +}; + +static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = { + .has_gals = true, + .config_port = mtk_smi_larb_config_port_gen2_general, + .larb_direct_to_common_mask = BIT(2) | BIT(3) | BIT(7), + /* IPU0 | IPU1 | CCU */ }; static const struct of_device_id mtk_smi_larb_of_ids[] = { @@ -269,6 +259,10 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = { .compatible = "mediatek,mt2712-smi-larb", .data = &mtk_smi_larb_mt2712 }, + { + .compatible = "mediatek,mt8183-smi-larb", + .data = &mtk_smi_larb_mt8183 + }, {} }; @@ -279,7 +273,6 @@ static int mtk_smi_larb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *smi_node; struct platform_device *smi_pdev; - int err; larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL); if (!larb) @@ -298,16 +291,16 @@ static int mtk_smi_larb_probe(struct platform_device *pdev) larb->smi.clk_smi = devm_clk_get(dev, "smi"); if (IS_ERR(larb->smi.clk_smi)) return PTR_ERR(larb->smi.clk_smi); - larb->smi.dev = dev; - if (larb->larb_gen->need_larbid) { - err = of_property_read_u32(dev->of_node, "mediatek,larb-id", - &larb->larbid); - if (err) { - dev_err(dev, "missing larbid property\n"); - return err; - } + if (larb->larb_gen->has_gals) { + /* The larbs may still haven't gals even if the SoC support.*/ + larb->smi.clk_gals0 = devm_clk_get(dev, "gals"); + if (PTR_ERR(larb->smi.clk_gals0) == -ENOENT) + larb->smi.clk_gals0 = NULL; + else if (IS_ERR(larb->smi.clk_gals0)) + return PTR_ERR(larb->smi.clk_gals0); } + larb->smi.dev = dev; smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0); if (!smi_node) @@ -336,27 +329,86 @@ static int mtk_smi_larb_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused mtk_smi_larb_resume(struct device *dev) +{ + struct mtk_smi_larb *larb = dev_get_drvdata(dev); + const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen; + int ret; + + /* Power on smi-common. */ + ret = pm_runtime_get_sync(larb->smi_common_dev); + if (ret < 0) { + dev_err(dev, "Failed to pm get for smi-common(%d).\n", ret); + return ret; + } + + ret = mtk_smi_clk_enable(&larb->smi); + if (ret < 0) { + dev_err(dev, "Failed to enable clock(%d).\n", ret); + pm_runtime_put_sync(larb->smi_common_dev); + return ret; + } + + /* Configure the basic setting for this larb */ + larb_gen->config_port(dev); + + return 0; +} + +static int __maybe_unused mtk_smi_larb_suspend(struct device *dev) +{ + struct mtk_smi_larb *larb = dev_get_drvdata(dev); + + mtk_smi_clk_disable(&larb->smi); + pm_runtime_put_sync(larb->smi_common_dev); + return 0; +} + +static const struct dev_pm_ops smi_larb_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_smi_larb_suspend, mtk_smi_larb_resume, NULL) +}; + static struct platform_driver mtk_smi_larb_driver = { .probe = mtk_smi_larb_probe, .remove = mtk_smi_larb_remove, .driver = { .name = "mtk-smi-larb", .of_match_table = mtk_smi_larb_of_ids, + .pm = &smi_larb_pm_ops, } }; +static const struct mtk_smi_common_plat mtk_smi_common_gen1 = { + .gen = MTK_SMI_GEN1, +}; + +static const struct mtk_smi_common_plat mtk_smi_common_gen2 = { + .gen = MTK_SMI_GEN2, +}; + +static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = { + .gen = MTK_SMI_GEN2, + .has_gals = true, + .bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(5) | + F_MMU1_LARB(7), +}; + static const struct of_device_id mtk_smi_common_of_ids[] = { { .compatible = "mediatek,mt8173-smi-common", - .data = (void *)MTK_SMI_GEN2 + .data = &mtk_smi_common_gen2, }, { .compatible = "mediatek,mt2701-smi-common", - .data = (void *)MTK_SMI_GEN1 + .data = &mtk_smi_common_gen1, }, { .compatible = "mediatek,mt2712-smi-common", - .data = (void *)MTK_SMI_GEN2 + .data = &mtk_smi_common_gen2, + }, + { + .compatible = "mediatek,mt8183-smi-common", + .data = &mtk_smi_common_mt8183, }, {} }; @@ -366,13 +418,13 @@ static int mtk_smi_common_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mtk_smi *common; struct resource *res; - enum mtk_smi_gen smi_gen; int ret; common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); if (!common) return -ENOMEM; common->dev = dev; + common->plat = of_device_get_match_data(dev); common->clk_apb = devm_clk_get(dev, "apb"); if (IS_ERR(common->clk_apb)) @@ -382,14 +434,23 @@ static int mtk_smi_common_probe(struct platform_device *pdev) if (IS_ERR(common->clk_smi)) return PTR_ERR(common->clk_smi); + if (common->plat->has_gals) { + common->clk_gals0 = devm_clk_get(dev, "gals0"); + if (IS_ERR(common->clk_gals0)) + return PTR_ERR(common->clk_gals0); + + common->clk_gals1 = devm_clk_get(dev, "gals1"); + if (IS_ERR(common->clk_gals1)) + return PTR_ERR(common->clk_gals1); + } + /* * for mtk smi gen 1, we need to get the ao(always on) base to config * m4u port, and we need to enable the aync clock for transform the smi * clock into emi clock domain, but for mtk smi gen2, there's no smi ao * base. */ - smi_gen = (enum mtk_smi_gen)of_device_get_match_data(dev); - if (smi_gen == MTK_SMI_GEN1) { + if (common->plat->gen == MTK_SMI_GEN1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); common->smi_ao_base = devm_ioremap_resource(dev, res); if (IS_ERR(common->smi_ao_base)) @@ -402,6 +463,11 @@ static int mtk_smi_common_probe(struct platform_device *pdev) ret = clk_prepare_enable(common->clk_async); if (ret) return ret; + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + common->base = devm_ioremap_resource(dev, res); + if (IS_ERR(common->base)) + return PTR_ERR(common->base); } pm_runtime_enable(dev); platform_set_drvdata(pdev, common); @@ -414,12 +480,42 @@ static int mtk_smi_common_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused mtk_smi_common_resume(struct device *dev) +{ + struct mtk_smi *common = dev_get_drvdata(dev); + u32 bus_sel = common->plat->bus_sel; + int ret; + + ret = mtk_smi_clk_enable(common); + if (ret) { + dev_err(common->dev, "Failed to enable clock(%d).\n", ret); + return ret; + } + + if (common->plat->gen == MTK_SMI_GEN2 && bus_sel) + writel(bus_sel, common->base + SMI_BUS_SEL); + return 0; +} + +static int __maybe_unused mtk_smi_common_suspend(struct device *dev) +{ + struct mtk_smi *common = dev_get_drvdata(dev); + + mtk_smi_clk_disable(common); + return 0; +} + +static const struct dev_pm_ops smi_common_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_smi_common_suspend, mtk_smi_common_resume, NULL) +}; + static struct platform_driver mtk_smi_common_driver = { .probe = mtk_smi_common_probe, .remove = mtk_smi_common_remove, .driver = { .name = "mtk-smi-common", .of_match_table = mtk_smi_common_of_ids, + .pm = &smi_common_pm_ops, } }; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 3f21e26b8d36..90e0f21bc49c 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -1590,8 +1590,10 @@ static unsigned long dsiclk_rate(u8 n) switch (divsel) { case PRCM_DSI_PLLOUT_SEL_PHI_4: div *= 2; + /* Fall through */ case PRCM_DSI_PLLOUT_SEL_PHI_2: div *= 2; + /* Fall through */ case PRCM_DSI_PLLOUT_SEL_PHI: return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), PLL_RAW) / div; diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 792b855a9104..4798d9f3f9d5 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -308,7 +308,7 @@ static int usbhs_runtime_resume(struct device *dev) i, r); } } - /* Fall through as HSIC mode needs utmi_clk */ + /* Fall through - as HSIC mode needs utmi_clk */ case OMAP_EHCI_PORT_MODE_TLL: if (!IS_ERR(omap->utmi_clk[i])) { @@ -344,7 +344,7 @@ static int usbhs_runtime_suspend(struct device *dev) if (!IS_ERR(omap->hsic480m_clk[i])) clk_disable_unprepare(omap->hsic480m_clk[i]); - /* Fall through as utmi_clks were used in HSIC mode */ + /* Fall through - as utmi_clks were used in HSIC mode */ case OMAP_EHCI_PORT_MODE_TLL: if (!IS_ERR(omap->utmi_clk[i])) diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 601cefb5c9d8..050478cabc95 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -729,7 +729,7 @@ static int rk808_remove(struct i2c_client *client) return 0; } -static int rk8xx_suspend(struct device *dev) +static int __maybe_unused rk8xx_suspend(struct device *dev) { struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); int ret = 0; @@ -749,7 +749,7 @@ static int rk8xx_suspend(struct device *dev) return ret; } -static int rk8xx_resume(struct device *dev) +static int __maybe_unused rk8xx_resume(struct device *dev) { struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client); int ret = 0; @@ -768,7 +768,7 @@ static int rk8xx_resume(struct device *dev) return ret; } -SIMPLE_DEV_PM_OPS(rk8xx_pm_ops, rk8xx_suspend, rk8xx_resume); +static SIMPLE_DEV_PM_OPS(rk8xx_pm_ops, rk8xx_suspend, rk8xx_resume); static struct i2c_driver rk808_i2c_driver = { .driver = { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 6abfc8e92fcc..16900357afc2 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -465,6 +465,7 @@ config PCI_ENDPOINT_TEST config XILINX_SDFEC tristate "Xilinx SDFEC 16" + depends on HAS_IOMEM help This option enables support for the Xilinx SDFEC (Soft Decision Forward Error Correction) driver. This enables a char driver diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index 6ad83d5ef4b0..f00d1c32f6d6 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -683,7 +683,7 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) rc = hl_poll_timeout_memory(hdev, &ctx->thread_ctx_switch_wait_token, tmp, (tmp == 1), - 100, jiffies_to_usecs(hdev->timeout_jiffies)); + 100, jiffies_to_usecs(hdev->timeout_jiffies), false); if (rc == -ETIMEDOUT) { dev_err(hdev->dev, diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 0c4894dd9c02..7a8f9d0b71b5 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -970,7 +970,8 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) rc = hl_ctx_init(hdev, hdev->kernel_ctx, true); if (rc) { dev_err(hdev->dev, "failed to initialize kernel context\n"); - goto free_ctx; + kfree(hdev->kernel_ctx); + goto mmu_fini; } rc = hl_cb_pool_init(hdev); @@ -1053,8 +1054,6 @@ release_ctx: if (hl_ctx_put(hdev->kernel_ctx) != 1) dev_err(hdev->dev, "kernel ctx is still alive on initialization failure\n"); -free_ctx: - kfree(hdev->kernel_ctx); mmu_fini: hl_mmu_fini(hdev); eq_fini: diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c index cc8168bacb24..ea2ca67fbfbf 100644 --- a/drivers/misc/habanalabs/firmware_if.c +++ b/drivers/misc/habanalabs/firmware_if.c @@ -24,7 +24,7 @@ int hl_fw_push_fw_to_device(struct hl_device *hdev, const char *fw_name, { const struct firmware *fw; const u64 *fw_data; - size_t fw_size, i; + size_t fw_size; int rc; rc = request_firmware(&fw, fw_name, hdev->dev); @@ -45,22 +45,7 @@ int hl_fw_push_fw_to_device(struct hl_device *hdev, const char *fw_name, fw_data = (const u64 *) fw->data; - if ((fw->size % 8) != 0) - fw_size -= 8; - - for (i = 0 ; i < fw_size ; i += 8, fw_data++, dst += 8) { - if (!(i & (0x80000 - 1))) { - dev_dbg(hdev->dev, - "copied so far %zu out of %zu for %s firmware", - i, fw_size, fw_name); - usleep_range(20, 100); - } - - writeq(*fw_data, dst); - } - - if ((fw->size % 8) != 0) - writel(*(const u32 *) fw_data, dst); + memcpy_toio(dst, fw_data, fw_size); out: release_firmware(fw); @@ -112,7 +97,8 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, } rc = hl_poll_timeout_memory(hdev, &pkt->fence, tmp, - (tmp == ARMCP_PACKET_FENCE_VAL), 1000, timeout); + (tmp == ARMCP_PACKET_FENCE_VAL), 1000, + timeout, true); hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 1a2c062a57d4..271c5c8f53b4 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2729,9 +2729,10 @@ void goya_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi) GOYA_ASYNC_EVENT_ID_PI_UPDATE); } -void goya_flush_pq_write(struct hl_device *hdev, u64 *pq, u64 exp_val) +void goya_pqe_write(struct hl_device *hdev, __le64 *pqe, struct hl_bd *bd) { - /* Not needed in Goya */ + /* The QMANs are on the SRAM so need to copy to IO space */ + memcpy_toio((void __iomem *) pqe, bd, sizeof(struct hl_bd)); } static void *goya_dma_alloc_coherent(struct hl_device *hdev, size_t size, @@ -2864,7 +2865,8 @@ static int goya_send_job_on_qman0(struct hl_device *hdev, struct hl_cs_job *job) } rc = hl_poll_timeout_memory(hdev, fence_ptr, tmp, - (tmp == GOYA_QMAN0_FENCE_VAL), 1000, timeout); + (tmp == GOYA_QMAN0_FENCE_VAL), 1000, + timeout, true); hl_hw_queue_inc_ci_kernel(hdev, GOYA_QUEUE_ID_DMA_0); @@ -2945,7 +2947,7 @@ int goya_test_queue(struct hl_device *hdev, u32 hw_queue_id) } rc = hl_poll_timeout_memory(hdev, fence_ptr, tmp, (tmp == fence_val), - 1000, GOYA_TEST_QUEUE_WAIT_USEC); + 1000, GOYA_TEST_QUEUE_WAIT_USEC, true); hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id); @@ -3312,9 +3314,11 @@ static int goya_validate_dma_pkt_no_mmu(struct hl_device *hdev, int rc; dev_dbg(hdev->dev, "DMA packet details:\n"); - dev_dbg(hdev->dev, "source == 0x%llx\n", user_dma_pkt->src_addr); - dev_dbg(hdev->dev, "destination == 0x%llx\n", user_dma_pkt->dst_addr); - dev_dbg(hdev->dev, "size == %u\n", user_dma_pkt->tsize); + dev_dbg(hdev->dev, "source == 0x%llx\n", + le64_to_cpu(user_dma_pkt->src_addr)); + dev_dbg(hdev->dev, "destination == 0x%llx\n", + le64_to_cpu(user_dma_pkt->dst_addr)); + dev_dbg(hdev->dev, "size == %u\n", le32_to_cpu(user_dma_pkt->tsize)); ctl = le32_to_cpu(user_dma_pkt->ctl); user_dir = (ctl & GOYA_PKT_LIN_DMA_CTL_DMA_DIR_MASK) >> @@ -3343,9 +3347,11 @@ static int goya_validate_dma_pkt_mmu(struct hl_device *hdev, struct packet_lin_dma *user_dma_pkt) { dev_dbg(hdev->dev, "DMA packet details:\n"); - dev_dbg(hdev->dev, "source == 0x%llx\n", user_dma_pkt->src_addr); - dev_dbg(hdev->dev, "destination == 0x%llx\n", user_dma_pkt->dst_addr); - dev_dbg(hdev->dev, "size == %u\n", user_dma_pkt->tsize); + dev_dbg(hdev->dev, "source == 0x%llx\n", + le64_to_cpu(user_dma_pkt->src_addr)); + dev_dbg(hdev->dev, "destination == 0x%llx\n", + le64_to_cpu(user_dma_pkt->dst_addr)); + dev_dbg(hdev->dev, "size == %u\n", le32_to_cpu(user_dma_pkt->tsize)); /* * WA for HW-23. @@ -3385,7 +3391,8 @@ static int goya_validate_wreg32(struct hl_device *hdev, dev_dbg(hdev->dev, "WREG32 packet details:\n"); dev_dbg(hdev->dev, "reg_offset == 0x%x\n", reg_offset); - dev_dbg(hdev->dev, "value == 0x%x\n", wreg_pkt->value); + dev_dbg(hdev->dev, "value == 0x%x\n", + le32_to_cpu(wreg_pkt->value)); if (reg_offset != (mmDMA_CH_0_WR_COMP_ADDR_LO & 0x1FFF)) { dev_err(hdev->dev, "WREG32 packet with illegal address 0x%x\n", @@ -3427,12 +3434,13 @@ static int goya_validate_cb(struct hl_device *hdev, while (cb_parsed_length < parser->user_cb_size) { enum packet_id pkt_id; u16 pkt_size; - void *user_pkt; + struct goya_packet *user_pkt; - user_pkt = (void *) (uintptr_t) + user_pkt = (struct goya_packet *) (uintptr_t) (parser->user_cb->kernel_address + cb_parsed_length); - pkt_id = (enum packet_id) (((*(u64 *) user_pkt) & + pkt_id = (enum packet_id) ( + (le64_to_cpu(user_pkt->header) & PACKET_HEADER_PACKET_ID_MASK) >> PACKET_HEADER_PACKET_ID_SHIFT); @@ -3452,7 +3460,8 @@ static int goya_validate_cb(struct hl_device *hdev, * need to validate here as well because patch_cb() is * not called in MMU path while this function is called */ - rc = goya_validate_wreg32(hdev, parser, user_pkt); + rc = goya_validate_wreg32(hdev, + parser, (struct packet_wreg32 *) user_pkt); break; case PACKET_WREG_BULK: @@ -3480,10 +3489,10 @@ static int goya_validate_cb(struct hl_device *hdev, case PACKET_LIN_DMA: if (is_mmu) rc = goya_validate_dma_pkt_mmu(hdev, parser, - user_pkt); + (struct packet_lin_dma *) user_pkt); else rc = goya_validate_dma_pkt_no_mmu(hdev, parser, - user_pkt); + (struct packet_lin_dma *) user_pkt); break; case PACKET_MSG_LONG: @@ -3656,15 +3665,16 @@ static int goya_patch_cb(struct hl_device *hdev, enum packet_id pkt_id; u16 pkt_size; u32 new_pkt_size = 0; - void *user_pkt, *kernel_pkt; + struct goya_packet *user_pkt, *kernel_pkt; - user_pkt = (void *) (uintptr_t) + user_pkt = (struct goya_packet *) (uintptr_t) (parser->user_cb->kernel_address + cb_parsed_length); - kernel_pkt = (void *) (uintptr_t) + kernel_pkt = (struct goya_packet *) (uintptr_t) (parser->patched_cb->kernel_address + cb_patched_cur_length); - pkt_id = (enum packet_id) (((*(u64 *) user_pkt) & + pkt_id = (enum packet_id) ( + (le64_to_cpu(user_pkt->header) & PACKET_HEADER_PACKET_ID_MASK) >> PACKET_HEADER_PACKET_ID_SHIFT); @@ -3679,15 +3689,18 @@ static int goya_patch_cb(struct hl_device *hdev, switch (pkt_id) { case PACKET_LIN_DMA: - rc = goya_patch_dma_packet(hdev, parser, user_pkt, - kernel_pkt, &new_pkt_size); + rc = goya_patch_dma_packet(hdev, parser, + (struct packet_lin_dma *) user_pkt, + (struct packet_lin_dma *) kernel_pkt, + &new_pkt_size); cb_patched_cur_length += new_pkt_size; break; case PACKET_WREG_32: memcpy(kernel_pkt, user_pkt, pkt_size); cb_patched_cur_length += pkt_size; - rc = goya_validate_wreg32(hdev, parser, kernel_pkt); + rc = goya_validate_wreg32(hdev, parser, + (struct packet_wreg32 *) kernel_pkt); break; case PACKET_WREG_BULK: @@ -4351,6 +4364,8 @@ static int goya_unmask_irq_arr(struct hl_device *hdev, u32 *irq_arr, size_t total_pkt_size; long result; int rc; + int irq_num_entries, irq_arr_index; + __le32 *goya_irq_arr; total_pkt_size = sizeof(struct armcp_unmask_irq_arr_packet) + irq_arr_size; @@ -4368,8 +4383,16 @@ static int goya_unmask_irq_arr(struct hl_device *hdev, u32 *irq_arr, if (!pkt) return -ENOMEM; - pkt->length = cpu_to_le32(irq_arr_size / sizeof(irq_arr[0])); - memcpy(&pkt->irqs, irq_arr, irq_arr_size); + irq_num_entries = irq_arr_size / sizeof(irq_arr[0]); + pkt->length = cpu_to_le32(irq_num_entries); + + /* We must perform any necessary endianness conversation on the irq + * array being passed to the goya hardware + */ + for (irq_arr_index = 0, goya_irq_arr = (__le32 *) &pkt->irqs; + irq_arr_index < irq_num_entries ; irq_arr_index++) + goya_irq_arr[irq_arr_index] = + cpu_to_le32(irq_arr[irq_arr_index]); pkt->armcp_pkt.ctl = cpu_to_le32(ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY << ARMCP_PKT_CTL_OPCODE_SHIFT); @@ -5041,7 +5064,7 @@ static const struct hl_asic_funcs goya_funcs = { .resume = goya_resume, .cb_mmap = goya_cb_mmap, .ring_doorbell = goya_ring_doorbell, - .flush_pq_write = goya_flush_pq_write, + .pqe_write = goya_pqe_write, .asic_dma_alloc_coherent = goya_dma_alloc_coherent, .asic_dma_free_coherent = goya_dma_free_coherent, .get_int_queue_base = goya_get_int_queue_base, diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index f8c611883dc1..d7f48c9c41cd 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -177,7 +177,7 @@ int goya_late_init(struct hl_device *hdev); void goya_late_fini(struct hl_device *hdev); void goya_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi); -void goya_flush_pq_write(struct hl_device *hdev, u64 *pq, u64 exp_val); +void goya_pqe_write(struct hl_device *hdev, __le64 *pqe, struct hl_bd *bd); void goya_update_eq_ci(struct hl_device *hdev, u32 val); void goya_restore_phase_topology(struct hl_device *hdev); int goya_context_switch(struct hl_device *hdev, u32 asid); diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 10da9940ee0d..ce83adafcf2d 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -441,7 +441,11 @@ enum hl_pll_frequency { * @resume: handles IP specific H/W or SW changes for resume. * @cb_mmap: maps a CB. * @ring_doorbell: increment PI on a given QMAN. - * @flush_pq_write: flush PQ entry write if necessary, WARN if flushing failed. + * @pqe_write: Write the PQ entry to the PQ. This is ASIC-specific + * function because the PQs are located in different memory areas + * per ASIC (SRAM, DRAM, Host memory) and therefore, the method of + * writing the PQE must match the destination memory area + * properties. * @asic_dma_alloc_coherent: Allocate coherent DMA memory by calling * dma_alloc_coherent(). This is ASIC function because * its implementation is not trivial when the driver @@ -510,7 +514,8 @@ struct hl_asic_funcs { int (*cb_mmap)(struct hl_device *hdev, struct vm_area_struct *vma, u64 kaddress, phys_addr_t paddress, u32 size); void (*ring_doorbell)(struct hl_device *hdev, u32 hw_queue_id, u32 pi); - void (*flush_pq_write)(struct hl_device *hdev, u64 *pq, u64 exp_val); + void (*pqe_write)(struct hl_device *hdev, __le64 *pqe, + struct hl_bd *bd); void* (*asic_dma_alloc_coherent)(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle, gfp_t flag); void (*asic_dma_free_coherent)(struct hl_device *hdev, size_t size, @@ -1062,9 +1067,17 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); /* * address in this macro points always to a memory location in the * host's (server's) memory. That location is updated asynchronously - * either by the direct access of the device or by another core + * either by the direct access of the device or by another core. + * + * To work both in LE and BE architectures, we need to distinguish between the + * two states (device or another core updates the memory location). Therefore, + * if mem_written_by_device is true, the host memory being polled will be + * updated directly by the device. If false, the host memory being polled will + * be updated by host CPU. Required so host knows whether or not the memory + * might need to be byte-swapped before returning value to caller. */ -#define hl_poll_timeout_memory(hdev, addr, val, cond, sleep_us, timeout_us) \ +#define hl_poll_timeout_memory(hdev, addr, val, cond, sleep_us, timeout_us, \ + mem_written_by_device) \ ({ \ ktime_t __timeout; \ /* timeout should be longer when working with simulator */ \ @@ -1077,10 +1090,14 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); /* Verify we read updates done by other cores or by device */ \ mb(); \ (val) = *((u32 *) (uintptr_t) (addr)); \ + if (mem_written_by_device) \ + (val) = le32_to_cpu(val); \ if (cond) \ break; \ if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ (val) = *((u32 *) (uintptr_t) (addr)); \ + if (mem_written_by_device) \ + (val) = le32_to_cpu(val); \ break; \ } \ if (sleep_us) \ diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index e3b5517897ea..5f5673b74985 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -290,23 +290,19 @@ static void int_hw_queue_schedule_job(struct hl_cs_job *job) struct hl_device *hdev = job->cs->ctx->hdev; struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; struct hl_bd bd; - u64 *pi, *pbd = (u64 *) &bd; + __le64 *pi; bd.ctl = 0; - bd.len = __cpu_to_le32(job->job_cb_size); - bd.ptr = __cpu_to_le64((u64) (uintptr_t) job->user_cb); + bd.len = cpu_to_le32(job->job_cb_size); + bd.ptr = cpu_to_le64((u64) (uintptr_t) job->user_cb); - pi = (u64 *) (uintptr_t) (q->kernel_address + + pi = (__le64 *) (uintptr_t) (q->kernel_address + ((q->pi & (q->int_queue_len - 1)) * sizeof(bd))); - pi[0] = pbd[0]; - pi[1] = pbd[1]; - q->pi++; q->pi &= ((q->int_queue_len << 1) - 1); - /* Flush PQ entry write. Relevant only for specific ASICs */ - hdev->asic_funcs->flush_pq_write(hdev, pi, pbd[0]); + hdev->asic_funcs->pqe_write(hdev, pi, &bd); hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi); } diff --git a/drivers/misc/habanalabs/include/goya/goya_packets.h b/drivers/misc/habanalabs/include/goya/goya_packets.h index a14407b975e4..ef54bad20509 100644 --- a/drivers/misc/habanalabs/include/goya/goya_packets.h +++ b/drivers/misc/habanalabs/include/goya/goya_packets.h @@ -52,6 +52,19 @@ enum goya_dma_direction { #define GOYA_PKT_CTL_MB_SHIFT 31 #define GOYA_PKT_CTL_MB_MASK 0x80000000 +/* All packets have, at least, an 8-byte header, which contains + * the packet type. The kernel driver uses the packet header for packet + * validation and to perform any necessary required preparation before + * sending them off to the hardware. + */ +struct goya_packet { + __le64 header; + /* The rest of the packet data follows. Use the corresponding + * packet_XXX struct to deference the data, based on packet type + */ + u8 contents[0]; +}; + struct packet_nop { __le32 reserved; __le32 ctl; diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c index ea9f72ff456c..199791b57caf 100644 --- a/drivers/misc/habanalabs/irq.c +++ b/drivers/misc/habanalabs/irq.c @@ -80,8 +80,7 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) struct hl_cs_job *job; bool shadow_index_valid; u16 shadow_index; - u32 *cq_entry; - u32 *cq_base; + struct hl_cq_entry *cq_entry, *cq_base; if (hdev->disabled) { dev_dbg(hdev->dev, @@ -90,29 +89,29 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) return IRQ_HANDLED; } - cq_base = (u32 *) (uintptr_t) cq->kernel_address; + cq_base = (struct hl_cq_entry *) (uintptr_t) cq->kernel_address; while (1) { - bool entry_ready = ((cq_base[cq->ci] & CQ_ENTRY_READY_MASK) + bool entry_ready = ((le32_to_cpu(cq_base[cq->ci].data) & + CQ_ENTRY_READY_MASK) >> CQ_ENTRY_READY_SHIFT); if (!entry_ready) break; - cq_entry = (u32 *) &cq_base[cq->ci]; + cq_entry = (struct hl_cq_entry *) &cq_base[cq->ci]; - /* - * Make sure we read CQ entry contents after we've + /* Make sure we read CQ entry contents after we've * checked the ownership bit. */ dma_rmb(); - shadow_index_valid = - ((*cq_entry & CQ_ENTRY_SHADOW_INDEX_VALID_MASK) + shadow_index_valid = ((le32_to_cpu(cq_entry->data) & + CQ_ENTRY_SHADOW_INDEX_VALID_MASK) >> CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT); - shadow_index = (u16) - ((*cq_entry & CQ_ENTRY_SHADOW_INDEX_MASK) + shadow_index = (u16) ((le32_to_cpu(cq_entry->data) & + CQ_ENTRY_SHADOW_INDEX_MASK) >> CQ_ENTRY_SHADOW_INDEX_SHIFT); queue = &hdev->kernel_queues[cq->hw_queue_id]; @@ -122,8 +121,7 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) queue_work(hdev->cq_wq, &job->finish_work); } - /* - * Update ci of the context's queue. There is no + /* Update ci of the context's queue. There is no * need to protect it with spinlock because this update is * done only inside IRQ and there is a different IRQ per * queue @@ -131,7 +129,8 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) queue->ci = hl_queue_inc_ptr(queue->ci); /* Clear CQ entry ready bit */ - cq_base[cq->ci] &= ~CQ_ENTRY_READY_MASK; + cq_entry->data = cpu_to_le32(le32_to_cpu(cq_entry->data) & + ~CQ_ENTRY_READY_MASK); cq->ci = hl_cq_inc_ptr(cq->ci); diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index 42d237cae1dc..365fb0cb8dff 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -1629,6 +1629,8 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) dev_dbg(hdev->dev, "page list 0x%p of asid %d is still alive\n", phys_pg_list, ctx->asid); + atomic64_sub(phys_pg_list->total_size, + &hdev->dram_used_mem); free_phys_pg_pack(hdev, phys_pg_list); idr_remove(&vm->phys_pg_pack_handles, i); } diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 1606658b9b7e..24245ccdba72 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -22,7 +22,7 @@ struct lkdtm_list { * recurse past the end of THREAD_SIZE by default. */ #if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0) -#define REC_STACK_SIZE (CONFIG_FRAME_WARN / 2) +#define REC_STACK_SIZE (_AC(CONFIG_FRAME_WARN, UL) / 2) #else #define REC_STACK_SIZE (THREAD_SIZE / 8) #endif @@ -91,7 +91,7 @@ void lkdtm_LOOP(void) void lkdtm_EXHAUST_STACK(void) { - pr_info("Calling function with %d frame size to depth %d ...\n", + pr_info("Calling function with %lu frame size to depth %d ...\n", REC_STACK_SIZE, recur_count); recursive_loop(recur_count); pr_info("FAIL: survived without exhausting stack?!\n"); diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 6c0173772162..77f7dff7098d 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -81,6 +81,8 @@ #define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ +#define MEI_DEV_ID_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ + #define MEI_DEV_ID_MCC 0x4B70 /* Mule Creek Canyon (EHL) */ #define MEI_DEV_ID_MCC_4 0x4B75 /* Mule Creek Canyon 4 (EHL) */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 57cb68f5cc64..541538eff8b1 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -98,6 +98,8 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_MCC, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_MCC_4, MEI_ME_PCH8_CFG)}, diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 8840299420e0..5e6be1527571 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -691,7 +691,6 @@ static int vmballoon_alloc_page_list(struct vmballoon *b, } if (page) { - vmballoon_mark_page_offline(page, ctl->page_size); /* Success. Add the page to the list and continue. */ list_add(&page->lru, &ctl->pages); continue; @@ -930,7 +929,6 @@ static void vmballoon_release_page_list(struct list_head *page_list, list_for_each_entry_safe(page, tmp, page_list, lru) { list_del(&page->lru); - vmballoon_mark_page_online(page, page_size); __free_pages(page, vmballoon_page_order(page_size)); } @@ -1005,6 +1003,7 @@ static void vmballoon_enqueue_page_list(struct vmballoon *b, enum vmballoon_page_size_type page_size) { unsigned long flags; + struct page *page; if (page_size == VMW_BALLOON_4K_PAGE) { balloon_page_list_enqueue(&b->b_dev_info, pages); @@ -1014,6 +1013,11 @@ static void vmballoon_enqueue_page_list(struct vmballoon *b, * for the balloon compaction mechanism. */ spin_lock_irqsave(&b->b_dev_info.pages_lock, flags); + + list_for_each_entry(page, pages, lru) { + vmballoon_mark_page_offline(page, VMW_BALLOON_2M_PAGE); + } + list_splice_init(pages, &b->huge_pages); __count_vm_events(BALLOON_INFLATE, *n_pages * vmballoon_page_in_frames(VMW_BALLOON_2M_PAGE)); @@ -1056,6 +1060,8 @@ static void vmballoon_dequeue_page_list(struct vmballoon *b, /* 2MB pages */ spin_lock_irqsave(&b->b_dev_info.pages_lock, flags); list_for_each_entry_safe(page, tmp, &b->huge_pages, lru) { + vmballoon_mark_page_online(page, VMW_BALLOON_2M_PAGE); + list_move(&page->lru, pages); if (++i == n_req_pages) break; diff --git a/drivers/misc/vmw_vmci/vmci_doorbell.c b/drivers/misc/vmw_vmci/vmci_doorbell.c index bad89b6e0802..345addd9306d 100644 --- a/drivers/misc/vmw_vmci/vmci_doorbell.c +++ b/drivers/misc/vmw_vmci/vmci_doorbell.c @@ -310,7 +310,8 @@ int vmci_dbell_host_context_notify(u32 src_cid, struct vmci_handle handle) entry = container_of(resource, struct dbell_entry, resource); if (entry->run_delayed) { - schedule_work(&entry->work); + if (!schedule_work(&entry->work)) + vmci_resource_put(resource); } else { entry->notify_cb(entry->client_data); vmci_resource_put(resource); @@ -361,7 +362,8 @@ static void dbell_fire_entries(u32 notify_idx) atomic_read(&dbell->active) == 1) { if (dbell->run_delayed) { vmci_resource_get(&dbell->resource); - schedule_work(&dbell->work); + if (!schedule_work(&dbell->work)) + vmci_resource_put(&dbell->resource); } else { dbell->notify_cb(dbell->client_data); } diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 74e4364bc9fb..09113b9ad679 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -564,7 +564,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, if (index == EXT_CSD_SANITIZE_START) cmd.sanitize_busy = true; - err = mmc_wait_for_cmd(host, &cmd, 0); + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); if (err) goto out; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index d681e8aaca83..fe914ff5f5d6 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1292,6 +1292,12 @@ int mmc_attach_sd(struct mmc_host *host) goto err; } + /* + * Some SD cards claims an out of spec VDD voltage range. Let's treat + * these bits as being in-valid and especially also bit7. + */ + ocr &= ~0x7FFF; + rocr = mmc_select_voltage(host, ocr); /* diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 7e0d3a49c06d..bb31e13648d6 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -597,7 +597,7 @@ static void bcm2835_finish_request(struct bcm2835_host *host) struct dma_chan *terminate_chan = NULL; struct mmc_request *mrq; - cancel_delayed_work_sync(&host->timeout_work); + cancel_delayed_work(&host->timeout_work); mrq = host->mrq; diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index ed5cefb83768..89deb451e0ac 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -374,6 +374,7 @@ static int finish_dma_single(struct cvm_mmc_host *host, struct mmc_data *data) { data->bytes_xfered = data->blocks * data->blksz; data->error = 0; + dma_unmap_sg(host->dev, data->sg, data->sg_len, get_dma_dir(data)); return 1; } @@ -1046,7 +1047,8 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) mmc->max_segs = 1; /* DMA size field can address up to 8 MB */ - mmc->max_seg_size = 8 * 1024 * 1024; + mmc->max_seg_size = min_t(unsigned int, 8 * 1024 * 1024, + dma_get_max_seg_size(host->dev)); mmc->max_req_size = mmc->max_seg_size; /* External DMA is in 512 byte blocks */ mmc->max_blk_size = 512; diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 64d3b5fb7fe5..4a2872f49a60 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -774,8 +774,6 @@ int renesas_sdhi_probe(struct platform_device *pdev, /* All SDHI have SDIO status bits which must be 1 */ mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS; - pm_runtime_enable(&pdev->dev); - ret = renesas_sdhi_clk_enable(host); if (ret) goto efree; @@ -856,8 +854,6 @@ edisclk: efree: tmio_mmc_host_free(host); - pm_runtime_disable(&pdev->dev); - return ret; } EXPORT_SYMBOL_GPL(renesas_sdhi_probe); @@ -869,8 +865,6 @@ int renesas_sdhi_remove(struct platform_device *pdev) tmio_mmc_host_remove(host); renesas_sdhi_clk_disable(host); - pm_runtime_disable(&pdev->dev); - return 0; } EXPORT_SYMBOL_GPL(renesas_sdhi_remove); diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index b3a130a9ee23..1604f512c7bd 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -883,7 +883,7 @@ static int sdhci_acpi_runtime_resume(struct device *dev) sdhci_acpi_byt_setting(&c->pdev->dev); - return sdhci_runtime_resume_host(c->host); + return sdhci_runtime_resume_host(c->host, 0); } #endif diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 163d1cf4367e..44139fceac24 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -369,6 +369,7 @@ static int sdhci_cdns_probe(struct platform_device *pdev) host->mmc_host_ops.execute_tuning = sdhci_cdns_execute_tuning; host->mmc_host_ops.hs400_enhanced_strobe = sdhci_cdns_hs400_enhanced_strobe; + sdhci_enable_v4_mode(host); sdhci_get_of_property(pdev); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c391510e9ef4..776a94216248 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1705,7 +1705,7 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) esdhc_pltfm_set_clock(host, imx_data->actual_clock); } - err = sdhci_runtime_resume_host(host); + err = sdhci_runtime_resume_host(host, 0); if (err) goto disable_ipg_clk; diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index e377b9bc55a4..e7d1920729fb 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -289,7 +289,7 @@ static int sdhci_at91_runtime_resume(struct device *dev) } out: - return sdhci_runtime_resume_host(host); + return sdhci_runtime_resume_host(host, 0); } #endif /* CONFIG_PM */ @@ -357,6 +357,9 @@ static int sdhci_at91_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); + /* HS200 is broken at this moment */ + host->quirks2 = SDHCI_QUIRK2_BROKEN_HS200; + ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 4041878eb0f3..7d06e2860c36 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -167,7 +167,7 @@ static int sdhci_pci_runtime_suspend_host(struct sdhci_pci_chip *chip) err_pci_runtime_suspend: while (--i >= 0) - sdhci_runtime_resume_host(chip->slots[i]->host); + sdhci_runtime_resume_host(chip->slots[i]->host, 0); return ret; } @@ -181,7 +181,7 @@ static int sdhci_pci_runtime_resume_host(struct sdhci_pci_chip *chip) if (!slot) continue; - ret = sdhci_runtime_resume_host(slot->host); + ret = sdhci_runtime_resume_host(slot->host, 0); if (ret) return ret; } diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 9dc4548271b4..19944b0049db 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -432,7 +432,6 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) mmc_hostname(host->mmc)); host->flags &= ~SDHCI_SIGNALING_330; host->flags |= SDHCI_SIGNALING_180; - host->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; host->mmc->caps2 |= MMC_CAP2_NO_SD; host->mmc->caps2 |= MMC_CAP2_NO_SDIO; pci_write_config_dword(chip->pdev, @@ -682,6 +681,7 @@ static const struct sdhci_ops sdhci_pci_o2_ops = { const struct sdhci_pci_fixes sdhci_o2 = { .probe = sdhci_pci_o2_probe, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, .probe_slot = sdhci_pci_o2_probe_slot, #ifdef CONFIG_PM_SLEEP .resume = sdhci_pci_o2_resume, diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 3ddecf479295..e55037ceda73 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -554,7 +554,7 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); - return sdhci_runtime_resume_host(host); + return sdhci_runtime_resume_host(host, 0); } #endif diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 8e4a8ba33f05..f5753aef7151 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -745,7 +745,7 @@ static int sdhci_s3c_runtime_resume(struct device *dev) clk_prepare_enable(busclk); if (ourhost->cur_clk >= 0) clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]); - ret = sdhci_runtime_resume_host(host); + ret = sdhci_runtime_resume_host(host, 0); return ret; } #endif diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index 603a5d9f045a..d07b9793380f 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -217,10 +217,11 @@ static inline void _sdhci_sprd_set_clock(struct sdhci_host *host, struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); u32 div, val, mask; - div = sdhci_sprd_calc_div(sprd_host->base_rate, clk); + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); - clk |= ((div & 0x300) >> 2) | ((div & 0xFF) << 8); - sdhci_enable_clk(host, clk); + div = sdhci_sprd_calc_div(sprd_host->base_rate, clk); + div = ((div & 0x300) >> 2) | ((div & 0xFF) << 8); + sdhci_enable_clk(host, div); /* enable auto gate sdhc_enable_auto_gate */ val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI); @@ -373,6 +374,11 @@ static unsigned int sdhci_sprd_get_max_timeout_count(struct sdhci_host *host) return 1 << 31; } +static unsigned int sdhci_sprd_get_ro(struct sdhci_host *host) +{ + return 0; +} + static struct sdhci_ops sdhci_sprd_ops = { .read_l = sdhci_sprd_readl, .write_l = sdhci_sprd_writel, @@ -385,6 +391,7 @@ static struct sdhci_ops sdhci_sprd_ops = { .set_uhs_signaling = sdhci_sprd_set_uhs_signaling, .hw_reset = sdhci_sprd_hw_reset, .get_max_timeout_count = sdhci_sprd_get_max_timeout_count, + .get_ro = sdhci_sprd_get_ro, }; static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -501,9 +508,12 @@ static void sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host, } static const struct sdhci_pltfm_data sdhci_sprd_pdata = { - .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_MISSING_CAPS, .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 | - SDHCI_QUIRK2_USE_32BIT_BLK_CNT, + SDHCI_QUIRK2_USE_32BIT_BLK_CNT | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .ops = &sdhci_sprd_ops, }; @@ -605,6 +615,16 @@ static int sdhci_sprd_probe(struct platform_device *pdev) sdhci_enable_v4_mode(host); + /* + * Supply the existing CAPS, but clear the UHS-I modes. This + * will allow these modes to be specified only by device + * tree properties through mmc_of_parse(). + */ + host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | + SDHCI_SUPPORT_DDR50); + ret = sdhci_setup_host(host); if (ret) goto pm_runtime_disable; @@ -696,7 +716,7 @@ static int sdhci_sprd_runtime_resume(struct device *dev) if (ret) goto clk_disable; - sdhci_runtime_resume_host(host); + sdhci_runtime_resume_host(host, 1); return 0; clk_disable: diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index f4d4761cf20a..02d8f524bb9e 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -258,6 +258,16 @@ static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg) } } +static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) +{ + /* + * Write-enable shall be assumed if GPIO is missing in a board's + * device-tree because SDHCI's WRITE_PROTECT bit doesn't work on + * Tegra. + */ + return mmc_gpio_get_ro(host->mmc); +} + static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1224,6 +1234,7 @@ static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = { }; static const struct sdhci_ops tegra_sdhci_ops = { + .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, .set_clock = tegra_sdhci_set_clock, @@ -1279,6 +1290,7 @@ static const struct sdhci_tegra_soc_data soc_data_tegra30 = { }; static const struct sdhci_ops tegra114_sdhci_ops = { + .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, .write_w = tegra_sdhci_writew, .write_l = tegra_sdhci_writel, @@ -1332,6 +1344,7 @@ static const struct sdhci_tegra_soc_data soc_data_tegra124 = { }; static const struct sdhci_ops tegra210_sdhci_ops = { + .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, .write_w = tegra210_sdhci_writew, .write_l = tegra_sdhci_writel, @@ -1366,6 +1379,7 @@ static const struct sdhci_tegra_soc_data soc_data_tegra210 = { }; static const struct sdhci_ops tegra186_sdhci_ops = { + .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, .set_clock = tegra_sdhci_set_clock, diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c index 8a18f14cf842..1dea1ba66f7b 100644 --- a/drivers/mmc/host/sdhci-xenon.c +++ b/drivers/mmc/host/sdhci-xenon.c @@ -638,7 +638,7 @@ static int xenon_runtime_resume(struct device *dev) priv->restore_needed = false; } - ret = sdhci_runtime_resume_host(host); + ret = sdhci_runtime_resume_host(host, 0); if (ret) goto out; return 0; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 59acf8e3331e..a5dc5aae973e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3320,7 +3320,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) } EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host); -int sdhci_runtime_resume_host(struct sdhci_host *host) +int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) { struct mmc_host *mmc = host->mmc; unsigned long flags; @@ -3331,7 +3331,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) host->ops->enable_dma(host); } - sdhci_init(host, 0); + sdhci_init(host, soft_reset); if (mmc->ios.power_mode != MMC_POWER_UNDEFINED && mmc->ios.power_mode != MMC_POWER_OFF) { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 89fd96596a1f..902f855efe8f 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -781,7 +781,7 @@ void sdhci_adma_write_desc(struct sdhci_host *host, void **desc, int sdhci_suspend_host(struct sdhci_host *host); int sdhci_resume_host(struct sdhci_host *host); int sdhci_runtime_suspend_host(struct sdhci_host *host); -int sdhci_runtime_resume_host(struct sdhci_host *host); +int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset); #endif void sdhci_cqe_enable(struct mmc_host *mmc); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 8539e10784b4..93e83ad25976 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -172,8 +172,6 @@ static int tmio_mmc_probe(struct platform_device *pdev) host->mmc->f_max = pdata->hclk; host->mmc->f_min = pdata->hclk / 512; - pm_runtime_enable(&pdev->dev); - ret = tmio_mmc_host_probe(host); if (ret) goto host_free; @@ -193,7 +191,6 @@ host_remove: tmio_mmc_host_remove(host); host_free: tmio_mmc_host_free(host); - pm_runtime_disable(&pdev->dev); cell_disable: if (cell->disable) cell->disable(pdev); @@ -210,8 +207,6 @@ static int tmio_mmc_remove(struct platform_device *pdev) if (cell->disable) cell->disable(pdev); - pm_runtime_disable(&pdev->dev); - return 0; } diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index c5ba13fae399..2f0b092d6dcc 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -163,6 +163,7 @@ struct tmio_mmc_host { unsigned long last_req_ts; struct mutex ios_lock; /* protect set_ios() context */ bool native_hotplug; + bool runtime_synced; bool sdio_irq_enabled; /* Mandatory callback */ diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 2cb3f951c3e2..9b6e1001e77c 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -1153,15 +1153,6 @@ void tmio_mmc_host_free(struct tmio_mmc_host *host) } EXPORT_SYMBOL_GPL(tmio_mmc_host_free); -/** - * tmio_mmc_host_probe() - Common probe for all implementations - * @_host: Host to probe - * - * Perform tasks common to all implementations probe functions. - * - * The caller should have called pm_runtime_enable() prior to calling - * the common probe function. - */ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) { struct platform_device *pdev = _host->pdev; @@ -1257,19 +1248,22 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) /* See if we also get DMA */ tmio_mmc_request_dma(_host, pdata); - pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); ret = mmc_add_host(mmc); if (ret) goto remove_host; dev_pm_qos_expose_latency_limit(&pdev->dev, 100); + pm_runtime_put(&pdev->dev); return 0; remove_host: + pm_runtime_put_noidle(&pdev->dev); tmio_mmc_host_remove(_host); return ret; } @@ -1280,12 +1274,11 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) struct platform_device *pdev = host->pdev; struct mmc_host *mmc = host->mmc; + pm_runtime_get_sync(&pdev->dev); + if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); - if (!host->native_hotplug) - pm_runtime_get_sync(&pdev->dev); - dev_pm_qos_hide_latency_limit(&pdev->dev); mmc_remove_host(mmc); @@ -1294,7 +1287,10 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) tmio_mmc_release_dma(host); pm_runtime_dont_use_autosuspend(&pdev->dev); + if (host->native_hotplug) + pm_runtime_put_noidle(&pdev->dev); pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); } EXPORT_SYMBOL_GPL(tmio_mmc_host_remove); @@ -1337,6 +1333,11 @@ int tmio_mmc_host_runtime_resume(struct device *dev) { struct tmio_mmc_host *host = dev_get_drvdata(dev); + if (!host->runtime_synced) { + host->runtime_synced = true; + return 0; + } + tmio_mmc_clk_enable(host); tmio_mmc_hw_reset(host->mmc); diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index 49aad9a79c18..91a2be41edf6 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -631,7 +631,6 @@ static int uniphier_sd_probe(struct platform_device *pdev) host->clk_disable = uniphier_sd_clk_disable; host->set_clock = uniphier_sd_set_clock; - pm_runtime_enable(&pdev->dev); ret = uniphier_sd_clk_enable(host); if (ret) goto free_host; @@ -653,7 +652,6 @@ static int uniphier_sd_probe(struct platform_device *pdev) free_host: tmio_mmc_host_free(host); - pm_runtime_disable(&pdev->dev); return ret; } @@ -664,7 +662,6 @@ static int uniphier_sd_remove(struct platform_device *pdev) tmio_mmc_host_remove(host); uniphier_sd_clk_disable(host); - pm_runtime_disable(&pdev->dev); return 0; } diff --git a/drivers/mtd/hyperbus/Kconfig b/drivers/mtd/hyperbus/Kconfig index b4e3caf7d799..a4d8968d133d 100644 --- a/drivers/mtd/hyperbus/Kconfig +++ b/drivers/mtd/hyperbus/Kconfig @@ -1,5 +1,6 @@ menuconfig MTD_HYPERBUS tristate "HyperBus support" + depends on HAS_IOMEM select MTD_CFI select MTD_MAP_BANK_WIDTH_2 select MTD_CFI_AMDSTD diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 895510d40ce4..47602af4ee34 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -81,6 +81,7 @@ static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *r default: printk(KERN_WARNING "SA1100 flash: unknown base address " "0x%08lx, assuming CS0\n", phys); + /* Fall through */ case SA1100_CS0_PHYS: subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 03cc788511d5..654bdc41fc99 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -3780,8 +3780,6 @@ static int spi_nor_init_params(struct spi_nor *nor, default: /* Kept only for backward compatibility purpose. */ params->quad_enable = spansion_quad_enable; - if (nor->clear_sr_bp) - nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp; break; } @@ -4035,6 +4033,9 @@ static int spi_nor_init(struct spi_nor *nor) int err; if (nor->clear_sr_bp) { + if (nor->quad_enable == spansion_quad_enable) + nor->clear_sr_bp = spi_nor_spansion_clear_sr_bp; + err = nor->clear_sr_bp(nor); if (err) { dev_err(nor->dev, diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c index 11c5bad95226..14a5fb378145 100644 --- a/drivers/net/arcnet/arc-rimi.c +++ b/drivers/net/arcnet/arc-rimi.c @@ -363,10 +363,13 @@ static int __init arcrimi_setup(char *s) switch (ints[0]) { default: /* ERROR */ pr_err("Too many arguments\n"); + /* Fall through */ case 3: /* Node ID */ node = ints[3]; + /* Fall through */ case 2: /* IRQ */ irq = ints[2]; + /* Fall through */ case 1: /* IO address */ io = ints[1]; } diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c index 28510e33924f..cd27fdc1059b 100644 --- a/drivers/net/arcnet/com20020-isa.c +++ b/drivers/net/arcnet/com20020-isa.c @@ -197,16 +197,22 @@ static int __init com20020isa_setup(char *s) switch (ints[0]) { default: /* ERROR */ pr_info("Too many arguments\n"); + /* Fall through */ case 6: /* Timeout */ timeout = ints[6]; + /* Fall through */ case 5: /* CKP value */ clockp = ints[5]; + /* Fall through */ case 4: /* Backplane flag */ backplane = ints[4]; + /* Fall through */ case 3: /* Node ID */ node = ints[3]; + /* Fall through */ case 2: /* IRQ */ irq = ints[2]; + /* Fall through */ case 1: /* IO address */ io = ints[1]; } diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index 2c546013a980..186bbf87bc84 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -363,8 +363,10 @@ static int __init com90io_setup(char *s) switch (ints[0]) { default: /* ERROR */ pr_err("Too many arguments\n"); + /* Fall through */ case 2: /* IRQ */ irq = ints[2]; + /* Fall through */ case 1: /* IO address */ io = ints[1]; } diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index ca4a57c30bf8..bd75d06ad7df 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -693,10 +693,13 @@ static int __init com90xx_setup(char *s) switch (ints[0]) { default: /* ERROR */ pr_err("Too many arguments\n"); + /* Fall through */ case 3: /* Mem address */ shmem = ints[3]; + /* Fall through */ case 2: /* IRQ */ irq = ints[2]; + /* Fall through */ case 1: /* IO address */ io = ints[1]; } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 9b7016abca2f..931d9d935686 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1126,6 +1126,8 @@ static void bond_compute_features(struct bonding *bond) done: bond_dev->vlan_features = vlan_features; bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX | NETIF_F_GSO_UDP_L4; bond_dev->mpls_features = mpls_features; bond_dev->gso_max_segs = gso_max_segs; @@ -2196,6 +2198,15 @@ static void bond_miimon_commit(struct bonding *bond) bond_for_each_slave(bond, slave, iter) { switch (slave->new_link) { case BOND_LINK_NOCHANGE: + /* For 802.3ad mode, check current slave speed and + * duplex again in case its port was disabled after + * invalid speed/duplex reporting but recovered before + * link monitoring could make a decision on the actual + * link status + */ + if (BOND_MODE(bond) == BOND_MODE_8023AD && + slave->link == BOND_LINK_UP) + bond_3ad_adapter_speed_duplex_changed(slave); continue; case BOND_LINK_UP: diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index b6b93a2d93a5..483d270664cc 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -1249,6 +1249,8 @@ int register_candev(struct net_device *dev) return -EINVAL; dev->rtnl_link_ops = &can_link_ops; + netif_carrier_off(dev); + return register_netdev(dev); } EXPORT_SYMBOL_GPL(register_candev); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index f2fe344593d5..fcec8bcb53d6 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -400,9 +400,10 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable) priv->write(reg_mcr, ®s->mcr); } -static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv) +static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; + unsigned int ackval; u32 reg_mcr; reg_mcr = priv->read(®s->mcr); @@ -412,20 +413,37 @@ static inline void flexcan_enter_stop_mode(struct flexcan_priv *priv) /* enable stop request */ regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 1 << priv->stm.req_bit); + + /* get stop acknowledgment */ + if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr, + ackval, ackval & (1 << priv->stm.ack_bit), + 0, FLEXCAN_TIMEOUT_US)) + return -ETIMEDOUT; + + return 0; } -static inline void flexcan_exit_stop_mode(struct flexcan_priv *priv) +static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->regs; + unsigned int ackval; u32 reg_mcr; /* remove stop request */ regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr, 1 << priv->stm.req_bit, 0); + /* get stop acknowledgment */ + if (regmap_read_poll_timeout(priv->stm.gpr, priv->stm.ack_gpr, + ackval, !(ackval & (1 << priv->stm.ack_bit)), + 0, FLEXCAN_TIMEOUT_US)) + return -ETIMEDOUT; + reg_mcr = priv->read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_SLF_WAK; priv->write(reg_mcr, ®s->mcr); + + return 0; } static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv) @@ -1437,10 +1455,10 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) priv = netdev_priv(dev); priv->stm.gpr = syscon_node_to_regmap(gpr_np); - of_node_put(gpr_np); if (IS_ERR(priv->stm.gpr)) { dev_dbg(&pdev->dev, "could not find gpr regmap\n"); - return PTR_ERR(priv->stm.gpr); + ret = PTR_ERR(priv->stm.gpr); + goto out_put_node; } priv->stm.req_gpr = out_val[1]; @@ -1455,7 +1473,9 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev) device_set_wakeup_capable(&pdev->dev, true); - return 0; +out_put_node: + of_node_put(gpr_np); + return ret; } static const struct of_device_id flexcan_of_match[] = { @@ -1612,7 +1632,9 @@ static int __maybe_unused flexcan_suspend(struct device *device) */ if (device_may_wakeup(device)) { enable_irq_wake(dev->irq); - flexcan_enter_stop_mode(priv); + err = flexcan_enter_stop_mode(priv); + if (err) + return err; } else { err = flexcan_chip_disable(priv); if (err) @@ -1662,10 +1684,13 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); + int err; if (netif_running(dev) && device_may_wakeup(device)) { flexcan_enable_wakeup_irq(priv, false); - flexcan_exit_stop_mode(priv); + err = flexcan_exit_stop_mode(priv); + if (err) + return err; } return 0; diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 05410008aa6b..de34a4b82d4a 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -1508,10 +1508,11 @@ static int rcar_canfd_rx_poll(struct napi_struct *napi, int quota) /* All packets processed */ if (num_pkts < quota) { - napi_complete_done(napi, num_pkts); - /* Enable Rx FIFO interrupts */ - rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), - RCANFD_RFCC_RFIE); + if (napi_complete_done(napi, num_pkts)) { + /* Enable Rx FIFO interrupts */ + rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), + RCANFD_RFCC_RFIE); + } } return num_pkts; } diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c index 185c7f7d38a4..5e0d5e8101c8 100644 --- a/drivers/net/can/sja1000/peak_pcmcia.c +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -479,7 +479,7 @@ static void pcan_free_channels(struct pcan_pccard *card) if (!netdev) continue; - strncpy(name, netdev->name, IFNAMSIZ); + strlcpy(name, netdev->name, IFNAMSIZ); unregister_sja1000dev(netdev); diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index 234cf1042df6..12358f06d194 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -664,17 +664,6 @@ static int mcp251x_power_enable(struct regulator *reg, int enable) return regulator_disable(reg); } -static void mcp251x_open_clean(struct net_device *net) -{ - struct mcp251x_priv *priv = netdev_priv(net); - struct spi_device *spi = priv->spi; - - free_irq(spi->irq, priv); - mcp251x_hw_sleep(spi); - mcp251x_power_enable(priv->transceiver, 0); - close_candev(net); -} - static int mcp251x_stop(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); @@ -941,37 +930,43 @@ static int mcp251x_open(struct net_device *net) flags | IRQF_ONESHOT, DEVICE_NAME, priv); if (ret) { dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); - mcp251x_power_enable(priv->transceiver, 0); - close_candev(net); - goto open_unlock; + goto out_close; } priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, 0); + if (!priv->wq) { + ret = -ENOMEM; + goto out_clean; + } INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler); INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler); ret = mcp251x_hw_reset(spi); - if (ret) { - mcp251x_open_clean(net); - goto open_unlock; - } + if (ret) + goto out_free_wq; ret = mcp251x_setup(net, spi); - if (ret) { - mcp251x_open_clean(net); - goto open_unlock; - } + if (ret) + goto out_free_wq; ret = mcp251x_set_normal_mode(spi); - if (ret) { - mcp251x_open_clean(net); - goto open_unlock; - } + if (ret) + goto out_free_wq; can_led_event(net, CAN_LED_EVENT_OPEN); netif_wake_queue(net); + mutex_unlock(&priv->mcp_lock); -open_unlock: + return 0; + +out_free_wq: + destroy_workqueue(priv->wq); +out_clean: + free_irq(spi->irq, priv); + mcp251x_hw_sleep(spi); +out_close: + mcp251x_power_enable(priv->transceiver, 0); + close_candev(net); mutex_unlock(&priv->mcp_lock); return ret; } diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 458154c9b482..65dce642b86b 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -568,16 +568,16 @@ static int peak_usb_ndo_stop(struct net_device *netdev) dev->state &= ~PCAN_USB_STATE_STARTED; netif_stop_queue(netdev); + close_candev(netdev); + + dev->can.state = CAN_STATE_STOPPED; + /* unlink all pending urbs and free used memory */ peak_usb_unlink_all_urbs(dev); if (dev->adapter->dev_stop) dev->adapter->dev_stop(dev); - close_candev(netdev); - - dev->can.state = CAN_STATE_STOPPED; - /* can set bus off now */ if (dev->adapter->dev_set_bus) { int err = dev->adapter->dev_set_bus(dev, 0); @@ -855,7 +855,7 @@ static void peak_usb_disconnect(struct usb_interface *intf) dev_prev_siblings = dev->prev_siblings; dev->state &= ~PCAN_USB_STATE_CONNECTED; - strncpy(name, netdev->name, IFNAMSIZ); + strlcpy(name, netdev->name, IFNAMSIZ); unregister_netdev(netdev); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 34761c3a6286..47cc1ff5b88e 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -841,7 +841,7 @@ static int pcan_usb_fd_init(struct peak_usb_device *dev) goto err_out; /* allocate command buffer once for all for the interface */ - pdev->cmd_buffer_addr = kmalloc(PCAN_UFD_CMD_BUFFER_SIZE, + pdev->cmd_buffer_addr = kzalloc(PCAN_UFD_CMD_BUFFER_SIZE, GFP_KERNEL); if (!pdev->cmd_buffer_addr) goto err_out_1; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index 178bb7cff0c1..53cb2f72bdd0 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -494,7 +494,7 @@ static int pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded) u8 *buffer; int err; - buffer = kmalloc(PCAN_USBPRO_FCT_DRVLD_REQ_LEN, GFP_KERNEL); + buffer = kzalloc(PCAN_USBPRO_FCT_DRVLD_REQ_LEN, GFP_KERNEL); if (!buffer) return -ENOMEM; diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 3811fdbda13e..28c963a21dac 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -478,6 +478,7 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) { + struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; if (!phy_interface_mode_is_rgmii(state->interface) && @@ -487,8 +488,10 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port, state->interface != PHY_INTERFACE_MODE_INTERNAL && state->interface != PHY_INTERFACE_MODE_MOCA) { bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); - dev_err(ds->dev, - "Unsupported interface: %d\n", state->interface); + if (port != core_readl(priv, CORE_IMP0_PRT_ID)) + dev_err(ds->dev, + "Unsupported interface: %d for port %d\n", + state->interface, port); return; } @@ -526,6 +529,9 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, u32 id_mode_dis = 0, port_mode; u32 reg, offset; + if (port == core_readl(priv, CORE_IMP0_PRT_ID)) + return; + if (priv->type == BCM7445_DEVICE_ID) offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); else diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c index 5a9e27b337a8..098b01e4ed1a 100644 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ b/drivers/net/dsa/microchip/ksz9477_spi.c @@ -81,6 +81,7 @@ static const struct of_device_id ksz9477_dt_ids[] = { { .compatible = "microchip,ksz9897" }, { .compatible = "microchip,ksz9893" }, { .compatible = "microchip,ksz9563" }, + { .compatible = "microchip,ksz8563" }, {}, }; MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index ee7096d8af07..72ec250b9540 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -128,6 +128,7 @@ static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset, #define KSZ_REGMAP_ENTRY(width, swp, regbits, regpad, regalign) \ { \ + .name = #width, \ .val_bits = (width), \ .reg_stride = (width) / 8, \ .reg_bits = (regbits) + (regalign), \ diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 6b17cd961d06..d0a97eb73a37 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -27,7 +27,6 @@ #include <linux/platform_data/mv88e6xxx.h> #include <linux/netdevice.h> #include <linux/gpio/consumer.h> -#include <linux/phy.h> #include <linux/phylink.h> #include <net/dsa.h> @@ -430,7 +429,7 @@ int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, return 0; /* Port's MAC control must not be changed unless the link is down */ - err = chip->info->ops->port_set_link(chip, port, 0); + err = chip->info->ops->port_set_link(chip, port, LINK_FORCED_DOWN); if (err) return err; @@ -482,30 +481,6 @@ static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port) return port < chip->info->num_internal_phys; } -/* We expect the switch to perform auto negotiation if there is a real - * phy. However, in the case of a fixed link phy, we force the port - * settings from the fixed link settings. - */ -static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - if (!phy_is_pseudo_fixed_link(phydev) && - mv88e6xxx_phy_is_internal(ds, port)) - return; - - mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, - phydev->duplex, phydev->pause, - phydev->interface); - mv88e6xxx_reg_unlock(chip); - - if (err && err != -EOPNOTSUPP) - dev_err(ds->dev, "p%d: failed to configure MAC\n", port); -} - static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, unsigned long *mask, struct phylink_link_state *state) @@ -2721,6 +2696,7 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, err = mv88e6xxx_mdio_register(chip, child, true); if (err) { mv88e6xxx_mdios_unregister(chip); + of_node_put(child); return err; } } @@ -4638,7 +4614,6 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port, static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .get_tag_protocol = mv88e6xxx_get_tag_protocol, .setup = mv88e6xxx_setup, - .adjust_link = mv88e6xxx_adjust_link, .phylink_validate = mv88e6xxx_validate, .phylink_mac_link_state = mv88e6xxx_link_state, .phylink_mac_config = mv88e6xxx_mac_config, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 232e8cc96f6d..16f15c93a102 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name> * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org> - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2016 John Crispin <john@phrozen.org> */ @@ -583,8 +583,11 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv) for_each_available_child_of_node(ports, port) { err = of_property_read_u32(port, "reg", ®); - if (err) + if (err) { + of_node_put(port); + of_node_put(ports); return err; + } if (!dsa_is_user_port(priv->ds, reg)) continue; @@ -595,6 +598,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv) internal_mdio_mask |= BIT(reg); } + of_node_put(ports); if (!external_mdio_mask && !internal_mdio_mask) { dev_err(priv->dev, "no PHYs are defined.\n"); return -EINVAL; @@ -935,6 +939,8 @@ qca8k_port_enable(struct dsa_switch *ds, int port, qca8k_port_set_status(priv, port, 1); priv->port_sts[port].enabled = 1; + phy_support_asym_pause(phy); + return 0; } diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 6bfb1696a6f2..9988c9d18567 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -277,6 +277,18 @@ sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, SJA1105ET_SIZE_L2_LOOKUP_ENTRY, op); } +static size_t sja1105et_dyn_l2_lookup_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_lookup_entry *entry = entry_ptr; + u8 *cmd = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY; + const int size = SJA1105_SIZE_DYN_CMD; + + sja1105_packing(cmd, &entry->lockeds, 28, 28, size, op); + + return sja1105et_l2_lookup_entry_packing(buf, entry_ptr, op); +} + static void sja1105et_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, enum packing_op op) @@ -477,7 +489,7 @@ sja1105et_general_params_entry_packing(void *buf, void *entry_ptr, /* SJA1105E/T: First generation */ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { [BLK_IDX_L2_LOOKUP] = { - .entry_packing = sja1105et_l2_lookup_entry_packing, + .entry_packing = sja1105et_dyn_l2_lookup_entry_packing, .cmd_packing = sja1105et_l2_lookup_cmd_packing, .access = (OP_READ | OP_WRITE | OP_DEL), .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 32bf3a7cc3b6..df976b259e43 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -218,7 +218,7 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) /* This selects between Independent VLAN Learning (IVL) and * Shared VLAN Learning (SVL) */ - .shared_learn = false, + .shared_learn = true, /* Don't discard management traffic based on ENFPORT - * we don't perform SMAC port enforcement anyway, so * what we are setting here doesn't matter. @@ -625,6 +625,7 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv, if (of_property_read_u32(child, "reg", &index) < 0) { dev_err(dev, "Port number not defined in device tree " "(property \"reg\")\n"); + of_node_put(child); return -ENODEV; } @@ -634,6 +635,7 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv, dev_err(dev, "Failed to read phy-mode or " "phy-interface-type property for port %d\n", index); + of_node_put(child); return -ENODEV; } ports[index].phy_mode = phy_mode; @@ -643,6 +645,7 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv, if (!of_phy_is_fixed_link(child)) { dev_err(dev, "phy-handle or fixed-link " "properties missing!\n"); + of_node_put(child); return -ENODEV; } /* phy-handle is missing, but fixed-link isn't. @@ -1089,8 +1092,13 @@ int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, l2_lookup.vlanid = vid; l2_lookup.iotag = SJA1105_S_TAG; l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); - l2_lookup.mask_vlanid = VLAN_VID_MASK; - l2_lookup.mask_iotag = BIT(0); + if (dsa_port_is_vlan_filtering(&ds->ports[port])) { + l2_lookup.mask_vlanid = VLAN_VID_MASK; + l2_lookup.mask_iotag = BIT(0); + } else { + l2_lookup.mask_vlanid = 0; + l2_lookup.mask_iotag = 0; + } l2_lookup.destports = BIT(port); rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, @@ -1147,8 +1155,13 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, l2_lookup.vlanid = vid; l2_lookup.iotag = SJA1105_S_TAG; l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); - l2_lookup.mask_vlanid = VLAN_VID_MASK; - l2_lookup.mask_iotag = BIT(0); + if (dsa_port_is_vlan_filtering(&ds->ports[port])) { + l2_lookup.mask_vlanid = VLAN_VID_MASK; + l2_lookup.mask_iotag = BIT(0); + } else { + l2_lookup.mask_vlanid = 0; + l2_lookup.mask_iotag = 0; + } l2_lookup.destports = BIT(port); rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, @@ -1178,60 +1191,31 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { struct sja1105_private *priv = ds->priv; - u16 rx_vid, tx_vid; - int rc, i; - if (dsa_port_is_vlan_filtering(&ds->ports[port])) - return priv->info->fdb_add_cmd(ds, port, addr, vid); - - /* Since we make use of VLANs even when the bridge core doesn't tell us - * to, translate these FDB entries into the correct dsa_8021q ones. - * The basic idea (also repeats for removal below) is: - * - Each of the other front-panel ports needs to be able to forward a - * pvid-tagged (aka tagged with their rx_vid) frame that matches this - * DMAC. - * - The CPU port (aka the tx_vid of this port) needs to be able to - * send a frame matching this DMAC to the specified port. - * For a better picture see net/dsa/tag_8021q.c. + /* dsa_8021q is in effect when the bridge's vlan_filtering isn't, + * so the switch still does some VLAN processing internally. + * But Shared VLAN Learning (SVL) is also active, and it will take + * care of autonomous forwarding between the unique pvid's of each + * port. Here we just make sure that users can't add duplicate FDB + * entries when in this mode - the actual VID doesn't matter except + * for what gets printed in 'bridge fdb show'. In the case of zero, + * no VID gets printed at all. */ - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - if (i == port) - continue; - if (i == dsa_upstream_port(priv->ds, port)) - continue; + if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + vid = 0; - rx_vid = dsa_8021q_rx_vid(ds, i); - rc = priv->info->fdb_add_cmd(ds, port, addr, rx_vid); - if (rc < 0) - return rc; - } - tx_vid = dsa_8021q_tx_vid(ds, port); - return priv->info->fdb_add_cmd(ds, port, addr, tx_vid); + return priv->info->fdb_add_cmd(ds, port, addr, vid); } static int sja1105_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { struct sja1105_private *priv = ds->priv; - u16 rx_vid, tx_vid; - int rc, i; - if (dsa_port_is_vlan_filtering(&ds->ports[port])) - return priv->info->fdb_del_cmd(ds, port, addr, vid); + if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + vid = 0; - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - if (i == port) - continue; - if (i == dsa_upstream_port(priv->ds, port)) - continue; - - rx_vid = dsa_8021q_rx_vid(ds, i); - rc = priv->info->fdb_del_cmd(ds, port, addr, rx_vid); - if (rc < 0) - return rc; - } - tx_vid = dsa_8021q_tx_vid(ds, port); - return priv->info->fdb_del_cmd(ds, port, addr, tx_vid); + return priv->info->fdb_del_cmd(ds, port, addr, vid); } static int sja1105_fdb_dump(struct dsa_switch *ds, int port, @@ -1239,12 +1223,8 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, { struct sja1105_private *priv = ds->priv; struct device *dev = ds->dev; - u16 rx_vid, tx_vid; int i; - rx_vid = dsa_8021q_rx_vid(ds, port); - tx_vid = dsa_8021q_tx_vid(ds, port); - for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { struct sja1105_l2_lookup_entry l2_lookup = {0}; u8 macaddr[ETH_ALEN]; @@ -1270,39 +1250,9 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, continue; u64_to_ether_addr(l2_lookup.macaddr, macaddr); - /* On SJA1105 E/T, the switch doesn't implement the LOCKEDS - * bit, so it doesn't tell us whether a FDB entry is static - * or not. - * But, of course, we can find out - we're the ones who added - * it in the first place. - */ - if (priv->info->device_id == SJA1105E_DEVICE_ID || - priv->info->device_id == SJA1105T_DEVICE_ID) { - int match; - - match = sja1105_find_static_fdb_entry(priv, port, - &l2_lookup); - l2_lookup.lockeds = (match >= 0); - } - - /* We need to hide the dsa_8021q VLANs from the user. This - * basically means hiding the duplicates and only showing - * the pvid that is supposed to be active in standalone and - * non-vlan_filtering modes (aka 1). - * - For statically added FDB entries (bridge fdb add), we - * can convert the TX VID (coming from the CPU port) into the - * pvid and ignore the RX VIDs of the other ports. - * - For dynamically learned FDB entries, a single entry with - * no duplicates is learned - that which has the real port's - * pvid, aka RX VID. - */ - if (!dsa_port_is_vlan_filtering(&ds->ports[port])) { - if (l2_lookup.vlanid == tx_vid || - l2_lookup.vlanid == rx_vid) - l2_lookup.vlanid = 1; - else - continue; - } + /* We need to hide the dsa_8021q VLANs from the user. */ + if (!dsa_port_is_vlan_filtering(&ds->ports[port])) + l2_lookup.vlanid = 0; cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); } return 0; @@ -1594,6 +1544,7 @@ static int sja1105_vlan_prepare(struct dsa_switch *ds, int port, */ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) { + struct sja1105_l2_lookup_params_entry *l2_lookup_params; struct sja1105_general_params_entry *general_params; struct sja1105_private *priv = ds->priv; struct sja1105_table *table; @@ -1622,6 +1573,28 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) general_params->incl_srcpt1 = enabled; general_params->incl_srcpt0 = enabled; + /* VLAN filtering => independent VLAN learning. + * No VLAN filtering => shared VLAN learning. + * + * In shared VLAN learning mode, untagged traffic still gets + * pvid-tagged, and the FDB table gets populated with entries + * containing the "real" (pvid or from VLAN tag) VLAN ID. + * However the switch performs a masked L2 lookup in the FDB, + * effectively only looking up a frame's DMAC (and not VID) for the + * forwarding decision. + * + * This is extremely convenient for us, because in modes with + * vlan_filtering=0, dsa_8021q actually installs unique pvid's into + * each front panel port. This is good for identification but breaks + * learning badly - the VID of the learnt FDB entry is unique, aka + * no frames coming from any other port are going to have it. So + * for forwarding purposes, this is as though learning was broken + * (all frames get flooded). + */ + table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; + l2_lookup_params = table->entries; + l2_lookup_params->shared_learn = !enabled; + rc = sja1105_static_config_reload(priv); if (rc) dev_err(ds->dev, "Failed to change VLAN Ethertype\n"); @@ -1751,6 +1724,8 @@ static void sja1105_teardown(struct dsa_switch *ds) cancel_work_sync(&priv->tagger_data.rxtstamp_work); skb_queue_purge(&priv->tagger_data.skb_rxtstamp_queue); + sja1105_ptp_clock_unregister(priv); + sja1105_static_config_free(&priv->static_config); } static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, @@ -2208,9 +2183,7 @@ static int sja1105_remove(struct spi_device *spi) { struct sja1105_private *priv = spi_get_drvdata(spi); - sja1105_ptp_clock_unregister(priv); dsa_unregister_switch(priv->ds); - sja1105_static_config_free(&priv->static_config); return 0; } diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index d19cfdf681af..d8e8dd59f3d1 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -369,16 +369,15 @@ int sja1105_ptp_clock_register(struct sja1105_private *priv) .mult = SJA1105_CC_MULT, }; mutex_init(&priv->ptp_lock); - INIT_DELAYED_WORK(&priv->refresh_work, sja1105_ptp_overflow_check); - - schedule_delayed_work(&priv->refresh_work, SJA1105_REFRESH_INTERVAL); - priv->ptp_caps = sja1105_ptp_caps; priv->clock = ptp_clock_register(&priv->ptp_caps, ds->dev); if (IS_ERR_OR_NULL(priv->clock)) return PTR_ERR(priv->clock); + INIT_DELAYED_WORK(&priv->refresh_work, sja1105_ptp_overflow_check); + schedule_delayed_work(&priv->refresh_work, SJA1105_REFRESH_INTERVAL); + return sja1105_ptp_reset(priv); } diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index 2a3e2450968e..a9478577b495 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -12,8 +12,8 @@ config NET_VENDOR_8390 Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all - the questions about Western Digital cards. If you say Y, you will be - asked for your specific card in the following questions. + the questions about National Semiconductor 8390 cards. If you say Y, + you will be asked for your specific card in the following questions. if NET_VENDOR_8390 diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index ea34bcb868b5..edbb4b3604c7 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -2362,7 +2362,7 @@ static int et131x_tx_dma_memory_alloc(struct et131x_adapter *adapter) /* Allocate memory for the TCB's (Transmit Control Block) */ tx_ring->tcb_ring = kcalloc(NUM_TCB, sizeof(struct tcb), - GFP_ATOMIC | GFP_DMA); + GFP_KERNEL | GFP_DMA); if (!tx_ring->tcb_ring) return -ENOMEM; diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 3434730a7699..0537df06a9b5 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -860,7 +860,9 @@ static int emac_probe(struct platform_device *pdev) goto out_clk_disable_unprepare; } - db->phy_node = of_parse_phandle(np, "phy", 0); + db->phy_node = of_parse_phandle(np, "phy-handle", 0); + if (!db->phy_node) + db->phy_node = of_parse_phandle(np, "phy", 0); if (!db->phy_node) { dev_err(&pdev->dev, "no associated PHY\n"); ret = -ENODEV; diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index de4950d2022e..9f965cdfff5c 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -14,7 +14,7 @@ config NET_VENDOR_AMD say Y. Note that the answer to this question does not directly affect - the kernel: saying N will just case the configurator to skip all + the kernel: saying N will just cause the configurator to skip all the questions regarding AMD chipsets. If you say Y, you will be asked for your specific chipset/driver in the following questions. diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index b41f23679a08..7ce9c69e9c44 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -469,13 +469,19 @@ static int __init xgbe_mod_init(void) ret = xgbe_platform_init(); if (ret) - return ret; + goto err_platform_init; ret = xgbe_pci_init(); if (ret) - return ret; + goto err_pci_init; return 0; + +err_pci_init: + xgbe_platform_exit(); +err_platform_init: + unregister_netdevice_notifier(&xgbe_netdev_notifier); + return ret; } static void __exit xgbe_mod_exit(void) diff --git a/drivers/net/ethernet/apple/Kconfig b/drivers/net/ethernet/apple/Kconfig index fde7ae33e302..f78b9c841296 100644 --- a/drivers/net/ethernet/apple/Kconfig +++ b/drivers/net/ethernet/apple/Kconfig @@ -11,8 +11,8 @@ config NET_VENDOR_APPLE If you have a network (Ethernet) card belonging to this class, say Y. Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about IBM devices. If you say Y, you will be asked for + kernel: saying N will just cause the configurator to skip all the + questions about Apple devices. If you say Y, you will be asked for your specific card in the following questions. if NET_VENDOR_APPLE diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c index 440690b18734..aee827f07c16 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c @@ -431,7 +431,8 @@ int aq_del_fvlan_by_vlan(struct aq_nic_s *aq_nic, u16 vlan_id) if (be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id) break; } - if (rule && be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id) { + if (rule && rule->type == aq_rx_filter_vlan && + be16_to_cpu(rule->aq_fsp.h_ext.vlan_tci) == vlan_id) { struct ethtool_rxnfc cmd; cmd.fs.location = rule->aq_fsp.location; @@ -843,7 +844,7 @@ int aq_filters_vlans_update(struct aq_nic_s *aq_nic) return err; if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) { - if (hweight < AQ_VLAN_MAX_FILTERS && hweight > 0) { + if (hweight <= AQ_VLAN_MAX_FILTERS && hweight > 0) { err = aq_hw_ops->hw_filter_vlan_ctrl(aq_hw, !(aq_nic->packet_filter & IFF_PROMISC)); aq_nic->aq_nic_cfg.is_vlan_force_promisc = false; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_main.c b/drivers/net/ethernet/aquantia/atlantic/aq_main.c index 100722ad5c2d..b4a0fb281e69 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_main.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_main.c @@ -61,6 +61,10 @@ static int aq_ndev_open(struct net_device *ndev) if (err < 0) goto err_exit; + err = aq_filters_vlans_update(aq_nic); + if (err < 0) + goto err_exit; + err = aq_nic_start(aq_nic); if (err < 0) goto err_exit; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index e1392766e21e..8f66e7817811 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -393,7 +393,7 @@ int aq_nic_start(struct aq_nic_s *self) self->aq_nic_cfg.link_irq_vec); err = request_threaded_irq(irqvec, NULL, aq_linkstate_threaded_isr, - IRQF_SHARED, + IRQF_SHARED | IRQF_ONESHOT, self->ndev->name, self); if (err < 0) goto err_exit; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index 715685aa48c3..28892b8acd0e 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -86,6 +86,7 @@ static int aq_vec_poll(struct napi_struct *napi, int budget) } } +err_exit: if (!was_tx_cleaned) work_done = budget; @@ -95,7 +96,7 @@ static int aq_vec_poll(struct napi_struct *napi, int budget) 1U << self->aq_ring_param.vec_idx); } } -err_exit: + return work_done; } diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c index 8b69d0d7e726..6703960c7cf5 100644 --- a/drivers/net/ethernet/atheros/ag71xx.c +++ b/drivers/net/ethernet/atheros/ag71xx.c @@ -1141,7 +1141,7 @@ static int ag71xx_rings_init(struct ag71xx *ag) tx->descs_cpu = dma_alloc_coherent(&ag->pdev->dev, ring_size * AG71XX_DESC_SIZE, - &tx->descs_dma, GFP_ATOMIC); + &tx->descs_dma, GFP_KERNEL); if (!tx->descs_cpu) { kfree(tx->buf); tx->buf = NULL; diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index e9017caf024d..e24f5d2b6afe 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -14,9 +14,9 @@ config NET_VENDOR_BROADCOM say Y. Note that the answer to this question does not directly affect - the kernel: saying N will just case the configurator to skip all - the questions regarding AMD chipsets. If you say Y, you will be asked - for your specific chipset/driver in the following questions. + the kernel: saying N will just cause the configurator to skip all + the questions regarding Broadcom chipsets. If you say Y, you will + be asked for your specific chipset/driver in the following questions. if NET_VENDOR_BROADCOM diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index b9c5cea8db16..9483553ce444 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -992,7 +992,7 @@ static int bcm_sysport_poll(struct napi_struct *napi, int budget) { struct bcm_sysport_priv *priv = container_of(napi, struct bcm_sysport_priv, napi); - struct dim_sample dim_sample; + struct dim_sample dim_sample = {}; unsigned int work_done = 0; work_done = bcm_sysport_desc_rx(priv, budget); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index e2be5a685130..d10b421ed1f1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1934,8 +1934,7 @@ u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb, } /* select a non-FCoE queue */ - return netdev_pick_tx(dev, skb, NULL) % - (BNX2X_NUM_ETH_QUEUES(bp) * bp->max_cos); + return netdev_pick_tx(dev, skb, NULL) % (BNX2X_NUM_ETH_QUEUES(bp)); } void bnx2x_set_num_queues(struct bnx2x *bp) @@ -3058,12 +3057,13 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link) /* if VF indicate to PF this function is going down (PF will delete sp * elements and clear initializations */ - if (IS_VF(bp)) + if (IS_VF(bp)) { + bnx2x_clear_vlan_info(bp); bnx2x_vfpf_close_vf(bp); - else if (unload_mode != UNLOAD_RECOVERY) + } else if (unload_mode != UNLOAD_RECOVERY) { /* if this is a normal/close unload need to clean up chip*/ bnx2x_chip_cleanup(bp, unload_mode, keep_link); - else { + } else { /* Send the UNLOAD_REQUEST to the MCP */ bnx2x_send_unload_req(bp, unload_mode); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index c2f6e44e9a3f..8b08cb18e363 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -425,6 +425,8 @@ void bnx2x_set_reset_global(struct bnx2x *bp); void bnx2x_disable_close_the_gate(struct bnx2x *bp); int bnx2x_init_hw_func_cnic(struct bnx2x *bp); +void bnx2x_clear_vlan_info(struct bnx2x *bp); + /** * bnx2x_sp_event - handle ramrods completion. * diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 2cc14db8f0ec..192ff8d5da32 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -8482,11 +8482,21 @@ int bnx2x_set_vlan_one(struct bnx2x *bp, u16 vlan, return rc; } +void bnx2x_clear_vlan_info(struct bnx2x *bp) +{ + struct bnx2x_vlan_entry *vlan; + + /* Mark that hw forgot all entries */ + list_for_each_entry(vlan, &bp->vlan_reg, link) + vlan->hw = false; + + bp->vlan_cnt = 0; +} + static int bnx2x_del_all_vlans(struct bnx2x *bp) { struct bnx2x_vlan_mac_obj *vlan_obj = &bp->sp_objs[0].vlan_obj; unsigned long ramrod_flags = 0, vlan_flags = 0; - struct bnx2x_vlan_entry *vlan; int rc; __set_bit(RAMROD_COMP_WAIT, &ramrod_flags); @@ -8495,10 +8505,7 @@ static int bnx2x_del_all_vlans(struct bnx2x *bp) if (rc) return rc; - /* Mark that hw forgot all entries */ - list_for_each_entry(vlan, &bp->vlan_reg, link) - vlan->hw = false; - bp->vlan_cnt = 0; + bnx2x_clear_vlan_info(bp); return 0; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7134d2c3eb1c..8dce4069472b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2021,9 +2021,9 @@ static void __bnxt_poll_work_done(struct bnxt *bp, struct bnxt_napi *bnapi) if (bnapi->events & BNXT_RX_EVENT) { struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; - bnxt_db_write(bp, &rxr->rx_db, rxr->rx_prod); if (bnapi->events & BNXT_AGG_EVENT) bnxt_db_write(bp, &rxr->rx_agg_db, rxr->rx_agg_prod); + bnxt_db_write(bp, &rxr->rx_db, rxr->rx_prod); } bnapi->events = 0; } @@ -2136,7 +2136,7 @@ static int bnxt_poll(struct napi_struct *napi, int budget) } } if (bp->flags & BNXT_FLAG_DIM) { - struct dim_sample dim_sample; + struct dim_sample dim_sample = {}; dim_update_sample(cpr->event_ctr, cpr->rx_packets, @@ -5064,6 +5064,7 @@ static void bnxt_set_db(struct bnxt *bp, struct bnxt_db_info *db, u32 ring_type, static int bnxt_hwrm_ring_alloc(struct bnxt *bp) { + bool agg_rings = !!(bp->flags & BNXT_FLAG_AGG_RINGS); int i, rc = 0; u32 type; @@ -5139,7 +5140,9 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) if (rc) goto err_out; bnxt_set_db(bp, &rxr->rx_db, type, map_idx, ring->fw_ring_id); - bnxt_db_write(bp, &rxr->rx_db, rxr->rx_prod); + /* If we have agg rings, post agg buffers first. */ + if (!agg_rings) + bnxt_db_write(bp, &rxr->rx_db, rxr->rx_prod); bp->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id; if (bp->flags & BNXT_FLAG_CHIP_P5) { struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; @@ -5158,7 +5161,7 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) } } - if (bp->flags & BNXT_FLAG_AGG_RINGS) { + if (agg_rings) { type = HWRM_RING_ALLOC_AGG; for (i = 0; i < bp->rx_nr_rings; i++) { struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; @@ -5174,6 +5177,7 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) bnxt_set_db(bp, &rxr->rx_agg_db, type, map_idx, ring->fw_ring_id); bnxt_db_write(bp, &rxr->rx_agg_db, rxr->rx_agg_prod); + bnxt_db_write(bp, &rxr->rx_db, rxr->rx_prod); bp->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id; } } @@ -7016,19 +7020,29 @@ static void bnxt_hwrm_clear_vnic_rss(struct bnxt *bp) bnxt_hwrm_vnic_set_rss(bp, i, false); } -static void bnxt_hwrm_resource_free(struct bnxt *bp, bool close_path, - bool irq_re_init) +static void bnxt_clear_vnic(struct bnxt *bp) { - if (bp->vnic_info) { - bnxt_hwrm_clear_vnic_filter(bp); + if (!bp->vnic_info) + return; + + bnxt_hwrm_clear_vnic_filter(bp); + if (!(bp->flags & BNXT_FLAG_CHIP_P5)) { /* clear all RSS setting before free vnic ctx */ bnxt_hwrm_clear_vnic_rss(bp); bnxt_hwrm_vnic_ctx_free(bp); - /* before free the vnic, undo the vnic tpa settings */ - if (bp->flags & BNXT_FLAG_TPA) - bnxt_set_tpa(bp, false); - bnxt_hwrm_vnic_free(bp); } + /* before free the vnic, undo the vnic tpa settings */ + if (bp->flags & BNXT_FLAG_TPA) + bnxt_set_tpa(bp, false); + bnxt_hwrm_vnic_free(bp); + if (bp->flags & BNXT_FLAG_CHIP_P5) + bnxt_hwrm_vnic_ctx_free(bp); +} + +static void bnxt_hwrm_resource_free(struct bnxt *bp, bool close_path, + bool irq_re_init) +{ + bnxt_clear_vnic(bp); bnxt_hwrm_ring_free(bp, close_path); bnxt_hwrm_ring_grp_free(bp); if (irq_re_init) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c index 549c90d3e465..c05d663212b2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c @@ -98,10 +98,13 @@ static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg, if (idx) req->dimensions = cpu_to_le16(1); - if (req->req_type == cpu_to_le16(HWRM_NVM_SET_VARIABLE)) + if (req->req_type == cpu_to_le16(HWRM_NVM_SET_VARIABLE)) { memcpy(data_addr, buf, bytesize); - - rc = hwrm_send_message(bp, msg, msg_len, HWRM_CMD_TIMEOUT); + rc = hwrm_send_message(bp, msg, msg_len, HWRM_CMD_TIMEOUT); + } else { + rc = hwrm_send_message_silent(bp, msg, msg_len, + HWRM_CMD_TIMEOUT); + } if (!rc && req->req_type == cpu_to_le16(HWRM_NVM_GET_VARIABLE)) memcpy(buf, data_addr, bytesize); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index c7ee63d69679..8445a0cce849 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2016,21 +2016,19 @@ static int bnxt_flash_package_from_file(struct net_device *dev, mutex_lock(&bp->hwrm_cmd_lock); hwrm_err = _hwrm_send_message(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); - if (hwrm_err) - goto flash_pkg_exit; - - if (resp->error_code) { + if (hwrm_err) { u8 error_code = ((struct hwrm_err_output *)resp)->cmd_err; - if (error_code == NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { + if (resp->error_code && error_code == + NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) { install.flags |= cpu_to_le16( NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG); hwrm_err = _hwrm_send_message(bp, &install, sizeof(install), INSTALL_PACKAGE_TIMEOUT); - if (hwrm_err) - goto flash_pkg_exit; } + if (hwrm_err) + goto flash_pkg_exit; } if (resp->result) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c index 6fe4a7174271..dd621f6bd127 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -1236,7 +1236,7 @@ static int __bnxt_tc_del_flow(struct bnxt *bp, static void bnxt_tc_set_flow_dir(struct bnxt *bp, struct bnxt_tc_flow *flow, u16 src_fid) { - flow->dir = (bp->pf.fw_fid == src_fid) ? BNXT_DIR_RX : BNXT_DIR_TX; + flow->l2_key.dir = (bp->pf.fw_fid == src_fid) ? BNXT_DIR_RX : BNXT_DIR_TX; } static void bnxt_tc_set_src_fid(struct bnxt *bp, struct bnxt_tc_flow *flow, @@ -1285,9 +1285,7 @@ static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid, goto free_node; bnxt_tc_set_src_fid(bp, flow, src_fid); - - if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) - bnxt_tc_set_flow_dir(bp, flow, src_fid); + bnxt_tc_set_flow_dir(bp, flow, flow->src_fid); if (!bnxt_tc_can_offload(bp, flow)) { rc = -EOPNOTSUPP; @@ -1407,7 +1405,7 @@ static void bnxt_fill_cfa_stats_req(struct bnxt *bp, * 2. 15th bit of flow_handle must specify the flow * direction (TX/RX). */ - if (flow_node->flow.dir == BNXT_DIR_RX) + if (flow_node->flow.l2_key.dir == BNXT_DIR_RX) handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_DIR_RX | CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK; else diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h index ffec57d1a5ec..4f05305052f2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h @@ -23,6 +23,9 @@ struct bnxt_tc_l2_key { __be16 inner_vlan_tci; __be16 ether_type; u8 num_vlans; + u8 dir; +#define BNXT_DIR_RX 1 +#define BNXT_DIR_TX 0 }; struct bnxt_tc_l3_key { @@ -98,9 +101,6 @@ struct bnxt_tc_flow { /* flow applicable to pkts ingressing on this fid */ u16 src_fid; - u8 dir; -#define BNXT_DIR_RX 1 -#define BNXT_DIR_TX 0 struct bnxt_tc_l2_key l2_key; struct bnxt_tc_l2_key l2_mask; struct bnxt_tc_l3_key l3_key; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index a2b57807453b..b22196880d6d 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1124,6 +1124,7 @@ static const struct ethtool_ops bcmgenet_ethtool_ops = { .set_coalesce = bcmgenet_set_coalesce, .get_link_ksettings = bcmgenet_get_link_ksettings, .set_link_ksettings = bcmgenet_set_link_ksettings, + .get_ts_info = ethtool_op_get_ts_info, }; /* Power down the unimac, based on mode. */ @@ -1895,7 +1896,7 @@ static int bcmgenet_rx_poll(struct napi_struct *napi, int budget) { struct bcmgenet_rx_ring *ring = container_of(napi, struct bcmgenet_rx_ring, napi); - struct dim_sample dim_sample; + struct dim_sample dim_sample = {}; unsigned int work_done; work_done = bcmgenet_desc_rx(ring, budget); diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 5ca17e62dc3e..35b59b5edf0f 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4154,7 +4154,7 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,emac", .data = &emac_config }, { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config}, { .compatible = "cdns,zynq-gem", .data = &zynq_config }, - { .compatible = "sifive,fu540-macb", .data = &fu540_c000_config }, + { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, macb_dt_ids); diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.c b/drivers/net/ethernet/cavium/common/cavium_ptp.c index 73632b843749..b821c9e1604c 100644 --- a/drivers/net/ethernet/cavium/common/cavium_ptp.c +++ b/drivers/net/ethernet/cavium/common/cavium_ptp.c @@ -10,7 +10,7 @@ #include "cavium_ptp.h" -#define DRV_NAME "Cavium PTP Driver" +#define DRV_NAME "cavium_ptp" #define PCI_DEVICE_ID_CAVIUM_PTP 0xA00C #define PCI_DEVICE_ID_CAVIUM_RST 0xA00E diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 032224178b64..6dd65f9b347c 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -237,8 +237,10 @@ int octeon_setup_iq(struct octeon_device *oct, } oct->num_iqs++; - if (oct->fn_list.enable_io_queues(oct)) + if (oct->fn_list.enable_io_queues(oct)) { + octeon_delete_instr_queue(oct, iq_no); return 1; + } return 0; } diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index ad22554857bf..acb016834f04 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -1381,24 +1381,18 @@ static int acpi_get_mac_address(struct device *dev, struct acpi_device *adev, u8 *dst) { u8 mac[ETH_ALEN]; - int ret; + u8 *addr; - ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), - "mac-address", mac, ETH_ALEN); - if (ret) - goto out; - - if (!is_valid_ether_addr(mac)) { + addr = fwnode_get_mac_address(acpi_fwnode_handle(adev), mac, ETH_ALEN); + if (!addr) { dev_err(dev, "MAC address invalid: %pM\n", mac); - ret = -EINVAL; - goto out; + return -EINVAL; } dev_info(dev, "MAC address set to: %pM\n", mac); - memcpy(dst, mac, ETH_ALEN); -out: - return ret; + ether_addr_copy(dst, mac); + return 0; } /* Currently only sets the MAC address. */ diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 1e82b9efe447..58f89f6a040f 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -3269,7 +3269,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!adapter->regs) { dev_err(&pdev->dev, "cannot map device registers\n"); err = -ENOMEM; - goto out_free_adapter; + goto out_free_adapter_nofail; } adapter->pdev = pdev; @@ -3397,6 +3397,9 @@ out_free_dev: if (adapter->port[i]) free_netdev(adapter->port[i]); +out_free_adapter_nofail: + kfree_skb(adapter->nofail_skb); + out_free_adapter: kfree(adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 02959035ed3f..d692251ee252 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -3236,8 +3236,10 @@ static ssize_t blocked_fl_write(struct file *filp, const char __user *ubuf, return -ENOMEM; err = bitmap_parse_user(ubuf, count, t, adap->sge.egr_sz); - if (err) + if (err) { + kvfree(t); return err; + } bitmap_copy(adap->sge.blocked_fl, t, adap->sge.egr_sz); kvfree(t); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index ef5d61d57597..323976c811e9 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -550,7 +550,7 @@ int be_process_mcc(struct be_adapter *adapter) int num = 0, status = 0; struct be_mcc_obj *mcc_obj = &adapter->mcc_obj; - spin_lock(&adapter->mcc_cq_lock); + spin_lock_bh(&adapter->mcc_cq_lock); while ((compl = be_mcc_compl_get(adapter))) { if (compl->flags & CQE_FLAGS_ASYNC_MASK) { @@ -566,7 +566,7 @@ int be_process_mcc(struct be_adapter *adapter) if (num) be_cq_notify(adapter, mcc_obj->cq.id, mcc_obj->rearm_cq, num); - spin_unlock(&adapter->mcc_cq_lock); + spin_unlock_bh(&adapter->mcc_cq_lock); return status; } @@ -581,9 +581,7 @@ static int be_mcc_wait_compl(struct be_adapter *adapter) if (be_check_error(adapter, BE_ERROR_ANY)) return -EIO; - local_bh_disable(); status = be_process_mcc(adapter); - local_bh_enable(); if (atomic_read(&mcc_obj->q.used) == 0) break; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 2edb86ec9fe9..4d8e40ac66d2 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -5630,9 +5630,7 @@ static void be_worker(struct work_struct *work) * mcc completions */ if (!netif_running(adapter->netdev)) { - local_bh_disable(); be_process_mcc(adapter); - local_bh_enable(); goto reschedule; } diff --git a/drivers/net/ethernet/ezchip/nps_enet.h b/drivers/net/ethernet/ezchip/nps_enet.h index 133acca0bf31..092da2d90026 100644 --- a/drivers/net/ethernet/ezchip/nps_enet.h +++ b/drivers/net/ethernet/ezchip/nps_enet.h @@ -167,7 +167,7 @@ struct nps_enet_priv { }; /** - * nps_reg_set - Sets ENET register with provided value. + * nps_enet_reg_set - Sets ENET register with provided value. * @priv: Pointer to EZchip ENET private data structure. * @reg: Register offset from base address. * @value: Value to set in register. @@ -179,7 +179,7 @@ static inline void nps_enet_reg_set(struct nps_enet_priv *priv, } /** - * nps_reg_get - Gets value of specified ENET register. + * nps_enet_reg_get - Gets value of specified ENET register. * @priv: Pointer to EZchip ENET private data structure. * @reg: Register offset from base address. * diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig index ed0d010c7cf2..04a59db03f2b 100644 --- a/drivers/net/ethernet/freescale/enetc/Kconfig +++ b/drivers/net/ethernet/freescale/enetc/Kconfig @@ -2,6 +2,7 @@ config FSL_ENETC tristate "ENETC PF driver" depends on PCI && PCI_MSI && (ARCH_LAYERSCAPE || COMPILE_TEST) + select PHYLIB help This driver supports NXP ENETC gigabit ethernet controller PCIe physical function (PF) devices, managing ENETC Ports at a privileged @@ -12,6 +13,7 @@ config FSL_ENETC config FSL_ENETC_VF tristate "ENETC VF driver" depends on PCI && PCI_MSI && (ARCH_LAYERSCAPE || COMPILE_TEST) + select PHYLIB help This driver supports NXP ENETC gigabit ethernet controller PCIe virtual function (VF) devices enabled by the ENETC PF driver. diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c index 2fd2586e42bf..bc594892507a 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ptp.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ptp.c @@ -82,7 +82,7 @@ static int enetc_ptp_probe(struct pci_dev *pdev, n = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX); if (n != 1) { err = -EPERM; - goto err_irq; + goto err_irq_vectors; } ptp_qoriq->irq = pci_irq_vector(pdev, 0); @@ -107,6 +107,8 @@ static int enetc_ptp_probe(struct pci_dev *pdev, err_no_clock: free_irq(ptp_qoriq->irq, ptp_qoriq); err_irq: + pci_free_irq_vectors(pdev); +err_irq_vectors: iounmap(base); err_ioremap: kfree(ptp_qoriq); @@ -125,6 +127,7 @@ static void enetc_ptp_remove(struct pci_dev *pdev) enetc_phc_index = -1; ptp_qoriq_free(ptp_qoriq); + pci_free_irq_vectors(pdev); kfree(ptp_qoriq); pci_release_mem_regions(pdev); diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index e80fedb27cee..210749bf1eac 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -2439,9 +2439,6 @@ MODULE_PARM_DESC(fsl_fm_rx_extra_headroom, "Extra headroom for Rx buffers"); * buffers when not using jumbo frames. * Must be large enough to accommodate the network MTU, but small enough * to avoid wasting skb memory. - * - * Could be overridden once, at boot-time, via the - * fm_set_max_frm() callback. */ static int fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE; module_param(fsl_fm_max_frm, int, 0); diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 92372dc43be8..ebc37e256922 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -31,9 +31,6 @@ struct gve_rx_desc_queue { struct gve_rx_desc *desc_ring; /* the descriptor ring */ dma_addr_t bus; /* the bus for the desc_ring */ - u32 cnt; /* free-running total number of completed packets */ - u32 fill_cnt; /* free-running total number of descriptors posted */ - u32 mask; /* masks the cnt to the size of the ring */ u8 seqno; /* the next expected seqno for this desc*/ }; @@ -60,8 +57,6 @@ struct gve_rx_data_queue { dma_addr_t data_bus; /* dma mapping of the slots */ struct gve_rx_slot_page_info *page_info; /* page info of the buffers */ struct gve_queue_page_list *qpl; /* qpl assigned to this queue */ - u32 mask; /* masks the cnt to the size of the ring */ - u32 cnt; /* free-running total number of completed packets */ }; struct gve_priv; @@ -73,6 +68,9 @@ struct gve_rx_ring { struct gve_rx_data_queue data; u64 rbytes; /* free-running bytes received */ u64 rpackets; /* free-running packets received */ + u32 cnt; /* free-running total number of completed packets */ + u32 fill_cnt; /* free-running total number of descs and buffs posted */ + u32 mask; /* masks the cnt and fill_cnt to the size of the ring */ u32 q_num; /* queue index */ u32 ntfy_id; /* notification block index */ struct gve_queue_resources *q_resources; /* head and tail pointer idx */ diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index 26540b856541..d8fa816f4473 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -138,8 +138,8 @@ gve_get_ethtool_stats(struct net_device *netdev, for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) { struct gve_rx_ring *rx = &priv->rx[ring]; - data[i++] = rx->desc.cnt; - data[i++] = rx->desc.fill_cnt; + data[i++] = rx->cnt; + data[i++] = rx->fill_cnt; } } else { i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS; diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 497298752381..aca95f64bde8 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -50,7 +50,7 @@ static void gve_get_stats(struct net_device *dev, struct rtnl_link_stats64 *s) u64_stats_fetch_begin(&priv->tx[ring].statss); s->tx_packets += priv->tx[ring].pkt_done; s->tx_bytes += priv->tx[ring].bytes_done; - } while (u64_stats_fetch_retry(&priv->rx[ring].statss, + } while (u64_stats_fetch_retry(&priv->tx[ring].statss, start)); } } diff --git a/drivers/net/ethernet/google/gve/gve_rx.c b/drivers/net/ethernet/google/gve/gve_rx.c index 1914b8350da7..59564ac99d2a 100644 --- a/drivers/net/ethernet/google/gve/gve_rx.c +++ b/drivers/net/ethernet/google/gve/gve_rx.c @@ -37,7 +37,7 @@ static void gve_rx_free_ring(struct gve_priv *priv, int idx) rx->data.qpl = NULL; kvfree(rx->data.page_info); - slots = rx->data.mask + 1; + slots = rx->mask + 1; bytes = sizeof(*rx->data.data_ring) * slots; dma_free_coherent(dev, bytes, rx->data.data_ring, rx->data.data_bus); @@ -64,7 +64,7 @@ static int gve_prefill_rx_pages(struct gve_rx_ring *rx) /* Allocate one page per Rx queue slot. Each page is split into two * packet buffers, when possible we "page flip" between the two. */ - slots = rx->data.mask + 1; + slots = rx->mask + 1; rx->data.page_info = kvzalloc(slots * sizeof(*rx->data.page_info), GFP_KERNEL); @@ -111,7 +111,7 @@ static int gve_rx_alloc_ring(struct gve_priv *priv, int idx) rx->q_num = idx; slots = priv->rx_pages_per_qpl; - rx->data.mask = slots - 1; + rx->mask = slots - 1; /* alloc rx data ring */ bytes = sizeof(*rx->data.data_ring) * slots; @@ -125,7 +125,7 @@ static int gve_rx_alloc_ring(struct gve_priv *priv, int idx) err = -ENOMEM; goto abort_with_slots; } - rx->desc.fill_cnt = filled_pages; + rx->fill_cnt = filled_pages; /* Ensure data ring slots (packet buffers) are visible. */ dma_wmb(); @@ -156,8 +156,8 @@ static int gve_rx_alloc_ring(struct gve_priv *priv, int idx) err = -ENOMEM; goto abort_with_q_resources; } - rx->desc.mask = slots - 1; - rx->desc.cnt = 0; + rx->mask = slots - 1; + rx->cnt = 0; rx->desc.seqno = 1; gve_rx_add_to_block(priv, idx); @@ -213,7 +213,7 @@ void gve_rx_write_doorbell(struct gve_priv *priv, struct gve_rx_ring *rx) { u32 db_idx = be32_to_cpu(rx->q_resources->db_index); - iowrite32be(rx->desc.fill_cnt, &priv->db_bar2[db_idx]); + iowrite32be(rx->fill_cnt, &priv->db_bar2[db_idx]); } static enum pkt_hash_types gve_rss_type(__be16 pkt_flags) @@ -273,7 +273,7 @@ static void gve_rx_flip_buff(struct gve_rx_slot_page_info *page_info, } static bool gve_rx(struct gve_rx_ring *rx, struct gve_rx_desc *rx_desc, - netdev_features_t feat) + netdev_features_t feat, u32 idx) { struct gve_rx_slot_page_info *page_info; struct gve_priv *priv = rx->gve; @@ -282,14 +282,12 @@ static bool gve_rx(struct gve_rx_ring *rx, struct gve_rx_desc *rx_desc, struct sk_buff *skb; int pagecount; u16 len; - u32 idx; /* drop this packet */ if (unlikely(rx_desc->flags_seq & GVE_RXF_ERR)) return true; len = be16_to_cpu(rx_desc->len) - GVE_RX_PAD; - idx = rx->data.cnt & rx->data.mask; page_info = &rx->data.page_info[idx]; /* gvnic can only receive into registered segments. If the buffer @@ -340,8 +338,6 @@ have_skb: if (!skb) return true; - rx->data.cnt++; - if (likely(feat & NETIF_F_RXCSUM)) { /* NIC passes up the partial sum */ if (rx_desc->csum) @@ -370,7 +366,7 @@ static bool gve_rx_work_pending(struct gve_rx_ring *rx) __be16 flags_seq; u32 next_idx; - next_idx = rx->desc.cnt & rx->desc.mask; + next_idx = rx->cnt & rx->mask; desc = rx->desc.desc_ring + next_idx; flags_seq = desc->flags_seq; @@ -385,8 +381,8 @@ bool gve_clean_rx_done(struct gve_rx_ring *rx, int budget, { struct gve_priv *priv = rx->gve; struct gve_rx_desc *desc; - u32 cnt = rx->desc.cnt; - u32 idx = cnt & rx->desc.mask; + u32 cnt = rx->cnt; + u32 idx = cnt & rx->mask; u32 work_done = 0; u64 bytes = 0; @@ -401,10 +397,10 @@ bool gve_clean_rx_done(struct gve_rx_ring *rx, int budget, rx->q_num, GVE_SEQNO(desc->flags_seq), rx->desc.seqno); bytes += be16_to_cpu(desc->len) - GVE_RX_PAD; - if (!gve_rx(rx, desc, feat)) + if (!gve_rx(rx, desc, feat, idx)) gve_schedule_reset(priv); cnt++; - idx = cnt & rx->desc.mask; + idx = cnt & rx->mask; desc = rx->desc.desc_ring + idx; rx->desc.seqno = gve_next_seqno(rx->desc.seqno); work_done++; @@ -417,8 +413,8 @@ bool gve_clean_rx_done(struct gve_rx_ring *rx, int budget, rx->rpackets += work_done; rx->rbytes += bytes; u64_stats_update_end(&rx->statss); - rx->desc.cnt = cnt; - rx->desc.fill_cnt += work_done; + rx->cnt = cnt; + rx->fill_cnt += work_done; /* restock desc ring slots */ dma_wmb(); /* Ensure descs are visible before ringing doorbell */ diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index d60452845539..c84167447abe 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -220,6 +220,7 @@ struct hip04_priv { unsigned int reg_inten; struct napi_struct napi; + struct device *dev; struct net_device *ndev; struct tx_desc *tx_desc; @@ -248,7 +249,7 @@ struct hip04_priv { static inline unsigned int tx_count(unsigned int head, unsigned int tail) { - return (head - tail) % (TX_DESC_NUM - 1); + return (head - tail) % TX_DESC_NUM; } static void hip04_config_port(struct net_device *ndev, u32 speed, u32 duplex) @@ -465,7 +466,7 @@ static int hip04_tx_reclaim(struct net_device *ndev, bool force) } if (priv->tx_phys[tx_tail]) { - dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail], + dma_unmap_single(priv->dev, priv->tx_phys[tx_tail], priv->tx_skb[tx_tail]->len, DMA_TO_DEVICE); priv->tx_phys[tx_tail] = 0; @@ -516,8 +517,8 @@ hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) return NETDEV_TX_BUSY; } - phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (dma_mapping_error(&ndev->dev, phys)) { + phys = dma_map_single(priv->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(priv->dev, phys)) { dev_kfree_skb(skb); return NETDEV_TX_OK; } @@ -585,6 +586,9 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) u16 len; u32 err; + /* clean up tx descriptors */ + tx_remaining = hip04_tx_reclaim(ndev, false); + while (cnt && !last) { buf = priv->rx_buf[priv->rx_head]; skb = build_skb(buf, priv->rx_buf_size); @@ -593,7 +597,7 @@ static int hip04_rx_poll(struct napi_struct *napi, int budget) goto refill; } - dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head], + dma_unmap_single(priv->dev, priv->rx_phys[priv->rx_head], RX_BUF_SIZE, DMA_FROM_DEVICE); priv->rx_phys[priv->rx_head] = 0; @@ -622,9 +626,9 @@ refill: buf = netdev_alloc_frag(priv->rx_buf_size); if (!buf) goto done; - phys = dma_map_single(&ndev->dev, buf, + phys = dma_map_single(priv->dev, buf, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(&ndev->dev, phys)) + if (dma_mapping_error(priv->dev, phys)) goto done; priv->rx_buf[priv->rx_head] = buf; priv->rx_phys[priv->rx_head] = phys; @@ -645,8 +649,7 @@ refill: } napi_complete_done(napi, rx); done: - /* clean up tx descriptors and start a new timer if necessary */ - tx_remaining = hip04_tx_reclaim(ndev, false); + /* start a new timer if necessary */ if (rx < budget && tx_remaining) hip04_start_tx_timer(priv); @@ -728,9 +731,9 @@ static int hip04_mac_open(struct net_device *ndev) for (i = 0; i < RX_DESC_NUM; i++) { dma_addr_t phys; - phys = dma_map_single(&ndev->dev, priv->rx_buf[i], + phys = dma_map_single(priv->dev, priv->rx_buf[i], RX_BUF_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(&ndev->dev, phys)) + if (dma_mapping_error(priv->dev, phys)) return -EIO; priv->rx_phys[i] = phys; @@ -764,7 +767,7 @@ static int hip04_mac_stop(struct net_device *ndev) for (i = 0; i < RX_DESC_NUM; i++) { if (priv->rx_phys[i]) { - dma_unmap_single(&ndev->dev, priv->rx_phys[i], + dma_unmap_single(priv->dev, priv->rx_phys[i], RX_BUF_SIZE, DMA_FROM_DEVICE); priv->rx_phys[i] = 0; } @@ -907,6 +910,7 @@ static int hip04_mac_probe(struct platform_device *pdev) return -ENOMEM; priv = netdev_priv(ndev); + priv->dev = d; priv->ndev = ndev; platform_set_drvdata(pdev, ndev); SET_NETDEV_DEV(ndev, &pdev->dev); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c index 0a7243825e7b..85a3b062371d 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c @@ -98,7 +98,7 @@ static const struct hclge_hw_error hclge_igu_egu_tnl_int[] = { .reset_level = HNAE3_GLOBAL_RESET }, { .int_msk = BIT(1), .msg = "rx_stp_fifo_overflow", .reset_level = HNAE3_GLOBAL_RESET }, - { .int_msk = BIT(2), .msg = "rx_stp_fifo_undeflow", + { .int_msk = BIT(2), .msg = "rx_stp_fifo_underflow", .reset_level = HNAE3_GLOBAL_RESET }, { .int_msk = BIT(3), .msg = "tx_buf_overflow", .reset_level = HNAE3_GLOBAL_RESET }, diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 4138a8480347..cca71ba7a74a 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3251,7 +3251,7 @@ static int ehea_mem_notifier(struct notifier_block *nb, switch (action) { case MEM_CANCEL_OFFLINE: pr_info("memory offlining canceled"); - /* Fall through: re-add canceled memory block */ + /* Fall through - re-add canceled memory block */ case MEM_ONLINE: pr_info("memory is going online"); diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index d654c234aaf7..c5be4ebd8437 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1605,7 +1605,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) struct net_device *netdev; struct ibmveth_adapter *adapter; unsigned char *mac_addr_p; - unsigned int *mcastFilterSize_p; + __be32 *mcastFilterSize_p; long ret; unsigned long ret_attr; @@ -1627,8 +1627,9 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) return -EINVAL; } - mcastFilterSize_p = (unsigned int *)vio_get_attribute(dev, - VETH_MCAST_FILTER_SIZE, NULL); + mcastFilterSize_p = (__be32 *)vio_get_attribute(dev, + VETH_MCAST_FILTER_SIZE, + NULL); if (!mcastFilterSize_p) { dev_err(&dev->dev, "Can't find VETH_MCAST_FILTER_SIZE " "attribute\n"); @@ -1645,7 +1646,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) adapter->vdev = dev; adapter->netdev = netdev; - adapter->mcastFilterSize = *mcastFilterSize_p; + adapter->mcastFilterSize = be32_to_cpu(*mcastFilterSize_p); adapter->pool_config = 0; netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16); diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 3da680073265..5cb55ea671e3 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1568,6 +1568,8 @@ static netdev_tx_t ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) lpar_rc = send_subcrq_indirect(adapter, handle_array[queue_num], (u64)tx_buff->indir_dma, (u64)num_entries); + dma_unmap_single(dev, tx_buff->indir_dma, + sizeof(tx_buff->indir_arr), DMA_TO_DEVICE); } else { tx_buff->num_entries = num_entries; lpar_rc = send_subcrq(adapter, handle_array[queue_num], @@ -1981,6 +1983,13 @@ static void __ibmvnic_reset(struct work_struct *work) rwi = get_next_rwi(adapter); while (rwi) { + if (adapter->state == VNIC_REMOVING || + adapter->state == VNIC_REMOVED) { + kfree(rwi); + rc = EBUSY; + break; + } + if (adapter->force_reset_recovery) { adapter->force_reset_recovery = false; rc = do_hard_reset(adapter, rwi, reset_state); @@ -2788,7 +2797,6 @@ static int ibmvnic_complete_tx(struct ibmvnic_adapter *adapter, union sub_crq *next; int index; int i, j; - u8 *first; restart_loop: while (pending_scrq(adapter, scrq)) { @@ -2818,14 +2826,6 @@ restart_loop: txbuff->data_dma[j] = 0; } - /* if sub_crq was sent indirectly */ - first = &txbuff->indir_arr[0].generic.first; - if (*first == IBMVNIC_CRQ_CMD) { - dma_unmap_single(dev, txbuff->indir_dma, - sizeof(txbuff->indir_arr), - DMA_TO_DEVICE); - *first = 0; - } if (txbuff->last_frag) { dev_kfree_skb_any(txbuff->skb); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index cbaf712d6529..51c696b6bf64 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -36,6 +36,7 @@ #include <net/vxlan.h> #include <net/mpls.h> #include <net/xdp_sock.h> +#include <net/xfrm.h> #include "ixgbe.h" #include "ixgbe_common.h" @@ -2621,7 +2622,7 @@ adjust_by_size: /* 16K ints/sec to 9.2K ints/sec */ avg_wire_size *= 15; avg_wire_size += 11452; - } else if (avg_wire_size <= 1980) { + } else if (avg_wire_size < 1968) { /* 9.2K ints/sec to 8K ints/sec */ avg_wire_size *= 5; avg_wire_size += 22420; @@ -2654,6 +2655,8 @@ adjust_by_size: case IXGBE_LINK_SPEED_2_5GB_FULL: case IXGBE_LINK_SPEED_1GB_FULL: case IXGBE_LINK_SPEED_10_FULL: + if (avg_wire_size > 8064) + avg_wire_size = 8064; itr += DIV_ROUND_UP(avg_wire_size, IXGBE_ITR_ADAPTIVE_MIN_INC * 64) * IXGBE_ITR_ADAPTIVE_MIN_INC; @@ -7897,11 +7900,8 @@ static void ixgbe_service_task(struct work_struct *work) return; } if (ixgbe_check_fw_error(adapter)) { - if (!test_bit(__IXGBE_DOWN, &adapter->state)) { - rtnl_lock(); + if (!test_bit(__IXGBE_DOWN, &adapter->state)) unregister_netdev(adapter->netdev); - rtnl_unlock(); - } ixgbe_service_event_complete(adapter); return; } @@ -8698,7 +8698,7 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, #endif /* IXGBE_FCOE */ #ifdef CONFIG_IXGBE_IPSEC - if (secpath_exists(skb) && + if (xfrm_offload(skb) && !ixgbe_ipsec_tx(tx_ring, first, &ipsec_tx)) goto out_drop; #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c index 6b609553329f..a3b6d8c89127 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_xsk.c @@ -633,19 +633,17 @@ static void ixgbe_clean_xdp_tx_buffer(struct ixgbe_ring *tx_ring, bool ixgbe_clean_xdp_tx_irq(struct ixgbe_q_vector *q_vector, struct ixgbe_ring *tx_ring, int napi_budget) { + u16 ntc = tx_ring->next_to_clean, ntu = tx_ring->next_to_use; unsigned int total_packets = 0, total_bytes = 0; - u32 i = tx_ring->next_to_clean, xsk_frames = 0; - unsigned int budget = q_vector->tx.work_limit; struct xdp_umem *umem = tx_ring->xsk_umem; union ixgbe_adv_tx_desc *tx_desc; struct ixgbe_tx_buffer *tx_bi; - bool xmit_done; + u32 xsk_frames = 0; - tx_bi = &tx_ring->tx_buffer_info[i]; - tx_desc = IXGBE_TX_DESC(tx_ring, i); - i -= tx_ring->count; + tx_bi = &tx_ring->tx_buffer_info[ntc]; + tx_desc = IXGBE_TX_DESC(tx_ring, ntc); - do { + while (ntc != ntu) { if (!(tx_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD))) break; @@ -661,22 +659,18 @@ bool ixgbe_clean_xdp_tx_irq(struct ixgbe_q_vector *q_vector, tx_bi++; tx_desc++; - i++; - if (unlikely(!i)) { - i -= tx_ring->count; + ntc++; + if (unlikely(ntc == tx_ring->count)) { + ntc = 0; tx_bi = tx_ring->tx_buffer_info; tx_desc = IXGBE_TX_DESC(tx_ring, 0); } /* issue prefetch for next Tx descriptor */ prefetch(tx_desc); + } - /* update budget accounting */ - budget--; - } while (likely(budget)); - - i += tx_ring->count; - tx_ring->next_to_clean = i; + tx_ring->next_to_clean = ntc; u64_stats_update_begin(&tx_ring->syncp); tx_ring->stats.bytes += total_bytes; @@ -688,8 +682,7 @@ bool ixgbe_clean_xdp_tx_irq(struct ixgbe_q_vector *q_vector, if (xsk_frames) xsk_umem_complete_tx(umem, xsk_frames); - xmit_done = ixgbe_xmit_zc(tx_ring, q_vector->tx.work_limit); - return budget > 0 && xmit_done; + return ixgbe_xmit_zc(tx_ring, q_vector->tx.work_limit); } int ixgbe_xsk_async_xmit(struct net_device *dev, u32 qid) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index d2b41f9f87f8..72872d6ca80c 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -30,6 +30,7 @@ #include <linux/bpf.h> #include <linux/bpf_trace.h> #include <linux/atomic.h> +#include <net/xfrm.h> #include "ixgbevf.h" @@ -4161,7 +4162,7 @@ static int ixgbevf_xmit_frame_ring(struct sk_buff *skb, first->protocol = vlan_get_protocol(skb); #ifdef CONFIG_IXGBEVF_IPSEC - if (secpath_exists(skb) && !ixgbevf_ipsec_tx(tx_ring, first, &ipsec_tx)) + if (xfrm_offload(skb) && !ixgbevf_ipsec_tx(tx_ring, first, &ipsec_tx)) goto out_drop; #endif tso = ixgbevf_tso(tx_ring, first, &hdr_len, &ipsec_tx); diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index f660cc2b8258..0b9e851f3da4 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -319,20 +319,33 @@ static int orion_mdio_probe(struct platform_device *pdev) init_waitqueue_head(&dev->smi_busy_wait); - for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { - dev->clk[i] = of_clk_get(pdev->dev.of_node, i); - if (PTR_ERR(dev->clk[i]) == -EPROBE_DEFER) { + if (pdev->dev.of_node) { + for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { + dev->clk[i] = of_clk_get(pdev->dev.of_node, i); + if (PTR_ERR(dev->clk[i]) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto out_clk; + } + if (IS_ERR(dev->clk[i])) + break; + clk_prepare_enable(dev->clk[i]); + } + + if (!IS_ERR(of_clk_get(pdev->dev.of_node, + ARRAY_SIZE(dev->clk)))) + dev_warn(&pdev->dev, + "unsupported number of clocks, limiting to the first " + __stringify(ARRAY_SIZE(dev->clk)) "\n"); + } else { + dev->clk[0] = clk_get(&pdev->dev, NULL); + if (PTR_ERR(dev->clk[0]) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto out_clk; } - if (IS_ERR(dev->clk[i])) - break; - clk_prepare_enable(dev->clk[i]); + if (!IS_ERR(dev->clk[0])) + clk_prepare_enable(dev->clk[0]); } - if (!IS_ERR(of_clk_get(pdev->dev.of_node, ARRAY_SIZE(dev->clk)))) - dev_warn(&pdev->dev, "unsupported number of clocks, limiting to the first " - __stringify(ARRAY_SIZE(dev->clk)) "\n"); dev->err_interrupt = platform_get_irq(pdev, 0); if (dev->err_interrupt > 0 && diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index c51f1d5b550b..ccdd47f3b8fb 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -811,6 +811,26 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) return 0; } +static void mvpp2_set_hw_csum(struct mvpp2_port *port, + enum mvpp2_bm_pool_log_num new_long_pool) +{ + const netdev_features_t csums = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + + /* Update L4 checksum when jumbo enable/disable on port. + * Only port 0 supports hardware checksum offload due to + * the Tx FIFO size limitation. + * Also, don't set NETIF_F_HW_CSUM because L3_offset in TX descriptor + * has 7 bits, so the maximum L3 offset is 128. + */ + if (new_long_pool == MVPP2_BM_JUMBO && port->id != 0) { + port->dev->features &= ~csums; + port->dev->hw_features &= ~csums; + } else { + port->dev->features |= csums; + port->dev->hw_features |= csums; + } +} + static int mvpp2_bm_update_mtu(struct net_device *dev, int mtu) { struct mvpp2_port *port = netdev_priv(dev); @@ -843,15 +863,7 @@ static int mvpp2_bm_update_mtu(struct net_device *dev, int mtu) /* Add port to new short & long pool */ mvpp2_swf_bm_pool_init(port); - /* Update L4 checksum when jumbo enable/disable on port */ - if (new_long_pool == MVPP2_BM_JUMBO && port->id != 0) { - dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); - dev->hw_features &= ~(NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM); - } else { - dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - } + mvpp2_set_hw_csum(port, new_long_pool); } dev->mtu = mtu; @@ -3700,6 +3712,7 @@ static int mvpp2_set_mac_address(struct net_device *dev, void *p) static int mvpp2_change_mtu(struct net_device *dev, int mtu) { struct mvpp2_port *port = netdev_priv(dev); + bool running = netif_running(dev); int err; if (!IS_ALIGNED(MVPP2_RX_PKT_SIZE(mtu), 8)) { @@ -3708,40 +3721,24 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) mtu = ALIGN(MVPP2_RX_PKT_SIZE(mtu), 8); } - if (!netif_running(dev)) { - err = mvpp2_bm_update_mtu(dev, mtu); - if (!err) { - port->pkt_size = MVPP2_RX_PKT_SIZE(mtu); - return 0; - } - - /* Reconfigure BM to the original MTU */ - err = mvpp2_bm_update_mtu(dev, dev->mtu); - if (err) - goto log_error; - } - - mvpp2_stop_dev(port); + if (running) + mvpp2_stop_dev(port); err = mvpp2_bm_update_mtu(dev, mtu); - if (!err) { + if (err) { + netdev_err(dev, "failed to change MTU\n"); + /* Reconfigure BM to the original MTU */ + mvpp2_bm_update_mtu(dev, dev->mtu); + } else { port->pkt_size = MVPP2_RX_PKT_SIZE(mtu); - goto out_start; } - /* Reconfigure BM to the original MTU */ - err = mvpp2_bm_update_mtu(dev, dev->mtu); - if (err) - goto log_error; - -out_start: - mvpp2_start_dev(port); - mvpp2_egress_enable(port); - mvpp2_ingress_enable(port); + if (running) { + mvpp2_start_dev(port); + mvpp2_egress_enable(port); + mvpp2_ingress_enable(port); + } - return 0; -log_error: - netdev_err(dev, "failed to change MTU\n"); return err; } @@ -4739,9 +4736,9 @@ static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode, else ctrl0 &= ~MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN; - ctrl4 &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC; - ctrl4 |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC | - MVPP22_XLG_CTRL4_EN_IDLE_CHECK; + ctrl4 &= ~(MVPP22_XLG_CTRL4_MACMODSELECT_GMAC | + MVPP22_XLG_CTRL4_EN_IDLE_CHECK); + ctrl4 |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC; if (old_ctrl0 != ctrl0) writel(ctrl0, port->base + MVPP22_XLG_CTRL0_REG); @@ -5208,10 +5205,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, dev->features |= NETIF_F_NTUPLE; } - if (port->pool_long->id == MVPP2_BM_JUMBO && port->id != 0) { - dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); - dev->hw_features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); - } + mvpp2_set_hw_csum(port, port->pool_long->id); dev->vlan_features |= features; dev->gso_max_segs = MVPP2_MAX_TSO_SEGS; @@ -5759,9 +5753,6 @@ static int mvpp2_remove(struct platform_device *pdev) mvpp2_dbgfs_cleanup(priv); - flush_workqueue(priv->stats_queue); - destroy_workqueue(priv->stats_queue); - fwnode_for_each_available_child_node(fwnode, port_fwnode) { if (priv->port_list[i]) { mutex_destroy(&priv->port_list[i]->gather_stats_lock); @@ -5770,6 +5761,8 @@ static int mvpp2_remove(struct platform_device *pdev) i++; } + destroy_workqueue(priv->stats_queue); + for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) { struct mvpp2_bm_pool *bm_pool = &priv->bm_pools[i]; diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index f518312ffe69..e0363870f3a5 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4924,6 +4924,20 @@ static const struct dmi_system_id msi_blacklist[] = { DMI_MATCH(DMI_PRODUCT_NAME, "P5W DH Deluxe"), }, }, + { + .ident = "ASUS P6T", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "P6T"), + }, + }, + { + .ident = "ASUS P6X", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_BOARD_NAME, "P6X"), + }, + }, {} }; diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig index 263cd0909fe0..1f7fff81f24d 100644 --- a/drivers/net/ethernet/mediatek/Kconfig +++ b/drivers/net/ethernet/mediatek/Kconfig @@ -9,7 +9,6 @@ if NET_VENDOR_MEDIATEK config NET_MEDIATEK_SOC tristate "MediaTek SoC Gigabit Ethernet support" - depends on NET_VENDOR_MEDIATEK select PHYLIB ---help--- This driver supports the gigabit ethernet MACs in the diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 6c01314e87b0..db3552f2d087 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1187,7 +1187,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) err = mlx4_qp_alloc(mdev->dev, priv->base_qpn, rss_map->indir_qp); if (err) { en_err(priv, "Failed to allocate RSS indirection QP\n"); - goto rss_err; + goto qp_alloc_err; } rss_map->indir_qp->event = mlx4_en_sqp_event; @@ -1241,6 +1241,7 @@ indir_err: MLX4_QP_STATE_RST, NULL, 0, 0, rss_map->indir_qp); mlx4_qp_remove(mdev->dev, rss_map->indir_qp); mlx4_qp_free(mdev->dev, rss_map->indir_qp); +qp_alloc_err: kfree(rss_map->indir_qp); rss_map->indir_qp = NULL; rss_err: diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 1f6e16d5ea6b..309470ec0219 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -2240,7 +2240,7 @@ static int mlx4_validate_optimized_steering(struct mlx4_dev *dev) for (i = 1; i <= dev->caps.num_ports; i++) { if (mlx4_dev_port(dev, i, &port_cap)) { mlx4_err(dev, - "QUERY_DEV_CAP command failed, can't veify DMFS high rate steering.\n"); + "QUERY_DEV_CAP command failed, can't verify DMFS high rate steering.\n"); } else if ((dev->caps.dmfs_high_steer_mode != MLX4_STEERING_DMFS_A0_DEFAULT) && (port_cap.dmfs_optimized_state == diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c index 5bb6a26ea267..50862275544e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c @@ -213,7 +213,7 @@ void mlx5_unregister_device(struct mlx5_core_dev *dev) struct mlx5_interface *intf; mutex_lock(&mlx5_intf_mutex); - list_for_each_entry(intf, &intf_list, list) + list_for_each_entry_reverse(intf, &intf_list, list) mlx5_remove_device(intf, priv); list_del(&priv->dev_list); mutex_unlock(&mlx5_intf_mutex); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 79d93d6c7d7a..65bec19a438f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -159,7 +159,7 @@ do { \ enum mlx5e_rq_group { MLX5E_RQ_GROUP_REGULAR, MLX5E_RQ_GROUP_XSK, - MLX5E_NUM_RQ_GROUPS /* Keep last. */ +#define MLX5E_NUM_RQ_GROUPS(g) (1 + MLX5E_RQ_GROUP_##g) }; static inline u16 mlx5_min_rx_wqes(int wq_type, u32 wq_size) @@ -182,18 +182,15 @@ static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) min_t(int, mlx5_comp_vectors_count(mdev), MLX5E_MAX_NUM_CHANNELS); } -/* Use this function to get max num channels after netdev was created */ -static inline int mlx5e_get_netdev_max_channels(struct net_device *netdev) -{ - return min_t(unsigned int, - netdev->num_rx_queues / MLX5E_NUM_RQ_GROUPS, - netdev->num_tx_queues); -} - struct mlx5e_tx_wqe { struct mlx5_wqe_ctrl_seg ctrl; - struct mlx5_wqe_eth_seg eth; - struct mlx5_wqe_data_seg data[0]; + union { + struct { + struct mlx5_wqe_eth_seg eth; + struct mlx5_wqe_data_seg data[0]; + }; + u8 tls_progress_params_ctx[0]; + }; }; struct mlx5e_rx_wqe_ll { @@ -830,6 +827,7 @@ struct mlx5e_priv { struct net_device *netdev; struct mlx5e_stats stats; struct mlx5e_channel_stats channel_stats[MLX5E_MAX_NUM_CHANNELS]; + u16 max_nch; u8 max_opened_tc; struct hwtstamp_config tstamp; u16 q_counter; @@ -871,6 +869,7 @@ struct mlx5e_profile { mlx5e_fp_handle_rx_cqe handle_rx_cqe_mpwqe; } rx_handlers; int max_tc; + u8 rq_groups; }; void mlx5e_build_ptys2ethtool_map(void); @@ -1106,6 +1105,8 @@ u32 mlx5e_ethtool_get_rxfh_key_size(struct mlx5e_priv *priv); u32 mlx5e_ethtool_get_rxfh_indir_size(struct mlx5e_priv *priv); int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv, struct ethtool_ts_info *info); +int mlx5e_ethtool_flash_device(struct mlx5e_priv *priv, + struct ethtool_flash *flash); void mlx5e_ethtool_get_pauseparam(struct mlx5e_priv *priv, struct ethtool_pauseparam *pauseparam); int mlx5e_ethtool_set_pauseparam(struct mlx5e_priv *priv, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h index bd882b5ee9a7..3a615d663d84 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.h @@ -66,9 +66,10 @@ static inline void mlx5e_qid_get_ch_and_group(struct mlx5e_params *params, *group = qid / nch; } -static inline bool mlx5e_qid_validate(struct mlx5e_params *params, u64 qid) +static inline bool mlx5e_qid_validate(const struct mlx5e_profile *profile, + struct mlx5e_params *params, u64 qid) { - return qid < params->num_channels * MLX5E_NUM_RQ_GROUPS; + return qid < params->num_channels * profile->rq_groups; } /* Parameter calculations */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c index d5e5afbdca6d..f777994f3005 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c @@ -78,9 +78,10 @@ static const u32 mlx5e_ext_link_speed[MLX5E_EXT_LINK_MODES_NUMBER] = { }; static void mlx5e_port_get_speed_arr(struct mlx5_core_dev *mdev, - const u32 **arr, u32 *size) + const u32 **arr, u32 *size, + bool force_legacy) { - bool ext = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet); + bool ext = force_legacy ? false : MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet); *size = ext ? ARRAY_SIZE(mlx5e_ext_link_speed) : ARRAY_SIZE(mlx5e_link_speed); @@ -152,7 +153,8 @@ int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable, sizeof(out), MLX5_REG_PTYS, 0, 1); } -u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper) +u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper, + bool force_legacy) { unsigned long temp = eth_proto_oper; const u32 *table; @@ -160,7 +162,7 @@ u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper) u32 max_size; int i; - mlx5e_port_get_speed_arr(mdev, &table, &max_size); + mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy); i = find_first_bit(&temp, max_size); if (i < max_size) speed = table[i]; @@ -170,6 +172,7 @@ u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper) int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) { struct mlx5e_port_eth_proto eproto; + bool force_legacy = false; bool ext; int err; @@ -177,8 +180,13 @@ int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto); if (err) goto out; - - *speed = mlx5e_port_ptys2speed(mdev, eproto.oper); + if (ext && !eproto.admin) { + force_legacy = true; + err = mlx5_port_query_eth_proto(mdev, 1, false, &eproto); + if (err) + goto out; + } + *speed = mlx5e_port_ptys2speed(mdev, eproto.oper, force_legacy); if (!(*speed)) err = -EINVAL; @@ -201,7 +209,7 @@ int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) if (err) return err; - mlx5e_port_get_speed_arr(mdev, &table, &max_size); + mlx5e_port_get_speed_arr(mdev, &table, &max_size, false); for (i = 0; i < max_size; ++i) if (eproto.cap & MLX5E_PROT_MASK(i)) max_speed = max(max_speed, table[i]); @@ -210,14 +218,15 @@ int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) return 0; } -u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed) +u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed, + bool force_legacy) { u32 link_modes = 0; const u32 *table; u32 max_size; int i; - mlx5e_port_get_speed_arr(mdev, &table, &max_size); + mlx5e_port_get_speed_arr(mdev, &table, &max_size, force_legacy); for (i = 0; i < max_size; ++i) { if (table[i] == speed) link_modes |= MLX5E_PROT_MASK(i); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.h b/drivers/net/ethernet/mellanox/mlx5/core/en/port.h index 70f536ec51c4..4a7f4497692b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.h @@ -48,10 +48,12 @@ void mlx5_port_query_eth_autoneg(struct mlx5_core_dev *dev, u8 *an_status, u8 *an_disable_cap, u8 *an_disable_admin); int mlx5_port_set_eth_ptys(struct mlx5_core_dev *dev, bool an_disable, u32 proto_admin, bool ext); -u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper); +u32 mlx5e_port_ptys2speed(struct mlx5_core_dev *mdev, u32 eth_proto_oper, + bool force_legacy); int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); int mlx5e_port_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); -u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed); +u32 mlx5e_port_speed2linkmodes(struct mlx5_core_dev *mdev, u32 speed, + bool force_legacy); int mlx5e_port_query_pbmc(struct mlx5_core_dev *mdev, void *out); int mlx5e_port_set_pbmc(struct mlx5_core_dev *mdev, void *in); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c index f3d98748b211..c7f86453c638 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c @@ -76,26 +76,21 @@ static int mlx5e_tx_reporter_err_cqe_recover(struct mlx5e_txqsq *sq) u8 state; int err; - if (!test_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state)) - return 0; - err = mlx5_core_query_sq_state(mdev, sq->sqn, &state); if (err) { netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n", sq->sqn, err); - return err; + goto out; } - if (state != MLX5_SQC_STATE_ERR) { - netdev_err(dev, "SQ 0x%x not in ERROR state\n", sq->sqn); - return -EINVAL; - } + if (state != MLX5_SQC_STATE_ERR) + goto out; mlx5e_tx_disable_queue(sq->txq); err = mlx5e_wait_for_sq_flush(sq); if (err) - return err; + goto out; /* At this point, no new packets will arrive from the stack as TXQ is * marked with QUEUE_STATE_DRV_XOFF. In addition, NAPI cleared all @@ -104,13 +99,17 @@ static int mlx5e_tx_reporter_err_cqe_recover(struct mlx5e_txqsq *sq) err = mlx5e_sq_to_ready(sq, state); if (err) - return err; + goto out; mlx5e_reset_txqsq_cc_pc(sq); sq->stats->recover++; + clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state); mlx5e_activate_txqsq(sq); return 0; +out: + clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state); + return err; } static int mlx5_tx_health_report(struct devlink_health_reporter *tx_reporter, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c index aaffa6f68dc0..7f78c004d12f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c @@ -143,7 +143,10 @@ void mlx5e_activate_xsk(struct mlx5e_channel *c) { set_bit(MLX5E_RQ_STATE_ENABLED, &c->xskrq.state); /* TX queue is created active. */ + + spin_lock(&c->xskicosq_lock); mlx5e_trigger_irq(&c->xskicosq); + spin_unlock(&c->xskicosq_lock); } void mlx5e_deactivate_xsk(struct mlx5e_channel *c) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h index 407da83474ef..b7298f9ee3d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h @@ -11,12 +11,14 @@ #include "accel/tls.h" #define MLX5E_KTLS_STATIC_UMR_WQE_SZ \ - (sizeof(struct mlx5e_umr_wqe) + MLX5_ST_SZ_BYTES(tls_static_params)) + (offsetof(struct mlx5e_umr_wqe, tls_static_params_ctx) + \ + MLX5_ST_SZ_BYTES(tls_static_params)) #define MLX5E_KTLS_STATIC_WQEBBS \ (DIV_ROUND_UP(MLX5E_KTLS_STATIC_UMR_WQE_SZ, MLX5_SEND_WQE_BB)) #define MLX5E_KTLS_PROGRESS_WQE_SZ \ - (sizeof(struct mlx5e_tx_wqe) + MLX5_ST_SZ_BYTES(tls_progress_params)) + (offsetof(struct mlx5e_tx_wqe, tls_progress_params_ctx) + \ + MLX5_ST_SZ_BYTES(tls_progress_params)) #define MLX5E_KTLS_PROGRESS_WQEBBS \ (DIV_ROUND_UP(MLX5E_KTLS_PROGRESS_WQE_SZ, MLX5_SEND_WQE_BB)) #define MLX5E_KTLS_MAX_DUMP_WQEBBS 2 diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c index ea032f54197e..7833ddef0427 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c @@ -69,7 +69,7 @@ build_static_params(struct mlx5e_umr_wqe *wqe, u16 pc, u32 sqn, cseg->qpn_ds = cpu_to_be32((sqn << MLX5_WQE_CTRL_QPN_SHIFT) | STATIC_PARAMS_DS_CNT); cseg->fm_ce_se = fence ? MLX5_FENCE_MODE_INITIATOR_SMALL : 0; - cseg->imm = cpu_to_be32(priv_tx->tisn); + cseg->tisn = cpu_to_be32(priv_tx->tisn << 8); ucseg->flags = MLX5_UMR_INLINE; ucseg->bsf_octowords = cpu_to_be16(MLX5_ST_SZ_BYTES(tls_static_params) / 16); @@ -80,7 +80,7 @@ build_static_params(struct mlx5e_umr_wqe *wqe, u16 pc, u32 sqn, static void fill_progress_params_ctx(void *ctx, struct mlx5e_ktls_offload_context_tx *priv_tx) { - MLX5_SET(tls_progress_params, ctx, pd, priv_tx->tisn); + MLX5_SET(tls_progress_params, ctx, tisn, priv_tx->tisn); MLX5_SET(tls_progress_params, ctx, record_tracker_state, MLX5E_TLS_PROGRESS_PARAMS_RECORD_TRACKER_STATE_START); MLX5_SET(tls_progress_params, ctx, auth_state, @@ -104,18 +104,20 @@ build_progress_params(struct mlx5e_tx_wqe *wqe, u16 pc, u32 sqn, PROGRESS_PARAMS_DS_CNT); cseg->fm_ce_se = fence ? MLX5_FENCE_MODE_INITIATOR_SMALL : 0; - fill_progress_params_ctx(wqe->data, priv_tx); + fill_progress_params_ctx(wqe->tls_progress_params_ctx, priv_tx); } static void tx_fill_wi(struct mlx5e_txqsq *sq, u16 pi, u8 num_wqebbs, - skb_frag_t *resync_dump_frag) + skb_frag_t *resync_dump_frag, + u32 num_bytes) { struct mlx5e_tx_wqe_info *wi = &sq->db.wqe_info[pi]; wi->skb = NULL; wi->num_wqebbs = num_wqebbs; wi->resync_dump_frag = resync_dump_frag; + wi->num_bytes = num_bytes; } void mlx5e_ktls_tx_offload_set_pending(struct mlx5e_ktls_offload_context_tx *priv_tx) @@ -143,7 +145,7 @@ post_static_params(struct mlx5e_txqsq *sq, umr_wqe = mlx5e_sq_fetch_wqe(sq, MLX5E_KTLS_STATIC_UMR_WQE_SZ, &pi); build_static_params(umr_wqe, sq->pc, sq->sqn, priv_tx, fence); - tx_fill_wi(sq, pi, MLX5E_KTLS_STATIC_WQEBBS, NULL); + tx_fill_wi(sq, pi, MLX5E_KTLS_STATIC_WQEBBS, NULL, 0); sq->pc += MLX5E_KTLS_STATIC_WQEBBS; } @@ -157,7 +159,7 @@ post_progress_params(struct mlx5e_txqsq *sq, wqe = mlx5e_sq_fetch_wqe(sq, MLX5E_KTLS_PROGRESS_WQE_SZ, &pi); build_progress_params(wqe, sq->pc, sq->sqn, priv_tx, fence); - tx_fill_wi(sq, pi, MLX5E_KTLS_PROGRESS_WQEBBS, NULL); + tx_fill_wi(sq, pi, MLX5E_KTLS_PROGRESS_WQEBBS, NULL, 0); sq->pc += MLX5E_KTLS_PROGRESS_WQEBBS; } @@ -248,43 +250,37 @@ tx_post_resync_params(struct mlx5e_txqsq *sq, mlx5e_ktls_tx_post_param_wqes(sq, priv_tx, skip_static_post, true); } +struct mlx5e_dump_wqe { + struct mlx5_wqe_ctrl_seg ctrl; + struct mlx5_wqe_data_seg data; +}; + static int tx_post_resync_dump(struct mlx5e_txqsq *sq, struct sk_buff *skb, skb_frag_t *frag, u32 tisn, bool first) { struct mlx5_wqe_ctrl_seg *cseg; - struct mlx5_wqe_eth_seg *eseg; struct mlx5_wqe_data_seg *dseg; - struct mlx5e_tx_wqe *wqe; + struct mlx5e_dump_wqe *wqe; dma_addr_t dma_addr = 0; - u16 ds_cnt, ds_cnt_inl; u8 num_wqebbs; - u16 pi, ihs; + u16 ds_cnt; int fsz; - - ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS; - ihs = eth_get_headlen(skb->dev, skb->data, skb_headlen(skb)); - ds_cnt_inl = DIV_ROUND_UP(ihs - INL_HDR_START_SZ, MLX5_SEND_WQE_DS); - ds_cnt += ds_cnt_inl; - ds_cnt += 1; /* one frag */ + u16 pi; wqe = mlx5e_sq_fetch_wqe(sq, sizeof(*wqe), &pi); + ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS; num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); cseg = &wqe->ctrl; - eseg = &wqe->eth; - dseg = wqe->data; + dseg = &wqe->data; cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_DUMP); cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); - cseg->imm = cpu_to_be32(tisn); + cseg->tisn = cpu_to_be32(tisn << 8); cseg->fm_ce_se = first ? MLX5_FENCE_MODE_INITIATOR_SMALL : 0; - eseg->inline_hdr.sz = cpu_to_be16(ihs); - memcpy(eseg->inline_hdr.start, skb->data, ihs); - dseg += ds_cnt_inl; - fsz = skb_frag_size(frag); dma_addr = skb_frag_dma_map(sq->pdev, frag, 0, fsz, DMA_TO_DEVICE); @@ -296,7 +292,7 @@ tx_post_resync_dump(struct mlx5e_txqsq *sq, struct sk_buff *skb, dseg->byte_count = cpu_to_be32(fsz); mlx5e_dma_push(sq, dma_addr, fsz, MLX5E_DMA_MAP_PAGE); - tx_fill_wi(sq, pi, num_wqebbs, frag); + tx_fill_wi(sq, pi, num_wqebbs, frag, fsz); sq->pc += num_wqebbs; WARN(num_wqebbs > MLX5E_KTLS_MAX_DUMP_WQEBBS, @@ -323,7 +319,7 @@ static void tx_post_fence_nop(struct mlx5e_txqsq *sq) struct mlx5_wq_cyc *wq = &sq->wq; u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); - tx_fill_wi(sq, pi, 1, NULL); + tx_fill_wi(sq, pi, 1, NULL, 0); mlx5e_post_nop_fence(wq, sq->sqn, &sq->pc); } @@ -412,7 +408,7 @@ struct sk_buff *mlx5e_ktls_handle_tx_skb(struct net_device *netdev, goto out; tls_ctx = tls_get_ctx(skb->sk); - if (unlikely(tls_ctx->netdev != netdev)) + if (unlikely(WARN_ON_ONCE(tls_ctx->netdev != netdev))) goto err_out; priv_tx = mlx5e_get_ktls_tx_priv_ctx(tls_ctx); @@ -434,7 +430,7 @@ struct sk_buff *mlx5e_ktls_handle_tx_skb(struct net_device *netdev, priv_tx->expected_seq = seq + datalen; cseg = &(*wqe)->ctrl; - cseg->imm = cpu_to_be32(priv_tx->tisn); + cseg->tisn = cpu_to_be32(priv_tx->tisn << 8); stats->tls_encrypted_packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1; stats->tls_encrypted_bytes += datalen; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index 8657e0f26995..2c75b2752f58 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -437,12 +437,6 @@ arfs_hash_bucket(struct arfs_table *arfs_t, __be16 src_port, return &arfs_t->rules_hash[bucket_idx]; } -static u8 arfs_get_ip_proto(const struct sk_buff *skb) -{ - return (skb->protocol == htons(ETH_P_IP)) ? - ip_hdr(skb)->protocol : ipv6_hdr(skb)->nexthdr; -} - static struct arfs_table *arfs_get_table(struct mlx5e_arfs_tables *arfs, u8 ip_proto, __be16 etype) { @@ -602,31 +596,9 @@ out: arfs_may_expire_flow(priv); } -/* return L4 destination port from ip4/6 packets */ -static __be16 arfs_get_dst_port(const struct sk_buff *skb) -{ - char *transport_header; - - transport_header = skb_transport_header(skb); - if (arfs_get_ip_proto(skb) == IPPROTO_TCP) - return ((struct tcphdr *)transport_header)->dest; - return ((struct udphdr *)transport_header)->dest; -} - -/* return L4 source port from ip4/6 packets */ -static __be16 arfs_get_src_port(const struct sk_buff *skb) -{ - char *transport_header; - - transport_header = skb_transport_header(skb); - if (arfs_get_ip_proto(skb) == IPPROTO_TCP) - return ((struct tcphdr *)transport_header)->source; - return ((struct udphdr *)transport_header)->source; -} - static struct arfs_rule *arfs_alloc_rule(struct mlx5e_priv *priv, struct arfs_table *arfs_t, - const struct sk_buff *skb, + const struct flow_keys *fk, u16 rxq, u32 flow_id) { struct arfs_rule *rule; @@ -641,19 +613,19 @@ static struct arfs_rule *arfs_alloc_rule(struct mlx5e_priv *priv, INIT_WORK(&rule->arfs_work, arfs_handle_work); tuple = &rule->tuple; - tuple->etype = skb->protocol; + tuple->etype = fk->basic.n_proto; + tuple->ip_proto = fk->basic.ip_proto; if (tuple->etype == htons(ETH_P_IP)) { - tuple->src_ipv4 = ip_hdr(skb)->saddr; - tuple->dst_ipv4 = ip_hdr(skb)->daddr; + tuple->src_ipv4 = fk->addrs.v4addrs.src; + tuple->dst_ipv4 = fk->addrs.v4addrs.dst; } else { - memcpy(&tuple->src_ipv6, &ipv6_hdr(skb)->saddr, + memcpy(&tuple->src_ipv6, &fk->addrs.v6addrs.src, sizeof(struct in6_addr)); - memcpy(&tuple->dst_ipv6, &ipv6_hdr(skb)->daddr, + memcpy(&tuple->dst_ipv6, &fk->addrs.v6addrs.dst, sizeof(struct in6_addr)); } - tuple->ip_proto = arfs_get_ip_proto(skb); - tuple->src_port = arfs_get_src_port(skb); - tuple->dst_port = arfs_get_dst_port(skb); + tuple->src_port = fk->ports.src; + tuple->dst_port = fk->ports.dst; rule->flow_id = flow_id; rule->filter_id = priv->fs.arfs.last_filter_id++ % RPS_NO_FILTER; @@ -664,37 +636,33 @@ static struct arfs_rule *arfs_alloc_rule(struct mlx5e_priv *priv, return rule; } -static bool arfs_cmp_ips(struct arfs_tuple *tuple, - const struct sk_buff *skb) +static bool arfs_cmp(const struct arfs_tuple *tuple, const struct flow_keys *fk) { - if (tuple->etype == htons(ETH_P_IP) && - tuple->src_ipv4 == ip_hdr(skb)->saddr && - tuple->dst_ipv4 == ip_hdr(skb)->daddr) - return true; - if (tuple->etype == htons(ETH_P_IPV6) && - (!memcmp(&tuple->src_ipv6, &ipv6_hdr(skb)->saddr, - sizeof(struct in6_addr))) && - (!memcmp(&tuple->dst_ipv6, &ipv6_hdr(skb)->daddr, - sizeof(struct in6_addr)))) - return true; + if (tuple->src_port != fk->ports.src || tuple->dst_port != fk->ports.dst) + return false; + if (tuple->etype != fk->basic.n_proto) + return false; + if (tuple->etype == htons(ETH_P_IP)) + return tuple->src_ipv4 == fk->addrs.v4addrs.src && + tuple->dst_ipv4 == fk->addrs.v4addrs.dst; + if (tuple->etype == htons(ETH_P_IPV6)) + return !memcmp(&tuple->src_ipv6, &fk->addrs.v6addrs.src, + sizeof(struct in6_addr)) && + !memcmp(&tuple->dst_ipv6, &fk->addrs.v6addrs.dst, + sizeof(struct in6_addr)); return false; } static struct arfs_rule *arfs_find_rule(struct arfs_table *arfs_t, - const struct sk_buff *skb) + const struct flow_keys *fk) { struct arfs_rule *arfs_rule; struct hlist_head *head; - __be16 src_port = arfs_get_src_port(skb); - __be16 dst_port = arfs_get_dst_port(skb); - head = arfs_hash_bucket(arfs_t, src_port, dst_port); + head = arfs_hash_bucket(arfs_t, fk->ports.src, fk->ports.dst); hlist_for_each_entry(arfs_rule, head, hlist) { - if (arfs_rule->tuple.src_port == src_port && - arfs_rule->tuple.dst_port == dst_port && - arfs_cmp_ips(&arfs_rule->tuple, skb)) { + if (arfs_cmp(&arfs_rule->tuple, fk)) return arfs_rule; - } } return NULL; @@ -707,20 +675,24 @@ int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, struct mlx5e_arfs_tables *arfs = &priv->fs.arfs; struct arfs_table *arfs_t; struct arfs_rule *arfs_rule; + struct flow_keys fk; + + if (!skb_flow_dissect_flow_keys(skb, &fk, 0)) + return -EPROTONOSUPPORT; - if (skb->protocol != htons(ETH_P_IP) && - skb->protocol != htons(ETH_P_IPV6)) + if (fk.basic.n_proto != htons(ETH_P_IP) && + fk.basic.n_proto != htons(ETH_P_IPV6)) return -EPROTONOSUPPORT; if (skb->encapsulation) return -EPROTONOSUPPORT; - arfs_t = arfs_get_table(arfs, arfs_get_ip_proto(skb), skb->protocol); + arfs_t = arfs_get_table(arfs, fk.basic.ip_proto, fk.basic.n_proto); if (!arfs_t) return -EPROTONOSUPPORT; spin_lock_bh(&arfs->arfs_lock); - arfs_rule = arfs_find_rule(arfs_t, skb); + arfs_rule = arfs_find_rule(arfs_t, &fk); if (arfs_rule) { if (arfs_rule->rxq == rxq_index) { spin_unlock_bh(&arfs->arfs_lock); @@ -728,8 +700,7 @@ int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, } arfs_rule->rxq = rxq_index; } else { - arfs_rule = arfs_alloc_rule(priv, arfs_t, skb, - rxq_index, flow_id); + arfs_rule = arfs_alloc_rule(priv, arfs_t, &fk, rxq_index, flow_id); if (!arfs_rule) { spin_unlock_bh(&arfs->arfs_lock); return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 126ec4181286..20e628c907e5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -391,7 +391,7 @@ void mlx5e_ethtool_get_channels(struct mlx5e_priv *priv, { mutex_lock(&priv->state_lock); - ch->max_combined = mlx5e_get_netdev_max_channels(priv->netdev); + ch->max_combined = priv->max_nch; ch->combined_count = priv->channels.params.num_channels; if (priv->xsk.refcnt) { /* The upper half are XSK queues. */ @@ -785,7 +785,7 @@ static void ptys2ethtool_supported_advertised_port(struct ethtool_link_ksettings } static void get_speed_duplex(struct net_device *netdev, - u32 eth_proto_oper, + u32 eth_proto_oper, bool force_legacy, struct ethtool_link_ksettings *link_ksettings) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -795,7 +795,7 @@ static void get_speed_duplex(struct net_device *netdev, if (!netif_carrier_ok(netdev)) goto out; - speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper); + speed = mlx5e_port_ptys2speed(priv->mdev, eth_proto_oper, force_legacy); if (!speed) { speed = SPEED_UNKNOWN; goto out; @@ -914,8 +914,8 @@ int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv, /* Fields: eth_proto_admin and ext_eth_proto_admin are * mutually exclusive. Hence try reading legacy advertising * when extended advertising is zero. - * admin_ext indicates how eth_proto_admin should be - * interpreted + * admin_ext indicates which proto_admin (ext vs. legacy) + * should be read and interpreted */ admin_ext = ext; if (ext && !eth_proto_admin) { @@ -924,7 +924,7 @@ int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv, admin_ext = false; } - eth_proto_oper = MLX5_GET_ETH_PROTO(ptys_reg, out, ext, + eth_proto_oper = MLX5_GET_ETH_PROTO(ptys_reg, out, admin_ext, eth_proto_oper); eth_proto_lp = MLX5_GET(ptys_reg, out, eth_proto_lp_advertise); an_disable_admin = MLX5_GET(ptys_reg, out, an_disable_admin); @@ -939,7 +939,8 @@ int mlx5e_ethtool_get_link_ksettings(struct mlx5e_priv *priv, get_supported(mdev, eth_proto_cap, link_ksettings); get_advertising(eth_proto_admin, tx_pause, rx_pause, link_ksettings, admin_ext); - get_speed_duplex(priv->netdev, eth_proto_oper, link_ksettings); + get_speed_duplex(priv->netdev, eth_proto_oper, !admin_ext, + link_ksettings); eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap; @@ -1016,45 +1017,77 @@ static u32 mlx5e_ethtool2ptys_ext_adver_link(const unsigned long *link_modes) return ptys_modes; } +static bool ext_link_mode_requested(const unsigned long *adver) +{ +#define MLX5E_MIN_PTYS_EXT_LINK_MODE_BIT ETHTOOL_LINK_MODE_50000baseKR_Full_BIT + int size = __ETHTOOL_LINK_MODE_MASK_NBITS - MLX5E_MIN_PTYS_EXT_LINK_MODE_BIT; + __ETHTOOL_DECLARE_LINK_MODE_MASK(modes); + + bitmap_set(modes, MLX5E_MIN_PTYS_EXT_LINK_MODE_BIT, size); + return bitmap_intersects(modes, adver, __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static bool ext_speed_requested(u32 speed) +{ +#define MLX5E_MAX_PTYS_LEGACY_SPEED 100000 + return !!(speed > MLX5E_MAX_PTYS_LEGACY_SPEED); +} + +static bool ext_requested(u8 autoneg, const unsigned long *adver, u32 speed) +{ + bool ext_link_mode = ext_link_mode_requested(adver); + bool ext_speed = ext_speed_requested(speed); + + return autoneg == AUTONEG_ENABLE ? ext_link_mode : ext_speed; +} + int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv, const struct ethtool_link_ksettings *link_ksettings) { struct mlx5_core_dev *mdev = priv->mdev; struct mlx5e_port_eth_proto eproto; + const unsigned long *adver; bool an_changes = false; u8 an_disable_admin; bool ext_supported; - bool ext_requested; u8 an_disable_cap; bool an_disable; u32 link_modes; u8 an_status; + u8 autoneg; u32 speed; + bool ext; int err; u32 (*ethtool2ptys_adver_func)(const unsigned long *adver); -#define MLX5E_PTYS_EXT ((1ULL << ETHTOOL_LINK_MODE_50000baseKR_Full_BIT) - 1) + adver = link_ksettings->link_modes.advertising; + autoneg = link_ksettings->base.autoneg; + speed = link_ksettings->base.speed; - ext_requested = !!(link_ksettings->link_modes.advertising[0] > - MLX5E_PTYS_EXT || - link_ksettings->link_modes.advertising[1]); + ext = ext_requested(autoneg, adver, speed), ext_supported = MLX5_CAP_PCAM_FEATURE(mdev, ptys_extended_ethernet); - ext_requested &= ext_supported; + if (!ext_supported && ext) + return -EOPNOTSUPP; - speed = link_ksettings->base.speed; - ethtool2ptys_adver_func = ext_requested ? - mlx5e_ethtool2ptys_ext_adver_link : + ethtool2ptys_adver_func = ext ? mlx5e_ethtool2ptys_ext_adver_link : mlx5e_ethtool2ptys_adver_link; - err = mlx5_port_query_eth_proto(mdev, 1, ext_requested, &eproto); + err = mlx5_port_query_eth_proto(mdev, 1, ext, &eproto); if (err) { netdev_err(priv->netdev, "%s: query port eth proto failed: %d\n", __func__, err); goto out; } - link_modes = link_ksettings->base.autoneg == AUTONEG_ENABLE ? - ethtool2ptys_adver_func(link_ksettings->link_modes.advertising) : - mlx5e_port_speed2linkmodes(mdev, speed); + link_modes = autoneg == AUTONEG_ENABLE ? ethtool2ptys_adver_func(adver) : + mlx5e_port_speed2linkmodes(mdev, speed, !ext); + + if ((link_modes & MLX5E_PROT_MASK(MLX5E_56GBASE_R4)) && + autoneg != AUTONEG_ENABLE) { + netdev_err(priv->netdev, "%s: 56G link speed requires autoneg enabled\n", + __func__); + err = -EINVAL; + goto out; + } link_modes = link_modes & eproto.cap; if (!link_modes) { @@ -1067,14 +1100,14 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv, mlx5_port_query_eth_autoneg(mdev, &an_status, &an_disable_cap, &an_disable_admin); - an_disable = link_ksettings->base.autoneg == AUTONEG_DISABLE; + an_disable = autoneg == AUTONEG_DISABLE; an_changes = ((!an_disable && an_disable_admin) || (an_disable && !an_disable_admin)); if (!an_changes && link_modes == eproto.admin) goto out; - mlx5_port_set_eth_ptys(mdev, an_disable, link_modes, ext_requested); + mlx5_port_set_eth_ptys(mdev, an_disable, link_modes, ext); mlx5_toggle_port_link(mdev); out: @@ -1313,6 +1346,9 @@ int mlx5e_ethtool_set_pauseparam(struct mlx5e_priv *priv, struct mlx5_core_dev *mdev = priv->mdev; int err; + if (!MLX5_CAP_GEN(mdev, vport_group_manager)) + return -EOPNOTSUPP; + if (pauseparam->autoneg) return -EINVAL; @@ -1654,6 +1690,40 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev, return 0; } +int mlx5e_ethtool_flash_device(struct mlx5e_priv *priv, + struct ethtool_flash *flash) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct net_device *dev = priv->netdev; + const struct firmware *fw; + int err; + + if (flash->region != ETHTOOL_FLASH_ALL_REGIONS) + return -EOPNOTSUPP; + + err = request_firmware_direct(&fw, flash->data, &dev->dev); + if (err) + return err; + + dev_hold(dev); + rtnl_unlock(); + + err = mlx5_firmware_flash(mdev, fw, NULL); + release_firmware(fw); + + rtnl_lock(); + dev_put(dev); + return err; +} + +static int mlx5e_flash_device(struct net_device *dev, + struct ethtool_flash *flash) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + + return mlx5e_ethtool_flash_device(priv, flash); +} + static int set_pflag_cqe_based_moder(struct net_device *netdev, bool enable, bool is_rx_cq) { @@ -1936,6 +2006,7 @@ const struct ethtool_ops mlx5e_ethtool_ops = { .set_wol = mlx5e_set_wol, .get_module_info = mlx5e_get_module_info, .get_module_eeprom = mlx5e_get_module_eeprom, + .flash_device = mlx5e_flash_device, .get_priv_flags = mlx5e_get_priv_flags, .set_priv_flags = mlx5e_set_priv_flags, .self_test = mlx5e_self_test, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c index ea3a490b569a..94304abc49e9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c @@ -611,7 +611,8 @@ static int validate_flow(struct mlx5e_priv *priv, return -ENOSPC; if (fs->ring_cookie != RX_CLS_FLOW_DISC) - if (!mlx5e_qid_validate(&priv->channels.params, fs->ring_cookie)) + if (!mlx5e_qid_validate(priv->profile, &priv->channels.params, + fs->ring_cookie)) return -EINVAL; switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 47eea6b3a1c3..9d5f6e56188f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -331,12 +331,11 @@ static inline u64 mlx5e_get_mpwqe_offset(struct mlx5e_rq *rq, u16 wqe_ix) static void mlx5e_init_frags_partition(struct mlx5e_rq *rq) { - struct mlx5e_wqe_frag_info next_frag, *prev; + struct mlx5e_wqe_frag_info next_frag = {}; + struct mlx5e_wqe_frag_info *prev = NULL; int i; next_frag.di = &rq->wqe.di[0]; - next_frag.offset = 0; - prev = NULL; for (i = 0; i < mlx5_wq_cyc_get_size(&rq->wqe.wq); i++) { struct mlx5e_rq_frag_info *frag_info = &rq->wqe.info.arr[0]; @@ -1322,7 +1321,6 @@ err_free_txqsq: void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq) { sq->txq = netdev_get_tx_queue(sq->channel->netdev, sq->txq_ix); - clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state); set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); netdev_tx_reset_queue(sq->txq); netif_tx_start_queue(sq->txq); @@ -1677,10 +1675,10 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c, struct mlx5e_channel_param *cparam) { struct mlx5e_priv *priv = c->priv; - int err, tc, max_nch = mlx5e_get_netdev_max_channels(priv->netdev); + int err, tc; for (tc = 0; tc < params->num_tc; tc++) { - int txq_ix = c->ix + tc * max_nch; + int txq_ix = c->ix + tc * priv->max_nch; err = mlx5e_open_txqsq(c, c->priv->tisn[tc], txq_ix, params, &cparam->sq, &c->sq[tc], tc); @@ -2438,11 +2436,10 @@ int mlx5e_create_indirect_rqt(struct mlx5e_priv *priv) int mlx5e_create_direct_rqts(struct mlx5e_priv *priv, struct mlx5e_tir *tirs) { - const int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); int err; int ix; - for (ix = 0; ix < max_nch; ix++) { + for (ix = 0; ix < priv->max_nch; ix++) { err = mlx5e_create_rqt(priv, 1 /*size */, &tirs[ix].rqt); if (unlikely(err)) goto err_destroy_rqts; @@ -2460,10 +2457,9 @@ err_destroy_rqts: void mlx5e_destroy_direct_rqts(struct mlx5e_priv *priv, struct mlx5e_tir *tirs) { - const int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); int i; - for (i = 0; i < max_nch; i++) + for (i = 0; i < priv->max_nch; i++) mlx5e_destroy_rqt(priv, &tirs[i].rqt); } @@ -2557,7 +2553,7 @@ static void mlx5e_redirect_rqts(struct mlx5e_priv *priv, mlx5e_redirect_rqt(priv, rqtn, MLX5E_INDIR_RQT_SIZE, rrp); } - for (ix = 0; ix < mlx5e_get_netdev_max_channels(priv->netdev); ix++) { + for (ix = 0; ix < priv->max_nch; ix++) { struct mlx5e_redirect_rqt_param direct_rrp = { .is_rss = false, { @@ -2758,7 +2754,7 @@ static int mlx5e_modify_tirs_lro(struct mlx5e_priv *priv) goto free_in; } - for (ix = 0; ix < mlx5e_get_netdev_max_channels(priv->netdev); ix++) { + for (ix = 0; ix < priv->max_nch; ix++) { err = mlx5_core_modify_tir(mdev, priv->direct_tir[ix].tirn, in, inlen); if (err) @@ -2858,12 +2854,11 @@ static void mlx5e_netdev_set_tcs(struct net_device *netdev) static void mlx5e_build_tc2txq_maps(struct mlx5e_priv *priv) { - int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); int i, tc; - for (i = 0; i < max_nch; i++) + for (i = 0; i < priv->max_nch; i++) for (tc = 0; tc < priv->profile->max_tc; tc++) - priv->channel_tc2txq[i][tc] = i + tc * max_nch; + priv->channel_tc2txq[i][tc] = i + tc * priv->max_nch; } static void mlx5e_build_tx2sq_maps(struct mlx5e_priv *priv) @@ -2884,7 +2879,7 @@ static void mlx5e_build_tx2sq_maps(struct mlx5e_priv *priv) void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) { int num_txqs = priv->channels.num * priv->channels.params.num_tc; - int num_rxqs = priv->channels.num * MLX5E_NUM_RQ_GROUPS; + int num_rxqs = priv->channels.num * priv->profile->rq_groups; struct net_device *netdev = priv->netdev; mlx5e_netdev_set_tcs(netdev); @@ -3306,7 +3301,6 @@ err_destroy_inner_tirs: int mlx5e_create_direct_tirs(struct mlx5e_priv *priv, struct mlx5e_tir *tirs) { - const int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); struct mlx5e_tir *tir; void *tirc; int inlen; @@ -3319,7 +3313,7 @@ int mlx5e_create_direct_tirs(struct mlx5e_priv *priv, struct mlx5e_tir *tirs) if (!in) return -ENOMEM; - for (ix = 0; ix < max_nch; ix++) { + for (ix = 0; ix < priv->max_nch; ix++) { memset(in, 0, inlen); tir = &tirs[ix]; tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); @@ -3358,10 +3352,9 @@ void mlx5e_destroy_indirect_tirs(struct mlx5e_priv *priv, bool inner_ttc) void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv, struct mlx5e_tir *tirs) { - const int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); int i; - for (i = 0; i < max_nch; i++) + for (i = 0; i < priv->max_nch; i++) mlx5e_destroy_tir(priv->mdev, &tirs[i]); } @@ -3487,7 +3480,7 @@ void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s) { int i; - for (i = 0; i < mlx5e_get_netdev_max_channels(priv->netdev); i++) { + for (i = 0; i < priv->max_nch; i++) { struct mlx5e_channel_stats *channel_stats = &priv->channel_stats[i]; struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq; struct mlx5e_rq_stats *rq_stats = &channel_stats->rq; @@ -4960,8 +4953,7 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev, return err; mlx5e_build_nic_params(mdev, &priv->xsk, rss, &priv->channels.params, - mlx5e_get_netdev_max_channels(netdev), - netdev->mtu); + priv->max_nch, netdev->mtu); mlx5e_timestamp_init(priv); @@ -5164,6 +5156,7 @@ static const struct mlx5e_profile mlx5e_nic_profile = { .rx_handlers.handle_rx_cqe = mlx5e_handle_rx_cqe, .rx_handlers.handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq, .max_tc = MLX5E_MAX_NUM_TC, + .rq_groups = MLX5E_NUM_RQ_GROUPS(XSK), }; /* mlx5e generic netdev management API (move to en_common.c) */ @@ -5181,6 +5174,7 @@ int mlx5e_netdev_init(struct net_device *netdev, priv->profile = profile; priv->ppriv = ppriv; priv->msglevel = MLX5E_MSG_LEVEL; + priv->max_nch = netdev->num_rx_queues / max_t(u8, profile->rq_groups, 1); priv->max_opened_tc = 1; mutex_init(&priv->state_lock); @@ -5218,7 +5212,7 @@ struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev, netdev = alloc_etherdev_mqs(sizeof(struct mlx5e_priv), nch * profile->max_tc, - nch * MLX5E_NUM_RQ_GROUPS); + nch * profile->rq_groups); if (!netdev) { mlx5_core_err(mdev, "alloc_etherdev_mqs() failed\n"); return NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 7f747cb1a4f4..d0684fdb69e1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -1701,6 +1701,7 @@ static const struct mlx5e_profile mlx5e_rep_profile = { .rx_handlers.handle_rx_cqe = mlx5e_handle_rx_cqe_rep, .rx_handlers.handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq, .max_tc = 1, + .rq_groups = MLX5E_NUM_RQ_GROUPS(REGULAR), }; static const struct mlx5e_profile mlx5e_uplink_rep_profile = { @@ -1718,6 +1719,7 @@ static const struct mlx5e_profile mlx5e_uplink_rep_profile = { .rx_handlers.handle_rx_cqe = mlx5e_handle_rx_cqe_rep, .rx_handlers.handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq, .max_tc = MLX5E_MAX_NUM_TC, + .rq_groups = MLX5E_NUM_RQ_GROUPS(REGULAR), }; static bool diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 539b4d3656da..57f9f346d213 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -172,7 +172,7 @@ static void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv) memset(s, 0, sizeof(*s)); - for (i = 0; i < mlx5e_get_netdev_max_channels(priv->netdev); i++) { + for (i = 0; i < priv->max_nch; i++) { struct mlx5e_channel_stats *channel_stats = &priv->channel_stats[i]; struct mlx5e_xdpsq_stats *xdpsq_red_stats = &channel_stats->xdpsq; @@ -1395,7 +1395,7 @@ static const struct counter_desc ch_stats_desc[] = { static int mlx5e_grp_channels_get_num_stats(struct mlx5e_priv *priv) { - int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); + int max_nch = priv->max_nch; return (NUM_RQ_STATS * max_nch) + (NUM_CH_STATS * max_nch) + @@ -1409,8 +1409,8 @@ static int mlx5e_grp_channels_get_num_stats(struct mlx5e_priv *priv) static int mlx5e_grp_channels_fill_strings(struct mlx5e_priv *priv, u8 *data, int idx) { - int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); bool is_xsk = priv->xsk.ever_used; + int max_nch = priv->max_nch; int i, j, tc; for (i = 0; i < max_nch; i++) @@ -1452,8 +1452,8 @@ static int mlx5e_grp_channels_fill_strings(struct mlx5e_priv *priv, u8 *data, static int mlx5e_grp_channels_fill_stats(struct mlx5e_priv *priv, u64 *data, int idx) { - int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); bool is_xsk = priv->xsk.ever_used; + int max_nch = priv->max_nch; int i, j, tc; for (i = 0; i < max_nch; i++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index cc096f6011d9..00b2d4a86159 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1230,13 +1230,13 @@ static struct mlx5_fc *mlx5e_tc_get_counter(struct mlx5e_tc_flow *flow) void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) { struct mlx5e_neigh *m_neigh = &nhe->m_neigh; - u64 bytes, packets, lastuse = 0; struct mlx5e_tc_flow *flow; struct mlx5e_encap_entry *e; struct mlx5_fc *counter; struct neigh_table *tbl; bool neigh_used = false; struct neighbour *n; + u64 lastuse; if (m_neigh->family == AF_INET) tbl = &arp_tbl; @@ -1256,7 +1256,7 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) encaps[efi->index]); if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) { counter = mlx5e_tc_get_counter(flow); - mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse); + lastuse = mlx5_fc_query_lastuse(counter); if (time_after((unsigned long)lastuse, nhe->reported_lastuse)) { neigh_used = true; break; @@ -1480,7 +1480,7 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec, struct flow_cls_offload *f, struct net_device *filter_dev, - u8 *match_level, u8 *tunnel_match_level) + u8 *inner_match_level, u8 *outer_match_level) { struct netlink_ext_ack *extack = f->common.extack; void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, @@ -1495,8 +1495,9 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, struct flow_dissector *dissector = rule->match.dissector; u16 addr_type = 0; u8 ip_proto = 0; + u8 *match_level; - *match_level = MLX5_MATCH_NONE; + match_level = outer_match_level; if (dissector->used_keys & ~(BIT(FLOW_DISSECTOR_KEY_META) | @@ -1524,12 +1525,14 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, } if (mlx5e_get_tc_tun(filter_dev)) { - if (parse_tunnel_attr(priv, spec, f, filter_dev, tunnel_match_level)) + if (parse_tunnel_attr(priv, spec, f, filter_dev, + outer_match_level)) return -EOPNOTSUPP; - /* In decap flow, header pointers should point to the inner + /* At this point, header pointers should point to the inner * headers, outer header were already set by parse_tunnel_attr */ + match_level = inner_match_level; headers_c = get_match_headers_criteria(MLX5_FLOW_CONTEXT_ACTION_DECAP, spec); headers_v = get_match_headers_value(MLX5_FLOW_CONTEXT_ACTION_DECAP, @@ -1831,35 +1834,41 @@ static int parse_cls_flower(struct mlx5e_priv *priv, struct flow_cls_offload *f, struct net_device *filter_dev) { + u8 inner_match_level, outer_match_level, non_tunnel_match_level; struct netlink_ext_ack *extack = f->common.extack; struct mlx5_core_dev *dev = priv->mdev; struct mlx5_eswitch *esw = dev->priv.eswitch; struct mlx5e_rep_priv *rpriv = priv->ppriv; - u8 match_level, tunnel_match_level = MLX5_MATCH_NONE; struct mlx5_eswitch_rep *rep; int err; - err = __parse_cls_flower(priv, spec, f, filter_dev, &match_level, &tunnel_match_level); + inner_match_level = MLX5_MATCH_NONE; + outer_match_level = MLX5_MATCH_NONE; + + err = __parse_cls_flower(priv, spec, f, filter_dev, &inner_match_level, + &outer_match_level); + non_tunnel_match_level = (inner_match_level == MLX5_MATCH_NONE) ? + outer_match_level : inner_match_level; if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH)) { rep = rpriv->rep; if (rep->vport != MLX5_VPORT_UPLINK && (esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE && - esw->offloads.inline_mode < match_level)) { + esw->offloads.inline_mode < non_tunnel_match_level)) { NL_SET_ERR_MSG_MOD(extack, "Flow is not offloaded due to min inline setting"); netdev_warn(priv->netdev, "Flow is not offloaded due to min inline setting, required %d actual %d\n", - match_level, esw->offloads.inline_mode); + non_tunnel_match_level, esw->offloads.inline_mode); return -EOPNOTSUPP; } } if (flow->flags & MLX5E_TC_FLOW_ESWITCH) { - flow->esw_attr->match_level = match_level; - flow->esw_attr->tunnel_match_level = tunnel_match_level; + flow->esw_attr->inner_match_level = inner_match_level; + flow->esw_attr->outer_match_level = outer_match_level; } else { - flow->nic_attr->match_level = match_level; + flow->nic_attr->match_level = non_tunnel_match_level; } return err; @@ -3158,7 +3167,7 @@ mlx5e_flow_esw_attr_init(struct mlx5_esw_flow_attr *esw_attr, esw_attr->parse_attr = parse_attr; esw_attr->chain = f->common.chain_index; - esw_attr->prio = TC_H_MAJ(f->common.prio) >> 16; + esw_attr->prio = f->common.prio; esw_attr->in_rep = in_rep; esw_attr->in_mdev = in_mdev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index c50b6f0769c8..49b06b256c92 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -49,7 +49,7 @@ static inline bool mlx5e_channel_no_affinity_change(struct mlx5e_channel *c) static void mlx5e_handle_tx_dim(struct mlx5e_txqsq *sq) { struct mlx5e_sq_stats *stats = sq->stats; - struct dim_sample dim_sample; + struct dim_sample dim_sample = {}; if (unlikely(!test_bit(MLX5E_SQ_STATE_AM, &sq->state))) return; @@ -61,7 +61,7 @@ static void mlx5e_handle_tx_dim(struct mlx5e_txqsq *sq) static void mlx5e_handle_rx_dim(struct mlx5e_rq *rq) { struct mlx5e_rq_stats *stats = rq->stats; - struct dim_sample dim_sample; + struct dim_sample dim_sample = {}; if (unlikely(!test_bit(MLX5E_RQ_STATE_AM, &rq->state))) return; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index a38e8a3c7c9a..04685dbb280c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -377,8 +377,8 @@ struct mlx5_esw_flow_attr { struct mlx5_termtbl_handle *termtbl; } dests[MLX5_MAX_FLOW_FWD_VPORTS]; u32 mod_hdr_id; - u8 match_level; - u8 tunnel_match_level; + u8 inner_match_level; + u8 outer_match_level; struct mlx5_fc *counter; u32 chain; u16 prio; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 089ae4d48a82..0323fd078271 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -207,14 +207,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, mlx5_eswitch_set_rule_source_port(esw, spec, attr); - if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) { - if (attr->tunnel_match_level != MLX5_MATCH_NONE) - spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; - if (attr->match_level != MLX5_MATCH_NONE) - spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS; - } else if (attr->match_level != MLX5_MATCH_NONE) { + if (attr->outer_match_level != MLX5_MATCH_NONE) spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; - } + if (attr->inner_match_level != MLX5_MATCH_NONE) + spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS; if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) flow_act.modify_id = attr->mod_hdr_id; @@ -290,7 +286,7 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw, mlx5_eswitch_set_rule_source_port(esw, spec, attr); spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS; - if (attr->match_level != MLX5_MATCH_NONE) + if (attr->outer_match_level != MLX5_MATCH_NONE) spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; rule = mlx5_add_flow_rules(fast_fdb, spec, &flow_act, dest, i); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index c48c382f926f..c1252d6be0ef 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -68,7 +68,7 @@ enum fs_flow_table_type { FS_FT_SNIFFER_RX = 0X5, FS_FT_SNIFFER_TX = 0X6, FS_FT_RDMA_RX = 0X7, - FS_FT_MAX_TYPE = FS_FT_SNIFFER_TX, + FS_FT_MAX_TYPE = FS_FT_RDMA_RX, }; enum fs_flow_table_op_mod { @@ -275,7 +275,8 @@ void mlx5_cleanup_fs(struct mlx5_core_dev *dev); (type == FS_FT_FDB) ? MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, cap) : \ (type == FS_FT_SNIFFER_RX) ? MLX5_CAP_FLOWTABLE_SNIFFER_RX(mdev, cap) : \ (type == FS_FT_SNIFFER_TX) ? MLX5_CAP_FLOWTABLE_SNIFFER_TX(mdev, cap) : \ - (BUILD_BUG_ON_ZERO(FS_FT_SNIFFER_TX != FS_FT_MAX_TYPE))\ + (type == FS_FT_RDMA_RX) ? MLX5_CAP_FLOWTABLE_RDMA_RX(mdev, cap) : \ + (BUILD_BUG_ON_ZERO(FS_FT_RDMA_RX != FS_FT_MAX_TYPE))\ ) #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c index b3762123a69c..1834d9f3aa1c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c @@ -369,6 +369,11 @@ int mlx5_fc_query(struct mlx5_core_dev *dev, struct mlx5_fc *counter, } EXPORT_SYMBOL(mlx5_fc_query); +u64 mlx5_fc_query_lastuse(struct mlx5_fc *counter) +{ + return counter->cache.lastuse; +} + void mlx5_fc_query_cached(struct mlx5_fc *counter, u64 *bytes, u64 *packets, u64 *lastuse) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 9314777d99e3..d685122d9ff7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -590,7 +590,8 @@ mlx5_fw_fatal_reporter_dump(struct devlink_health_reporter *reporter, data_size = crdump_size - offset; else data_size = MLX5_CR_DUMP_CHUNK_SIZE; - err = devlink_fmsg_binary_put(fmsg, cr_data, data_size); + err = devlink_fmsg_binary_put(fmsg, (char *)cr_data + offset, + data_size); if (err) goto free_data; } @@ -700,6 +701,16 @@ static void poll_health(struct timer_list *t) if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) goto out; + fatal_error = check_fatal_sensors(dev); + + if (fatal_error && !health->fatal_error) { + mlx5_core_err(dev, "Fatal error %u detected\n", fatal_error); + dev->priv.health.fatal_error = fatal_error; + print_health_info(dev); + mlx5_trigger_health_work(dev); + goto out; + } + count = ioread32be(health->health_counter); if (count == health->prev) ++health->miss_counter; @@ -718,15 +729,6 @@ static void poll_health(struct timer_list *t) if (health->synd && health->synd != prev_synd) queue_work(health->wq, &health->report_work); - fatal_error = check_fatal_sensors(dev); - - if (fatal_error && !health->fatal_error) { - mlx5_core_err(dev, "Fatal error %u detected\n", fatal_error); - dev->priv.health.fatal_error = fatal_error; - print_health_info(dev); - mlx5_trigger_health_work(dev); - } - out: mod_timer(&health->timer, get_next_poll_jiffies()); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index ebd81f6b556e..90cb50fe17fd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -122,6 +122,14 @@ static int mlx5i_get_ts_info(struct net_device *netdev, return mlx5e_ethtool_get_ts_info(priv, info); } +static int mlx5i_flash_device(struct net_device *netdev, + struct ethtool_flash *flash) +{ + struct mlx5e_priv *priv = mlx5i_epriv(netdev); + + return mlx5e_ethtool_flash_device(priv, flash); +} + enum mlx5_ptys_width { MLX5_PTYS_WIDTH_1X = 1 << 0, MLX5_PTYS_WIDTH_2X = 1 << 1, @@ -233,6 +241,7 @@ const struct ethtool_ops mlx5i_ethtool_ops = { .get_ethtool_stats = mlx5i_get_ethtool_stats, .get_ringparam = mlx5i_get_ringparam, .set_ringparam = mlx5i_set_ringparam, + .flash_device = mlx5i_flash_device, .get_channels = mlx5i_get_channels, .set_channels = mlx5i_set_channels, .get_coalesce = mlx5i_get_coalesce, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 6bfaaab362dc..1a2560e3bf7c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -88,8 +88,7 @@ int mlx5i_init(struct mlx5_core_dev *mdev, netdev->mtu = netdev->max_mtu; mlx5e_build_nic_params(mdev, NULL, &priv->rss_params, &priv->channels.params, - mlx5e_get_netdev_max_channels(netdev), - netdev->mtu); + priv->max_nch, netdev->mtu); mlx5i_build_nic_params(mdev, &priv->channels.params); mlx5e_timestamp_init(priv); @@ -118,11 +117,10 @@ void mlx5i_cleanup(struct mlx5e_priv *priv) static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv) { - int max_nch = mlx5e_get_netdev_max_channels(priv->netdev); struct mlx5e_sw_stats s = { 0 }; int i, j; - for (i = 0; i < max_nch; i++) { + for (i = 0; i < priv->max_nch; i++) { struct mlx5e_channel_stats *channel_stats; struct mlx5e_rq_stats *rq_stats; @@ -436,6 +434,7 @@ static const struct mlx5e_profile mlx5i_nic_profile = { .rx_handlers.handle_rx_cqe = mlx5i_handle_rx_cqe, .rx_handlers.handle_rx_cqe_mpwqe = NULL, /* Not supported */ .max_tc = MLX5I_MAX_NUM_TC, + .rq_groups = MLX5E_NUM_RQ_GROUPS(REGULAR), }; /* mlx5i netdev NDos */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c index 6e56fa769d2e..c5a491e22e55 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c @@ -355,6 +355,7 @@ static const struct mlx5e_profile mlx5i_pkey_nic_profile = { .rx_handlers.handle_rx_cqe = mlx5i_handle_rx_cqe, .rx_handlers.handle_rx_cqe_mpwqe = NULL, /* Not supported */ .max_tc = MLX5I_MAX_NUM_TC, + .rq_groups = MLX5E_NUM_RQ_GROUPS(REGULAR), }; const struct mlx5e_profile *mlx5i_pkey_get_profile(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c index ea9ee88491e5..ea1d4d26ece0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c @@ -27,6 +27,7 @@ int mlx5_create_encryption_key(struct mlx5_core_dev *mdev, case 128: general_obj_key_size = MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_KEY_SIZE_128; + key_p += sz_bytes; break; case 256: general_obj_key_size = diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 650638152bbc..eda9c23e87b2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -6330,7 +6330,7 @@ static int __init mlxsw_sp_module_init(void) return 0; err_sp2_pci_driver_register: - mlxsw_pci_driver_unregister(&mlxsw_sp2_pci_driver); + mlxsw_pci_driver_unregister(&mlxsw_sp1_pci_driver); err_sp1_pci_driver_register: mlxsw_core_driver_unregister(&mlxsw_sp2_driver); err_sp2_core_driver_register: diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 131f62ce9297..6664119fb0c8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -951,4 +951,8 @@ void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port); int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp); +/* spectrum_nve_vxlan.c */ +int mlxsw_sp_nve_inc_parsing_depth_get(struct mlxsw_sp *mlxsw_sp); +void mlxsw_sp_nve_inc_parsing_depth_put(struct mlxsw_sp *mlxsw_sp); + #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index e8ac90564dbe..84a87d059333 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -471,7 +471,7 @@ int mlxsw_sp_acl_rulei_commit(struct mlxsw_sp_acl_rule_info *rulei) void mlxsw_sp_acl_rulei_priority(struct mlxsw_sp_acl_rule_info *rulei, unsigned int priority) { - rulei->priority = priority >> 16; + rulei->priority = priority; } void mlxsw_sp_acl_rulei_keymask_u32(struct mlxsw_sp_acl_rule_info *rulei, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c index 1537f70bc26d..888ba4300bcc 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -437,8 +437,8 @@ static const struct mlxsw_sp_sb_pr mlxsw_sp1_sb_prs[] = { MLXSW_SP1_SB_PR_CPU_SIZE, true, false), }; -#define MLXSW_SP2_SB_PR_INGRESS_SIZE 38128752 -#define MLXSW_SP2_SB_PR_EGRESS_SIZE 38128752 +#define MLXSW_SP2_SB_PR_INGRESS_SIZE 35297568 +#define MLXSW_SP2_SB_PR_EGRESS_SIZE 35297568 #define MLXSW_SP2_SB_PR_CPU_SIZE (256 * 1000) /* Order according to mlxsw_sp2_sb_pool_dess */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c index 1df164a4b06d..17f334b46c40 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c @@ -775,6 +775,7 @@ static void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp) ops->fini(nve); mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1, nve->tunnel_index); + memset(&nve->config, 0, sizeof(nve->config)); } nve->num_nve_tunnels--; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h index 0035640156a1..12f664f42f21 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.h @@ -29,6 +29,7 @@ struct mlxsw_sp_nve { unsigned int num_max_mc_entries[MLXSW_SP_L3_PROTO_MAX]; u32 tunnel_index; u16 ul_rif_index; /* Reserved for Spectrum */ + unsigned int inc_parsing_depth_refs; }; struct mlxsw_sp_nve_ops { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c index 93ccd9fc2266..05517c7feaa5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve_vxlan.c @@ -103,9 +103,9 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve, config->udp_dport = cfg->dst_port; } -static int mlxsw_sp_nve_parsing_set(struct mlxsw_sp *mlxsw_sp, - unsigned int parsing_depth, - __be16 udp_dport) +static int __mlxsw_sp_nve_parsing_set(struct mlxsw_sp *mlxsw_sp, + unsigned int parsing_depth, + __be16 udp_dport) { char mprs_pl[MLXSW_REG_MPRS_LEN]; @@ -113,6 +113,56 @@ static int mlxsw_sp_nve_parsing_set(struct mlxsw_sp *mlxsw_sp, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mprs), mprs_pl); } +static int mlxsw_sp_nve_parsing_set(struct mlxsw_sp *mlxsw_sp, + __be16 udp_dport) +{ + int parsing_depth = mlxsw_sp->nve->inc_parsing_depth_refs ? + MLXSW_SP_NVE_VXLAN_PARSING_DEPTH : + MLXSW_SP_NVE_DEFAULT_PARSING_DEPTH; + + return __mlxsw_sp_nve_parsing_set(mlxsw_sp, parsing_depth, udp_dport); +} + +static int +__mlxsw_sp_nve_inc_parsing_depth_get(struct mlxsw_sp *mlxsw_sp, + __be16 udp_dport) +{ + int err; + + mlxsw_sp->nve->inc_parsing_depth_refs++; + + err = mlxsw_sp_nve_parsing_set(mlxsw_sp, udp_dport); + if (err) + goto err_nve_parsing_set; + return 0; + +err_nve_parsing_set: + mlxsw_sp->nve->inc_parsing_depth_refs--; + return err; +} + +static void +__mlxsw_sp_nve_inc_parsing_depth_put(struct mlxsw_sp *mlxsw_sp, + __be16 udp_dport) +{ + mlxsw_sp->nve->inc_parsing_depth_refs--; + mlxsw_sp_nve_parsing_set(mlxsw_sp, udp_dport); +} + +int mlxsw_sp_nve_inc_parsing_depth_get(struct mlxsw_sp *mlxsw_sp) +{ + __be16 udp_dport = mlxsw_sp->nve->config.udp_dport; + + return __mlxsw_sp_nve_inc_parsing_depth_get(mlxsw_sp, udp_dport); +} + +void mlxsw_sp_nve_inc_parsing_depth_put(struct mlxsw_sp *mlxsw_sp) +{ + __be16 udp_dport = mlxsw_sp->nve->config.udp_dport; + + __mlxsw_sp_nve_inc_parsing_depth_put(mlxsw_sp, udp_dport); +} + static void mlxsw_sp_nve_vxlan_config_prepare(char *tngcr_pl, const struct mlxsw_sp_nve_config *config) @@ -176,9 +226,7 @@ static int mlxsw_sp1_nve_vxlan_init(struct mlxsw_sp_nve *nve, struct mlxsw_sp *mlxsw_sp = nve->mlxsw_sp; int err; - err = mlxsw_sp_nve_parsing_set(mlxsw_sp, - MLXSW_SP_NVE_VXLAN_PARSING_DEPTH, - config->udp_dport); + err = __mlxsw_sp_nve_inc_parsing_depth_get(mlxsw_sp, config->udp_dport); if (err) return err; @@ -203,8 +251,7 @@ err_promote_decap: err_rtdp_set: mlxsw_sp1_nve_vxlan_config_clear(mlxsw_sp); err_config_set: - mlxsw_sp_nve_parsing_set(mlxsw_sp, MLXSW_SP_NVE_DEFAULT_PARSING_DEPTH, - config->udp_dport); + __mlxsw_sp_nve_inc_parsing_depth_put(mlxsw_sp, 0); return err; } @@ -216,8 +263,7 @@ static void mlxsw_sp1_nve_vxlan_fini(struct mlxsw_sp_nve *nve) mlxsw_sp_router_nve_demote_decap(mlxsw_sp, config->ul_tb_id, config->ul_proto, &config->ul_sip); mlxsw_sp1_nve_vxlan_config_clear(mlxsw_sp); - mlxsw_sp_nve_parsing_set(mlxsw_sp, MLXSW_SP_NVE_DEFAULT_PARSING_DEPTH, - config->udp_dport); + __mlxsw_sp_nve_inc_parsing_depth_put(mlxsw_sp, 0); } static int @@ -320,9 +366,7 @@ static int mlxsw_sp2_nve_vxlan_init(struct mlxsw_sp_nve *nve, struct mlxsw_sp *mlxsw_sp = nve->mlxsw_sp; int err; - err = mlxsw_sp_nve_parsing_set(mlxsw_sp, - MLXSW_SP_NVE_VXLAN_PARSING_DEPTH, - config->udp_dport); + err = __mlxsw_sp_nve_inc_parsing_depth_get(mlxsw_sp, config->udp_dport); if (err) return err; @@ -348,8 +392,7 @@ err_promote_decap: err_rtdp_set: mlxsw_sp2_nve_vxlan_config_clear(mlxsw_sp); err_config_set: - mlxsw_sp_nve_parsing_set(mlxsw_sp, MLXSW_SP_NVE_DEFAULT_PARSING_DEPTH, - config->udp_dport); + __mlxsw_sp_nve_inc_parsing_depth_put(mlxsw_sp, 0); return err; } @@ -361,8 +404,7 @@ static void mlxsw_sp2_nve_vxlan_fini(struct mlxsw_sp_nve *nve) mlxsw_sp_router_nve_demote_decap(mlxsw_sp, config->ul_tb_id, config->ul_proto, &config->ul_sip); mlxsw_sp2_nve_vxlan_config_clear(mlxsw_sp); - mlxsw_sp_nve_parsing_set(mlxsw_sp, MLXSW_SP_NVE_DEFAULT_PARSING_DEPTH, - config->udp_dport); + __mlxsw_sp_nve_inc_parsing_depth_put(mlxsw_sp, 0); } const struct mlxsw_sp_nve_ops mlxsw_sp2_nve_vxlan_ops = { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c index bd9c2bc2d5d6..38bb1cfe4e8c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c @@ -29,7 +29,7 @@ struct mlxsw_sp_ptp_state { struct mlxsw_sp *mlxsw_sp; - struct rhashtable unmatched_ht; + struct rhltable unmatched_ht; spinlock_t unmatched_lock; /* protects the HT */ struct delayed_work ht_gc_dw; u32 gc_cycle; @@ -45,7 +45,7 @@ struct mlxsw_sp1_ptp_key { struct mlxsw_sp1_ptp_unmatched { struct mlxsw_sp1_ptp_key key; - struct rhash_head ht_node; + struct rhlist_head ht_node; struct rcu_head rcu; struct sk_buff *skb; u64 timestamp; @@ -359,7 +359,7 @@ static int mlxsw_sp_ptp_parse(struct sk_buff *skb, /* Returns NULL on successful insertion, a pointer on conflict, or an ERR_PTR on * error. */ -static struct mlxsw_sp1_ptp_unmatched * +static int mlxsw_sp1_ptp_unmatched_save(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp1_ptp_key key, struct sk_buff *skb, @@ -368,41 +368,51 @@ mlxsw_sp1_ptp_unmatched_save(struct mlxsw_sp *mlxsw_sp, int cycles = MLXSW_SP1_PTP_HT_GC_TIMEOUT / MLXSW_SP1_PTP_HT_GC_INTERVAL; struct mlxsw_sp_ptp_state *ptp_state = mlxsw_sp->ptp_state; struct mlxsw_sp1_ptp_unmatched *unmatched; - struct mlxsw_sp1_ptp_unmatched *conflict; + int err; unmatched = kzalloc(sizeof(*unmatched), GFP_ATOMIC); if (!unmatched) - return ERR_PTR(-ENOMEM); + return -ENOMEM; unmatched->key = key; unmatched->skb = skb; unmatched->timestamp = timestamp; unmatched->gc_cycle = mlxsw_sp->ptp_state->gc_cycle + cycles; - conflict = rhashtable_lookup_get_insert_fast(&ptp_state->unmatched_ht, - &unmatched->ht_node, - mlxsw_sp1_ptp_unmatched_ht_params); - if (conflict) + err = rhltable_insert(&ptp_state->unmatched_ht, &unmatched->ht_node, + mlxsw_sp1_ptp_unmatched_ht_params); + if (err) kfree(unmatched); - return conflict; + return err; } static struct mlxsw_sp1_ptp_unmatched * mlxsw_sp1_ptp_unmatched_lookup(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp1_ptp_key key) + struct mlxsw_sp1_ptp_key key, int *p_length) { - return rhashtable_lookup(&mlxsw_sp->ptp_state->unmatched_ht, &key, - mlxsw_sp1_ptp_unmatched_ht_params); + struct mlxsw_sp1_ptp_unmatched *unmatched, *last = NULL; + struct rhlist_head *tmp, *list; + int length = 0; + + list = rhltable_lookup(&mlxsw_sp->ptp_state->unmatched_ht, &key, + mlxsw_sp1_ptp_unmatched_ht_params); + rhl_for_each_entry_rcu(unmatched, tmp, list, ht_node) { + last = unmatched; + length++; + } + + *p_length = length; + return last; } static int mlxsw_sp1_ptp_unmatched_remove(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp1_ptp_unmatched *unmatched) { - return rhashtable_remove_fast(&mlxsw_sp->ptp_state->unmatched_ht, - &unmatched->ht_node, - mlxsw_sp1_ptp_unmatched_ht_params); + return rhltable_remove(&mlxsw_sp->ptp_state->unmatched_ht, + &unmatched->ht_node, + mlxsw_sp1_ptp_unmatched_ht_params); } /* This function is called in the following scenarios: @@ -489,75 +499,38 @@ static void mlxsw_sp1_ptp_got_piece(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp1_ptp_key key, struct sk_buff *skb, u64 timestamp) { - struct mlxsw_sp1_ptp_unmatched *unmatched, *conflict; + struct mlxsw_sp1_ptp_unmatched *unmatched; + int length; int err; rcu_read_lock(); - unmatched = mlxsw_sp1_ptp_unmatched_lookup(mlxsw_sp, key); - spin_lock(&mlxsw_sp->ptp_state->unmatched_lock); - if (unmatched) { - /* There was an unmatched entry when we looked, but it may have - * been removed before we took the lock. - */ - err = mlxsw_sp1_ptp_unmatched_remove(mlxsw_sp, unmatched); - if (err) - unmatched = NULL; - } - - if (!unmatched) { - /* We have no unmatched entry, but one may have been added after - * we looked, but before we took the lock. - */ - unmatched = mlxsw_sp1_ptp_unmatched_save(mlxsw_sp, key, - skb, timestamp); - if (IS_ERR(unmatched)) { - if (skb) - mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, - key.local_port, - key.ingress, NULL); - unmatched = NULL; - } else if (unmatched) { - /* Save just told us, under lock, that the entry is - * there, so this has to work. - */ - err = mlxsw_sp1_ptp_unmatched_remove(mlxsw_sp, - unmatched); - WARN_ON_ONCE(err); - } - } - - /* If unmatched is non-NULL here, it comes either from the lookup, or - * from the save attempt above. In either case the entry was removed - * from the hash table. If unmatched is NULL, a new unmatched entry was - * added to the hash table, and there was no conflict. - */ - + unmatched = mlxsw_sp1_ptp_unmatched_lookup(mlxsw_sp, key, &length); if (skb && unmatched && unmatched->timestamp) { unmatched->skb = skb; } else if (timestamp && unmatched && unmatched->skb) { unmatched->timestamp = timestamp; - } else if (unmatched) { - /* unmatched holds an older entry of the same type: either an - * skb if we are handling skb, or a timestamp if we are handling - * timestamp. We can't match that up, so save what we have. + } else { + /* Either there is no entry to match, or one that is there is + * incompatible. */ - conflict = mlxsw_sp1_ptp_unmatched_save(mlxsw_sp, key, - skb, timestamp); - if (IS_ERR(conflict)) { - if (skb) - mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, - key.local_port, - key.ingress, NULL); - } else { - /* Above, we removed an object with this key from the - * hash table, under lock, so conflict can not be a - * valid pointer. - */ - WARN_ON_ONCE(conflict); - } + if (length < 100) + err = mlxsw_sp1_ptp_unmatched_save(mlxsw_sp, key, + skb, timestamp); + else + err = -E2BIG; + if (err && skb) + mlxsw_sp1_ptp_packet_finish(mlxsw_sp, skb, + key.local_port, + key.ingress, NULL); + unmatched = NULL; + } + + if (unmatched) { + err = mlxsw_sp1_ptp_unmatched_remove(mlxsw_sp, unmatched); + WARN_ON_ONCE(err); } spin_unlock(&mlxsw_sp->ptp_state->unmatched_lock); @@ -669,9 +642,8 @@ mlxsw_sp1_ptp_ht_gc_collect(struct mlxsw_sp_ptp_state *ptp_state, local_bh_disable(); spin_lock(&ptp_state->unmatched_lock); - err = rhashtable_remove_fast(&ptp_state->unmatched_ht, - &unmatched->ht_node, - mlxsw_sp1_ptp_unmatched_ht_params); + err = rhltable_remove(&ptp_state->unmatched_ht, &unmatched->ht_node, + mlxsw_sp1_ptp_unmatched_ht_params); spin_unlock(&ptp_state->unmatched_lock); if (err) @@ -702,7 +674,7 @@ static void mlxsw_sp1_ptp_ht_gc(struct work_struct *work) ptp_state = container_of(dwork, struct mlxsw_sp_ptp_state, ht_gc_dw); gc_cycle = ptp_state->gc_cycle++; - rhashtable_walk_enter(&ptp_state->unmatched_ht, &iter); + rhltable_walk_enter(&ptp_state->unmatched_ht, &iter); rhashtable_walk_start(&iter); while ((obj = rhashtable_walk_next(&iter))) { if (IS_ERR(obj)) @@ -855,8 +827,8 @@ struct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp) spin_lock_init(&ptp_state->unmatched_lock); - err = rhashtable_init(&ptp_state->unmatched_ht, - &mlxsw_sp1_ptp_unmatched_ht_params); + err = rhltable_init(&ptp_state->unmatched_ht, + &mlxsw_sp1_ptp_unmatched_ht_params); if (err) goto err_hashtable_init; @@ -891,7 +863,7 @@ err_fifo_clr: err_mtptpt1_set: mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0); err_mtptpt_set: - rhashtable_destroy(&ptp_state->unmatched_ht); + rhltable_destroy(&ptp_state->unmatched_ht); err_hashtable_init: kfree(ptp_state); return ERR_PTR(err); @@ -906,8 +878,8 @@ void mlxsw_sp1_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state) mlxsw_sp1_ptp_set_fifo_clr_on_trap(mlxsw_sp, false); mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP1, 0); mlxsw_sp_ptp_mtptpt_set(mlxsw_sp, MLXSW_REG_MTPTPT_TRAP_ID_PTP0, 0); - rhashtable_free_and_destroy(&ptp_state->unmatched_ht, - &mlxsw_sp1_ptp_unmatched_free_fn, NULL); + rhltable_free_and_destroy(&ptp_state->unmatched_ht, + &mlxsw_sp1_ptp_unmatched_free_fn, NULL); kfree(ptp_state); } @@ -979,6 +951,9 @@ static int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port, { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_port *tmp; + u16 orig_ing_types = 0; + u16 orig_egr_types = 0; + int err; int i; /* MTPPPC configures timestamping globally, not per port. Find the @@ -986,12 +961,26 @@ static int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port, */ for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) { tmp = mlxsw_sp->ports[i]; + if (tmp) { + orig_ing_types |= tmp->ptp.ing_types; + orig_egr_types |= tmp->ptp.egr_types; + } if (tmp && tmp != mlxsw_sp_port) { ing_types |= tmp->ptp.ing_types; egr_types |= tmp->ptp.egr_types; } } + if ((ing_types || egr_types) && !(orig_ing_types || orig_egr_types)) { + err = mlxsw_sp_nve_inc_parsing_depth_get(mlxsw_sp); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Failed to increase parsing depth"); + return err; + } + } + if (!(ing_types || egr_types) && (orig_ing_types || orig_egr_types)) + mlxsw_sp_nve_inc_parsing_depth_put(mlxsw_sp); + return mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp_port->mlxsw_sp, ing_types, egr_types); } diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index b71e4ecbe469..6932e615d4b0 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -1818,6 +1818,7 @@ EXPORT_SYMBOL(ocelot_init); void ocelot_deinit(struct ocelot *ocelot) { + cancel_delayed_work(&ocelot->stats_work); destroy_workqueue(ocelot->stats_queue); mutex_destroy(&ocelot->stats_lock); ocelot_ace_deinit(); diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_ace.c index 39aca1ab4687..86fc6e6b46dd 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.c +++ b/drivers/net/ethernet/mscc/ocelot_ace.c @@ -317,7 +317,7 @@ static void is2_action_set(struct vcap_data *data, break; case OCELOT_ACL_ACTION_TRAP: VCAP_ACT_SET(PORT_MASK, 0x0); - VCAP_ACT_SET(MASK_MODE, 0x0); + VCAP_ACT_SET(MASK_MODE, 0x1); VCAP_ACT_SET(POLICE_ENA, 0x0); VCAP_ACT_SET(POLICE_IDX, 0x0); VCAP_ACT_SET(CPU_QU_NUM, 0x0); diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index 59487d446a09..b894bc0c9c16 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -13,12 +13,6 @@ struct ocelot_port_block { struct ocelot_port *port; }; -static u16 get_prio(u32 prio) -{ - /* prio starts from 0x1000 while the ids starts from 0 */ - return prio >> 16; -} - static int ocelot_flower_parse_action(struct flow_cls_offload *f, struct ocelot_ace_rule *rule) { @@ -168,7 +162,7 @@ static int ocelot_flower_parse(struct flow_cls_offload *f, } finished_key_parsing: - ocelot_rule->prio = get_prio(f->common.prio); + ocelot_rule->prio = f->common.prio; ocelot_rule->id = f->cookie; return ocelot_flower_parse_action(f, ocelot_rule); } @@ -218,7 +212,7 @@ static int ocelot_flower_destroy(struct flow_cls_offload *f, struct ocelot_ace_rule rule; int ret; - rule.prio = get_prio(f->common.prio); + rule.prio = f->common.prio; rule.port = port_block->port; rule.id = f->cookie; @@ -236,7 +230,7 @@ static int ocelot_flower_stats_update(struct flow_cls_offload *f, struct ocelot_ace_rule rule; int ret; - rule.prio = get_prio(f->common.prio); + rule.prio = f->common.prio; rule.port = port_block->port; rule.id = f->cookie; ret = ocelot_ace_rule_stats_update(&rule); diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index d8b7fba96d58..337b0cbfd153 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -3919,7 +3919,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * setup (if available). */ status = myri10ge_request_irq(mgp); if (status != 0) - goto abort_with_firmware; + goto abort_with_slices; myri10ge_free_irq(mgp); /* Save configuration space to be restored if the diff --git a/drivers/net/ethernet/natsemi/sonic.c b/drivers/net/ethernet/natsemi/sonic.c index d0a01e8f000a..b339125b2f09 100644 --- a/drivers/net/ethernet/natsemi/sonic.c +++ b/drivers/net/ethernet/natsemi/sonic.c @@ -232,9 +232,9 @@ static int sonic_send_packet(struct sk_buff *skb, struct net_device *dev) laddr = dma_map_single(lp->device, skb->data, length, DMA_TO_DEVICE); if (!laddr) { - printk(KERN_ERR "%s: failed to map tx DMA buffer.\n", dev->name); - dev_kfree_skb(skb); - return NETDEV_TX_BUSY; + pr_err_ratelimited("%s: failed to map tx DMA buffer.\n", dev->name); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; } sonic_tda_put(dev, entry, SONIC_TD_STATUS, 0); /* clear status */ diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index 4054b70d7719..5afcb3c4c2ef 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -1163,7 +1163,7 @@ mem_op_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, bool clr_gpr, lmem_step step) { s32 off = nfp_prog->stack_frame_depth + meta->insn.off + ptr_off; - bool first = true, last; + bool first = true, narrow_ld, last; bool needs_inc = false; swreg stack_off_reg; u8 prev_gpr = 255; @@ -1209,13 +1209,22 @@ mem_op_stack(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, needs_inc = true; } + + narrow_ld = clr_gpr && size < 8; + if (lm3) { + unsigned int nop_cnt; + emit_csr_wr(nfp_prog, imm_b(nfp_prog), NFP_CSR_ACT_LM_ADDR3); - /* For size < 4 one slot will be filled by zeroing of upper. */ - wrp_nops(nfp_prog, clr_gpr && size < 8 ? 2 : 3); + /* For size < 4 one slot will be filled by zeroing of upper, + * but be careful, that zeroing could be eliminated by zext + * optimization. + */ + nop_cnt = narrow_ld && meta->flags & FLAG_INSN_DO_ZEXT ? 2 : 3; + wrp_nops(nfp_prog, nop_cnt); } - if (clr_gpr && size < 8) + if (narrow_ld) wrp_zext(nfp_prog, meta, gpr); while (size) { diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index d5bbe3d6048b..05981b54eaab 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -260,9 +260,6 @@ nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb) type = cmsg_hdr->type; switch (type) { - case NFP_FLOWER_CMSG_TYPE_PORT_REIFY: - nfp_flower_cmsg_portreify_rx(app, skb); - break; case NFP_FLOWER_CMSG_TYPE_PORT_MOD: nfp_flower_cmsg_portmod_rx(app, skb); break; @@ -328,8 +325,7 @@ nfp_flower_queue_ctl_msg(struct nfp_app *app, struct sk_buff *skb, int type) struct nfp_flower_priv *priv = app->priv; struct sk_buff_head *skb_head; - if (type == NFP_FLOWER_CMSG_TYPE_PORT_REIFY || - type == NFP_FLOWER_CMSG_TYPE_PORT_MOD) + if (type == NFP_FLOWER_CMSG_TYPE_PORT_MOD) skb_head = &priv->cmsg_skbs_high; else skb_head = &priv->cmsg_skbs_low; @@ -368,6 +364,10 @@ void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) } else if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH) { /* Acks from the NFP that the route is added - ignore. */ dev_consume_skb_any(skb); + } else if (cmsg_hdr->type == NFP_FLOWER_CMSG_TYPE_PORT_REIFY) { + /* Handle REIFY acks outside wq to prevent RTNL conflict. */ + nfp_flower_cmsg_portreify_rx(app, skb); + dev_consume_skb_any(skb); } else { nfp_flower_queue_ctl_msg(app, skb, cmsg_hdr->type); } diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index e209f150c5f2..457bdc60f3ee 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -1409,13 +1409,21 @@ nfp_flower_setup_indr_tc_block(struct net_device *netdev, struct nfp_app *app, struct nfp_flower_priv *priv = app->priv; struct flow_block_cb *block_cb; - if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && - !(f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS && - nfp_flower_internal_port_can_offload(app, netdev))) + if ((f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS && + !nfp_flower_internal_port_can_offload(app, netdev)) || + (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS && + nfp_flower_internal_port_can_offload(app, netdev))) return -EOPNOTSUPP; switch (f->command) { case FLOW_BLOCK_BIND: + cb_priv = nfp_flower_indr_block_cb_priv_lookup(app, netdev); + if (cb_priv && + flow_block_cb_is_busy(nfp_flower_setup_indr_block_cb, + cb_priv, + &nfp_block_cb_list)) + return -EBUSY; + cb_priv = kmalloc(sizeof(*cb_priv), GFP_KERNEL); if (!cb_priv) return -ENOMEM; diff --git a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c index 86e968cd5ffd..124a43dc136a 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c @@ -93,7 +93,7 @@ nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev, return -EOPNOTSUPP; } - if (flow->common.prio != (1 << 16)) { + if (flow->common.prio != 1) { NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload requires highest priority"); return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index a7a80f4b722a..f0ee982eb1b5 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -328,13 +328,13 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, flow.daddr = *(__be32 *)n->primary_key; - /* Only concerned with route changes for representors. */ - if (!nfp_netdev_is_nfp_repr(n->dev)) - return NOTIFY_DONE; - app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb); app = app_priv->app; + if (!nfp_netdev_is_nfp_repr(n->dev) && + !nfp_flower_internal_port_can_offload(app, n->dev)) + return NOTIFY_DONE; + /* Only concerned with changes to routes already added to NFP. */ if (!nfp_tun_has_route(app, flow.daddr)) return NOTIFY_DONE; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index d9cbe84ac6ad..1b840ee47339 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -444,12 +444,12 @@ static u8 *nfp_vnic_get_sw_stats_strings(struct net_device *netdev, u8 *data) data = nfp_pr_et(data, "hw_rx_csum_complete"); data = nfp_pr_et(data, "hw_rx_csum_err"); data = nfp_pr_et(data, "rx_replace_buf_alloc_fail"); - data = nfp_pr_et(data, "rx_tls_decrypted"); + data = nfp_pr_et(data, "rx_tls_decrypted_packets"); data = nfp_pr_et(data, "hw_tx_csum"); data = nfp_pr_et(data, "hw_tx_inner_csum"); data = nfp_pr_et(data, "tx_gather"); data = nfp_pr_et(data, "tx_lso"); - data = nfp_pr_et(data, "tx_tls_encrypted"); + data = nfp_pr_et(data, "tx_tls_encrypted_packets"); data = nfp_pr_et(data, "tx_tls_ooo"); data = nfp_pr_et(data, "tx_tls_drop_no_sync_data"); diff --git a/drivers/net/ethernet/ni/Kconfig b/drivers/net/ethernet/ni/Kconfig index 70b1a03c0953..01229190132d 100644 --- a/drivers/net/ethernet/ni/Kconfig +++ b/drivers/net/ethernet/ni/Kconfig @@ -11,7 +11,7 @@ config NET_VENDOR_NI Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all - the questions about National Instrument devices. + the questions about National Instruments devices. If you say Y, you will be asked for your specific device in the following questions. diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index b327b29f5d57..a6b4bfae4684 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -713,6 +713,21 @@ struct nv_skb_map { struct nv_skb_map *next_tx_ctx; }; +struct nv_txrx_stats { + u64 stat_rx_packets; + u64 stat_rx_bytes; /* not always available in HW */ + u64 stat_rx_missed_errors; + u64 stat_rx_dropped; + u64 stat_tx_packets; /* not always available in HW */ + u64 stat_tx_bytes; + u64 stat_tx_dropped; +}; + +#define nv_txrx_stats_inc(member) \ + __this_cpu_inc(np->txrx_stats->member) +#define nv_txrx_stats_add(member, count) \ + __this_cpu_add(np->txrx_stats->member, (count)) + /* * SMP locking: * All hardware access under netdev_priv(dev)->lock, except the performance @@ -797,10 +812,7 @@ struct fe_priv { /* RX software stats */ struct u64_stats_sync swstats_rx_syncp; - u64 stat_rx_packets; - u64 stat_rx_bytes; /* not always available in HW */ - u64 stat_rx_missed_errors; - u64 stat_rx_dropped; + struct nv_txrx_stats __percpu *txrx_stats; /* media detection workaround. * Locking: Within irq hander or disable_irq+spin_lock(&np->lock); @@ -826,9 +838,6 @@ struct fe_priv { /* TX software stats */ struct u64_stats_sync swstats_tx_syncp; - u64 stat_tx_packets; /* not always available in HW */ - u64 stat_tx_bytes; - u64 stat_tx_dropped; /* msi/msi-x fields */ u32 msi_flags; @@ -1721,6 +1730,39 @@ static void nv_update_stats(struct net_device *dev) } } +static void nv_get_stats(int cpu, struct fe_priv *np, + struct rtnl_link_stats64 *storage) +{ + struct nv_txrx_stats *src = per_cpu_ptr(np->txrx_stats, cpu); + unsigned int syncp_start; + u64 rx_packets, rx_bytes, rx_dropped, rx_missed_errors; + u64 tx_packets, tx_bytes, tx_dropped; + + do { + syncp_start = u64_stats_fetch_begin_irq(&np->swstats_rx_syncp); + rx_packets = src->stat_rx_packets; + rx_bytes = src->stat_rx_bytes; + rx_dropped = src->stat_rx_dropped; + rx_missed_errors = src->stat_rx_missed_errors; + } while (u64_stats_fetch_retry_irq(&np->swstats_rx_syncp, syncp_start)); + + storage->rx_packets += rx_packets; + storage->rx_bytes += rx_bytes; + storage->rx_dropped += rx_dropped; + storage->rx_missed_errors += rx_missed_errors; + + do { + syncp_start = u64_stats_fetch_begin_irq(&np->swstats_tx_syncp); + tx_packets = src->stat_tx_packets; + tx_bytes = src->stat_tx_bytes; + tx_dropped = src->stat_tx_dropped; + } while (u64_stats_fetch_retry_irq(&np->swstats_tx_syncp, syncp_start)); + + storage->tx_packets += tx_packets; + storage->tx_bytes += tx_bytes; + storage->tx_dropped += tx_dropped; +} + /* * nv_get_stats64: dev->ndo_get_stats64 function * Get latest stats value from the nic. @@ -1733,7 +1775,7 @@ nv_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *storage) __releases(&netdev_priv(dev)->hwstats_lock) { struct fe_priv *np = netdev_priv(dev); - unsigned int syncp_start; + int cpu; /* * Note: because HW stats are not always available and for @@ -1746,20 +1788,8 @@ nv_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *storage) */ /* software stats */ - do { - syncp_start = u64_stats_fetch_begin_irq(&np->swstats_rx_syncp); - storage->rx_packets = np->stat_rx_packets; - storage->rx_bytes = np->stat_rx_bytes; - storage->rx_dropped = np->stat_rx_dropped; - storage->rx_missed_errors = np->stat_rx_missed_errors; - } while (u64_stats_fetch_retry_irq(&np->swstats_rx_syncp, syncp_start)); - - do { - syncp_start = u64_stats_fetch_begin_irq(&np->swstats_tx_syncp); - storage->tx_packets = np->stat_tx_packets; - storage->tx_bytes = np->stat_tx_bytes; - storage->tx_dropped = np->stat_tx_dropped; - } while (u64_stats_fetch_retry_irq(&np->swstats_tx_syncp, syncp_start)); + for_each_online_cpu(cpu) + nv_get_stats(cpu, np, storage); /* If the nic supports hw counters then retrieve latest values */ if (np->driver_data & DEV_HAS_STATISTICS_V123) { @@ -1827,7 +1857,7 @@ static int nv_alloc_rx(struct net_device *dev) } else { packet_dropped: u64_stats_update_begin(&np->swstats_rx_syncp); - np->stat_rx_dropped++; + nv_txrx_stats_inc(stat_rx_dropped); u64_stats_update_end(&np->swstats_rx_syncp); return 1; } @@ -1869,7 +1899,7 @@ static int nv_alloc_rx_optimized(struct net_device *dev) } else { packet_dropped: u64_stats_update_begin(&np->swstats_rx_syncp); - np->stat_rx_dropped++; + nv_txrx_stats_inc(stat_rx_dropped); u64_stats_update_end(&np->swstats_rx_syncp); return 1; } @@ -2013,7 +2043,7 @@ static void nv_drain_tx(struct net_device *dev) } if (nv_release_txskb(np, &np->tx_skb[i])) { u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_dropped++; + nv_txrx_stats_inc(stat_tx_dropped); u64_stats_update_end(&np->swstats_tx_syncp); } np->tx_skb[i].dma = 0; @@ -2227,7 +2257,7 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev) /* on DMA mapping error - drop the packet */ dev_kfree_skb_any(skb); u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_dropped++; + nv_txrx_stats_inc(stat_tx_dropped); u64_stats_update_end(&np->swstats_tx_syncp); return NETDEV_TX_OK; } @@ -2273,7 +2303,7 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb_any(skb); np->put_tx_ctx = start_tx_ctx; u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_dropped++; + nv_txrx_stats_inc(stat_tx_dropped); u64_stats_update_end(&np->swstats_tx_syncp); return NETDEV_TX_OK; } @@ -2384,7 +2414,7 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb, /* on DMA mapping error - drop the packet */ dev_kfree_skb_any(skb); u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_dropped++; + nv_txrx_stats_inc(stat_tx_dropped); u64_stats_update_end(&np->swstats_tx_syncp); return NETDEV_TX_OK; } @@ -2431,7 +2461,7 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb, dev_kfree_skb_any(skb); np->put_tx_ctx = start_tx_ctx; u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_dropped++; + nv_txrx_stats_inc(stat_tx_dropped); u64_stats_update_end(&np->swstats_tx_syncp); return NETDEV_TX_OK; } @@ -2560,9 +2590,12 @@ static int nv_tx_done(struct net_device *dev, int limit) && !(flags & NV_TX_RETRYCOUNT_MASK)) nv_legacybackoff_reseed(dev); } else { + unsigned int len; + u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_packets++; - np->stat_tx_bytes += np->get_tx_ctx->skb->len; + nv_txrx_stats_inc(stat_tx_packets); + len = np->get_tx_ctx->skb->len; + nv_txrx_stats_add(stat_tx_bytes, len); u64_stats_update_end(&np->swstats_tx_syncp); } bytes_compl += np->get_tx_ctx->skb->len; @@ -2577,9 +2610,12 @@ static int nv_tx_done(struct net_device *dev, int limit) && !(flags & NV_TX2_RETRYCOUNT_MASK)) nv_legacybackoff_reseed(dev); } else { + unsigned int len; + u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_packets++; - np->stat_tx_bytes += np->get_tx_ctx->skb->len; + nv_txrx_stats_inc(stat_tx_packets); + len = np->get_tx_ctx->skb->len; + nv_txrx_stats_add(stat_tx_bytes, len); u64_stats_update_end(&np->swstats_tx_syncp); } bytes_compl += np->get_tx_ctx->skb->len; @@ -2627,9 +2663,12 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit) nv_legacybackoff_reseed(dev); } } else { + unsigned int len; + u64_stats_update_begin(&np->swstats_tx_syncp); - np->stat_tx_packets++; - np->stat_tx_bytes += np->get_tx_ctx->skb->len; + nv_txrx_stats_inc(stat_tx_packets); + len = np->get_tx_ctx->skb->len; + nv_txrx_stats_add(stat_tx_bytes, len); u64_stats_update_end(&np->swstats_tx_syncp); } @@ -2806,6 +2845,15 @@ static int nv_getlen(struct net_device *dev, void *packet, int datalen) } } +static void rx_missing_handler(u32 flags, struct fe_priv *np) +{ + if (flags & NV_RX_MISSEDFRAME) { + u64_stats_update_begin(&np->swstats_rx_syncp); + nv_txrx_stats_inc(stat_rx_missed_errors); + u64_stats_update_end(&np->swstats_rx_syncp); + } +} + static int nv_rx_process(struct net_device *dev, int limit) { struct fe_priv *np = netdev_priv(dev); @@ -2848,11 +2896,7 @@ static int nv_rx_process(struct net_device *dev, int limit) } /* the rest are hard errors */ else { - if (flags & NV_RX_MISSEDFRAME) { - u64_stats_update_begin(&np->swstats_rx_syncp); - np->stat_rx_missed_errors++; - u64_stats_update_end(&np->swstats_rx_syncp); - } + rx_missing_handler(flags, np); dev_kfree_skb(skb); goto next_pkt; } @@ -2896,8 +2940,8 @@ static int nv_rx_process(struct net_device *dev, int limit) skb->protocol = eth_type_trans(skb, dev); napi_gro_receive(&np->napi, skb); u64_stats_update_begin(&np->swstats_rx_syncp); - np->stat_rx_packets++; - np->stat_rx_bytes += len; + nv_txrx_stats_inc(stat_rx_packets); + nv_txrx_stats_add(stat_rx_bytes, len); u64_stats_update_end(&np->swstats_rx_syncp); next_pkt: if (unlikely(np->get_rx.orig++ == np->last_rx.orig)) @@ -2982,8 +3026,8 @@ static int nv_rx_process_optimized(struct net_device *dev, int limit) } napi_gro_receive(&np->napi, skb); u64_stats_update_begin(&np->swstats_rx_syncp); - np->stat_rx_packets++; - np->stat_rx_bytes += len; + nv_txrx_stats_inc(stat_rx_packets); + nv_txrx_stats_add(stat_rx_bytes, len); u64_stats_update_end(&np->swstats_rx_syncp); } else { dev_kfree_skb(skb); @@ -5651,6 +5695,12 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) SET_NETDEV_DEV(dev, &pci_dev->dev); u64_stats_init(&np->swstats_rx_syncp); u64_stats_init(&np->swstats_tx_syncp); + np->txrx_stats = alloc_percpu(struct nv_txrx_stats); + if (!np->txrx_stats) { + pr_err("np->txrx_stats, alloc memory error.\n"); + err = -ENOMEM; + goto out_alloc_percpu; + } timer_setup(&np->oom_kick, nv_do_rx_refill, 0); timer_setup(&np->nic_poll, nv_do_nic_poll, 0); @@ -6060,6 +6110,8 @@ out_relreg: out_disable: pci_disable_device(pci_dev); out_free: + free_percpu(np->txrx_stats); +out_alloc_percpu: free_netdev(dev); out: return err; @@ -6105,6 +6157,9 @@ static void nv_restore_mac_addr(struct pci_dev *pci_dev) static void nv_remove(struct pci_dev *pci_dev) { struct net_device *dev = pci_get_drvdata(pci_dev); + struct fe_priv *np = netdev_priv(dev); + + free_percpu(np->txrx_stats); unregister_netdev(dev); diff --git a/drivers/net/ethernet/packetengines/Kconfig b/drivers/net/ethernet/packetengines/Kconfig index 8161e308e64b..ead3750b4489 100644 --- a/drivers/net/ethernet/packetengines/Kconfig +++ b/drivers/net/ethernet/packetengines/Kconfig @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only # -# Packet engine device configuration +# Packet Engines device configuration # config NET_VENDOR_PACKET_ENGINES - bool "Packet Engine devices" + bool "Packet Engines devices" default y depends on PCI ---help--- @@ -12,7 +12,7 @@ config NET_VENDOR_PACKET_ENGINES Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all - the questions about packet engine devices. If you say Y, you will + the questions about Packet Engines devices. If you say Y, you will be asked for your specific card in the following questions. if NET_VENDOR_PACKET_ENGINES diff --git a/drivers/net/ethernet/packetengines/Makefile b/drivers/net/ethernet/packetengines/Makefile index 1553c9cfc254..cf054b796d11 100644 --- a/drivers/net/ethernet/packetengines/Makefile +++ b/drivers/net/ethernet/packetengines/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only # -# Makefile for the Packet Engine network device drivers. +# Makefile for the Packet Engines network device drivers. # obj-$(CONFIG_HAMACHI) += hamachi.o diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c index 4e8118a08654..9f5113639eaf 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.c +++ b/drivers/net/ethernet/qlogic/qed/qed_int.c @@ -1093,7 +1093,7 @@ static int qed_int_deassertion(struct qed_hwfn *p_hwfn, snprintf(bit_name, 30, p_aeu->bit_name, num); else - strncpy(bit_name, + strlcpy(bit_name, p_aeu->bit_name, 30); /* We now need to pass bitmask in its diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 829dd60ab937..1efff7f68ef6 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1325,7 +1325,7 @@ static int qed_slowpath_start(struct qed_dev *cdev, &drv_version); if (rc) { DP_NOTICE(cdev, "Failed sending drv version command\n"); - return rc; + goto err4; } } @@ -1333,6 +1333,8 @@ static int qed_slowpath_start(struct qed_dev *cdev, return 0; +err4: + qed_ll2_dealloc_if(cdev); err3: qed_hw_stop(cdev); err2: diff --git a/drivers/net/ethernet/qlogic/qed/qed_rdma.c b/drivers/net/ethernet/qlogic/qed/qed_rdma.c index 17c64e43d6c3..158ac0738911 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_rdma.c +++ b/drivers/net/ethernet/qlogic/qed/qed_rdma.c @@ -442,7 +442,7 @@ static void qed_rdma_init_devinfo(struct qed_hwfn *p_hwfn, /* Vendor specific information */ dev->vendor_id = cdev->vendor_id; dev->vendor_part_id = cdev->device_id; - dev->hw_ver = 0; + dev->hw_ver = cdev->chip_rev; dev->fw_ver = (FW_MAJOR_VERSION << 24) | (FW_MINOR_VERSION << 16) | (FW_REVISION_VERSION << 8) | (FW_ENGINEERING_VERSION); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 60189923737a..21d38167f961 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -206,9 +206,9 @@ rmnet_map_ipv4_ul_csum_header(void *iphdr, ul_header->csum_insert_offset = skb->csum_offset; ul_header->csum_enabled = 1; if (ip4h->protocol == IPPROTO_UDP) - ul_header->udp_ip4_ind = 1; + ul_header->udp_ind = 1; else - ul_header->udp_ip4_ind = 0; + ul_header->udp_ind = 0; /* Changing remaining fields to network order */ hdr++; @@ -239,6 +239,7 @@ rmnet_map_ipv6_ul_csum_header(void *ip6hdr, struct rmnet_map_ul_csum_header *ul_header, struct sk_buff *skb) { + struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr; __be16 *hdr = (__be16 *)ul_header, offset; offset = htons((__force u16)(skb_transport_header(skb) - @@ -246,7 +247,11 @@ rmnet_map_ipv6_ul_csum_header(void *ip6hdr, ul_header->csum_start_offset = offset; ul_header->csum_insert_offset = skb->csum_offset; ul_header->csum_enabled = 1; - ul_header->udp_ip4_ind = 0; + + if (ip6h->nexthdr == IPPROTO_UDP) + ul_header->udp_ind = 1; + else + ul_header->udp_ind = 0; /* Changing remaining fields to network order */ hdr++; @@ -419,7 +424,7 @@ sw_csum: ul_header->csum_start_offset = 0; ul_header->csum_insert_offset = 0; ul_header->csum_enabled = 0; - ul_header->udp_ip4_ind = 0; + ul_header->udp_ind = 0; priv->stats.csum_sw++; } diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 6272115b2848..bae0074ab9aa 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -5921,6 +5921,7 @@ static struct sk_buff *rtl8169_try_rx_copy(void *data, skb = napi_alloc_skb(&tp->napi, pkt_size); if (skb) skb_copy_to_linear_data(skb, data, pkt_size); + dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE); return skb; } @@ -6136,10 +6137,7 @@ static int r8169_phy_connect(struct rtl8169_private *tp) if (ret) return ret; - if (tp->supports_gmii) - phy_remove_link_mode(phydev, - ETHTOOL_LINK_MODE_1000baseT_Half_BIT); - else + if (!tp->supports_gmii) phy_set_max_speed(phydev, SPEED_100); phy_support_asym_pause(phydev); @@ -6589,13 +6587,18 @@ static int rtl_alloc_irq(struct rtl8169_private *tp) { unsigned int flags; - if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: rtl_unlock_config_regs(tp); RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~MSIEnable); rtl_lock_config_regs(tp); + /* fall through */ + case RTL_GIGA_MAC_VER_07 ... RTL_GIGA_MAC_VER_24: flags = PCI_IRQ_LEGACY; - } else { + break; + default: flags = PCI_IRQ_ALL_TYPES; + break; } return pci_alloc_irq_vectors(tp->pci_dev, 1, 1, flags); diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index ef8f08931fe8..6cacd5e893ac 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Renesas Ethernet AVB device driver * - * Copyright (C) 2014-2015 Renesas Electronics Corporation + * Copyright (C) 2014-2019 Renesas Electronics Corporation * Copyright (C) 2015 Renesas Solutions Corp. * Copyright (C) 2015-2016 Cogent Embedded, Inc. <source@cogentembedded.com> * @@ -513,7 +513,10 @@ static void ravb_get_tx_tstamp(struct net_device *ndev) kfree(ts_skb); if (tag == tfa_tag) { skb_tstamp_tx(skb, &shhwtstamps); + dev_consume_skb_any(skb); break; + } else { + dev_kfree_skb_any(skb); } } ravb_modify(ndev, TCCR, TCCR_TFR, TCCR_TFR); @@ -1564,7 +1567,7 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) } goto unmap; } - ts_skb->skb = skb; + ts_skb->skb = skb_get(skb); ts_skb->tag = priv->ts_skb_tag++; priv->ts_skb_tag &= 0x3ff; list_add_tail(&ts_skb->list, &priv->ts_skb_list); @@ -1693,6 +1696,7 @@ static int ravb_close(struct net_device *ndev) /* Clear the timestamp list */ list_for_each_entry_safe(ts_skb, ts_skb2, &priv->ts_skb_list, list) { list_del(&ts_skb->list); + kfree_skb(ts_skb->skb); kfree(ts_skb); } diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 079f459c73a5..2c5d3f5b84dd 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -2208,10 +2208,12 @@ static int rocker_router_fib_event(struct notifier_block *nb, if (fen_info->fi->fib_nh_is_v6) { NL_SET_ERR_MSG_MOD(info->extack, "IPv6 gateway with IPv4 route is not supported"); + kfree(fib_work); return notifier_from_errno(-EINVAL); } if (fen_info->fi->nh) { NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported"); + kfree(fib_work); return notifier_from_errno(-EINVAL); } } diff --git a/drivers/net/ethernet/samsung/Kconfig b/drivers/net/ethernet/samsung/Kconfig index 027938017579..e92a178a76df 100644 --- a/drivers/net/ethernet/samsung/Kconfig +++ b/drivers/net/ethernet/samsung/Kconfig @@ -11,7 +11,7 @@ config NET_VENDOR_SAMSUNG say Y. Note that the answer to this question does not directly affect - the kernel: saying N will just case the configurator to skip all + the kernel: saying N will just cause the configurator to skip all the questions about Samsung chipsets. If you say Y, you will be asked for your specific chipset/driver in the following questions. diff --git a/drivers/net/ethernet/seeq/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c index 7a5e6c5abb57..276c7cae7cee 100644 --- a/drivers/net/ethernet/seeq/sgiseeq.c +++ b/drivers/net/ethernet/seeq/sgiseeq.c @@ -794,15 +794,16 @@ static int sgiseeq_probe(struct platform_device *pdev) printk(KERN_ERR "Sgiseeq: Cannot register net device, " "aborting.\n"); err = -ENODEV; - goto err_out_free_page; + goto err_out_free_attrs; } printk(KERN_INFO "%s: %s %pM\n", dev->name, sgiseeqstr, dev->dev_addr); return 0; -err_out_free_page: - free_page((unsigned long) sp->srings); +err_out_free_attrs: + dma_free_attrs(&pdev->dev, sizeof(*sp->srings), sp->srings, + sp->srings_dma, DMA_ATTR_NON_CONSISTENT); err_out_free_dev: free_netdev(dev); diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index bd14803545de..8d88e4083456 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -712,6 +712,7 @@ static void smc911x_phy_detect(struct net_device *dev) /* Found an external PHY */ break; } + /* Else, fall through */ default: /* Internal media only */ SMC_GET_PHY_ID1(lp, 1, id1); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 4644b2aeeba1..e2e469c37a4d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -1194,10 +1194,8 @@ static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable) int ret; struct device *dev = &bsp_priv->pdev->dev; - if (!ldo) { - dev_err(dev, "no regulator found\n"); - return -1; - } + if (!ldo) + return 0; if (enable) { ret = regulator_enable(ldo); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 4083019c547a..f97a4096f8fc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -873,7 +873,12 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv) int ret; u32 reg, val; - regmap_field_read(gmac->regmap_field, &val); + ret = regmap_field_read(gmac->regmap_field, &val); + if (ret) { + dev_err(priv->device, "Fail to read from regmap field.\n"); + return ret; + } + reg = gmac->variant->default_syscon_value; if (reg != val) dev_warn(priv->device, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 01c2e2d83e76..fc9954e4a772 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -85,6 +85,8 @@ static void dwmac4_rx_queue_priority(struct mac_device_info *hw, u32 value; base_register = (queue < 4) ? GMAC_RXQ_CTRL2 : GMAC_RXQ_CTRL3; + if (queue >= 4) + queue -= 4; value = readl(ioaddr + base_register); @@ -102,6 +104,8 @@ static void dwmac4_tx_queue_priority(struct mac_device_info *hw, u32 value; base_register = (queue < 4) ? GMAC_TXQ_PRTY_MAP0 : GMAC_TXQ_PRTY_MAP1; + if (queue >= 4) + queue -= 4; value = readl(ioaddr + base_register); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h index 7f86dffb264d..3174b701aa90 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h @@ -44,11 +44,13 @@ #define XGMAC_CORE_INIT_RX 0 #define XGMAC_PACKET_FILTER 0x00000008 #define XGMAC_FILTER_RA BIT(31) +#define XGMAC_FILTER_HPF BIT(10) #define XGMAC_FILTER_PCF BIT(7) #define XGMAC_FILTER_PM BIT(4) #define XGMAC_FILTER_HMC BIT(2) #define XGMAC_FILTER_PR BIT(0) #define XGMAC_HASH_TABLE(x) (0x00000010 + (x) * 4) +#define XGMAC_MAX_HASH_TABLE 8 #define XGMAC_RXQ_CTRL0 0x000000a0 #define XGMAC_RXQEN(x) GENMASK((x) * 2 + 1, (x) * 2) #define XGMAC_RXQEN_SHIFT(x) ((x) * 2) @@ -99,11 +101,12 @@ #define XGMAC_MDIO_ADDR 0x00000200 #define XGMAC_MDIO_DATA 0x00000204 #define XGMAC_MDIO_C22P 0x00000220 -#define XGMAC_ADDR0_HIGH 0x00000300 +#define XGMAC_ADDRx_HIGH(x) (0x00000300 + (x) * 0x8) +#define XGMAC_ADDR_MAX 32 #define XGMAC_AE BIT(31) #define XGMAC_DCS GENMASK(19, 16) #define XGMAC_DCS_SHIFT 16 -#define XGMAC_ADDR0_LOW 0x00000304 +#define XGMAC_ADDRx_LOW(x) (0x00000304 + (x) * 0x8) #define XGMAC_ARP_ADDR 0x00000c10 #define XGMAC_TIMESTAMP_STATUS 0x00000d20 #define XGMAC_TXTSC BIT(15) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 0a32c96a7854..85c68b7ee8c6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -4,6 +4,8 @@ * stmmac XGMAC support. */ +#include <linux/bitrev.h> +#include <linux/crc32.h> #include "stmmac.h" #include "dwxgmac2.h" @@ -106,6 +108,8 @@ static void dwxgmac2_rx_queue_prio(struct mac_device_info *hw, u32 prio, u32 value, reg; reg = (queue < 4) ? XGMAC_RXQ_CTRL2 : XGMAC_RXQ_CTRL3; + if (queue >= 4) + queue -= 4; value = readl(ioaddr + reg); value &= ~XGMAC_PSRQ(queue); @@ -169,6 +173,8 @@ static void dwxgmac2_map_mtl_to_dma(struct mac_device_info *hw, u32 queue, u32 value, reg; reg = (queue < 4) ? XGMAC_MTL_RXQ_DMA_MAP0 : XGMAC_MTL_RXQ_DMA_MAP1; + if (queue >= 4) + queue -= 4; value = readl(ioaddr + reg); value &= ~XGMAC_QxMDMACH(queue); @@ -278,10 +284,10 @@ static void dwxgmac2_set_umac_addr(struct mac_device_info *hw, u32 value; value = (addr[5] << 8) | addr[4]; - writel(value | XGMAC_AE, ioaddr + XGMAC_ADDR0_HIGH); + writel(value | XGMAC_AE, ioaddr + XGMAC_ADDRx_HIGH(reg_n)); value = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; - writel(value, ioaddr + XGMAC_ADDR0_LOW); + writel(value, ioaddr + XGMAC_ADDRx_LOW(reg_n)); } static void dwxgmac2_get_umac_addr(struct mac_device_info *hw, @@ -291,8 +297,8 @@ static void dwxgmac2_get_umac_addr(struct mac_device_info *hw, u32 hi_addr, lo_addr; /* Read the MAC address from the hardware */ - hi_addr = readl(ioaddr + XGMAC_ADDR0_HIGH); - lo_addr = readl(ioaddr + XGMAC_ADDR0_LOW); + hi_addr = readl(ioaddr + XGMAC_ADDRx_HIGH(reg_n)); + lo_addr = readl(ioaddr + XGMAC_ADDRx_LOW(reg_n)); /* Extract the MAC address from the high and low words */ addr[0] = lo_addr & 0xff; @@ -303,19 +309,82 @@ static void dwxgmac2_get_umac_addr(struct mac_device_info *hw, addr[5] = (hi_addr >> 8) & 0xff; } +static void dwxgmac2_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits, + int mcbitslog2) +{ + int numhashregs, regs; + + switch (mcbitslog2) { + case 6: + numhashregs = 2; + break; + case 7: + numhashregs = 4; + break; + case 8: + numhashregs = 8; + break; + default: + return; + } + + for (regs = 0; regs < numhashregs; regs++) + writel(mcfilterbits[regs], ioaddr + XGMAC_HASH_TABLE(regs)); +} + static void dwxgmac2_set_filter(struct mac_device_info *hw, struct net_device *dev) { void __iomem *ioaddr = (void __iomem *)dev->base_addr; - u32 value = XGMAC_FILTER_RA; + u32 value = readl(ioaddr + XGMAC_PACKET_FILTER); + int mcbitslog2 = hw->mcast_bits_log2; + u32 mc_filter[8]; + int i; + + value &= ~(XGMAC_FILTER_PR | XGMAC_FILTER_HMC | XGMAC_FILTER_PM); + value |= XGMAC_FILTER_HPF; + + memset(mc_filter, 0, sizeof(mc_filter)); if (dev->flags & IFF_PROMISC) { - value |= XGMAC_FILTER_PR | XGMAC_FILTER_PCF; + value |= XGMAC_FILTER_PR; + value |= XGMAC_FILTER_PCF; } else if ((dev->flags & IFF_ALLMULTI) || - (netdev_mc_count(dev) > HASH_TABLE_SIZE)) { + (netdev_mc_count(dev) > hw->multicast_filter_bins)) { value |= XGMAC_FILTER_PM; - writel(~0x0, ioaddr + XGMAC_HASH_TABLE(0)); - writel(~0x0, ioaddr + XGMAC_HASH_TABLE(1)); + + for (i = 0; i < XGMAC_MAX_HASH_TABLE; i++) + writel(~0x0, ioaddr + XGMAC_HASH_TABLE(i)); + } else if (!netdev_mc_empty(dev)) { + struct netdev_hw_addr *ha; + + value |= XGMAC_FILTER_HMC; + + netdev_for_each_mc_addr(ha, dev) { + int nr = (bitrev32(~crc32_le(~0, ha->addr, 6)) >> + (32 - mcbitslog2)); + mc_filter[nr >> 5] |= (1 << (nr & 0x1F)); + } + } + + dwxgmac2_set_mchash(ioaddr, mc_filter, mcbitslog2); + + /* Handle multiple unicast addresses */ + if (netdev_uc_count(dev) > XGMAC_ADDR_MAX) { + value |= XGMAC_FILTER_PR; + } else { + struct netdev_hw_addr *ha; + int reg = 1; + + netdev_for_each_uc_addr(ha, dev) { + dwxgmac2_set_umac_addr(hw, ha->addr, reg); + reg++; + } + + for ( ; reg < XGMAC_ADDR_MAX; reg++) { + writel(0, ioaddr + XGMAC_ADDRx_HIGH(reg)); + writel(0, ioaddr + XGMAC_ADDRx_LOW(reg)); + } } writel(value, ioaddr + XGMAC_PACKET_FILTER); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c7c9e5f162e6..fd54c7c87485 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -814,20 +814,15 @@ static void stmmac_validate(struct phylink_config *config, phylink_set(mac_supported, 10baseT_Full); phylink_set(mac_supported, 100baseT_Half); phylink_set(mac_supported, 100baseT_Full); + phylink_set(mac_supported, 1000baseT_Half); + phylink_set(mac_supported, 1000baseT_Full); + phylink_set(mac_supported, 1000baseKX_Full); phylink_set(mac_supported, Autoneg); phylink_set(mac_supported, Pause); phylink_set(mac_supported, Asym_Pause); phylink_set_port_modes(mac_supported); - if (priv->plat->has_gmac || - priv->plat->has_gmac4 || - priv->plat->has_xgmac) { - phylink_set(mac_supported, 1000baseT_Half); - phylink_set(mac_supported, 1000baseT_Full); - phylink_set(mac_supported, 1000baseKX_Full); - } - /* Cut down 1G if asked to */ if ((max_speed > 0) && (max_speed < 1000)) { phylink_set(mask, 1000baseT_Full); @@ -1295,6 +1290,8 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) "(%s) dma_rx_phy=0x%08x\n", __func__, (u32)rx_q->dma_rx_phy); + stmmac_clear_rx_descriptors(priv, queue); + for (i = 0; i < DMA_RX_SIZE; i++) { struct dma_desc *p; @@ -1312,8 +1309,6 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) rx_q->cur_rx = 0; rx_q->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); - stmmac_clear_rx_descriptors(priv, queue); - /* Setup the chained descriptor addresses */ if (priv->mode == STMMAC_CHAIN_MODE) { if (priv->extend_desc) @@ -1555,9 +1550,8 @@ static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv) goto err_dma; } - rx_q->buf_pool = kmalloc_array(DMA_RX_SIZE, - sizeof(*rx_q->buf_pool), - GFP_KERNEL); + rx_q->buf_pool = kcalloc(DMA_RX_SIZE, sizeof(*rx_q->buf_pool), + GFP_KERNEL); if (!rx_q->buf_pool) goto err_dma; @@ -1608,15 +1602,15 @@ static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv) tx_q->queue_index = queue; tx_q->priv_data = priv; - tx_q->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, - sizeof(*tx_q->tx_skbuff_dma), - GFP_KERNEL); + tx_q->tx_skbuff_dma = kcalloc(DMA_TX_SIZE, + sizeof(*tx_q->tx_skbuff_dma), + GFP_KERNEL); if (!tx_q->tx_skbuff_dma) goto err_dma; - tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE, - sizeof(struct sk_buff *), - GFP_KERNEL); + tx_q->tx_skbuff = kcalloc(DMA_TX_SIZE, + sizeof(struct sk_buff *), + GFP_KERNEL); if (!tx_q->tx_skbuff) goto err_dma; @@ -3277,9 +3271,11 @@ static inline int stmmac_rx_threshold_count(struct stmmac_rx_queue *rx_q) static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) { struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - int dirty = stmmac_rx_dirty(priv, queue); + int len, dirty = stmmac_rx_dirty(priv, queue); unsigned int entry = rx_q->dirty_rx; + len = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; + while (dirty-- > 0) { struct stmmac_rx_buffer *buf = &rx_q->buf_pool[entry]; struct dma_desc *p; @@ -3297,6 +3293,13 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) } buf->addr = page_pool_get_dma_addr(buf->page); + + /* Sync whole allocation to device. This will invalidate old + * data. + */ + dma_sync_single_for_device(priv->device, buf->addr, len, + DMA_FROM_DEVICE); + stmmac_set_desc_addr(priv, p, buf->addr); stmmac_refill_desc3(priv, rx_q, p); @@ -3431,8 +3434,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) skb_copy_to_linear_data(skb, page_address(buf->page), frame_len); skb_put(skb, frame_len); - dma_sync_single_for_device(priv->device, buf->addr, - frame_len, DMA_FROM_DEVICE); if (netif_msg_pktdata(priv)) { netdev_dbg(priv->dev, "frame received (%dbytes)", @@ -4319,8 +4320,9 @@ int stmmac_dvr_probe(struct device *device, NAPI_POLL_WEIGHT); } if (queue < priv->plat->tx_queues_to_use) { - netif_napi_add(ndev, &ch->tx_napi, stmmac_napi_poll_tx, - NAPI_POLL_WEIGHT); + netif_tx_napi_add(ndev, &ch->tx_napi, + stmmac_napi_poll_tx, + NAPI_POLL_WEIGHT); } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 73fc2524372e..154daf4d1072 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -370,6 +370,13 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) return ERR_PTR(-ENOMEM); *mac = of_get_mac_address(np); + if (IS_ERR(*mac)) { + if (PTR_ERR(*mac) == -EPROBE_DEFER) + return ERR_CAST(*mac); + + *mac = NULL; + } + plat->interface = of_get_phy_mode(np); /* Some wrapper drivers still rely on phy_node. Let's save it while diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 58ea18af9813..6c305b6ecad0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -37,7 +37,7 @@ static struct stmmac_tc_entry *tc_find_entry(struct stmmac_priv *priv, entry = &priv->tc_entries[i]; if (!entry->in_use && !first && free) first = entry; - if (entry->handle == loc && !free) + if ((entry->handle == loc) && !free && !entry->is_frag) dup = entry; } @@ -94,7 +94,7 @@ static int tc_fill_entry(struct stmmac_priv *priv, struct stmmac_tc_entry *entry, *frag = NULL; struct tc_u32_sel *sel = cls->knode.sel; u32 off, data, mask, real_off, rem; - u32 prio = cls->common.prio; + u32 prio = cls->common.prio << 16; int ret; /* Only 1 match per entry */ diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 32a89744972d..a46b8b2e44e1 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2775,6 +2775,7 @@ static int cpsw_probe(struct platform_device *pdev) if (!cpsw) return -ENOMEM; + platform_set_drvdata(pdev, cpsw); cpsw->dev = dev; mode = devm_gpiod_get_array_optional(dev, "mode", GPIOD_OUT_LOW); @@ -2879,7 +2880,6 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_cpts; } - platform_set_drvdata(pdev, cpsw); priv = netdev_priv(ndev); priv->cpsw = cpsw; priv->ndev = ndev; diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c index 5b196ebfed49..0f346761a2b2 100644 --- a/drivers/net/ethernet/toshiba/spider_net.c +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -788,6 +788,7 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal) /* fallthrough, if we release the descriptors * brutally (then we don't care about * SPIDER_NET_DESCR_CARDOWNED) */ + /* Fall through */ case SPIDER_NET_DESCR_RESPONSE_ERROR: case SPIDER_NET_DESCR_PROTECTION_ERROR: diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 8479a440527b..12466a72cefc 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -1504,7 +1504,7 @@ tc35815_rx(struct net_device *dev, int limit) pci_unmap_single(lp->pci_dev, lp->rx_skbs[cur_bd].skb_dma, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - if (!HAVE_DMA_RXALIGN(lp) && NET_IP_ALIGN) + if (!HAVE_DMA_RXALIGN(lp) && NET_IP_ALIGN != 0) memmove(skb->data, skb->data - NET_IP_ALIGN, pkt_len); data = skb_put(skb, pkt_len); diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c index 78a7de3fb622..c62f474b6d08 100644 --- a/drivers/net/ethernet/tundra/tsi108_eth.c +++ b/drivers/net/ethernet/tundra/tsi108_eth.c @@ -371,9 +371,10 @@ tsi108_stat_carry_one(int carry, int carry_bit, int carry_shift, static void tsi108_stat_carry(struct net_device *dev) { struct tsi108_prv_data *data = netdev_priv(dev); + unsigned long flags; u32 carry1, carry2; - spin_lock_irq(&data->misclock); + spin_lock_irqsave(&data->misclock, flags); carry1 = TSI_READ(TSI108_STAT_CARRY1); carry2 = TSI_READ(TSI108_STAT_CARRY2); @@ -441,7 +442,7 @@ static void tsi108_stat_carry(struct net_device *dev) TSI108_STAT_TXPAUSEDROP_CARRY, &data->tx_pause_drop); - spin_unlock_irq(&data->misclock); + spin_unlock_irqrestore(&data->misclock, flags); } /* Read a stat counter atomically with respect to carries. diff --git a/drivers/net/ethernet/xscale/Kconfig b/drivers/net/ethernet/xscale/Kconfig index 2f354ba029a6..cd0a8f46e7c6 100644 --- a/drivers/net/ethernet/xscale/Kconfig +++ b/drivers/net/ethernet/xscale/Kconfig @@ -13,7 +13,7 @@ config NET_VENDOR_XSCALE Note that the answer to this question does not directly affect the kernel: saying N will just cause the configurator to skip all - the questions about XSacle IXP devices. If you say Y, you will be + the questions about XScale IXP devices. If you say Y, you will be asked for your specific card in the following questions. if NET_VENDOR_XSCALE diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 331c16d30d5d..23281aeeb222 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -344,10 +344,10 @@ static void sp_bump(struct sixpack *sp, char cmd) sp->dev->stats.rx_bytes += count; - if ((skb = dev_alloc_skb(count)) == NULL) + if ((skb = dev_alloc_skb(count + 1)) == NULL) goto out_mem; - ptr = skb_put(skb, count); + ptr = skb_put(skb, count + 1); *ptr++ = cmd; /* KISS command */ memcpy(ptr, sp->cooked_buf + 1, count); diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index daab2c07d891..9303aeb2595f 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -500,8 +500,9 @@ static int transmit(struct baycom_state *bc, int cnt, unsigned char stat) } break; } + /* fall through */ - default: /* fall through */ + default: if (bc->hdlctx.calibrate <= 0) return 0; i = min_t(int, cnt, bc->hdlctx.calibrate); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 3544e1991579..e8fce6d715ef 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1239,12 +1239,15 @@ static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { struct net_device_context *ndev_ctx = netdev_priv(net); - struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); + struct netvsc_device *nvdev; struct netvsc_vf_pcpu_stats vf_tot; int i; + rcu_read_lock(); + + nvdev = rcu_dereference(ndev_ctx->nvdev); if (!nvdev) - return; + goto out; netdev_stats_to_stats64(t, &net->stats); @@ -1283,6 +1286,8 @@ static void netvsc_get_stats64(struct net_device *net, t->rx_packets += packets; t->multicast += multicast; } +out: + rcu_read_unlock(); } static int netvsc_set_mac_addr(struct net_device *ndev, void *p) diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c index b41696e16bdc..c20e7ef18bc9 100644 --- a/drivers/net/ieee802154/mac802154_hwsim.c +++ b/drivers/net/ieee802154/mac802154_hwsim.c @@ -802,7 +802,7 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev, err = hwsim_subscribe_all_others(phy); if (err < 0) { mutex_unlock(&hwsim_phys_lock); - goto err_reg; + goto err_subscribe; } } list_add_tail(&phy->list, &hwsim_phys); @@ -812,6 +812,8 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev, return idx; +err_subscribe: + ieee802154_unregister_hw(phy->hw); err_reg: kfree(pib); err_pib: @@ -901,9 +903,9 @@ static __init int hwsim_init_module(void) return 0; platform_drv: - genl_unregister_family(&hwsim_genl_family); -platform_dev: platform_device_unregister(mac802154hwsim_dev); +platform_dev: + genl_unregister_family(&hwsim_genl_family); return rc; } diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index c5c417a3c0ce..bcc40a236624 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -73,46 +73,47 @@ static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port) debugfs_remove_recursive(nsim_dev_port->ddir); } +static struct net *nsim_devlink_net(struct devlink *devlink) +{ + return &init_net; +} + static u64 nsim_dev_ipv4_fib_resource_occ_get(void *priv) { - struct nsim_dev *nsim_dev = priv; + struct net *net = priv; - return nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV4_FIB, false); + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false); } static u64 nsim_dev_ipv4_fib_rules_res_occ_get(void *priv) { - struct nsim_dev *nsim_dev = priv; + struct net *net = priv; - return nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV4_FIB_RULES, false); + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false); } static u64 nsim_dev_ipv6_fib_resource_occ_get(void *priv) { - struct nsim_dev *nsim_dev = priv; + struct net *net = priv; - return nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV6_FIB, false); + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false); } static u64 nsim_dev_ipv6_fib_rules_res_occ_get(void *priv) { - struct nsim_dev *nsim_dev = priv; + struct net *net = priv; - return nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV6_FIB_RULES, false); + return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false); } static int nsim_dev_resources_register(struct devlink *devlink) { - struct nsim_dev *nsim_dev = devlink_priv(devlink); struct devlink_resource_size_params params = { .size_max = (u64)-1, .size_granularity = 1, .unit = DEVLINK_RESOURCE_UNIT_ENTRY }; + struct net *net = nsim_devlink_net(devlink); int err; u64 n; @@ -126,8 +127,7 @@ static int nsim_dev_resources_register(struct devlink *devlink) goto out; } - n = nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV4_FIB, true); + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true); err = devlink_resource_register(devlink, "fib", n, NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4, ¶ms); @@ -136,8 +136,7 @@ static int nsim_dev_resources_register(struct devlink *devlink) return err; } - n = nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV4_FIB_RULES, true); + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true); err = devlink_resource_register(devlink, "fib-rules", n, NSIM_RESOURCE_IPV4_FIB_RULES, NSIM_RESOURCE_IPV4, ¶ms); @@ -156,8 +155,7 @@ static int nsim_dev_resources_register(struct devlink *devlink) goto out; } - n = nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV6_FIB, true); + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true); err = devlink_resource_register(devlink, "fib", n, NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6, ¶ms); @@ -166,8 +164,7 @@ static int nsim_dev_resources_register(struct devlink *devlink) return err; } - n = nsim_fib_get_val(nsim_dev->fib_data, - NSIM_RESOURCE_IPV6_FIB_RULES, true); + n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true); err = devlink_resource_register(devlink, "fib-rules", n, NSIM_RESOURCE_IPV6_FIB_RULES, NSIM_RESOURCE_IPV6, ¶ms); @@ -179,19 +176,19 @@ static int nsim_dev_resources_register(struct devlink *devlink) devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV4_FIB, nsim_dev_ipv4_fib_resource_occ_get, - nsim_dev); + net); devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV4_FIB_RULES, nsim_dev_ipv4_fib_rules_res_occ_get, - nsim_dev); + net); devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV6_FIB, nsim_dev_ipv6_fib_resource_occ_get, - nsim_dev); + net); devlink_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV6_FIB_RULES, nsim_dev_ipv6_fib_rules_res_occ_get, - nsim_dev); + net); out: return err; } @@ -199,11 +196,11 @@ out: static int nsim_dev_reload(struct devlink *devlink, struct netlink_ext_ack *extack) { - struct nsim_dev *nsim_dev = devlink_priv(devlink); enum nsim_resource_id res_ids[] = { NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES }; + struct net *net = nsim_devlink_net(devlink); int i; for (i = 0; i < ARRAY_SIZE(res_ids); ++i) { @@ -212,8 +209,7 @@ static int nsim_dev_reload(struct devlink *devlink, err = devlink_resource_size_get(devlink, res_ids[i], &val); if (!err) { - err = nsim_fib_set_max(nsim_dev->fib_data, - res_ids[i], val, extack); + err = nsim_fib_set_max(net, res_ids[i], val, extack); if (err) return err; } @@ -285,15 +281,9 @@ nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count) mutex_init(&nsim_dev->port_list_lock); nsim_dev->fw_update_status = true; - nsim_dev->fib_data = nsim_fib_create(); - if (IS_ERR(nsim_dev->fib_data)) { - err = PTR_ERR(nsim_dev->fib_data); - goto err_devlink_free; - } - err = nsim_dev_resources_register(devlink); if (err) - goto err_fib_destroy; + goto err_devlink_free; err = devlink_register(devlink, &nsim_bus_dev->dev); if (err) @@ -315,8 +305,6 @@ err_dl_unregister: devlink_unregister(devlink); err_resources_unregister: devlink_resources_unregister(devlink, NULL); -err_fib_destroy: - nsim_fib_destroy(nsim_dev->fib_data); err_devlink_free: devlink_free(devlink); return ERR_PTR(err); @@ -330,7 +318,6 @@ static void nsim_dev_destroy(struct nsim_dev *nsim_dev) nsim_dev_debugfs_exit(nsim_dev); devlink_unregister(devlink); devlink_resources_unregister(devlink, NULL); - nsim_fib_destroy(nsim_dev->fib_data); mutex_destroy(&nsim_dev->port_list_lock); devlink_free(devlink); } diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index 8c57ba747772..f61d094746c0 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -18,6 +18,7 @@ #include <net/ip_fib.h> #include <net/ip6_fib.h> #include <net/fib_rules.h> +#include <net/netns/generic.h> #include "netdevsim.h" @@ -32,14 +33,15 @@ struct nsim_per_fib_data { }; struct nsim_fib_data { - struct notifier_block fib_nb; struct nsim_per_fib_data ipv4; struct nsim_per_fib_data ipv6; }; -u64 nsim_fib_get_val(struct nsim_fib_data *fib_data, - enum nsim_resource_id res_id, bool max) +static unsigned int nsim_fib_net_id; + +u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max) { + struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id); struct nsim_fib_entry *entry; switch (res_id) { @@ -62,10 +64,10 @@ u64 nsim_fib_get_val(struct nsim_fib_data *fib_data, return max ? entry->max : entry->num; } -int nsim_fib_set_max(struct nsim_fib_data *fib_data, - enum nsim_resource_id res_id, u64 val, +int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val, struct netlink_ext_ack *extack) { + struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id); struct nsim_fib_entry *entry; int err = 0; @@ -118,9 +120,9 @@ static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add, return err; } -static int nsim_fib_rule_event(struct nsim_fib_data *data, - struct fib_notifier_info *info, bool add) +static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add) { + struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id); struct netlink_ext_ack *extack = info->extack; int err = 0; @@ -155,9 +157,9 @@ static int nsim_fib_account(struct nsim_fib_entry *entry, bool add, return err; } -static int nsim_fib_event(struct nsim_fib_data *data, - struct fib_notifier_info *info, bool add) +static int nsim_fib_event(struct fib_notifier_info *info, bool add) { + struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id); struct netlink_ext_ack *extack = info->extack; int err = 0; @@ -176,22 +178,18 @@ static int nsim_fib_event(struct nsim_fib_data *data, static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, void *ptr) { - struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, - fib_nb); struct fib_notifier_info *info = ptr; int err = 0; switch (event) { case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: - err = nsim_fib_rule_event(data, info, - event == FIB_EVENT_RULE_ADD); + err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD); break; case FIB_EVENT_ENTRY_ADD: /* fall through */ case FIB_EVENT_ENTRY_DEL: - err = nsim_fib_event(data, info, - event == FIB_EVENT_ENTRY_ADD); + err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD); break; } @@ -201,23 +199,30 @@ static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, /* inconsistent dump, trying again */ static void nsim_fib_dump_inconsistent(struct notifier_block *nb) { - struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, - fib_nb); + struct nsim_fib_data *data; + struct net *net; + + rcu_read_lock(); + for_each_net_rcu(net) { + data = net_generic(net, nsim_fib_net_id); + + data->ipv4.fib.num = 0ULL; + data->ipv4.rules.num = 0ULL; - data->ipv4.fib.num = 0ULL; - data->ipv4.rules.num = 0ULL; - data->ipv6.fib.num = 0ULL; - data->ipv6.rules.num = 0ULL; + data->ipv6.fib.num = 0ULL; + data->ipv6.rules.num = 0ULL; + } + rcu_read_unlock(); } -struct nsim_fib_data *nsim_fib_create(void) -{ - struct nsim_fib_data *data; - int err; +static struct notifier_block nsim_fib_nb = { + .notifier_call = nsim_fib_event_nb, +}; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return ERR_PTR(-ENOMEM); +/* Initialize per network namespace state */ +static int __net_init nsim_fib_netns_init(struct net *net) +{ + struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id); data->ipv4.fib.max = (u64)-1; data->ipv4.rules.max = (u64)-1; @@ -225,22 +230,37 @@ struct nsim_fib_data *nsim_fib_create(void) data->ipv6.fib.max = (u64)-1; data->ipv6.rules.max = (u64)-1; - data->fib_nb.notifier_call = nsim_fib_event_nb; - err = register_fib_notifier(&data->fib_nb, nsim_fib_dump_inconsistent); - if (err) { - pr_err("Failed to register fib notifier\n"); - goto err_out; - } + return 0; +} - return data; +static struct pernet_operations nsim_fib_net_ops = { + .init = nsim_fib_netns_init, + .id = &nsim_fib_net_id, + .size = sizeof(struct nsim_fib_data), +}; -err_out: - kfree(data); - return ERR_PTR(err); +void nsim_fib_exit(void) +{ + unregister_pernet_subsys(&nsim_fib_net_ops); + unregister_fib_notifier(&nsim_fib_nb); } -void nsim_fib_destroy(struct nsim_fib_data *data) +int nsim_fib_init(void) { - unregister_fib_notifier(&data->fib_nb); - kfree(data); + int err; + + err = register_pernet_subsys(&nsim_fib_net_ops); + if (err < 0) { + pr_err("Failed to register pernet subsystem\n"); + goto err_out; + } + + err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent); + if (err < 0) { + pr_err("Failed to register fib notifier\n"); + goto err_out; + } + +err_out: + return err; } diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 0740940f41b1..55f57f76d01b 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -357,12 +357,18 @@ static int __init nsim_module_init(void) if (err) goto err_dev_exit; - err = rtnl_link_register(&nsim_link_ops); + err = nsim_fib_init(); if (err) goto err_bus_exit; + err = rtnl_link_register(&nsim_link_ops); + if (err) + goto err_fib_exit; + return 0; +err_fib_exit: + nsim_fib_exit(); err_bus_exit: nsim_bus_exit(); err_dev_exit: @@ -373,6 +379,7 @@ err_dev_exit: static void __exit nsim_module_exit(void) { rtnl_link_unregister(&nsim_link_ops); + nsim_fib_exit(); nsim_bus_exit(); nsim_dev_exit(); } diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 79c05af2a7c0..9404637d34b7 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -169,12 +169,10 @@ int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_index); -struct nsim_fib_data *nsim_fib_create(void); -void nsim_fib_destroy(struct nsim_fib_data *fib_data); -u64 nsim_fib_get_val(struct nsim_fib_data *fib_data, - enum nsim_resource_id res_id, bool max); -int nsim_fib_set_max(struct nsim_fib_data *fib_data, - enum nsim_resource_id res_id, u64 val, +int nsim_fib_init(void); +void nsim_fib_exit(void); +u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max); +int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val, struct netlink_ext_ack *extack); #if IS_ENABLED(CONFIG_XFRM_OFFLOAD) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 222ccd9ecfce..6ad8b1c63c34 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -257,36 +257,20 @@ static int at803x_config_init(struct phy_device *phydev) * after HW reset: RX delay enabled and TX delay disabled * after SW reset: RX delay enabled, while TX delay retains the * value before reset. - * - * So let's first disable the RX and TX delays in PHY and enable - * them based on the mode selected (this also takes care of RGMII - * mode where we expect delays to be disabled) */ - - ret = at803x_disable_rx_delay(phydev); - if (ret < 0) - return ret; - ret = at803x_disable_tx_delay(phydev); - if (ret < 0) - return ret; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { - /* If RGMII_ID or RGMII_RXID are specified enable RX delay, - * otherwise keep it disabled - */ + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ret = at803x_enable_rx_delay(phydev); - if (ret < 0) - return ret; - } + else + ret = at803x_disable_rx_delay(phydev); + if (ret < 0) + return ret; if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - /* If RGMII_ID or RGMII_TXID are specified enable TX delay, - * otherwise keep it disabled - */ + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) ret = at803x_enable_tx_delay(phydev); - } + else + ret = at803x_disable_tx_delay(phydev); return ret; } diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 3ffe46df249e..7c5265fd2b94 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -216,8 +216,10 @@ static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np) if (IS_ERR(gpiod)) { if (PTR_ERR(gpiod) == -EPROBE_DEFER) return gpiod; - pr_err("error getting GPIO for fixed link %pOF, proceed without\n", - fixed_link_node); + + if (PTR_ERR(gpiod) != -ENOENT) + pr_err("error getting GPIO for fixed link %pOF, proceed without\n", + fixed_link_node); gpiod = NULL; } diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index 28676af97b42..645d354ffb48 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -2226,8 +2226,8 @@ static int vsc8514_probe(struct phy_device *phydev) vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES; vsc8531->hw_stats = vsc85xx_hw_stats; vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats); - vsc8531->stats = devm_kmalloc_array(&phydev->mdio.dev, vsc8531->nstats, - sizeof(u64), GFP_KERNEL); + vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats, + sizeof(u64), GFP_KERNEL); if (!vsc8531->stats) return -ENOMEM; @@ -2251,8 +2251,8 @@ static int vsc8574_probe(struct phy_device *phydev) vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES; vsc8531->hw_stats = vsc8584_hw_stats; vsc8531->nstats = ARRAY_SIZE(vsc8584_hw_stats); - vsc8531->stats = devm_kmalloc_array(&phydev->mdio.dev, vsc8531->nstats, - sizeof(u64), GFP_KERNEL); + vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats, + sizeof(u64), GFP_KERNEL); if (!vsc8531->stats) return -ENOMEM; @@ -2281,8 +2281,8 @@ static int vsc8584_probe(struct phy_device *phydev) vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES; vsc8531->hw_stats = vsc8584_hw_stats; vsc8531->nstats = ARRAY_SIZE(vsc8584_hw_stats); - vsc8531->stats = devm_kmalloc_array(&phydev->mdio.dev, vsc8531->nstats, - sizeof(u64), GFP_KERNEL); + vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats, + sizeof(u64), GFP_KERNEL); if (!vsc8531->stats) return -ENOMEM; @@ -2311,8 +2311,8 @@ static int vsc85xx_probe(struct phy_device *phydev) vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES; vsc8531->hw_stats = vsc85xx_hw_stats; vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats); - vsc8531->stats = devm_kmalloc_array(&phydev->mdio.dev, vsc8531->nstats, - sizeof(u64), GFP_KERNEL); + vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats, + sizeof(u64), GFP_KERNEL); if (!vsc8531->stats) return -ENOMEM; diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index b9d4145781ca..7935593debb1 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -219,6 +219,20 @@ int genphy_c45_read_link(struct phy_device *phydev) int val, devad; bool link = true; + if (phydev->c45_ids.devices_in_package & MDIO_DEVS_AN) { + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); + if (val < 0) + return val; + + /* Autoneg is being started, therefore disregard current + * link status and report link as down. + */ + if (val & MDIO_AN_CTRL1_RESTART) { + phydev->link = 0; + return 0; + } + } + while (mmd_mask && link) { devad = __ffs(mmd_mask); mmd_mask &= ~BIT(devad); @@ -509,6 +523,32 @@ int genphy_c45_read_status(struct phy_device *phydev) } EXPORT_SYMBOL_GPL(genphy_c45_read_status); +/** + * genphy_c45_config_aneg - restart auto-negotiation or forced setup + * @phydev: target phy_device struct + * + * Description: If auto-negotiation is enabled, we configure the + * advertising, and then restart auto-negotiation. If it is not + * enabled, then we force a configuration. + */ +int genphy_c45_config_aneg(struct phy_device *phydev) +{ + bool changed = false; + int ret; + + if (phydev->autoneg == AUTONEG_DISABLE) + return genphy_c45_pma_setup_forced(phydev); + + ret = genphy_c45_an_config_aneg(phydev); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + return genphy_c45_check_and_restart_aneg(phydev, changed); +} +EXPORT_SYMBOL_GPL(genphy_c45_config_aneg); + /* The gen10g_* functions are the old Clause 45 stub */ int gen10g_config_aneg(struct phy_device *phydev) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ef7aa738e0dc..6b0f89369b46 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -507,7 +507,7 @@ static int phy_config_aneg(struct phy_device *phydev) * allowed to call genphy_config_aneg() */ if (phydev->is_c45 && !(phydev->c45_ids.devices_in_package & BIT(0))) - return -EOPNOTSUPP; + return genphy_c45_config_aneg(phydev); return genphy_config_aneg(phydev); } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 6b5cb87f3866..27ebc2c6c2d0 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1752,7 +1752,17 @@ EXPORT_SYMBOL(genphy_aneg_done); */ int genphy_update_link(struct phy_device *phydev) { - int status; + int status = 0, bmcr; + + bmcr = phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + + /* Autoneg is being started, therefore disregard BMSR value and + * report link as down. + */ + if (bmcr & BMCR_ANRESTART) + goto done; /* The link state is latched low so that momentary link * drops can be detected. Do not double-read the status @@ -1774,6 +1784,12 @@ done: phydev->link = status & BMSR_LSTATUS ? 1 : 0; phydev->autoneg_complete = status & BMSR_ANEGCOMPLETE ? 1 : 0; + /* Consider the case that autoneg was started and "aneg complete" + * bit has been reset, but "link up" bit not yet. + */ + if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) + phydev->link = 0; + return 0; } EXPORT_SYMBOL(genphy_update_link); diff --git a/drivers/net/phy/phy_led_triggers.c b/drivers/net/phy/phy_led_triggers.c index b86a4b2116f8..59a94e07e7c5 100644 --- a/drivers/net/phy/phy_led_triggers.c +++ b/drivers/net/phy/phy_led_triggers.c @@ -48,8 +48,9 @@ void phy_led_trigger_change_speed(struct phy_device *phy) if (!phy->last_triggered) led_trigger_event(&phy->led_link_trigger->trigger, LED_FULL); + else + led_trigger_event(&phy->last_triggered->trigger, LED_OFF); - led_trigger_event(&phy->last_triggered->trigger, LED_OFF); led_trigger_event(&plt->trigger, LED_FULL); phy->last_triggered = plt; } diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 5d0af041b8f9..a5a57ca94c1a 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -216,6 +216,8 @@ static int phylink_parse_fixedlink(struct phylink *pl, pl->supported, true); linkmode_zero(pl->supported); phylink_set(pl->supported, MII); + phylink_set(pl->supported, Pause); + phylink_set(pl->supported, Asym_Pause); if (s) { __set_bit(s->bit, pl->supported); } else { @@ -374,8 +376,8 @@ static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_stat * Local device Link partner * Pause AsymDir Pause AsymDir Result * 1 X 1 X TX+RX - * 0 1 1 1 RX - * 1 1 0 1 TX + * 0 1 1 1 TX + * 1 1 0 1 RX */ static void phylink_resolve_flow(struct phylink *pl, struct phylink_link_state *state) @@ -396,7 +398,7 @@ static void phylink_resolve_flow(struct phylink *pl, new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; else if (pause & MLO_PAUSE_ASYM) new_pause = state->pause & MLO_PAUSE_SYM ? - MLO_PAUSE_RX : MLO_PAUSE_TX; + MLO_PAUSE_TX : MLO_PAUSE_RX; } else { new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK; } @@ -990,10 +992,10 @@ void phylink_start(struct phylink *pl) } if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) mod_timer(&pl->link_poll, jiffies + HZ); - if (pl->sfp_bus) - sfp_upstream_start(pl->sfp_bus); if (pl->phydev) phy_start(pl->phydev); + if (pl->sfp_bus) + sfp_upstream_start(pl->sfp_bus); } EXPORT_SYMBOL_GPL(phylink_start); @@ -1010,10 +1012,10 @@ void phylink_stop(struct phylink *pl) { ASSERT_RTNL(); - if (pl->phydev) - phy_stop(pl->phydev); if (pl->sfp_bus) sfp_upstream_stop(pl->sfp_bus); + if (pl->phydev) + phy_stop(pl->phydev); del_timer_sync(&pl->link_poll); if (pl->link_irq) { free_irq(pl->link_irq, pl); diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 1d902ecb4aa8..a44dd3c8af63 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -1115,6 +1115,9 @@ static const struct proto_ops pppoe_ops = { .recvmsg = pppoe_recvmsg, .mmap = sock_no_mmap, .ioctl = pppox_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = pppox_compat_ioctl, +#endif }; static const struct pppox_proto pppoe_proto = { diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c index 5ef422a43d70..08364f10a43f 100644 --- a/drivers/net/ppp/pppox.c +++ b/drivers/net/ppp/pppox.c @@ -17,6 +17,7 @@ #include <linux/string.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/compat.h> #include <linux/errno.h> #include <linux/netdevice.h> #include <linux/net.h> @@ -98,6 +99,18 @@ int pppox_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) EXPORT_SYMBOL(pppox_ioctl); +#ifdef CONFIG_COMPAT +int pppox_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + if (cmd == PPPOEIOCSFWD32) + cmd = PPPOEIOCSFWD; + + return pppox_ioctl(sock, cmd, (unsigned long)compat_ptr(arg)); +} + +EXPORT_SYMBOL(pppox_compat_ioctl); +#endif + static int pppox_create(struct net *net, struct socket *sock, int protocol, int kern) { diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index a8e52c8e4128..734de7de03f7 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -623,6 +623,9 @@ static const struct proto_ops pptp_ops = { .recvmsg = sock_no_recvmsg, .mmap = sock_no_mmap, .ioctl = pppox_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = pppox_compat_ioctl, +#endif }; static const struct pppox_proto pppox_pptp_proto = { diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index abfa0da9bbd2..e8089def5a46 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1004,6 +1004,8 @@ static void __team_compute_features(struct team *team) team->dev->vlan_features = vlan_features; team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX | NETIF_F_GSO_UDP_L4; team->dev->hard_header_len = max_hard_header_len; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3d443597bd04..aab0be40d443 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -787,7 +787,8 @@ static void tun_detach_all(struct net_device *dev) } static int tun_attach(struct tun_struct *tun, struct file *file, - bool skip_filter, bool napi, bool napi_frags) + bool skip_filter, bool napi, bool napi_frags, + bool publish_tun) { struct tun_file *tfile = file->private_data; struct net_device *dev = tun->dev; @@ -870,7 +871,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file, * initialized tfile; otherwise we risk using half-initialized * object. */ - rcu_assign_pointer(tfile->tun, tun); + if (publish_tun) + rcu_assign_pointer(tfile->tun, tun); rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); tun->numqueues++; tun_set_real_num_queues(tun); @@ -1599,7 +1601,8 @@ static bool tun_can_build_skb(struct tun_struct *tun, struct tun_file *tfile, return true; } -static struct sk_buff *__tun_build_skb(struct page_frag *alloc_frag, char *buf, +static struct sk_buff *__tun_build_skb(struct tun_file *tfile, + struct page_frag *alloc_frag, char *buf, int buflen, int len, int pad) { struct sk_buff *skb = build_skb(buf, buflen); @@ -1609,6 +1612,7 @@ static struct sk_buff *__tun_build_skb(struct page_frag *alloc_frag, char *buf, skb_reserve(skb, pad); skb_put(skb, len); + skb_set_owner_w(skb, tfile->socket.sk); get_page(alloc_frag->page); alloc_frag->offset += buflen; @@ -1686,7 +1690,8 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, */ if (hdr->gso_type || !xdp_prog) { *skb_xdp = 1; - return __tun_build_skb(alloc_frag, buf, buflen, len, pad); + return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, + pad); } *skb_xdp = 0; @@ -1723,7 +1728,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, rcu_read_unlock(); local_bh_enable(); - return __tun_build_skb(alloc_frag, buf, buflen, len, pad); + return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, pad); err_xdp: put_page(alloc_frag->page); @@ -2727,7 +2732,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER, ifr->ifr_flags & IFF_NAPI, - ifr->ifr_flags & IFF_NAPI_FRAGS); + ifr->ifr_flags & IFF_NAPI_FRAGS, true); if (err < 0) return err; @@ -2826,13 +2831,17 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) INIT_LIST_HEAD(&tun->disabled); err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI, - ifr->ifr_flags & IFF_NAPI_FRAGS); + ifr->ifr_flags & IFF_NAPI_FRAGS, false); if (err < 0) goto err_free_flow; err = register_netdevice(tun->dev); if (err < 0) goto err_detach; + /* free_netdev() won't check refcnt, to aovid race + * with dev_put() we need publish tun after registration. + */ + rcu_assign_pointer(tfile->tun, tun); } netif_carrier_on(tun->dev); @@ -2975,7 +2984,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) if (ret < 0) goto unlock; ret = tun_attach(tun, file, false, tun->flags & IFF_NAPI, - tun->flags & IFF_NAPI_FRAGS); + tun->flags & IFF_NAPI_FRAGS, true); } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { tun = rtnl_dereference(tfile->tun); if (!tun || !(tun->flags & IFF_MULTI_QUEUE) || tfile->detached) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 8458e88c18e9..32f53de5b1fe 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -206,7 +206,15 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) goto bad_desc; } skip: - if (rndis && header.usb_cdc_acm_descriptor && + /* Communcation class functions with bmCapabilities are not + * RNDIS. But some Wireless class RNDIS functions use + * bmCapabilities for their own purpose. The failsafe is + * therefore applied only to Communication class RNDIS + * functions. The rndis test is redundant, but a cheap + * optimization. + */ + if (rndis && is_rndis(&intf->cur_altsetting->desc) && + header.usb_cdc_acm_descriptor && header.usb_cdc_acm_descriptor->bmCapabilities) { dev_dbg(&intf->dev, "ACM capabilities %02x, not really RNDIS?\n", diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c index 5519248a791e..32b08b18e120 100644 --- a/drivers/net/usb/cx82310_eth.c +++ b/drivers/net/usb/cx82310_eth.c @@ -163,7 +163,8 @@ static int cx82310_bind(struct usbnet *dev, struct usb_interface *intf) } if (!timeout) { dev_err(&udev->dev, "firmware not ready in time\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto err; } /* enable ethernet mode (?) */ diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c index d62b6706a537..fc5895f85cee 100644 --- a/drivers/net/usb/kalmia.c +++ b/drivers/net/usb/kalmia.c @@ -113,16 +113,16 @@ kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr) status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_1), usb_buf, 24); if (status != 0) - return status; + goto out; memcpy(usb_buf, init_msg_2, 12); status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_2), usb_buf, 28); if (status != 0) - return status; + goto out; memcpy(ethernet_addr, usb_buf + 10, ETH_ALEN); - +out: kfree(usb_buf); return status; } diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 3d92ea6fcc02..f033fee225a1 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -3792,7 +3792,7 @@ static int lan78xx_probe(struct usb_interface *intf, ret = register_netdev(netdev); if (ret != 0) { netif_err(dev, probe, netdev, "couldn't register the device\n"); - goto out3; + goto out4; } usb_set_intfdata(intf, dev); @@ -3807,12 +3807,14 @@ static int lan78xx_probe(struct usb_interface *intf, ret = lan78xx_phy_init(dev); if (ret < 0) - goto out4; + goto out5; return 0; -out4: +out5: unregister_netdev(netdev); +out4: + usb_free_urb(dev->urb_intr); out3: lan78xx_unbind(dev, intf); out2: diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 6d25dea5ad4b..f7d117d80cfb 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -282,7 +282,7 @@ static void mdio_write(struct net_device *dev, int phy_id, int loc, int val) static int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata) { int i; - __u8 tmp; + __u8 tmp = 0; __le16 retdatai; int ret; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 69e0a2acfcb0..b6dc5d714b5e 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1295,6 +1295,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x2001, 0x7e3d, 4)}, /* D-Link DWM-222 A2 */ {QMI_FIXED_INTF(0x2020, 0x2031, 4)}, /* Olicard 600 */ {QMI_FIXED_INTF(0x2020, 0x2033, 4)}, /* BroadMobi BM806U */ + {QMI_FIXED_INTF(0x2020, 0x2060, 4)}, /* BroadMobi BM818 */ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */ {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 39e0768d734d..04137ac373b0 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -50,7 +50,7 @@ #define PLA_TEREDO_WAKE_BASE 0xc0c4 #define PLA_MAR 0xcd00 #define PLA_BACKUP 0xd000 -#define PAL_BDC_CR 0xd1a0 +#define PLA_BDC_CR 0xd1a0 #define PLA_TEREDO_TIMER 0xd2cc #define PLA_REALWOW_TIMER 0xd2e8 #define PLA_SUSPEND_FLAG 0xd38a @@ -274,7 +274,7 @@ #define TEREDO_RS_EVENT_MASK 0x00fe #define OOB_TEREDO_EN 0x0001 -/* PAL_BDC_CR */ +/* PLA_BDC_CR */ #define ALDPS_PROXY_MODE 0x0001 /* PLA_EFUSE_CMD */ @@ -799,8 +799,11 @@ int get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, void *data) ret = usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0), RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, value, index, tmp, size, 500); + if (ret < 0) + memset(data, 0xff, size); + else + memcpy(data, tmp, size); - memcpy(data, tmp, size); kfree(tmp); return ret; @@ -3191,9 +3194,9 @@ static void r8152b_enter_oob(struct r8152 *tp) rtl_rx_vlan_en(tp, true); - ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BDC_CR); ocp_data |= ALDPS_PROXY_MODE; - ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BDC_CR, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; @@ -3577,9 +3580,9 @@ static void r8153_enter_oob(struct r8152 *tp) rtl_rx_vlan_en(tp, true); - ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PAL_BDC_CR); + ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_BDC_CR); ocp_data |= ALDPS_PROXY_MODE; - ocp_write_word(tp, MCU_TYPE_PLA, PAL_BDC_CR, ocp_data); + ocp_write_word(tp, MCU_TYPE_PLA, PLA_BDC_CR, ocp_data); ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); ocp_data |= NOW_IS_OOB | DIS_MCU_CLROOB; @@ -4018,8 +4021,7 @@ static int rtl8152_close(struct net_device *netdev) #ifdef CONFIG_PM_SLEEP unregister_pm_notifier(&tp->pm_notifier); #endif - if (!test_bit(RTL8152_UNPLUG, &tp->flags)) - napi_disable(&tp->napi); + napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); @@ -5350,7 +5352,6 @@ static int rtl8152_probe(struct usb_interface *intf, return 0; out1: - netif_napi_del(&tp->napi); usb_set_intfdata(intf, NULL); out: free_netdev(netdev); @@ -5365,7 +5366,6 @@ static void rtl8152_disconnect(struct usb_interface *intf) if (tp) { rtl_set_unplug(tp); - netif_napi_del(&tp->napi); unregister_netdev(tp->netdev); cancel_delayed_work_sync(&tp->hw_phy_work); tp->rtl_ops.unload(tp); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 4f3de0ac8b0b..ba98e0971b84 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1331,7 +1331,7 @@ static int virtnet_receive(struct receive_queue *rq, int budget, } } - if (rq->vq->num_free > virtqueue_get_vring_size(rq->vq) / 2) { + if (rq->vq->num_free > min((unsigned int)budget, virtqueue_get_vring_size(rq->vq)) / 2) { if (!try_fill_recv(vi, rq, GFP_ATOMIC)) schedule_delayed_work(&vi->refill, 0); } diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index d74349628db2..0e6a51525d91 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -1115,7 +1115,7 @@ static void lmc_running_reset (struct net_device *dev) /*fold00*/ sc->lmc_cmdmode |= (TULIP_CMD_TXRUN | TULIP_CMD_RXRUN); LMC_CSR_WRITE (sc, csr_command, sc->lmc_cmdmode); - lmc_trace(dev, "lmc_runnin_reset_out"); + lmc_trace(dev, "lmc_running_reset_out"); } diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index a9ac3f37b904..e2e679a01b65 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -413,6 +413,7 @@ static void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int case SDLA_RET_NO_BUFS: if (cmd == SDLA_INFORMATION_WRITE) break; + /* Else, fall through */ default: netdev_dbg(dev, "Cmd 0x%02X generated return code 0x%02X\n", diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c index e9fc168bb734..489cba9b284d 100644 --- a/drivers/net/wimax/i2400m/fw.c +++ b/drivers/net/wimax/i2400m/fw.c @@ -351,13 +351,15 @@ int i2400m_barker_db_init(const char *_options) } result = i2400m_barker_db_add(barker); if (result < 0) - goto error_add; + goto error_parse_add; } kfree(options_orig); } return 0; +error_parse_add: error_parse: + kfree(options_orig); error_add: kfree(i2400m_barker_db); return result; diff --git a/drivers/net/wimax/i2400m/op-rfkill.c b/drivers/net/wimax/i2400m/op-rfkill.c index 6642bcb27761..8efb493ceec2 100644 --- a/drivers/net/wimax/i2400m/op-rfkill.c +++ b/drivers/net/wimax/i2400m/op-rfkill.c @@ -127,6 +127,7 @@ int i2400m_op_rfkill_sw_toggle(struct wimax_dev *wimax_dev, "%d\n", result); result = 0; error_cmd: + kfree(cmd); kfree_skb(ack_skb); error_msg_to_dev: error_alloc: diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 1f500cddb3a7..55b713255b8e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -556,6 +556,30 @@ const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0 = { .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, }; +const struct iwl_cfg killer1650s_2ax_cfg_qu_c0_hr_b0 = { + .name = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201NGW)", + .fw_name_pre = IWL_QU_C_HR_B_FW_PRE, + IWL_DEVICE_22500, + /* + * This device doesn't support receiving BlockAck with a large bitmap + * so we need to restrict the size of transmitted aggregation to the + * HT size; mac80211 would otherwise pick the HE max (256) by default. + */ + .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, +}; + +const struct iwl_cfg killer1650i_2ax_cfg_qu_c0_hr_b0 = { + .name = "Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201D2W)", + .fw_name_pre = IWL_QU_C_HR_B_FW_PRE, + IWL_DEVICE_22500, + /* + * This device doesn't support receiving BlockAck with a large bitmap + * so we need to restrict the size of transmitted aggregation to the + * HT size; mac80211 would otherwise pick the HE max (256) by default. + */ + .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, +}; + const struct iwl_cfg iwl22000_2ax_cfg_jf = { .name = "Intel(R) Dual Band Wireless AX 22000", .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index d55312ef58c9..9b0bb89599fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -776,7 +776,6 @@ struct iwl_rss_config_cmd { u8 indirection_table[IWL_RSS_INDIRECTION_TABLE_SIZE]; } __packed; /* RSS_CONFIG_CMD_API_S_VER_1 */ -#define IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE 128 #define IWL_MULTI_QUEUE_SYNC_SENDER_POS 0 #define IWL_MULTI_QUEUE_SYNC_SENDER_MSK 0xf @@ -812,10 +811,12 @@ struct iwl_rxq_sync_notification { * * @IWL_MVM_RXQ_EMPTY: empty sync notification * @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA + * @IWL_MVM_RXQ_NSSN_SYNC: notify all the RSS queues with the new NSSN */ enum iwl_mvm_rxq_notif_type { IWL_MVM_RXQ_EMPTY, IWL_MVM_RXQ_NOTIF_DEL_BA, + IWL_MVM_RXQ_NSSN_SYNC, }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index e411ac98290d..4d81776f576d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -2438,17 +2438,19 @@ static void iwl_fw_dbg_info_apply(struct iwl_fw_runtime *fwrt, { u32 img_name_len = le32_to_cpu(dbg_info->img_name_len); u32 dbg_cfg_name_len = le32_to_cpu(dbg_info->dbg_cfg_name_len); - const char err_str[] = - "WRT: ext=%d. Invalid %s name length %d, expected %d\n"; if (img_name_len != IWL_FW_INI_MAX_IMG_NAME_LEN) { - IWL_WARN(fwrt, err_str, ext, "image", img_name_len, + IWL_WARN(fwrt, + "WRT: ext=%d. Invalid image name length %d, expected %d\n", + ext, img_name_len, IWL_FW_INI_MAX_IMG_NAME_LEN); return; } if (dbg_cfg_name_len != IWL_FW_INI_MAX_DBG_CFG_NAME_LEN) { - IWL_WARN(fwrt, err_str, ext, "debug cfg", dbg_cfg_name_len, + IWL_WARN(fwrt, + "WRT: ext=%d. Invalid debug cfg name length %d, expected %d\n", + ext, dbg_cfg_name_len, IWL_FW_INI_MAX_DBG_CFG_NAME_LEN); return; } @@ -2775,8 +2777,6 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, struct iwl_ucode_tlv *tlv = iter; void *ini_tlv = (void *)tlv->data; u32 type = le32_to_cpu(tlv->type); - const char invalid_ap_str[] = - "WRT: ext=%d. Invalid apply point %d for %s\n"; switch (type) { case IWL_UCODE_TLV_TYPE_DEBUG_INFO: @@ -2786,8 +2786,9 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_allocation_data *buf_alloc = ini_tlv; if (pnt != IWL_FW_INI_APPLY_EARLY) { - IWL_ERR(fwrt, invalid_ap_str, ext, pnt, - "buffer allocation"); + IWL_ERR(fwrt, + "WRT: ext=%d. Invalid apply point %d for buffer allocation\n", + ext, pnt); goto next; } @@ -2797,8 +2798,9 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, } case IWL_UCODE_TLV_TYPE_HCMD: if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) { - IWL_ERR(fwrt, invalid_ap_str, ext, pnt, - "host command"); + IWL_ERR(fwrt, + "WRT: ext=%d. Invalid apply point %d for host command\n", + ext, pnt); goto next; } iwl_fw_dbg_send_hcmd(fwrt, tlv, ext); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 1c1bf1b281cd..6c04f8223aff 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -577,6 +577,8 @@ extern const struct iwl_cfg iwl_ax1650i_cfg_quz_hr; extern const struct iwl_cfg iwl_ax1650s_cfg_quz_hr; extern const struct iwl_cfg killer1650s_2ax_cfg_qu_b0_hr_b0; extern const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0; +extern const struct iwl_cfg killer1650s_2ax_cfg_qu_c0_hr_b0; +extern const struct iwl_cfg killer1650i_2ax_cfg_qu_c0_hr_b0; extern const struct iwl_cfg killer1650x_2ax_cfg; extern const struct iwl_cfg killer1650w_2ax_cfg; extern const struct iwl_cfg iwl9461_2ac_cfg_qu_b0_jf_b0; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 57d09049e615..38672dd5aae9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1640,6 +1640,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) init_completion(&drv->request_firmware_complete); INIT_LIST_HEAD(&drv->list); + iwl_load_fw_dbg_tlv(drv->trans->dev, drv->trans); + #ifdef CONFIG_IWLWIFI_DEBUGFS /* Create the device debugfs entries. */ drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev), @@ -1660,8 +1662,8 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) err_fw: #ifdef CONFIG_IWLWIFI_DEBUGFS debugfs_remove_recursive(drv->dbgfs_drv); - iwl_fw_dbg_free(drv->trans); #endif + iwl_fw_dbg_free(drv->trans); kfree(drv); err: return ERR_PTR(ret); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 1d608e9e9101..5de54d1559dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -755,7 +755,7 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) for (i = 0; i < n_profiles; i++) { /* the tables start at element 3 */ - static int pos = 3; + int pos = 3; /* The EWRD profiles officially go from 2 to 4, but we * save them in sar_profiles[1-3] (because we don't @@ -880,6 +880,22 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); } +static bool iwl_mvm_sar_geo_support(struct iwl_mvm *mvm) +{ + /* + * The GEO_TX_POWER_LIMIT command is not supported on earlier + * firmware versions. Unfortunately, we don't have a TLV API + * flag to rely on, so rely on the major version which is in + * the first byte of ucode_ver. This was implemented + * initially on version 38 and then backported to 36, 29 and + * 17. + */ + return IWL_UCODE_SERIAL(mvm->fw->ucode_ver) >= 38 || + IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 36 || + IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 29 || + IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 17; +} + int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) { struct iwl_geo_tx_power_profiles_resp *resp; @@ -909,6 +925,9 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) .data = { data }, }; + if (!iwl_mvm_sar_geo_support(mvm)) + return -EOPNOTSUPP; + ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret) { IWL_ERR(mvm, "Failed to get geographic profile info %d\n", ret); @@ -934,13 +953,7 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) int ret, i, j; u16 cmd_wide_id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT); - /* - * This command is not supported on earlier firmware versions. - * Unfortunately, we don't have a TLV API flag to rely on, so - * rely on the major version which is in the first byte of - * ucode_ver. - */ - if (IWL_UCODE_SERIAL(mvm->fw->ucode_ver) < 41) + if (!iwl_mvm_sar_geo_support(mvm)) return 0; ret = iwl_mvm_sar_get_wgds_table(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index cb22d447fcb8..fe776e35b9d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -554,7 +554,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cpu_to_le32(vif->bss_conf.use_short_slot ? MAC_FLG_SHORT_SLOT : 0); - cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); + cmd->filter_flags = 0; for (i = 0; i < IEEE80211_NUM_ACS; i++) { u8 txf = iwl_mvm_mac_ac_to_tx_fifo(mvm, i); @@ -623,6 +623,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, /* We need the dtim_period to set the MAC as associated */ if (vif->bss_conf.assoc && vif->bss_conf.dtim_period && !force_assoc_off) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u8 ap_sta_id = mvmvif->ap_sta_id; u32 dtim_offs; /* @@ -658,6 +660,29 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, dtim_offs); ctxt_sta->is_assoc = cpu_to_le32(1); + + /* + * allow multicast data frames only as long as the station is + * authorized, i.e., GTK keys are already installed (if needed) + */ + if (ap_sta_id < IWL_MVM_STATION_COUNT) { + struct ieee80211_sta *sta; + + rcu_read_lock(); + + sta = rcu_dereference(mvm->fw_id_to_mac_id[ap_sta_id]); + if (!IS_ERR_OR_NULL(sta)) { + struct iwl_mvm_sta *mvmsta = + iwl_mvm_sta_from_mac80211(sta); + + if (mvmsta->sta_state == + IEEE80211_STA_AUTHORIZED) + cmd.filter_flags |= + cpu_to_le32(MAC_FILTER_ACCEPT_GRP); + } + + rcu_read_unlock(); + } } else { ctxt_sta->is_assoc = cpu_to_le32(0); @@ -703,7 +728,8 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, MAC_FILTER_IN_CONTROL_AND_MGMT | MAC_FILTER_IN_BEACON | MAC_FILTER_IN_PROBE_REQUEST | - MAC_FILTER_IN_CRC32); + MAC_FILTER_IN_CRC32 | + MAC_FILTER_ACCEPT_GRP); ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); /* Allocate sniffer station */ @@ -727,7 +753,8 @@ static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON | - MAC_FILTER_IN_PROBE_REQUEST); + MAC_FILTER_IN_PROBE_REQUEST | + MAC_FILTER_ACCEPT_GRP); /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */ cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 55cd49ccbf0b..a7bc00d1296f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -207,11 +207,11 @@ static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = { }, }; -static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, - enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); +static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) { @@ -474,7 +474,19 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); ieee80211_hw_set(hw, BUFF_MMPDU_TXQ); ieee80211_hw_set(hw, STA_MMPDU_TXQ); - ieee80211_hw_set(hw, TX_AMSDU); + /* + * On older devices, enabling TX A-MSDU occasionally leads to + * something getting messed up, the command read from the FIFO + * gets out of sync and isn't a TX command, so that we have an + * assert EDC. + * + * It's not clear where the bug is, but since we didn't used to + * support A-MSDU until moving the mac80211 iTXQs, just leave it + * for older devices. We also don't see this issue on any newer + * devices. + */ + if (mvm->cfg->device_family >= IWL_DEVICE_FAMILY_9000) + ieee80211_hw_set(hw, TX_AMSDU); ieee80211_hw_set(hw, TX_FRAG_LIST); if (iwl_mvm_has_tlc_offload(mvm)) { @@ -2726,7 +2738,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, mvmvif->ap_early_keys[i] = NULL; - ret = iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key); + ret = __iwl_mvm_mac_set_key(hw, SET_KEY, vif, NULL, key); if (ret) goto out_quota_failed; } @@ -3315,10 +3327,20 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, /* enable beacon filtering */ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); + /* + * Now that the station is authorized, i.e., keys were already + * installed, need to indicate to the FW that + * multicast data frames can be forwarded to the driver + */ + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, true); } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { + /* Multicast data frames are no longer allowed */ + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + /* disable beacon filtering */ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); WARN_ON(ret && @@ -3494,11 +3516,11 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, return ret; } -static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, - enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) +static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -3553,8 +3575,6 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, return -EOPNOTSUPP; } - mutex_lock(&mvm->mutex); - switch (cmd) { case SET_KEY: if ((vif->type == NL80211_IFTYPE_ADHOC || @@ -3700,7 +3720,22 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, ret = -EINVAL; } + return ret; +} + +static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + ret = __iwl_mvm_mac_set_key(hw, cmd, vif, sta, key); mutex_unlock(&mvm->mutex); + return ret; } @@ -5041,7 +5076,6 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, u32 qmask = BIT(mvm->trans->num_rx_queues) - 1; int ret; - lockdep_assert_held(&mvm->mutex); if (!iwl_mvm_has_new_rx_api(mvm)) return; @@ -5052,13 +5086,15 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, atomic_set(&mvm->queue_sync_counter, mvm->trans->num_rx_queues); - ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)notif, size); + ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)notif, + size, !notif->sync); if (ret) { IWL_ERR(mvm, "Failed to trigger RX queues sync (%d)\n", ret); goto out; } if (notif->sync) { + lockdep_assert_held(&mvm->mutex); ret = wait_event_timeout(mvm->rx_sync_waitq, atomic_read(&mvm->queue_sync_counter) == 0 || iwl_mvm_is_radio_killed(mvm), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 48c77af54e99..a263cc629d75 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1664,9 +1664,9 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue); int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, - const u8 *data, u32 count); -void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, - int queue); + const u8 *data, u32 count, bool async); +void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue); void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); @@ -1813,7 +1813,7 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) #endif /* CONFIG_IWLWIFI_DEBUGFS */ /* rate scaling */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool sync); +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq); void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg); int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate); void rs_update_last_rssi(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 719f793b3487..a9bb43a2f27b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -620,7 +620,7 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, enum iwl_mcc_source src; char mcc[3]; struct ieee80211_regdomain *regd; - u32 wgds_tbl_idx; + int wgds_tbl_idx; lockdep_assert_held(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index d7d6f3398f86..4888054dc3d8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1088,7 +1088,7 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0); else if (unlikely(cmd == WIDE_ID(DATA_PATH_GROUP, RX_QUEUES_NOTIFICATION))) - iwl_mvm_rx_queue_notif(mvm, rxb, 0); + iwl_mvm_rx_queue_notif(mvm, napi, rxb, 0); else if (cmd == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)) iwl_mvm_rx_frame_release(mvm, napi, rxb, 0); else if (cmd == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF)) @@ -1812,7 +1812,7 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, iwl_mvm_rx_frame_release(mvm, napi, rxb, queue); else if (unlikely(cmd == WIDE_ID(DATA_PATH_GROUP, RX_QUEUES_NOTIFICATION))) - iwl_mvm_rx_queue_notif(mvm, rxb, queue); + iwl_mvm_rx_queue_notif(mvm, napi, rxb, queue); else if (likely(cmd == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))) iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 8c9069f28a58..d3f04acfbacb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1197,239 +1197,6 @@ static u8 rs_get_tid(struct ieee80211_hdr *hdr) return tid; } -void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, struct ieee80211_tx_info *info, bool ndp) -{ - int legacy_success; - int retries; - int i; - struct iwl_lq_cmd *table; - u32 lq_hwrate; - struct rs_rate lq_rate, tx_resp_rate; - struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - u32 tlc_info = (uintptr_t)info->status.status_driver_data[0]; - u8 reduced_txp = tlc_info & RS_DRV_DATA_TXP_MSK; - u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info); - u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1]; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (!lq_sta) { - IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n"); - return; - } else if (!lq_sta->pers.drv) { - IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); - return; - } - - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - if (rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, - &tx_resp_rate)) { - WARN_ON_ONCE(1); - return; - } - -#ifdef CONFIG_MAC80211_DEBUGFS - /* Disable last tx check if we are debugging with fixed rate but - * update tx stats */ - if (lq_sta->pers.dbg_fixed_rate) { - int index = tx_resp_rate.index; - enum rs_column column; - int attempts, success; - - column = rs_get_column_from_rate(&tx_resp_rate); - if (WARN_ONCE(column == RS_COLUMN_INVALID, - "Can't map rate 0x%x to column", - tx_resp_hwrate)) - return; - - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - attempts = info->status.ampdu_len; - success = info->status.ampdu_ack_len; - } else { - attempts = info->status.rates[0].count; - success = !!(info->flags & IEEE80211_TX_STAT_ACK); - } - - lq_sta->pers.tx_stats[column][index].total += attempts; - lq_sta->pers.tx_stats[column][index].success += success; - - IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n", - tx_resp_hwrate, success, attempts); - return; - } -#endif - - if (time_after(jiffies, - (unsigned long)(lq_sta->last_tx + - (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { - IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); - iwl_mvm_rs_rate_init(mvm, sta, info->band, true); - return; - } - lq_sta->last_tx = jiffies; - - /* Ignore this Tx frame response if its initial rate doesn't match - * that of latest Link Quality command. There may be stragglers - * from a previous Link Quality command, but we're no longer interested - * in those; they're either from the "active" mode while we're trying - * to check "search" mode, or a prior "search" mode after we've moved - * to a new "search" mode (which might become the new "active" mode). - */ - table = &lq_sta->lq; - lq_hwrate = le32_to_cpu(table->rs_table[0]); - if (rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate)) { - WARN_ON_ONCE(1); - return; - } - - /* Here we actually compare this rate to the latest LQ command */ - if (lq_color != LQ_FLAG_COLOR_GET(table->flags)) { - IWL_DEBUG_RATE(mvm, - "tx resp color 0x%x does not match 0x%x\n", - lq_color, LQ_FLAG_COLOR_GET(table->flags)); - - /* - * Since rates mis-match, the last LQ command may have failed. - * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with - * ... driver. - */ - lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { - lq_sta->missed_rate_counter = 0; - IWL_DEBUG_RATE(mvm, - "Too many rates mismatch. Send sync LQ. rs_state %d\n", - lq_sta->rs_state); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); - } - /* Regardless, ignore this status info for outdated rate */ - return; - } else - /* Rate did match, so reset the missed_rate_counter */ - lq_sta->missed_rate_counter = 0; - - if (!lq_sta->search_better_tbl) { - curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else { - curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - } - - if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) { - IWL_DEBUG_RATE(mvm, - "Neither active nor search matches tx rate\n"); - tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); - tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); - rs_dump_rate(mvm, &lq_rate, "ACTUAL"); - - /* - * no matching table found, let's by-pass the data collection - * and continue to perform rate scale to find the rate table - */ - rs_stay_in_table(lq_sta, true); - goto done; - } - - /* - * Updating the frame history depends on whether packets were - * aggregated. - * - * For aggregation, all packets were transmitted at the same rate, the - * first index into rate scale table. - */ - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index, - info->status.ampdu_len, - info->status.ampdu_ack_len, - reduced_txp); - - /* ampdu_ack_len = 0 marks no BA was received. For TLC, treat - * it as a single frame loss as we don't want the success ratio - * to dip too quickly because a BA wasn't received. - * For TPC, there's no need for this optimisation since we want - * to recover very quickly from a bad power reduction and, - * therefore we'd like the success ratio to get an immediate hit - * when failing to get a BA, so we'd switch back to a lower or - * zero power reduction. When FW transmits agg with a rate - * different from the initial rate, it will not use reduced txp - * and will send BA notification twice (one empty with reduced - * txp equal to the value from LQ and one with reduced txp 0). - * We need to update counters for each txp level accordingly. - */ - if (info->status.ampdu_ack_len == 0) - info->status.ampdu_len = 1; - - rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl, tx_resp_rate.index, - info->status.ampdu_len, - info->status.ampdu_ack_len); - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - lq_sta->total_success += info->status.ampdu_ack_len; - lq_sta->total_failed += (info->status.ampdu_len - - info->status.ampdu_ack_len); - } - } else { - /* For legacy, update frame history with for each Tx retry. */ - retries = info->status.rates[0].count - 1; - /* HW doesn't send more than 15 retries */ - retries = min(retries, 15); - - /* The last transmission may have been successful */ - legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); - /* Collect data for each rate used during failed TX attempts */ - for (i = 0; i <= retries; ++i) { - lq_hwrate = le32_to_cpu(table->rs_table[i]); - if (rs_rate_from_ucode_rate(lq_hwrate, info->band, - &lq_rate)) { - WARN_ON_ONCE(1); - return; - } - - /* - * Only collect stats if retried rate is in the same RS - * table as active/search. - */ - if (rs_rate_column_match(&lq_rate, &curr_tbl->rate)) - tmp_tbl = curr_tbl; - else if (rs_rate_column_match(&lq_rate, - &other_tbl->rate)) - tmp_tbl = other_tbl; - else - continue; - - rs_collect_tpc_data(mvm, lq_sta, tmp_tbl, - tx_resp_rate.index, 1, - i < retries ? 0 : legacy_success, - reduced_txp); - rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl, - tx_resp_rate.index, 1, - i < retries ? 0 : legacy_success); - } - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - lq_sta->total_success += legacy_success; - lq_sta->total_failed += retries + (1 - legacy_success); - } - } - /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = lq_hwrate; - IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp); -done: - /* See if there's a better rate or modulation mode to try. */ - if (sta->supp_rates[info->band]) - rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp); -} - /* * mac80211 sends us Tx status */ @@ -1442,8 +1209,9 @@ static void rs_drv_mac80211_tx_status(void *mvm_r, struct iwl_op_mode *op_mode = mvm_r; struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (!iwl_mvm_sta_from_mac80211(sta)->vif) + if (!mvmsta->vif) return; if (!ieee80211_is_data(hdr->frame_control) || @@ -1584,6 +1352,18 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); } +/* rs uses two tables, one is active and the second is for searching better + * configuration. This function, according to the index of the currently + * active table returns the search table, which is located at the + * index complementary to 1 according to the active table (active = 1, + * search = 0 or active = 0, search = 1). + * Since lq_info is an arary of size 2, make sure index cannot be out of bounds. + */ +static inline u8 rs_search_tbl(u8 active_tbl) +{ + return (active_tbl ^ 1) & 1; +} + static s32 rs_get_best_rate(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, /* "search" */ @@ -1794,7 +1574,7 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm, struct iwl_scale_tbl_info *tbl) { rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq); } static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm, @@ -1931,9 +1711,9 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, struct ieee80211_sta *sta, enum rs_column col_id) { - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; struct rs_rate *rate = &search_tbl->rate; const struct rs_tx_column *column = &rs_tx_columns[col_id]; const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; @@ -2341,7 +2121,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; else - active_tbl = 1 - lq_sta->active_tbl; + active_tbl = rs_search_tbl(lq_sta->active_tbl); tbl = &(lq_sta->lq_info[active_tbl]); rate = &tbl->rate; @@ -2565,7 +2345,7 @@ lq_update: /* If new "search" mode was selected, set up in uCode table */ if (lq_sta->search_better_tbl) { /* Access the "search" table, clear its history. */ - tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; rs_rate_scale_clear_tbl_windows(mvm, tbl); /* Use new "search" start rate */ @@ -2896,7 +2676,7 @@ void rs_update_last_rssi(struct iwl_mvm *mvm, static void rs_initialize_lq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - enum nl80211_band band, bool update) + enum nl80211_band band) { struct iwl_scale_tbl_info *tbl; struct rs_rate *rate; @@ -2908,7 +2688,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; else - active_tbl = 1 - lq_sta->active_tbl; + active_tbl = rs_search_tbl(lq_sta->active_tbl); tbl = &(lq_sta->lq_info[active_tbl]); rate = &tbl->rate; @@ -2926,7 +2706,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_lq_cmd(mvm, sta, lq_sta, rate); /* TODO restore station should remember the lq cmd */ - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, !update); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq); } static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, @@ -3175,7 +2955,7 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) * Called after adding a new station to initialize rate scaling */ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum nl80211_band band, bool update) + enum nl80211_band band) { int i, j; struct ieee80211_hw *hw = mvm->hw; @@ -3186,6 +2966,8 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_supported_band *sband; unsigned long supp; /* must be unsigned long for for_each_set_bit */ + lockdep_assert_held(&mvmsta->lq_sta.rs_drv.pers.lock); + /* clear all non-persistent lq data */ memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); @@ -3255,7 +3037,7 @@ static void rs_drv_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, #ifdef CONFIG_IWLWIFI_DEBUGFS iwl_mvm_reset_frame_stats(mvm); #endif - rs_initialize_lq(mvm, sta, lq_sta, band, update); + rs_initialize_lq(mvm, sta, lq_sta, band); } static void rs_drv_rate_update(void *mvm_r, @@ -3278,6 +3060,258 @@ static void rs_drv_rate_update(void *mvm_r, iwl_mvm_rs_rate_init(mvm, sta, sband->band, true); } +static void __iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + int tid, struct ieee80211_tx_info *info, + bool ndp) +{ + int legacy_success; + int retries; + int i; + struct iwl_lq_cmd *table; + u32 lq_hwrate; + struct rs_rate lq_rate, tx_resp_rate; + struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; + u32 tlc_info = (uintptr_t)info->status.status_driver_data[0]; + u8 reduced_txp = tlc_info & RS_DRV_DATA_TXP_MSK; + u8 lq_color = RS_DRV_DATA_LQ_COLOR_GET(tlc_info); + u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1]; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta.rs_drv; + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->pers.drv) { + IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); + return; + } + + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + if (rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, + &tx_resp_rate)) { + WARN_ON_ONCE(1); + return; + } + +#ifdef CONFIG_MAC80211_DEBUGFS + /* Disable last tx check if we are debugging with fixed rate but + * update tx stats + */ + if (lq_sta->pers.dbg_fixed_rate) { + int index = tx_resp_rate.index; + enum rs_column column; + int attempts, success; + + column = rs_get_column_from_rate(&tx_resp_rate); + if (WARN_ONCE(column == RS_COLUMN_INVALID, + "Can't map rate 0x%x to column", + tx_resp_hwrate)) + return; + + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + attempts = info->status.ampdu_len; + success = info->status.ampdu_ack_len; + } else { + attempts = info->status.rates[0].count; + success = !!(info->flags & IEEE80211_TX_STAT_ACK); + } + + lq_sta->pers.tx_stats[column][index].total += attempts; + lq_sta->pers.tx_stats[column][index].success += success; + + IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n", + tx_resp_hwrate, success, attempts); + return; + } +#endif + + if (time_after(jiffies, + (unsigned long)(lq_sta->last_tx + + (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { + IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); + /* reach here only in case of driver RS, call directly + * the unlocked version + */ + rs_drv_rate_init(mvm, sta, info->band); + return; + } + lq_sta->last_tx = jiffies; + + /* Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ + table = &lq_sta->lq; + lq_hwrate = le32_to_cpu(table->rs_table[0]); + if (rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate)) { + WARN_ON_ONCE(1); + return; + } + + /* Here we actually compare this rate to the latest LQ command */ + if (lq_color != LQ_FLAG_COLOR_GET(table->flags)) { + IWL_DEBUG_RATE(mvm, + "tx resp color 0x%x does not match 0x%x\n", + lq_color, LQ_FLAG_COLOR_GET(table->flags)); + + /* Since rates mis-match, the last LQ command may have failed. + * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with + * ... driver. + */ + lq_sta->missed_rate_counter++; + if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { + lq_sta->missed_rate_counter = 0; + IWL_DEBUG_RATE(mvm, + "Too many rates mismatch. Send sync LQ. rs_state %d\n", + lq_sta->rs_state); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq); + } + /* Regardless, ignore this status info for outdated rate */ + return; + } + + /* Rate did match, so reset the missed_rate_counter */ + lq_sta->missed_rate_counter = 0; + + if (!lq_sta->search_better_tbl) { + curr_tbl = &lq_sta->lq_info[lq_sta->active_tbl]; + other_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; + } else { + curr_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; + other_tbl = &lq_sta->lq_info[lq_sta->active_tbl]; + } + + if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) { + IWL_DEBUG_RATE(mvm, + "Neither active nor search matches tx rate\n"); + tmp_tbl = &lq_sta->lq_info[lq_sta->active_tbl]; + rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); + tmp_tbl = &lq_sta->lq_info[rs_search_tbl(lq_sta->active_tbl)]; + rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); + rs_dump_rate(mvm, &lq_rate, "ACTUAL"); + + /* no matching table found, let's by-pass the data collection + * and continue to perform rate scale to find the rate table + */ + rs_stay_in_table(lq_sta, true); + goto done; + } + + /* Updating the frame history depends on whether packets were + * aggregated. + * + * For aggregation, all packets were transmitted at the same rate, the + * first index into rate scale table. + */ + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index, + info->status.ampdu_len, + info->status.ampdu_ack_len, + reduced_txp); + + /* ampdu_ack_len = 0 marks no BA was received. For TLC, treat + * it as a single frame loss as we don't want the success ratio + * to dip too quickly because a BA wasn't received. + * For TPC, there's no need for this optimisation since we want + * to recover very quickly from a bad power reduction and, + * therefore we'd like the success ratio to get an immediate hit + * when failing to get a BA, so we'd switch back to a lower or + * zero power reduction. When FW transmits agg with a rate + * different from the initial rate, it will not use reduced txp + * and will send BA notification twice (one empty with reduced + * txp equal to the value from LQ and one with reduced txp 0). + * We need to update counters for each txp level accordingly. + */ + if (info->status.ampdu_ack_len == 0) + info->status.ampdu_len = 1; + + rs_collect_tlc_data(mvm, mvmsta, tid, curr_tbl, + tx_resp_rate.index, + info->status.ampdu_len, + info->status.ampdu_ack_len); + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { + lq_sta->total_success += info->status.ampdu_ack_len; + lq_sta->total_failed += (info->status.ampdu_len - + info->status.ampdu_ack_len); + } + } else { + /* For legacy, update frame history with for each Tx retry. */ + retries = info->status.rates[0].count - 1; + /* HW doesn't send more than 15 retries */ + retries = min(retries, 15); + + /* The last transmission may have been successful */ + legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); + /* Collect data for each rate used during failed TX attempts */ + for (i = 0; i <= retries; ++i) { + lq_hwrate = le32_to_cpu(table->rs_table[i]); + if (rs_rate_from_ucode_rate(lq_hwrate, info->band, + &lq_rate)) { + WARN_ON_ONCE(1); + return; + } + + /* Only collect stats if retried rate is in the same RS + * table as active/search. + */ + if (rs_rate_column_match(&lq_rate, &curr_tbl->rate)) + tmp_tbl = curr_tbl; + else if (rs_rate_column_match(&lq_rate, + &other_tbl->rate)) + tmp_tbl = other_tbl; + else + continue; + + rs_collect_tpc_data(mvm, lq_sta, tmp_tbl, + tx_resp_rate.index, 1, + i < retries ? 0 : legacy_success, + reduced_txp); + rs_collect_tlc_data(mvm, mvmsta, tid, tmp_tbl, + tx_resp_rate.index, 1, + i < retries ? 0 : legacy_success); + } + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { + lq_sta->total_success += legacy_success; + lq_sta->total_failed += retries + (1 - legacy_success); + } + } + /* The last TX rate is cached in lq_sta; it's set in if/else above */ + lq_sta->last_rate_n_flags = lq_hwrate; + IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp); +done: + /* See if there's a better rate or modulation mode to try. */ + if (sta->supp_rates[info->band]) + rs_rate_scale_perform(mvm, sta, lq_sta, tid, ndp); +} + +void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, struct ieee80211_tx_info *info, bool ndp) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + /* If it's locked we are in middle of init flow + * just wait for next tx status to update the lq_sta data + */ + if (!spin_trylock(&mvmsta->lq_sta.rs_drv.pers.lock)) + return; + + __iwl_mvm_rs_tx_status(mvm, sta, tid, info, ndp); + spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock); +} + #ifdef CONFIG_MAC80211_DEBUGFS static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq_cmd, @@ -3569,7 +3603,7 @@ static void rs_set_lq_ss_params(struct iwl_mvm *mvm, bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params); - iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false); + iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd); ss_params |= LQ_SS_BFER_ALLOWED; IWL_DEBUG_RATE(mvm, @@ -3735,7 +3769,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm, if (lq_sta->pers.dbg_fixed_rate) { rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL); - iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false); + iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq); } } @@ -4132,10 +4166,15 @@ static const struct rate_control_ops rs_mvm_ops_drv = { void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, enum nl80211_band band, bool update) { - if (iwl_mvm_has_tlc_offload(mvm)) + if (iwl_mvm_has_tlc_offload(mvm)) { rs_fw_rate_init(mvm, sta, band, update); - else - rs_drv_rate_init(mvm, sta, band, update); + } else { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + spin_lock(&mvmsta->lq_sta.rs_drv.pers.lock); + rs_drv_rate_init(mvm, sta, band); + spin_unlock(&mvmsta->lq_sta.rs_drv.pers.lock); + } } int iwl_mvm_rate_control_register(void) @@ -4165,7 +4204,7 @@ static int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, lq->flags &= ~LQ_FLAG_USE_RTS_MSK; } - return iwl_mvm_send_lq_cmd(mvm, lq, false); + return iwl_mvm_send_lq_cmd(mvm, lq); } /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index f7eb60dbaf20..428642e66658 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -4,7 +4,7 @@ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Mobile Communications GmbH * Copyright(c) 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * * Contact Information: * Intel Linux Wireless <linuxwifi@intel.com> @@ -390,6 +390,7 @@ struct iwl_lq_sta { s8 last_rssi; struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT]; struct iwl_mvm *drv; + spinlock_t lock; /* for races in reinit/update table */ } pers; }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 64f950501287..854edd7d7103 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -463,20 +463,22 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue, } int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, - const u8 *data, u32 count) + const u8 *data, u32 count, bool async) { - struct iwl_rxq_sync_cmd *cmd; + u8 buf[sizeof(struct iwl_rxq_sync_cmd) + + sizeof(struct iwl_mvm_rss_sync_notif)]; + struct iwl_rxq_sync_cmd *cmd = (void *)buf; u32 data_size = sizeof(*cmd) + count; int ret; - /* should be DWORD aligned */ - if (WARN_ON(count & 3 || count > IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE)) + /* + * size must be a multiple of DWORD + * Ensure we don't overflow buf + */ + if (WARN_ON(count & 3 || + count > sizeof(struct iwl_mvm_rss_sync_notif))) return -EINVAL; - cmd = kzalloc(data_size, GFP_KERNEL); - if (!cmd) - return -ENOMEM; - cmd->rxq_mask = cpu_to_le32(rxq_mask); cmd->count = cpu_to_le32(count); cmd->flags = 0; @@ -485,9 +487,8 @@ int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD), - 0, data_size, cmd); + async ? CMD_ASYNC : 0, data_size, cmd); - kfree(cmd); return ret; } @@ -503,14 +504,31 @@ static bool iwl_mvm_is_sn_less(u16 sn1, u16 sn2, u16 buffer_size) !ieee80211_sn_less(sn1, sn2 - buffer_size); } +static void iwl_mvm_sync_nssn(struct iwl_mvm *mvm, u8 baid, u16 nssn) +{ + struct iwl_mvm_rss_sync_notif notif = { + .metadata.type = IWL_MVM_RXQ_NSSN_SYNC, + .metadata.sync = 0, + .nssn_sync.baid = baid, + .nssn_sync.nssn = nssn, + }; + + iwl_mvm_sync_rx_queues_internal(mvm, (void *)¬if, sizeof(notif)); +} + #define RX_REORDER_BUF_TIMEOUT_MQ (HZ / 10) +enum iwl_mvm_release_flags { + IWL_MVM_RELEASE_SEND_RSS_SYNC = BIT(0), + IWL_MVM_RELEASE_FROM_RSS_SYNC = BIT(1), +}; + static void iwl_mvm_release_frames(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct napi_struct *napi, struct iwl_mvm_baid_data *baid_data, struct iwl_mvm_reorder_buffer *reorder_buf, - u16 nssn) + u16 nssn, u32 flags) { struct iwl_mvm_reorder_buf_entry *entries = &baid_data->entries[reorder_buf->queue * @@ -519,6 +537,18 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm, lockdep_assert_held(&reorder_buf->lock); + /* + * We keep the NSSN not too far behind, if we are sync'ing it and it + * is more than 2048 ahead of us, it must be behind us. Discard it. + * This can happen if the queue that hit the 0 / 2048 seqno was lagging + * behind and this queue already processed packets. The next if + * would have caught cases where this queue would have processed less + * than 64 packets, but it may have processed more than 64 packets. + */ + if ((flags & IWL_MVM_RELEASE_FROM_RSS_SYNC) && + ieee80211_sn_less(nssn, ssn)) + goto set_timer; + /* ignore nssn smaller than head sn - this can happen due to timeout */ if (iwl_mvm_is_sn_less(nssn, ssn, reorder_buf->buf_size)) goto set_timer; @@ -529,6 +559,9 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm, struct sk_buff *skb; ssn = ieee80211_sn_inc(ssn); + if ((flags & IWL_MVM_RELEASE_SEND_RSS_SYNC) && + (ssn == 2048 || ssn == 0)) + iwl_mvm_sync_nssn(mvm, baid_data->baid, ssn); /* * Empty the list. Will have more than one frame for A-MSDU. @@ -615,7 +648,8 @@ void iwl_mvm_reorder_timer_expired(struct timer_list *t) sta_id, sn); iwl_mvm_event_frame_timeout_callback(buf->mvm, mvmsta->vif, sta, baid_data->tid); - iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data, buf, sn); + iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data, + buf, sn, IWL_MVM_RELEASE_SEND_RSS_SYNC); rcu_read_unlock(); } else { /* @@ -657,7 +691,8 @@ static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue, spin_lock_bh(&reorder_buf->lock); iwl_mvm_release_frames(mvm, sta, NULL, ba_data, reorder_buf, ieee80211_sn_add(reorder_buf->head_sn, - reorder_buf->buf_size)); + reorder_buf->buf_size), + 0); spin_unlock_bh(&reorder_buf->lock); del_timer_sync(&reorder_buf->reorder_timer); @@ -665,8 +700,54 @@ out: rcu_read_unlock(); } -void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, - int queue) +static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm, + struct napi_struct *napi, + u8 baid, u16 nssn, int queue, + u32 flags) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_reorder_buffer *reorder_buf; + struct iwl_mvm_baid_data *ba_data; + + IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n", + baid, nssn); + + if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID || + baid >= ARRAY_SIZE(mvm->baid_map))) + return; + + rcu_read_lock(); + + ba_data = rcu_dereference(mvm->baid_map[baid]); + if (WARN_ON_ONCE(!ba_data)) + goto out; + + sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]); + if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) + goto out; + + reorder_buf = &ba_data->reorder_buf[queue]; + + spin_lock_bh(&reorder_buf->lock); + iwl_mvm_release_frames(mvm, sta, napi, ba_data, + reorder_buf, nssn, flags); + spin_unlock_bh(&reorder_buf->lock); + +out: + rcu_read_unlock(); +} + +static void iwl_mvm_nssn_sync(struct iwl_mvm *mvm, + struct napi_struct *napi, int queue, + const struct iwl_mvm_nssn_sync_data *data) +{ + iwl_mvm_release_frames_from_notif(mvm, napi, data->baid, + data->nssn, queue, + IWL_MVM_RELEASE_FROM_RSS_SYNC); +} + +void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rxq_sync_notification *notif; @@ -687,6 +768,10 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, case IWL_MVM_RXQ_NOTIF_DEL_BA: iwl_mvm_del_ba(mvm, queue, (void *)internal_notif->data); break; + case IWL_MVM_RXQ_NSSN_SYNC: + iwl_mvm_nssn_sync(mvm, napi, queue, + (void *)internal_notif->data); + break; default: WARN_ONCE(1, "Invalid identifier %d", internal_notif->type); } @@ -785,7 +870,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, } if (ieee80211_is_back_req(hdr->frame_control)) { - iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn); + iwl_mvm_release_frames(mvm, sta, napi, baid_data, + buffer, nssn, 0); goto drop; } @@ -794,7 +880,10 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * If the SN is smaller than the NSSN it might need to first go into * the reorder buffer, in which case we just release up to it and the * rest of the function will take care of storing it and releasing up to - * the nssn + * the nssn. + * This should not happen. This queue has been lagging and it should + * have been updated by a IWL_MVM_RXQ_NSSN_SYNC notification. Be nice + * and update the other queues. */ if (!iwl_mvm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size, buffer->buf_size) || @@ -802,7 +891,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn; iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, - min_sn); + min_sn, IWL_MVM_RELEASE_SEND_RSS_SYNC); } /* drop any oudated packets */ @@ -813,8 +902,23 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { if (iwl_mvm_is_sn_less(buffer->head_sn, nssn, buffer->buf_size) && - (!amsdu || last_subframe)) + (!amsdu || last_subframe)) { + /* + * If we crossed the 2048 or 0 SN, notify all the + * queues. This is done in order to avoid having a + * head_sn that lags behind for too long. When that + * happens, we can get to a situation where the head_sn + * is within the interval [nssn - buf_size : nssn] + * which will make us think that the nssn is a packet + * that we already freed because of the reordering + * buffer and we will ignore it. So maintain the + * head_sn somewhat updated across all the queues: + * when it crosses 0 and 2048. + */ + if (sn == 2048 || sn == 0) + iwl_mvm_sync_nssn(mvm, baid, sn); buffer->head_sn = nssn; + } /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; @@ -829,8 +933,11 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * while technically there is no hole and we can move forward. */ if (!buffer->num_stored && sn == buffer->head_sn) { - if (!amsdu || last_subframe) + if (!amsdu || last_subframe) { + if (sn == 2048 || sn == 0) + iwl_mvm_sync_nssn(mvm, baid, sn); buffer->head_sn = ieee80211_sn_inc(buffer->head_sn); + } /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; @@ -875,7 +982,9 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * release notification with up to date NSSN. */ if (!amsdu || last_subframe) - iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn); + iwl_mvm_release_frames(mvm, sta, napi, baid_data, + buffer, nssn, + IWL_MVM_RELEASE_SEND_RSS_SYNC); spin_unlock_bh(&buffer->lock); return true; @@ -1840,40 +1949,14 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, out: rcu_read_unlock(); } + void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_frame_release *release = (void *)pkt->data; - struct ieee80211_sta *sta; - struct iwl_mvm_reorder_buffer *reorder_buf; - struct iwl_mvm_baid_data *ba_data; - - int baid = release->baid; - - IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n", - release->baid, le16_to_cpu(release->nssn)); - if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID)) - return; - - rcu_read_lock(); - - ba_data = rcu_dereference(mvm->baid_map[baid]); - if (WARN_ON_ONCE(!ba_data)) - goto out; - - sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]); - if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) - goto out; - - reorder_buf = &ba_data->reorder_buf[queue]; - - spin_lock_bh(&reorder_buf->lock); - iwl_mvm_release_frames(mvm, sta, napi, ba_data, reorder_buf, - le16_to_cpu(release->nssn)); - spin_unlock_bh(&reorder_buf->lock); - -out: - rcu_read_unlock(); + iwl_mvm_release_frames_from_notif(mvm, napi, release->baid, + le16_to_cpu(release->nssn), + queue, 0); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index f545a737a92d..10f18536dd0d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1684,6 +1684,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, */ if (iwl_mvm_has_tlc_offload(mvm)) iwl_mvm_rs_add_sta(mvm, mvm_sta); + else + spin_lock_init(&mvm_sta->lq_sta.rs_drv.pers.lock); iwl_mvm_toggle_tx_ant(mvm, &mvm_sta->tx_ant); @@ -2421,7 +2423,7 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) static void iwl_mvm_sync_rxq_del_ba(struct iwl_mvm *mvm, u8 baid) { - struct iwl_mvm_delba_notif notif = { + struct iwl_mvm_rss_sync_notif notif = { .metadata.type = IWL_MVM_RXQ_NOTIF_DEL_BA, .metadata.sync = 1, .delba.baid = baid, @@ -2972,7 +2974,7 @@ out: IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", sta->addr, tid); - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.rs_drv.lq, false); + return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.rs_drv.lq); } static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 4487cc3e07c1..8d70093847cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -343,9 +343,17 @@ struct iwl_mvm_delba_data { u32 baid; } __packed; -struct iwl_mvm_delba_notif { +struct iwl_mvm_nssn_sync_data { + u32 baid; + u32 nssn; +} __packed; + +struct iwl_mvm_rss_sync_notif { struct iwl_mvm_internal_rxq_notif metadata; - struct iwl_mvm_delba_data delba; + union { + struct iwl_mvm_delba_data delba; + struct iwl_mvm_nssn_sync_data nssn_sync; + }; } __packed; /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index a3e5d88f1c07..6ac114a393cc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -831,6 +831,7 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, unsigned int tcp_payload_len; unsigned int mss = skb_shinfo(skb)->gso_size; bool ipv4 = (skb->protocol == htons(ETH_P_IP)); + bool qos = ieee80211_is_data_qos(hdr->frame_control); u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; skb_shinfo(skb)->gso_size = num_subframes * mss; @@ -864,7 +865,7 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes, if (tcp_payload_len > mss) { skb_shinfo(tmp)->gso_size = mss; } else { - if (ieee80211_is_data_qos(hdr->frame_control)) { + if (qos) { u8 *qc; if (ipv4) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 9ecd5f09615a..b8e20a01c192 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -653,12 +653,12 @@ int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id, * this case to clear the state indicating that station creation is in * progress. */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool sync) +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq) { struct iwl_host_cmd cmd = { .id = LQ_CMD, .len = { sizeof(struct iwl_lq_cmd), }, - .flags = sync ? 0 : CMD_ASYNC, + .flags = CMD_ASYNC, .data = { lq, }, }; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index ea2a03d4bf55..3b12e7ad35e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -604,10 +604,13 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x2526, 0x40A4, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x4234, iwl9560_2ac_cfg_soc)}, {IWL_PCI_DEVICE(0x2526, 0x42A4, iwl9462_2ac_cfg_soc)}, + {IWL_PCI_DEVICE(0x2526, 0x6010, iwl9260_2ac_160_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x6014, iwl9260_2ac_160_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x8014, iwl9260_2ac_160_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x8010, iwl9260_2ac_160_cfg)}, {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_160_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0xE010, iwl9260_2ac_160_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0xE014, iwl9260_2ac_160_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0010, iwl9160_2ac_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0014, iwl9160_2ac_cfg)}, {IWL_PCI_DEVICE(0x271B, 0x0210, iwl9160_2ac_cfg)}, @@ -1059,7 +1062,28 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_trans->cfg = &iwl9560_2ac_cfg_qu_c0_jf_b0; else if (iwl_trans->cfg == &iwl9560_2ac_160_cfg_qu_b0_jf_b0) iwl_trans->cfg = &iwl9560_2ac_160_cfg_qu_c0_jf_b0; + else if (iwl_trans->cfg == &killer1650s_2ax_cfg_qu_b0_hr_b0) + iwl_trans->cfg = &killer1650s_2ax_cfg_qu_c0_hr_b0; + else if (iwl_trans->cfg == &killer1650i_2ax_cfg_qu_b0_hr_b0) + iwl_trans->cfg = &killer1650i_2ax_cfg_qu_c0_hr_b0; } + + /* same thing for QuZ... */ + if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QUZ) { + if (iwl_trans->cfg == &iwl_ax101_cfg_qu_hr) + iwl_trans->cfg = &iwl_ax101_cfg_quz_hr; + else if (iwl_trans->cfg == &iwl_ax201_cfg_qu_hr) + iwl_trans->cfg = &iwl_ax201_cfg_quz_hr; + else if (iwl_trans->cfg == &iwl9461_2ac_cfg_qu_b0_jf_b0) + iwl_trans->cfg = &iwl9461_2ac_cfg_quz_a0_jf_b0_soc; + else if (iwl_trans->cfg == &iwl9462_2ac_cfg_qu_b0_jf_b0) + iwl_trans->cfg = &iwl9462_2ac_cfg_quz_a0_jf_b0_soc; + else if (iwl_trans->cfg == &iwl9560_2ac_cfg_qu_b0_jf_b0) + iwl_trans->cfg = &iwl9560_2ac_cfg_quz_a0_jf_b0_soc; + else if (iwl_trans->cfg == &iwl9560_2ac_160_cfg_qu_b0_jf_b0) + iwl_trans->cfg = &iwl9560_2ac_160_cfg_quz_a0_jf_b0_soc; + } + #endif pci_set_drvdata(pdev, iwl_trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index f5df5b370d78..db62c8314603 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3602,11 +3602,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } } else if (CSR_HW_RF_ID_TYPE_CHIP_ID(trans->hw_rf_id) == CSR_HW_RF_ID_TYPE_CHIP_ID(CSR_HW_RF_ID_TYPE_HR) && - ((trans->cfg != &iwl_ax200_cfg_cc && - trans->cfg != &killer1650x_2ax_cfg && - trans->cfg != &killer1650w_2ax_cfg && - trans->cfg != &iwl_ax201_cfg_quz_hr) || - trans->hw_rev == CSR_HW_REV_TYPE_QNJ_B0)) { + trans->hw_rev == CSR_HW_REV_TYPE_QNJ_B0) { u32 hw_status; hw_status = iwl_read_prph(trans, UMAG_GEN_HW_STATUS); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 38d110338987..9ef6b8fe03c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -99,10 +99,7 @@ void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans_pcie *trans_pcie, u16 len = byte_cnt; __le16 bc_ent; - if (trans_pcie->bc_table_dword) - len = DIV_ROUND_UP(len, 4); - - if (WARN_ON(len > 0xFFF || idx >= txq->n_window)) + if (WARN(idx >= txq->n_window, "%d >= %d\n", idx, txq->n_window)) return; filled_tfd_size = offsetof(struct iwl_tfh_tfd, tbs) + @@ -117,11 +114,20 @@ void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans_pcie *trans_pcie, */ num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1; - bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12)); - if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) { + /* Starting from 22560, the HW expects bytes */ + WARN_ON(trans_pcie->bc_table_dword); + WARN_ON(len > 0x3FFF); + bc_ent = cpu_to_le16(len | (num_fetch_chunks << 14)); scd_bc_tbl_gen3->tfd_offset[idx] = bc_ent; - else + } else { + /* Until 22560, the HW expects DW */ + WARN_ON(!trans_pcie->bc_table_dword); + len = DIV_ROUND_UP(len, 4); + WARN_ON(len > 0xFFF); + bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12)); scd_bc_tbl->tfd_offset[idx] = bc_ent; + } } /* diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index fa4245d0d4a8..2f0ba7ef53b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -435,6 +435,8 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, DMA_TO_DEVICE); } + meta->tbs = 0; + if (trans->cfg->use_tfh) { struct iwl_tfh_tfd *tfd_fh = (void *)tfd; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 519b4ee88c5c..772e54f0696f 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -3617,10 +3617,12 @@ static int hwsim_dump_radio_nl(struct sk_buff *skb, hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, &hwsim_genl_family, NLM_F_MULTI, HWSIM_CMD_GET_RADIO); - if (!hdr) + if (hdr) { + genl_dump_check_consistent(cb, hdr); + genlmsg_end(skb, hdr); + } else { res = -EMSGSIZE; - genl_dump_check_consistent(cb, hdr); - genlmsg_end(skb, hdr); + } } done: diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c index 653d347a9a19..580387f9f12a 100644 --- a/drivers/net/wireless/marvell/mwifiex/ie.c +++ b/drivers/net/wireless/marvell/mwifiex/ie.c @@ -241,6 +241,9 @@ static int mwifiex_update_vs_ie(const u8 *ies, int ies_len, } vs_ie = (struct ieee_types_header *)vendor_ie; + if (le16_to_cpu(ie->ie_length) + vs_ie->len + 2 > + IEEE_MAX_IE_SIZE) + return -EINVAL; memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), vs_ie, vs_ie->len + 2); le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 2); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 3e442c7f7882..095837fba300 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -124,6 +124,7 @@ enum { #define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) +#define WPA_GTK_OUI_OFFSET 2 #define RSN_GTK_OUI_OFFSET 2 #define MWIFIEX_OUI_NOT_PRESENT 0 diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 0d6d41727037..21dda385f6c6 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -181,7 +181,8 @@ mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) u8 ret = MWIFIEX_OUI_NOT_PRESENT; if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) { - iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; + iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data + + WPA_GTK_OUI_OFFSET); oui = &mwifiex_wpa_oui[cipher][0]; ret = mwifiex_search_oui_in_ie(iebody, oui); if (ret) diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c index 18f7d9bf30b2..0939a8c8f3ab 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -265,6 +265,8 @@ mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); if (rate_ie) { + if (rate_ie->len > MWIFIEX_SUPPORTED_RATES) + return; memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); rate_len = rate_ie->len; } @@ -272,8 +274,11 @@ mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, params->beacon.tail, params->beacon.tail_len); - if (rate_ie) + if (rate_ie) { + if (rate_ie->len > MWIFIEX_SUPPORTED_RATES - rate_len) + return; memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len); + } return; } @@ -391,6 +396,8 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv, params->beacon.tail_len); if (vendor_ie) { wmm_ie = vendor_ie; + if (*(wmm_ie + 1) > sizeof(struct mwifiex_types_wmm_info)) + return; memcpy(&bss_cfg->wmm_info, wmm_ie + sizeof(struct ieee_types_header), *(wmm_ie + 1)); priv->wmm_enabled = 1; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c index 40c0d536e20d..9d4426f6905f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -59,6 +59,11 @@ static void mt76x0_set_chip_cap(struct mt76x02_dev *dev) dev_dbg(dev->mt76.dev, "mask out 2GHz support\n"); } + if (is_mt7630(dev)) { + dev->mt76.cap.has_5ghz = false; + dev_dbg(dev->mt76.dev, "mask out 5GHz support\n"); + } + if (!mt76x02_field_valid(nic_conf1 & 0xff)) nic_conf1 &= 0xff00; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c index 4585e1b756c2..6117e6ca08cb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -62,6 +62,19 @@ static void mt76x0e_stop(struct ieee80211_hw *hw) mt76x0e_stop_hw(dev); } +static int +mt76x0e_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt76x02_dev *dev = hw->priv; + + if (is_mt7630(dev)) + return -EOPNOTSUPP; + + return mt76x02_set_key(hw, cmd, vif, sta, key); +} + static void mt76x0e_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) @@ -78,7 +91,7 @@ static const struct ieee80211_ops mt76x0e_ops = { .configure_filter = mt76x02_configure_filter, .bss_info_changed = mt76x02_bss_info_changed, .sta_state = mt76_sta_state, - .set_key = mt76x02_set_key, + .set_key = mt76x0e_set_key, .conf_tx = mt76x02_conf_tx, .sw_scan_start = mt76x02_sw_scan, .sw_scan_complete = mt76x02_sw_scan_complete, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index 627ed1fc7b15..645f4d15fb61 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -136,11 +136,11 @@ static const struct ieee80211_ops mt76x0u_ops = { .release_buffered_frames = mt76_release_buffered_frames, }; -static int mt76x0u_init_hardware(struct mt76x02_dev *dev) +static int mt76x0u_init_hardware(struct mt76x02_dev *dev, bool reset) { int err; - mt76x0_chip_onoff(dev, true, true); + mt76x0_chip_onoff(dev, true, reset); if (!mt76x02_wait_for_mac(&dev->mt76)) return -ETIMEDOUT; @@ -173,7 +173,7 @@ static int mt76x0u_register_device(struct mt76x02_dev *dev) if (err < 0) goto out_err; - err = mt76x0u_init_hardware(dev); + err = mt76x0u_init_hardware(dev, true); if (err < 0) goto out_err; @@ -309,7 +309,7 @@ static int __maybe_unused mt76x0_resume(struct usb_interface *usb_intf) if (ret < 0) goto err; - ret = mt76x0u_init_hardware(dev); + ret = mt76x0u_init_hardware(dev, false); if (ret) goto err; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index c9b957ac5733..f1cdcd61c54a 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -1654,13 +1654,18 @@ static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev, offset = MAC_IVEIV_ENTRY(key->hw_key_idx); - rt2800_register_multiread(rt2x00dev, offset, - &iveiv_entry, sizeof(iveiv_entry)); - if ((crypto->cipher == CIPHER_TKIP) || - (crypto->cipher == CIPHER_TKIP_NO_MIC) || - (crypto->cipher == CIPHER_AES)) - iveiv_entry.iv[3] |= 0x20; - iveiv_entry.iv[3] |= key->keyidx << 6; + if (crypto->cmd == SET_KEY) { + rt2800_register_multiread(rt2x00dev, offset, + &iveiv_entry, sizeof(iveiv_entry)); + if ((crypto->cipher == CIPHER_TKIP) || + (crypto->cipher == CIPHER_TKIP_NO_MIC) || + (crypto->cipher == CIPHER_AES)) + iveiv_entry.iv[3] |= 0x20; + iveiv_entry.iv[3] |= key->keyidx << 6; + } else { + memset(&iveiv_entry, 0, sizeof(iveiv_entry)); + } + rt2800_register_multiwrite(rt2x00dev, offset, &iveiv_entry, sizeof(iveiv_entry)); } @@ -4237,24 +4242,18 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, switch (rt2x00dev->default_ant.rx_chain_num) { case 3: /* Turn on tertiary LNAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A2_EN, - rf->channel > 14); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G2_EN, - rf->channel <= 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A2_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G2_EN, 1); /* fall-through */ case 2: /* Turn on secondary LNAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, - rf->channel > 14); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, - rf->channel <= 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); /* fall-through */ case 1: /* Turn on primary LNAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, - rf->channel > 14); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, - rf->channel <= 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); break; } @@ -6095,6 +6094,15 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) } /* + * Clear encryption initialization vectors on start, but keep them + * for watchdog reset. Otherwise we will have wrong IVs and not be + * able to keep connections after reset. + */ + if (!test_bit(DEVICE_STATE_RESET, &rt2x00dev->flags)) + for (i = 0; i < 256; i++) + rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); + + /* * Clear all beacons */ for (i = 0; i < 8; i++) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 7e43690a861c..2b216edd0c7d 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -658,6 +658,7 @@ enum rt2x00_state_flags { DEVICE_STATE_ENABLED_RADIO, DEVICE_STATE_SCANNING, DEVICE_STATE_FLUSHING, + DEVICE_STATE_RESET, /* * Driver configuration diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 35414f97a978..9d158237ac67 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1256,13 +1256,14 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) { - int retval; + int retval = 0; if (test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) { /* * This is special case for ieee80211_restart_hw(), otherwise * mac80211 never call start() two times in row without stop(); */ + set_bit(DEVICE_STATE_RESET, &rt2x00dev->flags); rt2x00dev->ops->lib->pre_reset_hw(rt2x00dev); rt2x00lib_stop(rt2x00dev); } @@ -1273,14 +1274,14 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) */ retval = rt2x00lib_load_firmware(rt2x00dev); if (retval) - return retval; + goto out; /* * Initialize the device. */ retval = rt2x00lib_initialize(rt2x00dev); if (retval) - return retval; + goto out; rt2x00dev->intf_ap_count = 0; rt2x00dev->intf_sta_count = 0; @@ -1289,11 +1290,13 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) /* Enable the radio */ retval = rt2x00lib_enable_radio(rt2x00dev); if (retval) - return retval; + goto out; set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); - return 0; +out: + clear_bit(DEVICE_STATE_RESET, &rt2x00dev->flags); + return retval; } void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c index f5048d4b8cb6..760eaffeebd6 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -645,7 +645,6 @@ fail_rx: kfree(rsi_dev->tx_buffer); fail_eps: - kfree(rsi_dev); return status; } diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 1d9940d4e8c7..c9262ffeefe4 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -925,6 +925,7 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, skb_shinfo(skb)->nr_frags = MAX_SKB_FRAGS; nskb = xenvif_alloc_skb(0); if (unlikely(nskb == NULL)) { + skb_shinfo(skb)->nr_frags = 0; kfree_skb(skb); xenvif_tx_err(queue, &txreq, extra_count, idx); if (net_ratelimit()) @@ -940,6 +941,7 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, if (xenvif_set_skb_gso(queue->vif, skb, gso)) { /* Failure in xenvif_set_skb_gso is fatal. */ + skb_shinfo(skb)->nr_frags = 0; kfree_skb(skb); kfree_skb(nskb); break; diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index e65d027b91fa..529be35ac178 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c @@ -244,7 +244,7 @@ void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) /* Reset possible fault of previous session */ clear_bit(NFCMRVL_PHY_ERROR, &priv->flags); - if (priv->config.reset_n_io) { + if (gpio_is_valid(priv->config.reset_n_io)) { nfc_info(priv->dev, "reset the chip\n"); gpio_set_value(priv->config.reset_n_io, 0); usleep_range(5000, 10000); @@ -255,7 +255,7 @@ void nfcmrvl_chip_reset(struct nfcmrvl_private *priv) void nfcmrvl_chip_halt(struct nfcmrvl_private *priv) { - if (priv->config.reset_n_io) + if (gpio_is_valid(priv->config.reset_n_io)) gpio_set_value(priv->config.reset_n_io, 0); } diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c index 9a22056e8d9e..e5a622ce4b95 100644 --- a/drivers/nfc/nfcmrvl/uart.c +++ b/drivers/nfc/nfcmrvl/uart.c @@ -26,7 +26,7 @@ static unsigned int hci_muxed; static unsigned int flow_control; static unsigned int break_control; -static unsigned int reset_n_io; +static int reset_n_io = -EINVAL; /* ** NFCMRVL NCI OPS @@ -231,5 +231,5 @@ MODULE_PARM_DESC(break_control, "Tell if UART driver must drive break signal."); module_param(hci_muxed, uint, 0); MODULE_PARM_DESC(hci_muxed, "Tell if transport is muxed in HCI one."); -module_param(reset_n_io, uint, 0); +module_param(reset_n_io, int, 0); MODULE_PARM_DESC(reset_n_io, "GPIO that is wired to RESET_N signal."); diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index 945cc903d8f1..888e298f610b 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c @@ -305,6 +305,7 @@ static int nfcmrvl_probe(struct usb_interface *intf, /* No configuration for USB */ memset(&config, 0, sizeof(config)); + config.reset_n_io = -EINVAL; nfc_info(&udev->dev, "intf %p id %p\n", intf, id); diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c index c3e10b6ab3a4..f25f1ec5f9e9 100644 --- a/drivers/nfc/st-nci/se.c +++ b/drivers/nfc/st-nci/se.c @@ -333,6 +333,8 @@ static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev, transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev, skb->len - 2, GFP_KERNEL); + if (!transaction) + return -ENOMEM; transaction->aid_len = skb->data[1]; memcpy(transaction->aid, &skb->data[2], transaction->aid_len); diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c index 06fc542fd198..6586378cacb0 100644 --- a/drivers/nfc/st21nfca/se.c +++ b/drivers/nfc/st21nfca/se.c @@ -317,6 +317,8 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev, skb->len - 2, GFP_KERNEL); + if (!transaction) + return -ENOMEM; transaction->aid_len = skb->data[1]; memcpy(transaction->aid, &skb->data[2], diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c index e42850095892..7eda62a9e0df 100644 --- a/drivers/nfc/st95hf/core.c +++ b/drivers/nfc/st95hf/core.c @@ -316,7 +316,7 @@ static int st95hf_echo_command(struct st95hf_context *st95context) &echo_response); if (result) { dev_err(&st95context->spicontext.spidev->dev, - "err: echo response receieve error = 0x%x\n", result); + "err: echo response receive error = 0x%x\n", result); return result; } diff --git a/drivers/ntb/msi.c b/drivers/ntb/msi.c index 9dddf133658f..0a5e884a920c 100644 --- a/drivers/ntb/msi.c +++ b/drivers/ntb/msi.c @@ -6,11 +6,6 @@ #include <linux/msi.h> #include <linux/pci.h> -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_VERSION("0.1"); -MODULE_AUTHOR("Logan Gunthorpe <logang@deltatee.com>"); -MODULE_DESCRIPTION("NTB MSI Interrupt Library"); - struct ntb_msi { u64 base_addr; u64 end_addr; diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 3e7b11cf1aae..cb98b8fe786e 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -655,6 +655,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) resource_size_t start, size; struct nd_region *nd_region; unsigned long npfns, align; + u32 end_trunc; struct nd_pfn_sb *pfn_sb; phys_addr_t offset; const char *sig; @@ -696,6 +697,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) size = resource_size(&nsio->res); npfns = PHYS_PFN(size - SZ_8K); align = max(nd_pfn->align, (1UL << SUBSECTION_SHIFT)); + end_trunc = start + size - ALIGN_DOWN(start + size, align); if (nd_pfn->mode == PFN_MODE_PMEM) { /* * The altmap should be padded out to the block size used @@ -714,7 +716,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) return -ENXIO; } - npfns = PHYS_PFN(size - offset); + npfns = PHYS_PFN(size - offset - end_trunc); pfn_sb->mode = cpu_to_le32(nd_pfn->mode); pfn_sb->dataoff = cpu_to_le64(offset); pfn_sb->npfns = cpu_to_le64(npfns); @@ -723,6 +725,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) memcpy(pfn_sb->parent_uuid, nd_dev_to_uuid(&ndns->dev), 16); pfn_sb->version_major = cpu_to_le16(1); pfn_sb->version_minor = cpu_to_le16(3); + pfn_sb->end_trunc = cpu_to_le32(end_trunc); pfn_sb->align = cpu_to_le32(nd_pfn->align); checksum = nd_sb_checksum((struct nd_gen_sb *) pfn_sb); pfn_sb->checksum = cpu_to_le64(checksum); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 8f3fbe5ca937..d3d6b7bd6903 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1286,6 +1286,9 @@ static u32 nvme_passthru_start(struct nvme_ctrl *ctrl, struct nvme_ns *ns, */ if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK)) { mutex_lock(&ctrl->scan_lock); + mutex_lock(&ctrl->subsys->lock); + nvme_mpath_start_freeze(ctrl->subsys); + nvme_mpath_wait_freeze(ctrl->subsys); nvme_start_freeze(ctrl); nvme_wait_freeze(ctrl); } @@ -1316,6 +1319,8 @@ static void nvme_passthru_end(struct nvme_ctrl *ctrl, u32 effects) nvme_update_formats(ctrl); if (effects & (NVME_CMD_EFFECTS_LBCC | NVME_CMD_EFFECTS_CSE_MASK)) { nvme_unfreeze(ctrl); + nvme_mpath_unfreeze(ctrl->subsys); + mutex_unlock(&ctrl->subsys->lock); mutex_unlock(&ctrl->scan_lock); } if (effects & NVME_CMD_EFFECTS_CCC) @@ -1715,6 +1720,7 @@ static void __nvme_revalidate_disk(struct gendisk *disk, struct nvme_id_ns *id) if (ns->head->disk) { nvme_update_disk_info(ns->head->disk, ns, id); blk_queue_stack_limits(ns->head->disk->queue, ns->queue); + revalidate_disk(ns->head->disk); } #endif } @@ -2251,6 +2257,16 @@ static const struct nvme_core_quirk_entry core_quirks[] = { .vid = 0x1179, .mn = "THNSF5256GPUK TOSHIBA", .quirks = NVME_QUIRK_NO_APST, + }, + { + /* + * This LiteON CL1-3D*-Q11 firmware version has a race + * condition associated with actions related to suspend to idle + * LiteON has resolved the problem in future firmware + */ + .vid = 0x14a4, + .fr = "22301111", + .quirks = NVME_QUIRK_SIMPLE_SUSPEND, } }; @@ -2487,6 +2503,7 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id) if (ret) { dev_err(ctrl->device, "failed to register subsystem device.\n"); + put_device(&subsys->dev); goto out_unlock; } ida_init(&subsys->ns_ida); @@ -2509,7 +2526,6 @@ out_put_subsystem: nvme_put_subsystem(subsys); out_unlock: mutex_unlock(&nvme_subsystems_lock); - put_device(&subsys->dev); return ret; } @@ -2591,6 +2607,9 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) goto out_free; } + if (!(ctrl->ops->flags & NVME_F_FABRICS)) + ctrl->cntlid = le16_to_cpu(id->cntlid); + if (!ctrl->identified) { int i; @@ -2691,7 +2710,6 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) goto out_free; } } else { - ctrl->cntlid = le16_to_cpu(id->cntlid); ctrl->hmpre = le32_to_cpu(id->hmpre); ctrl->hmmin = le32_to_cpu(id->hmmin); ctrl->hmminds = le32_to_cpu(id->hmminds); @@ -3571,6 +3589,13 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl) struct nvme_ns *ns, *next; LIST_HEAD(ns_list); + /* + * make sure to requeue I/O to all namespaces as these + * might result from the scan itself and must complete + * for the scan_work to make progress + */ + nvme_mpath_clear_ctrl_paths(ctrl); + /* prevent racing with ns scanning */ flush_work(&ctrl->scan_work); diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index 4f0d0d12744e..af831d3d15d0 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -12,6 +12,36 @@ module_param(multipath, bool, 0444); MODULE_PARM_DESC(multipath, "turn on native support for multiple controllers per subsystem"); +void nvme_mpath_unfreeze(struct nvme_subsystem *subsys) +{ + struct nvme_ns_head *h; + + lockdep_assert_held(&subsys->lock); + list_for_each_entry(h, &subsys->nsheads, entry) + if (h->disk) + blk_mq_unfreeze_queue(h->disk->queue); +} + +void nvme_mpath_wait_freeze(struct nvme_subsystem *subsys) +{ + struct nvme_ns_head *h; + + lockdep_assert_held(&subsys->lock); + list_for_each_entry(h, &subsys->nsheads, entry) + if (h->disk) + blk_mq_freeze_queue_wait(h->disk->queue); +} + +void nvme_mpath_start_freeze(struct nvme_subsystem *subsys) +{ + struct nvme_ns_head *h; + + lockdep_assert_held(&subsys->lock); + list_for_each_entry(h, &subsys->nsheads, entry) + if (h->disk) + blk_freeze_queue_start(h->disk->queue); +} + /* * If multipathing is enabled we need to always use the subsystem instance * number for numbering our devices to avoid conflicts between subsystems that @@ -104,18 +134,34 @@ static const char *nvme_ana_state_names[] = { [NVME_ANA_CHANGE] = "change", }; -void nvme_mpath_clear_current_path(struct nvme_ns *ns) +bool nvme_mpath_clear_current_path(struct nvme_ns *ns) { struct nvme_ns_head *head = ns->head; + bool changed = false; int node; if (!head) - return; + goto out; for_each_node(node) { - if (ns == rcu_access_pointer(head->current_path[node])) + if (ns == rcu_access_pointer(head->current_path[node])) { rcu_assign_pointer(head->current_path[node], NULL); + changed = true; + } } +out: + return changed; +} + +void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl) +{ + struct nvme_ns *ns; + + mutex_lock(&ctrl->scan_lock); + list_for_each_entry(ns, &ctrl->namespaces, list) + if (nvme_mpath_clear_current_path(ns)) + kblockd_schedule_work(&ns->head->requeue_work); + mutex_unlock(&ctrl->scan_lock); } static bool nvme_path_is_disabled(struct nvme_ns *ns) @@ -226,6 +272,24 @@ inline struct nvme_ns *nvme_find_path(struct nvme_ns_head *head) return ns; } +static bool nvme_available_path(struct nvme_ns_head *head) +{ + struct nvme_ns *ns; + + list_for_each_entry_rcu(ns, &head->list, siblings) { + switch (ns->ctrl->state) { + case NVME_CTRL_LIVE: + case NVME_CTRL_RESETTING: + case NVME_CTRL_CONNECTING: + /* fallthru */ + return true; + default: + break; + } + } + return false; +} + static blk_qc_t nvme_ns_head_make_request(struct request_queue *q, struct bio *bio) { @@ -252,14 +316,14 @@ static blk_qc_t nvme_ns_head_make_request(struct request_queue *q, disk_devt(ns->head->disk), bio->bi_iter.bi_sector); ret = direct_make_request(bio); - } else if (!list_empty_careful(&head->list)) { - dev_warn_ratelimited(dev, "no path available - requeuing I/O\n"); + } else if (nvme_available_path(head)) { + dev_warn_ratelimited(dev, "no usable path - requeuing I/O\n"); spin_lock_irq(&head->requeue_lock); bio_list_add(&head->requeue_list, bio); spin_unlock_irq(&head->requeue_lock); } else { - dev_warn_ratelimited(dev, "no path - failing I/O\n"); + dev_warn_ratelimited(dev, "no available path - failing I/O\n"); bio->bi_status = BLK_STS_IOERR; bio_endio(bio); @@ -364,6 +428,7 @@ static void nvme_mpath_set_live(struct nvme_ns *ns) srcu_read_unlock(&head->srcu, srcu_idx); } + synchronize_srcu(&ns->head->srcu); kblockd_schedule_work(&ns->head->requeue_work); } diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 26b563f9985b..2d678fb968c7 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -92,6 +92,11 @@ enum nvme_quirks { * Broken Write Zeroes. */ NVME_QUIRK_DISABLE_WRITE_ZEROES = (1 << 9), + + /* + * Force simple suspend/resume path. + */ + NVME_QUIRK_SIMPLE_SUSPEND = (1 << 10), }; /* @@ -490,6 +495,9 @@ static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl) return ctrl->ana_log_buf != NULL; } +void nvme_mpath_unfreeze(struct nvme_subsystem *subsys); +void nvme_mpath_wait_freeze(struct nvme_subsystem *subsys); +void nvme_mpath_start_freeze(struct nvme_subsystem *subsys); void nvme_set_disk_name(char *disk_name, struct nvme_ns *ns, struct nvme_ctrl *ctrl, int *flags); void nvme_failover_req(struct request *req); @@ -500,7 +508,8 @@ void nvme_mpath_remove_disk(struct nvme_ns_head *head); int nvme_mpath_init(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id); void nvme_mpath_uninit(struct nvme_ctrl *ctrl); void nvme_mpath_stop(struct nvme_ctrl *ctrl); -void nvme_mpath_clear_current_path(struct nvme_ns *ns); +bool nvme_mpath_clear_current_path(struct nvme_ns *ns); +void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl); struct nvme_ns *nvme_find_path(struct nvme_ns_head *head); static inline void nvme_mpath_check_last_path(struct nvme_ns *ns) @@ -548,7 +557,11 @@ static inline void nvme_mpath_add_disk(struct nvme_ns *ns, static inline void nvme_mpath_remove_disk(struct nvme_ns_head *head) { } -static inline void nvme_mpath_clear_current_path(struct nvme_ns *ns) +static inline bool nvme_mpath_clear_current_path(struct nvme_ns *ns) +{ + return false; +} +static inline void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl) { } static inline void nvme_mpath_check_last_path(struct nvme_ns *ns) @@ -568,6 +581,15 @@ static inline void nvme_mpath_uninit(struct nvme_ctrl *ctrl) static inline void nvme_mpath_stop(struct nvme_ctrl *ctrl) { } +static inline void nvme_mpath_unfreeze(struct nvme_subsystem *subsys) +{ +} +static inline void nvme_mpath_wait_freeze(struct nvme_subsystem *subsys) +{ +} +static inline void nvme_mpath_start_freeze(struct nvme_subsystem *subsys) +{ +} #endif /* CONFIG_NVME_MULTIPATH */ #ifdef CONFIG_NVM diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index db160cee42ad..732d5b63ec05 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2695,7 +2695,7 @@ static void nvme_async_probe(void *data, async_cookie_t cookie) { struct nvme_dev *dev = data; - nvme_reset_ctrl_sync(&dev->ctrl); + flush_work(&dev->ctrl.reset_work); flush_work(&dev->ctrl.scan_work); nvme_put_ctrl(&dev->ctrl); } @@ -2761,6 +2761,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_info(dev->ctrl.device, "pci function %s\n", dev_name(&pdev->dev)); + nvme_reset_ctrl(&dev->ctrl); nvme_get_ctrl(&dev->ctrl); async_schedule(nvme_async_probe, dev); @@ -2846,7 +2847,7 @@ static int nvme_resume(struct device *dev) struct nvme_dev *ndev = pci_get_drvdata(to_pci_dev(dev)); struct nvme_ctrl *ctrl = &ndev->ctrl; - if (pm_resume_via_firmware() || !ctrl->npss || + if (ndev->last_ps == U32_MAX || nvme_set_power_state(ctrl, ndev->last_ps) != 0) nvme_reset_ctrl(ctrl); return 0; @@ -2859,6 +2860,8 @@ static int nvme_suspend(struct device *dev) struct nvme_ctrl *ctrl = &ndev->ctrl; int ret = -EBUSY; + ndev->last_ps = U32_MAX; + /* * The platform does not remove power for a kernel managed suspend so * use host managed nvme power settings for lowest idle power if @@ -2866,8 +2869,15 @@ static int nvme_suspend(struct device *dev) * shutdown. But if the firmware is involved after the suspend or the * device does not support any non-default power states, shut down the * device fully. + * + * If ASPM is not enabled for the device, shut down the device and allow + * the PCI bus layer to put it into D3 in order to take the PCIe link + * down, so as to allow the platform to achieve its minimum low-power + * state (which may not be possible if the link is up). */ - if (pm_suspend_via_firmware() || !ctrl->npss) { + if (pm_suspend_via_firmware() || !ctrl->npss || + !pcie_aspm_enabled(pdev) || + (ndev->ctrl.quirks & NVME_QUIRK_SIMPLE_SUSPEND)) { nvme_dev_disable(ndev, true); return 0; } @@ -2880,7 +2890,6 @@ static int nvme_suspend(struct device *dev) ctrl->state != NVME_CTRL_ADMIN_ONLY) goto unfreeze; - ndev->last_ps = 0; ret = nvme_get_power_state(ctrl, &ndev->last_ps); if (ret < 0) goto unfreeze; diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index a249db528d54..1a6449bc547b 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -562,13 +562,17 @@ out_destroy_cm_id: return ret; } +static void __nvme_rdma_stop_queue(struct nvme_rdma_queue *queue) +{ + rdma_disconnect(queue->cm_id); + ib_drain_qp(queue->qp); +} + static void nvme_rdma_stop_queue(struct nvme_rdma_queue *queue) { if (!test_and_clear_bit(NVME_RDMA_Q_LIVE, &queue->flags)) return; - - rdma_disconnect(queue->cm_id); - ib_drain_qp(queue->qp); + __nvme_rdma_stop_queue(queue); } static void nvme_rdma_free_queue(struct nvme_rdma_queue *queue) @@ -607,11 +611,13 @@ static int nvme_rdma_start_queue(struct nvme_rdma_ctrl *ctrl, int idx) else ret = nvmf_connect_admin_queue(&ctrl->ctrl); - if (!ret) + if (!ret) { set_bit(NVME_RDMA_Q_LIVE, &queue->flags); - else + } else { + __nvme_rdma_stop_queue(queue); dev_info(ctrl->ctrl.device, "failed to connect queue: %d ret=%d\n", idx, ret); + } return ret; } diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index cd52b9f15376..98613a45bd3b 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -675,6 +675,7 @@ static void nvmet_port_subsys_drop_link(struct config_item *parent, found: list_del(&p->entry); + nvmet_port_del_ctrls(port, subsys); nvmet_port_disc_changed(port, subsys); if (list_empty(&port->subsystems)) diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index dad0243c7c96..3a67e244e568 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -46,6 +46,9 @@ inline u16 errno_to_nvme_status(struct nvmet_req *req, int errno) u16 status; switch (errno) { + case 0: + status = NVME_SC_SUCCESS; + break; case -ENOSPC: req->error_loc = offsetof(struct nvme_rw_command, length); status = NVME_SC_CAP_EXCEEDED | NVME_SC_DNR; @@ -280,6 +283,18 @@ void nvmet_unregister_transport(const struct nvmet_fabrics_ops *ops) } EXPORT_SYMBOL_GPL(nvmet_unregister_transport); +void nvmet_port_del_ctrls(struct nvmet_port *port, struct nvmet_subsys *subsys) +{ + struct nvmet_ctrl *ctrl; + + mutex_lock(&subsys->lock); + list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) { + if (ctrl->port == port) + ctrl->ops->delete_ctrl(ctrl); + } + mutex_unlock(&subsys->lock); +} + int nvmet_enable_port(struct nvmet_port *port) { const struct nvmet_fabrics_ops *ops; diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index b16dc3981c69..0940c5024a34 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -654,6 +654,14 @@ static void nvme_loop_remove_port(struct nvmet_port *port) mutex_lock(&nvme_loop_ports_mutex); list_del_init(&port->entry); mutex_unlock(&nvme_loop_ports_mutex); + + /* + * Ensure any ctrls that are in the process of being + * deleted are in fact deleted before we return + * and free the port. This is to prevent active + * ctrls from using a port after it's freed. + */ + flush_workqueue(nvme_delete_wq); } static const struct nvmet_fabrics_ops nvme_loop_ops = { diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 6ee66c610739..c51f8dd01dc4 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -418,6 +418,9 @@ void nvmet_port_send_ana_event(struct nvmet_port *port); int nvmet_register_transport(const struct nvmet_fabrics_ops *ops); void nvmet_unregister_transport(const struct nvmet_fabrics_ops *ops); +void nvmet_port_del_ctrls(struct nvmet_port *port, + struct nvmet_subsys *subsys); + int nvmet_enable_port(struct nvmet_port *port); void nvmet_disable_port(struct nvmet_port *port); diff --git a/drivers/nvmem/nvmem-sysfs.c b/drivers/nvmem/nvmem-sysfs.c index 6f303b91f6e7..9e0c429cd08a 100644 --- a/drivers/nvmem/nvmem-sysfs.c +++ b/drivers/nvmem/nvmem-sysfs.c @@ -224,10 +224,17 @@ int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, if (!config->base_dev) return -EINVAL; - if (nvmem->read_only) - nvmem->eeprom = bin_attr_ro_root_nvmem; - else - nvmem->eeprom = bin_attr_rw_root_nvmem; + if (nvmem->read_only) { + if (config->root_only) + nvmem->eeprom = bin_attr_ro_root_nvmem; + else + nvmem->eeprom = bin_attr_ro_nvmem; + } else { + if (config->root_only) + nvmem->eeprom = bin_attr_rw_root_nvmem; + else + nvmem->eeprom = bin_attr_rw_nvmem; + } nvmem->eeprom.attr.name = "eeprom"; nvmem->eeprom.size = nvmem->size; #ifdef CONFIG_DEBUG_LOCK_ALLOC diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 7f84bb4903ca..a296eaf52a5b 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -277,7 +277,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_raw); * of_irq_parse_one - Resolve an interrupt for a device * @device: the device whose interrupt is to be resolved * @index: index of the interrupt to resolve - * @out_irq: structure of_irq filled by this function + * @out_irq: structure of_phandle_args filled by this function * * This function resolves an interrupt for a node by walking the interrupt tree, * finding which interrupt controller node it is attached to, and returning the diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index c1b67dd7cd6e..83c766233181 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -206,16 +206,22 @@ static int adjust_local_phandle_references(struct device_node *local_fixups, for_each_child_of_node(local_fixups, child) { for_each_child_of_node(overlay, overlay_child) - if (!node_name_cmp(child, overlay_child)) + if (!node_name_cmp(child, overlay_child)) { + of_node_put(overlay_child); break; + } - if (!overlay_child) + if (!overlay_child) { + of_node_put(child); return -EINVAL; + } err = adjust_local_phandle_references(child, overlay_child, phandle_delta); - if (err) + if (err) { + of_node_put(child); return err; + } } return 0; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 29ed5ec1ac27..1b27b5af3d55 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1025,10 +1025,15 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) if (state == PCI_D0) { pci_platform_power_transition(dev, PCI_D0); /* - * Mandatory power management transition delays are - * handled in the PCIe portdrv resume hooks. + * Mandatory power management transition delays, see + * PCI Express Base Specification Revision 2.0 Section + * 6.6.1: Conventional Reset. Do not delay for + * devices powered on/off by corresponding bridge, + * because have already delayed for the bridge. */ if (dev->runtime_d3cold) { + if (dev->d3cold_delay && !dev->imm_ready) + msleep(dev->d3cold_delay); /* * When powering on a bridge from D3cold, the * whole hierarchy may be powered on into @@ -4602,16 +4607,14 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) return pci_dev_wait(dev, "PM D3->D0", PCIE_RESET_READY_POLL_MS); } - /** - * pcie_wait_for_link_delay - Wait until link is active or inactive + * pcie_wait_for_link - Wait until link is active or inactive * @pdev: Bridge device * @active: waiting for active or inactive? - * @delay: Delay to wait after link has become active (in ms) * * Use this to wait till link becomes active or inactive. */ -bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, int delay) +bool pcie_wait_for_link(struct pci_dev *pdev, bool active) { int timeout = 1000; bool ret; @@ -4648,25 +4651,13 @@ bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, int delay) timeout -= 10; } if (active && ret) - msleep(delay); + msleep(100); else if (ret != active) pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n", active ? "set" : "cleared"); return ret == active; } -/** - * pcie_wait_for_link - Wait until link is active or inactive - * @pdev: Bridge device - * @active: waiting for active or inactive? - * - * Use this to wait till link becomes active or inactive. - */ -bool pcie_wait_for_link(struct pci_dev *pdev, bool active) -{ - return pcie_wait_for_link_delay(pdev, active, 100); -} - void pci_reset_secondary_bus(struct pci_dev *dev) { u16 ctrl; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1be03a97cb92..d22d1b807701 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -497,7 +497,6 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state, u32 service); -bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, int delay); bool pcie_wait_for_link(struct pci_dev *pdev, bool active); #ifdef CONFIG_PCIEASPM void pcie_aspm_init_link_state(struct pci_dev *pdev); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index e44af7f4d37f..464f8f92653f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1170,6 +1170,26 @@ static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp) module_param_call(policy, pcie_aspm_set_policy, pcie_aspm_get_policy, NULL, 0644); +/** + * pcie_aspm_enabled - Check if PCIe ASPM has been enabled for a device. + * @pdev: Target device. + */ +bool pcie_aspm_enabled(struct pci_dev *pdev) +{ + struct pci_dev *bridge = pci_upstream_bridge(pdev); + bool ret; + + if (!bridge) + return false; + + mutex_lock(&aspm_lock); + ret = bridge->link_state ? !!bridge->link_state->aspm_enabled : false; + mutex_unlock(&aspm_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pcie_aspm_enabled); + #ifdef CONFIG_PCIEASPM_DEBUG static ssize_t link_state_show(struct device *dev, struct device_attribute *attr, diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 308c3e0c4a34..1b330129089f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -9,7 +9,6 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/delay.h> #include <linux/errno.h> #include <linux/pm.h> #include <linux/pm_runtime.h> @@ -379,67 +378,6 @@ static int pm_iter(struct device *dev, void *data) return 0; } -static int get_downstream_delay(struct pci_bus *bus) -{ - struct pci_dev *pdev; - int min_delay = 100; - int max_delay = 0; - - list_for_each_entry(pdev, &bus->devices, bus_list) { - if (!pdev->imm_ready) - min_delay = 0; - else if (pdev->d3cold_delay < min_delay) - min_delay = pdev->d3cold_delay; - if (pdev->d3cold_delay > max_delay) - max_delay = pdev->d3cold_delay; - } - - return max(min_delay, max_delay); -} - -/* - * wait_for_downstream_link - Wait for downstream link to establish - * @pdev: PCIe port whose downstream link is waited - * - * Handle delays according to PCIe 4.0 section 6.6.1 before configuration - * access to the downstream component is permitted. - * - * This blocks PCI core resume of the hierarchy below this port until the - * link is trained. Should be called before resuming port services to - * prevent pciehp from starting to tear-down the hierarchy too soon. - */ -static void wait_for_downstream_link(struct pci_dev *pdev) -{ - int delay; - - if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT && - pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) - return; - - if (pci_dev_is_disconnected(pdev)) - return; - - if (!pdev->subordinate || list_empty(&pdev->subordinate->devices) || - !pdev->bridge_d3) - return; - - delay = get_downstream_delay(pdev->subordinate); - if (!delay) - return; - - dev_dbg(&pdev->dev, "waiting downstream link for %d ms\n", delay); - - /* - * If downstream port does not support speeds greater than 5 GT/s - * need to wait 100ms. For higher speeds (gen3) we need to wait - * first for the data link layer to become active. - */ - if (pcie_get_speed_cap(pdev) <= PCIE_SPEED_5_0GT) - msleep(delay); - else - pcie_wait_for_link_delay(pdev, true, delay); -} - /** * pcie_port_device_suspend - suspend port services associated with a PCIe port * @dev: PCI Express port to handle @@ -453,8 +391,6 @@ int pcie_port_device_suspend(struct device *dev) int pcie_port_device_resume_noirq(struct device *dev) { size_t off = offsetof(struct pcie_port_service_driver, resume_noirq); - - wait_for_downstream_link(to_pci_dev(dev)); return device_for_each_child(dev, &off, pm_iter); } @@ -485,8 +421,6 @@ int pcie_port_device_runtime_suspend(struct device *dev) int pcie_port_device_runtime_resume(struct device *dev) { size_t off = offsetof(struct pcie_port_service_driver, runtime_resume); - - wait_for_downstream_link(to_pci_dev(dev)); return device_for_each_child(dev, &off, pm_iter); } #endif /* PM */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 208aacf39329..44c4ae1abd00 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5256,7 +5256,7 @@ static void quirk_reset_lenovo_thinkpad_p50_nvgpu(struct pci_dev *pdev) */ if (ioread32(map + 0x2240c) & 0x2) { pci_info(pdev, FW_BUG "GPU left initialized by EFI, resetting\n"); - ret = pci_reset_function(pdev); + ret = pci_reset_bus(pdev); if (ret < 0) pci_err(pdev, "Failed to reset GPU: %d\n", ret); } diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c index eb6168e6ac43..590e594092f2 100644 --- a/drivers/pcmcia/db1xxx_ss.c +++ b/drivers/pcmcia/db1xxx_ss.c @@ -255,8 +255,10 @@ static int db1x_pcmcia_configure(struct pcmcia_socket *skt, switch (state->Vcc) { case 50: ++v; + /* fall through */ case 33: ++v; + /* fall through */ case 0: break; default: @@ -267,9 +269,11 @@ static int db1x_pcmcia_configure(struct pcmcia_socket *skt, switch (state->Vpp) { case 12: ++p; + /* fall through */ case 33: case 50: ++p; + /* fall through */ case 0: break; default: diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c index 384396cbb22d..22256576b69a 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g4.c @@ -2412,7 +2412,7 @@ static const struct aspeed_pin_config aspeed_g4_configs[] = { { PIN_CONFIG_INPUT_DEBOUNCE, { C14, B14 }, SCUA8, 27 }, }; -static int aspeed_g4_sig_expr_set(const struct aspeed_pinmux_data *ctx, +static int aspeed_g4_sig_expr_set(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr *expr, bool enable) { diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c index 053101f795a2..ff84d1afd229 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c @@ -2507,6 +2507,88 @@ static struct aspeed_pin_config aspeed_g5_configs[] = { { PIN_CONFIG_INPUT_DEBOUNCE, { A20, B19 }, SCUA8, 27 }, }; +static struct regmap *aspeed_g5_acquire_regmap(struct aspeed_pinmux_data *ctx, + int ip) +{ + if (ip == ASPEED_IP_SCU) { + WARN(!ctx->maps[ip], "Missing SCU syscon!"); + return ctx->maps[ip]; + } + + if (ip >= ASPEED_NR_PINMUX_IPS) + return ERR_PTR(-EINVAL); + + if (likely(ctx->maps[ip])) + return ctx->maps[ip]; + + if (ip == ASPEED_IP_GFX) { + struct device_node *node; + struct regmap *map; + + node = of_parse_phandle(ctx->dev->of_node, + "aspeed,external-nodes", 0); + if (node) { + map = syscon_node_to_regmap(node); + of_node_put(node); + if (IS_ERR(map)) + return map; + } else + return ERR_PTR(-ENODEV); + + ctx->maps[ASPEED_IP_GFX] = map; + dev_dbg(ctx->dev, "Acquired GFX regmap"); + return map; + } + + if (ip == ASPEED_IP_LPC) { + struct device_node *node; + struct regmap *map; + + node = of_parse_phandle(ctx->dev->of_node, + "aspeed,external-nodes", 1); + if (node) { + map = syscon_node_to_regmap(node->parent); + of_node_put(node); + if (IS_ERR(map)) + return map; + } else + return ERR_PTR(-ENODEV); + + ctx->maps[ASPEED_IP_LPC] = map; + dev_dbg(ctx->dev, "Acquired LPC regmap"); + return map; + } + + return ERR_PTR(-EINVAL); +} + +static int aspeed_g5_sig_expr_eval(struct aspeed_pinmux_data *ctx, + const struct aspeed_sig_expr *expr, + bool enabled) +{ + int ret; + int i; + + for (i = 0; i < expr->ndescs; i++) { + const struct aspeed_sig_desc *desc = &expr->descs[i]; + struct regmap *map; + + map = aspeed_g5_acquire_regmap(ctx, desc->ip); + if (IS_ERR(map)) { + dev_err(ctx->dev, + "Failed to acquire regmap for IP block %d\n", + desc->ip); + return PTR_ERR(map); + } + + ret = aspeed_sig_desc_eval(desc, enabled, ctx->maps[desc->ip]); + if (ret <= 0) + return ret; + } + + return 1; +} + /** * Configure a pin's signal by applying an expression's descriptor state for * all descriptors in the expression. @@ -2520,7 +2602,7 @@ static struct aspeed_pin_config aspeed_g5_configs[] = { * Return: 0 if the expression is configured as requested and a negative error * code otherwise */ -static int aspeed_g5_sig_expr_set(const struct aspeed_pinmux_data *ctx, +static int aspeed_g5_sig_expr_set(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr *expr, bool enable) { @@ -2531,9 +2613,15 @@ static int aspeed_g5_sig_expr_set(const struct aspeed_pinmux_data *ctx, const struct aspeed_sig_desc *desc = &expr->descs[i]; u32 pattern = enable ? desc->enable : desc->disable; u32 val = (pattern << __ffs(desc->mask)); + struct regmap *map; - if (!ctx->maps[desc->ip]) - return -ENODEV; + map = aspeed_g5_acquire_regmap(ctx, desc->ip); + if (IS_ERR(map)) { + dev_err(ctx->dev, + "Failed to acquire regmap for IP block %d\n", + desc->ip); + return PTR_ERR(map); + } /* * Strap registers are configured in hardware or by early-boot @@ -2586,6 +2674,7 @@ static int aspeed_g5_sig_expr_set(const struct aspeed_pinmux_data *ctx, } static const struct aspeed_pinmux_ops aspeed_g5_ops = { + .eval = aspeed_g5_sig_expr_eval, .set = aspeed_g5_sig_expr_set, }; @@ -2641,34 +2730,11 @@ static struct pinctrl_desc aspeed_g5_pinctrl_desc = { static int aspeed_g5_pinctrl_probe(struct platform_device *pdev) { int i; - struct regmap *map; - struct device_node *node; for (i = 0; i < ARRAY_SIZE(aspeed_g5_pins); i++) aspeed_g5_pins[i].number = i; - node = of_parse_phandle(pdev->dev.of_node, "aspeed,external-nodes", 0); - map = syscon_node_to_regmap(node); - of_node_put(node); - if (IS_ERR(map)) { - dev_warn(&pdev->dev, "No GFX phandle found, some mux configurations may fail\n"); - map = NULL; - } - aspeed_g5_pinctrl_data.pinmux.maps[ASPEED_IP_GFX] = map; - - node = of_parse_phandle(pdev->dev.of_node, "aspeed,external-nodes", 1); - if (node) { - map = syscon_node_to_regmap(node->parent); - if (IS_ERR(map)) { - dev_warn(&pdev->dev, "LHC parent is not a syscon, some mux configurations may fail\n"); - map = NULL; - } - } else { - dev_warn(&pdev->dev, "No LHC phandle found, some mux configurations may fail\n"); - map = NULL; - } - of_node_put(node); - aspeed_g5_pinctrl_data.pinmux.maps[ASPEED_IP_LPC] = map; + aspeed_g5_pinctrl_data.pinmux.dev = &pdev->dev; return aspeed_pinctrl_probe(pdev, &aspeed_g5_pinctrl_desc, &aspeed_g5_pinctrl_data); diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c index 535db3de490b..54933665b5f8 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c @@ -71,7 +71,7 @@ int aspeed_pinmux_get_fn_groups(struct pinctrl_dev *pctldev, return 0; } -static int aspeed_sig_expr_enable(const struct aspeed_pinmux_data *ctx, +static int aspeed_sig_expr_enable(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr *expr) { int ret; @@ -86,7 +86,7 @@ static int aspeed_sig_expr_enable(const struct aspeed_pinmux_data *ctx, return 0; } -static int aspeed_sig_expr_disable(const struct aspeed_pinmux_data *ctx, +static int aspeed_sig_expr_disable(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr *expr) { int ret; @@ -109,7 +109,7 @@ static int aspeed_sig_expr_disable(const struct aspeed_pinmux_data *ctx, * * Return: 0 if all expressions are disabled, otherwise a negative error code */ -static int aspeed_disable_sig(const struct aspeed_pinmux_data *ctx, +static int aspeed_disable_sig(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr **exprs) { int ret = 0; @@ -217,8 +217,7 @@ int aspeed_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int function, { int i; int ret; - const struct aspeed_pinctrl_data *pdata = - pinctrl_dev_get_drvdata(pctldev); + struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); const struct aspeed_pin_group *pgroup = &pdata->pinmux.groups[group]; const struct aspeed_pin_function *pfunc = &pdata->pinmux.functions[function]; @@ -306,8 +305,7 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev, unsigned int offset) { int ret; - const struct aspeed_pinctrl_data *pdata = - pinctrl_dev_get_drvdata(pctldev); + struct aspeed_pinctrl_data *pdata = pinctrl_dev_get_drvdata(pctldev); const struct aspeed_pin_desc *pdesc = pdata->pins[offset].drv_data; const struct aspeed_sig_expr ***prios, **funcs, *expr; diff --git a/drivers/pinctrl/aspeed/pinmux-aspeed.c b/drivers/pinctrl/aspeed/pinmux-aspeed.c index 5b0fe178ccf2..57305ca838a7 100644 --- a/drivers/pinctrl/aspeed/pinmux-aspeed.c +++ b/drivers/pinctrl/aspeed/pinmux-aspeed.c @@ -5,7 +5,7 @@ #include "pinmux-aspeed.h" -const char *const aspeed_pinmux_ips[] = { +static const char *const aspeed_pinmux_ips[] = { [ASPEED_IP_SCU] = "SCU", [ASPEED_IP_GFX] = "GFX", [ASPEED_IP_LPC] = "LPC", @@ -78,11 +78,14 @@ int aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc, * neither the enabled nor disabled state. Thus we must explicitly test for * either condition as required. */ -int aspeed_sig_expr_eval(const struct aspeed_pinmux_data *ctx, +int aspeed_sig_expr_eval(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr *expr, bool enabled) { - int i; int ret; + int i; + + if (ctx->ops->eval) + return ctx->ops->eval(ctx, expr, enabled); for (i = 0; i < expr->ndescs; i++) { const struct aspeed_sig_desc *desc = &expr->descs[i]; diff --git a/drivers/pinctrl/aspeed/pinmux-aspeed.h b/drivers/pinctrl/aspeed/pinmux-aspeed.h index 329d54d48667..db3457c86f48 100644 --- a/drivers/pinctrl/aspeed/pinmux-aspeed.h +++ b/drivers/pinctrl/aspeed/pinmux-aspeed.h @@ -702,11 +702,14 @@ struct aspeed_pin_function { struct aspeed_pinmux_data; struct aspeed_pinmux_ops { - int (*set)(const struct aspeed_pinmux_data *ctx, + int (*eval)(struct aspeed_pinmux_data *ctx, + const struct aspeed_sig_expr *expr, bool enabled); + int (*set)(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr *expr, bool enabled); }; struct aspeed_pinmux_data { + struct device *dev; struct regmap *maps[ASPEED_NR_PINMUX_IPS]; const struct aspeed_pinmux_ops *ops; @@ -721,11 +724,10 @@ struct aspeed_pinmux_data { int aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc, bool enabled, struct regmap *map); -int aspeed_sig_expr_eval(const struct aspeed_pinmux_data *ctx, - const struct aspeed_sig_expr *expr, - bool enabled); +int aspeed_sig_expr_eval(struct aspeed_pinmux_data *ctx, + const struct aspeed_sig_expr *expr, bool enabled); -static inline int aspeed_sig_expr_set(const struct aspeed_pinmux_data *ctx, +static inline int aspeed_sig_expr_set(struct aspeed_pinmux_data *ctx, const struct aspeed_sig_expr *expr, bool enabled) { diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index e5a112a8e067..297b7b5fcb28 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -1455,6 +1455,20 @@ static void byt_gpio_irq_handler(struct irq_desc *desc) chip->irq_eoi(data); } +static void byt_init_irq_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + /* + * FIXME: currently the valid_mask is filled in as part of + * initializing the irq_chip below in byt_gpio_irq_init_hw(). + * when converting this driver to the new way of passing the + * gpio_irq_chip along when adding the gpio_chip, move the + * mask initialization into this callback instead. Right now + * this callback is here to make sure the mask gets allocated. + */ +} + static void byt_gpio_irq_init_hw(struct byt_gpio *vg) { struct gpio_chip *gc = &vg->chip; @@ -1525,7 +1539,7 @@ static int byt_gpio_probe(struct byt_gpio *vg) gc->can_sleep = false; gc->parent = &vg->pdev->dev; gc->ngpio = vg->soc_data->npins; - gc->irq.need_valid_mask = true; + gc->irq.init_valid_mask = byt_init_irq_valid_mask; #ifdef CONFIG_PM_SLEEP vg->saved_context = devm_kcalloc(&vg->pdev->dev, gc->ngpio, diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 03ec7a5d9d0b..ab681d1a3a74 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1543,6 +1543,30 @@ static const struct dmi_system_id chv_no_valid_mask[] = { {} }; +static void chv_init_irq_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct chv_pinctrl *pctrl = gpiochip_get_data(chip); + const struct chv_community *community = pctrl->community; + int i; + + /* Do not add GPIOs that can only generate GPEs to the IRQ domain */ + for (i = 0; i < community->npins; i++) { + const struct pinctrl_pin_desc *desc; + u32 intsel; + + desc = &community->pins[i]; + + intsel = readl(chv_padreg(pctrl, desc->number, CHV_PADCTRL0)); + intsel &= CHV_PADCTRL0_INTSEL_MASK; + intsel >>= CHV_PADCTRL0_INTSEL_SHIFT; + + if (intsel >= community->nirqs) + clear_bit(i, valid_mask); + } +} + static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) { const struct chv_gpio_pinrange *range; @@ -1557,7 +1581,8 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) chip->label = dev_name(pctrl->dev); chip->parent = pctrl->dev; chip->base = -1; - chip->irq.need_valid_mask = need_valid_mask; + if (need_valid_mask) + chip->irq.init_valid_mask = chv_init_irq_valid_mask; ret = devm_gpiochip_add_data(pctrl->dev, chip, pctrl); if (ret) { @@ -1576,21 +1601,6 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) } } - /* Do not add GPIOs that can only generate GPEs to the IRQ domain */ - for (i = 0; i < community->npins; i++) { - const struct pinctrl_pin_desc *desc; - u32 intsel; - - desc = &community->pins[i]; - - intsel = readl(chv_padreg(pctrl, desc->number, CHV_PADCTRL0)); - intsel &= CHV_PADCTRL0_INTSEL_MASK; - intsel >>= CHV_PADCTRL0_INTSEL_SHIFT; - - if (need_valid_mask && intsel >= community->nirqs) - clear_bit(i, chip->irq.valid_mask); - } - /* * The same set of machines in chv_no_valid_mask[] have incorrectly * configured GPIOs that generate spurious interrupts so we use diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c index d3332da35637..dd5aa9a2dfe5 100644 --- a/drivers/pinctrl/pinctrl-stmfx.c +++ b/drivers/pinctrl/pinctrl-stmfx.c @@ -585,12 +585,24 @@ static int stmfx_pinctrl_gpio_function_enable(struct stmfx_pinctrl *pctl) return stmfx_function_enable(pctl->stmfx, func); } +static int stmfx_pinctrl_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); + u32 n; + + for_each_clear_bit(n, &pctl->gpio_valid_mask, ngpios) + clear_bit(n, valid_mask); + + return 0; +} + static int stmfx_pinctrl_probe(struct platform_device *pdev) { struct stmfx *stmfx = dev_get_drvdata(pdev->dev.parent); struct device_node *np = pdev->dev.of_node; struct stmfx_pinctrl *pctl; - u32 n; int irq, ret; pctl = devm_kzalloc(stmfx->dev, sizeof(*pctl), GFP_KERNEL); @@ -650,7 +662,7 @@ static int stmfx_pinctrl_probe(struct platform_device *pdev) pctl->gpio_chip.ngpio = pctl->pctl_desc.npins; pctl->gpio_chip.can_sleep = true; pctl->gpio_chip.of_node = np; - pctl->gpio_chip.need_valid_mask = true; + pctl->gpio_chip.init_valid_mask = stmfx_pinctrl_gpio_init_valid_mask; ret = devm_gpiochip_add_data(pctl->dev, &pctl->gpio_chip, pctl); if (ret) { @@ -668,8 +680,6 @@ static int stmfx_pinctrl_probe(struct platform_device *pdev) pctl->irq_chip.irq_set_type = stmfx_pinctrl_irq_set_type; pctl->irq_chip.irq_bus_lock = stmfx_pinctrl_irq_bus_lock; pctl->irq_chip.irq_bus_sync_unlock = stmfx_pinctrl_irq_bus_sync_unlock; - for_each_clear_bit(n, &pctl->gpio_valid_mask, pctl->gpio_chip.ngpio) - clear_bit(n, pctl->gpio_chip.valid_mask); ret = gpiochip_irqchip_add_nested(&pctl->gpio_chip, &pctl->irq_chip, 0, handle_bad_irq, IRQ_TYPE_NONE); diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index 8e14a5f2e970..fa2c87821401 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -138,6 +138,7 @@ config PINCTRL_QCOM_SPMI_PMIC select PINMUX select PINCONF select GENERIC_PINCONF + select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY help This is the pinctrl, pinmux, pinconf and gpiolib driver for the diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 7f35c196bb3e..b8a1c43222f8 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -593,24 +593,25 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) #define msm_gpio_dbg_show NULL #endif -static int msm_gpio_init_valid_mask(struct gpio_chip *chip) +static int msm_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) { - struct msm_pinctrl *pctrl = gpiochip_get_data(chip); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); int ret; unsigned int len, i; - unsigned int max_gpios = pctrl->soc->ngpios; const int *reserved = pctrl->soc->reserved_gpios; u16 *tmp; /* Driver provided reserved list overrides DT and ACPI */ if (reserved) { - bitmap_fill(chip->valid_mask, max_gpios); + bitmap_fill(valid_mask, ngpios); for (i = 0; reserved[i] >= 0; i++) { - if (i >= max_gpios || reserved[i] >= max_gpios) { + if (i >= ngpios || reserved[i] >= ngpios) { dev_err(pctrl->dev, "invalid list of reserved GPIOs\n"); return -EINVAL; } - clear_bit(reserved[i], chip->valid_mask); + clear_bit(reserved[i], valid_mask); } return 0; @@ -622,7 +623,7 @@ static int msm_gpio_init_valid_mask(struct gpio_chip *chip) if (ret < 0) return 0; - if (ret > max_gpios) + if (ret > ngpios) return -EINVAL; tmp = kmalloc_array(len, sizeof(*tmp), GFP_KERNEL); @@ -635,9 +636,9 @@ static int msm_gpio_init_valid_mask(struct gpio_chip *chip) goto out; } - bitmap_zero(chip->valid_mask, max_gpios); + bitmap_zero(valid_mask, ngpios); for (i = 0; i < len; i++) - set_bit(tmp[i], chip->valid_mask); + set_bit(tmp[i], valid_mask); out: kfree(tmp); @@ -653,7 +654,6 @@ static const struct gpio_chip msm_gpio_template = { .request = gpiochip_generic_request, .free = gpiochip_generic_free, .dbg_show = msm_gpio_dbg_show, - .init_valid_mask = msm_gpio_init_valid_mask, }; /* For dual-edge interrupts in software, since some hardware has no @@ -1015,7 +1015,8 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) chip->parent = pctrl->dev; chip->owner = THIS_MODULE; chip->of_node = pctrl->dev->of_node; - chip->need_valid_mask = msm_gpio_needs_valid_mask(pctrl); + if (msm_gpio_needs_valid_mask(pctrl)) + chip->init_valid_mask = msm_gpio_init_valid_mask; pctrl->irq_chip.name = "msmgpio"; pctrl->irq_chip.irq_enable = msm_gpio_irq_enable; diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index f39da87ea185..442db15e0729 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -170,8 +170,6 @@ struct pmic_gpio_state { struct regmap *map; struct pinctrl_dev *ctrl; struct gpio_chip chip; - struct fwnode_handle *fwnode; - struct irq_domain *domain; }; static const struct pinconf_generic_params pmic_gpio_bindings[] = { @@ -751,23 +749,6 @@ static int pmic_gpio_of_xlate(struct gpio_chip *chip, return gpio_desc->args[0] - PMIC_GPIO_PHYSICAL_OFFSET; } -static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned pin) -{ - struct pmic_gpio_state *state = gpiochip_get_data(chip); - struct irq_fwspec fwspec; - - fwspec.fwnode = state->fwnode; - fwspec.param_count = 2; - fwspec.param[0] = pin + PMIC_GPIO_PHYSICAL_OFFSET; - /* - * Set the type to a safe value temporarily. This will be overwritten - * later with the proper value by irq_set_type. - */ - fwspec.param[1] = IRQ_TYPE_EDGE_RISING; - - return irq_create_fwspec_mapping(&fwspec); -} - static void pmic_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct pmic_gpio_state *state = gpiochip_get_data(chip); @@ -787,7 +768,6 @@ static const struct gpio_chip pmic_gpio_gpio_template = { .request = gpiochip_generic_request, .free = gpiochip_generic_free, .of_xlate = pmic_gpio_of_xlate, - .to_irq = pmic_gpio_to_irq, .dbg_show = pmic_gpio_dbg_show, }; @@ -964,46 +944,24 @@ static int pmic_gpio_domain_translate(struct irq_domain *domain, return 0; } -static int pmic_gpio_domain_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *data) +static unsigned int pmic_gpio_child_offset_to_irq(struct gpio_chip *chip, + unsigned int offset) { - struct pmic_gpio_state *state = container_of(domain->host_data, - struct pmic_gpio_state, - chip); - struct irq_fwspec *fwspec = data; - struct irq_fwspec parent_fwspec; - irq_hw_number_t hwirq; - unsigned int type; - int ret, i; - - ret = pmic_gpio_domain_translate(domain, fwspec, &hwirq, &type); - if (ret) - return ret; - - for (i = 0; i < nr_irqs; i++) - irq_domain_set_info(domain, virq + i, hwirq + i, - &pmic_gpio_irq_chip, state, - handle_level_irq, NULL, NULL); + return offset + PMIC_GPIO_PHYSICAL_OFFSET; +} - parent_fwspec.fwnode = domain->parent->fwnode; - parent_fwspec.param_count = 4; - parent_fwspec.param[0] = 0; - parent_fwspec.param[1] = hwirq + 0xc0; - parent_fwspec.param[2] = 0; - parent_fwspec.param[3] = fwspec->param[1]; +static int pmic_gpio_child_to_parent_hwirq(struct gpio_chip *chip, + unsigned int child_hwirq, + unsigned int child_type, + unsigned int *parent_hwirq, + unsigned int *parent_type) +{ + *parent_hwirq = child_hwirq + 0xc0; + *parent_type = child_type; - return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, - &parent_fwspec); + return 0; } -static const struct irq_domain_ops pmic_gpio_domain_ops = { - .activate = gpiochip_irq_domain_activate, - .alloc = pmic_gpio_domain_alloc, - .deactivate = gpiochip_irq_domain_deactivate, - .free = irq_domain_free_irqs_common, - .translate = pmic_gpio_domain_translate, -}; - static int pmic_gpio_probe(struct platform_device *pdev) { struct irq_domain *parent_domain; @@ -1013,6 +971,7 @@ static int pmic_gpio_probe(struct platform_device *pdev) struct pinctrl_desc *pctrldesc; struct pmic_gpio_pad *pad, *pads; struct pmic_gpio_state *state; + struct gpio_irq_chip *girq; int ret, npins, i; u32 reg; @@ -1092,19 +1051,21 @@ static int pmic_gpio_probe(struct platform_device *pdev) if (!parent_domain) return -ENXIO; - state->fwnode = of_node_to_fwnode(state->dev->of_node); - state->domain = irq_domain_create_hierarchy(parent_domain, 0, - state->chip.ngpio, - state->fwnode, - &pmic_gpio_domain_ops, - &state->chip); - if (!state->domain) - return -ENODEV; + girq = &state->chip.irq; + girq->chip = &pmic_gpio_irq_chip; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + girq->fwnode = of_node_to_fwnode(state->dev->of_node); + girq->parent_domain = parent_domain; + girq->child_to_parent_hwirq = pmic_gpio_child_to_parent_hwirq; + girq->populate_parent_fwspec = gpiochip_populate_parent_fwspec_fourcell; + girq->child_offset_to_irq = pmic_gpio_child_offset_to_irq; + girq->child_irq_domain_ops.translate = pmic_gpio_domain_translate; ret = gpiochip_add_data(&state->chip, state); if (ret) { dev_err(state->dev, "can't add gpio chip\n"); - goto err_chip_add_data; + return ret; } /* @@ -1130,8 +1091,6 @@ static int pmic_gpio_probe(struct platform_device *pdev) err_range: gpiochip_remove(&state->chip); -err_chip_add_data: - irq_domain_remove(state->domain); return ret; } @@ -1140,7 +1099,6 @@ static int pmic_gpio_remove(struct platform_device *pdev) struct pmic_gpio_state *state = platform_get_drvdata(pdev); gpiochip_remove(&state->chip); - irq_domain_remove(state->domain); return 0; } diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c index e504d255d5ce..430731cdf827 100644 --- a/drivers/platform/chrome/cros_ec_ishtp.c +++ b/drivers/platform/chrome/cros_ec_ishtp.c @@ -707,7 +707,7 @@ static int cros_ec_ishtp_reset(struct ishtp_cl_device *cl_device) */ static int __maybe_unused cros_ec_ishtp_suspend(struct device *device) { - struct ishtp_cl_device *cl_device = dev_get_drvdata(device); + struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device); struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); @@ -722,7 +722,7 @@ static int __maybe_unused cros_ec_ishtp_suspend(struct device *device) */ static int __maybe_unused cros_ec_ishtp_resume(struct device *device) { - struct ishtp_cl_device *cl_device = dev_get_drvdata(device); + struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device); struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 006a8ff64057..714306bc3f79 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -706,7 +706,7 @@ static int cros_ec_spi_devm_high_pri_alloc(struct device *dev, struct cros_ec_spi *ec_spi) { struct sched_param sched_priority = { - .sched_priority = MAX_RT_PRIO - 1, + .sched_priority = MAX_RT_PRIO / 2, }; int err; diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index d9542c661ddc..f57bbbeb2495 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -152,6 +152,13 @@ static const struct x86_cpu_id int0002_cpu_ids[] = { {} }; +static void int0002_init_irq_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + bitmap_clear(valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); +} + static int int0002_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -184,7 +191,7 @@ static int int0002_probe(struct platform_device *pdev) chip->direction_output = int0002_gpio_direction_output; chip->base = -1; chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1; - chip->irq.need_valid_mask = true; + chip->irq.init_valid_mask = int0002_init_irq_valid_mask; ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL); if (ret) { @@ -192,8 +199,6 @@ static int int0002_probe(struct platform_device *pdev) return ret; } - bitmap_clear(chip->irq.valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); - /* * We manually request the irq here instead of passing a flow-handler * to gpiochip_set_chained_irqchip, because the irq is shared. diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 30de448de802..86d88aec94a1 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -742,6 +742,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, USB_CH_IP_CUR_LVL_1P5; break; } + /* Else, fall through */ case USB_STAT_HM_IDGND: dev_err(di->dev, "USB Type - Charging not allowed\n"); di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c3ab07ab31a9..8edfac17364e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -882,8 +882,11 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) return of_pwm_get(dev, dev->of_node, con_id); /* then lookup via ACPI */ - if (dev && is_acpi_node(dev->fwnode)) - return acpi_pwm_get(dev->fwnode); + if (dev && is_acpi_node(dev->fwnode)) { + pwm = acpi_pwm_get(dev->fwnode); + if (!IS_ERR(pwm) || PTR_ERR(pwm) != -ENOENT) + return pwm; + } /* * We look up the provider in the static table typically provided by diff --git a/drivers/ras/Makefile b/drivers/ras/Makefile index ef6777e14d3d..6f0404f50107 100644 --- a/drivers/ras/Makefile +++ b/drivers/ras/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_RAS) += ras.o debugfs.o +obj-$(CONFIG_RAS) += ras.o +obj-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_RAS_CEC) += cec.o diff --git a/drivers/ras/cec.c b/drivers/ras/cec.c index 5d545806d930..c09cf55e2d20 100644 --- a/drivers/ras/cec.c +++ b/drivers/ras/cec.c @@ -4,6 +4,7 @@ */ #include <linux/mm.h> #include <linux/gfp.h> +#include <linux/ras.h> #include <linux/kernel.h> #include <linux/workqueue.h> diff --git a/drivers/ras/debugfs.c b/drivers/ras/debugfs.c index 9c1b717efad8..0d4f985afbf3 100644 --- a/drivers/ras/debugfs.c +++ b/drivers/ras/debugfs.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include <linux/debugfs.h> +#include <linux/ras.h> +#include "debugfs.h" struct dentry *ras_debugfs_dir; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index b57093d7c01f..3ee63531f6d5 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -83,6 +83,7 @@ config REGULATOR_88PM8607 config REGULATOR_ACT8865 tristate "Active-semi act8865 voltage regulator" depends on I2C + depends on POWER_SUPPLY select REGMAP_I2C help This driver controls a active-semi act8865 voltage output @@ -618,6 +619,15 @@ config REGULATOR_MT6323 This driver supports the control of different power rails of device through regulator interface. +config REGULATOR_MT6358 + tristate "MediaTek MT6358 PMIC" + depends on MFD_MT6397 && BROKEN + help + Say y here to select this option to enable the power regulator of + MediaTek MT6358 PMIC. + This driver supports the control of different power rails of device + through regulator interface. + config REGULATOR_MT6380 tristate "MediaTek MT6380 PMIC" depends on MTK_PMIC_WRAP @@ -906,6 +916,13 @@ config REGULATOR_SY8106A help This driver supports SY8106A single output regulator. +config REGULATOR_SY8824X + tristate "Silergy SY8824C/SY8824E regulator" + depends on I2C && (OF || COMPILE_TEST) + select REGMAP_I2C + help + This driver supports SY8824C single output regulator. + config REGULATOR_TPS51632 tristate "TI TPS51632 Power Regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index eef73b5a35a4..2210ba56f9bd 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o +obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o @@ -111,6 +112,7 @@ obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o +obj-$(CONFIG_REGULATOR_SY8824X) += sy8824x.o obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index cf72d7c6b8c9..0fa97f934df4 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -16,8 +16,10 @@ #include <linux/regulator/act8865.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/power_supply.h> #include <linux/regulator/of_regulator.h> #include <linux/regmap.h> +#include <dt-bindings/regulator/active-semi,8865-regulator.h> /* * ACT8600 Global Register Map. @@ -89,35 +91,50 @@ */ #define ACT8865_SYS_MODE 0x00 #define ACT8865_SYS_CTRL 0x01 +#define ACT8865_SYS_UNLK_REGS 0x0b #define ACT8865_DCDC1_VSET1 0x20 #define ACT8865_DCDC1_VSET2 0x21 #define ACT8865_DCDC1_CTRL 0x22 +#define ACT8865_DCDC1_SUS 0x24 #define ACT8865_DCDC2_VSET1 0x30 #define ACT8865_DCDC2_VSET2 0x31 #define ACT8865_DCDC2_CTRL 0x32 +#define ACT8865_DCDC2_SUS 0x34 #define ACT8865_DCDC3_VSET1 0x40 #define ACT8865_DCDC3_VSET2 0x41 #define ACT8865_DCDC3_CTRL 0x42 +#define ACT8865_DCDC3_SUS 0x44 #define ACT8865_LDO1_VSET 0x50 #define ACT8865_LDO1_CTRL 0x51 +#define ACT8865_LDO1_SUS 0x52 #define ACT8865_LDO2_VSET 0x54 #define ACT8865_LDO2_CTRL 0x55 +#define ACT8865_LDO2_SUS 0x56 #define ACT8865_LDO3_VSET 0x60 #define ACT8865_LDO3_CTRL 0x61 +#define ACT8865_LDO3_SUS 0x62 #define ACT8865_LDO4_VSET 0x64 #define ACT8865_LDO4_CTRL 0x65 +#define ACT8865_LDO4_SUS 0x66 #define ACT8865_MSTROFF 0x20 /* * Field Definitions. */ #define ACT8865_ENA 0x80 /* ON - [7] */ +#define ACT8865_DIS 0x40 /* DIS - [6] */ + #define ACT8865_VSEL_MASK 0x3F /* VSET - [5:0] */ #define ACT8600_LDO10_ENA 0x40 /* ON - [6] */ #define ACT8600_SUDCDC_VSEL_MASK 0xFF /* SUDCDC VSET - [7:0] */ +#define ACT8600_APCH_CHG_ACIN BIT(7) +#define ACT8600_APCH_CHG_USB BIT(6) +#define ACT8600_APCH_CSTATE0 BIT(5) +#define ACT8600_APCH_CSTATE1 BIT(4) + /* * ACT8865 voltage number */ @@ -217,6 +234,171 @@ static const struct regulator_linear_range act8600_sudcdc_voltage_ranges[] = { REGULATOR_LINEAR_RANGE(41400000, 248, 255, 0), }; +static int act8865_set_suspend_state(struct regulator_dev *rdev, bool enable) +{ + struct regmap *regmap = rdev->regmap; + int id = rdev->desc->id, reg, val; + + switch (id) { + case ACT8865_ID_DCDC1: + reg = ACT8865_DCDC1_SUS; + val = 0xa8; + break; + case ACT8865_ID_DCDC2: + reg = ACT8865_DCDC2_SUS; + val = 0xa8; + break; + case ACT8865_ID_DCDC3: + reg = ACT8865_DCDC3_SUS; + val = 0xa8; + break; + case ACT8865_ID_LDO1: + reg = ACT8865_LDO1_SUS; + val = 0xe8; + break; + case ACT8865_ID_LDO2: + reg = ACT8865_LDO2_SUS; + val = 0xe8; + break; + case ACT8865_ID_LDO3: + reg = ACT8865_LDO3_SUS; + val = 0xe8; + break; + case ACT8865_ID_LDO4: + reg = ACT8865_LDO4_SUS; + val = 0xe8; + break; + default: + return -EINVAL; + } + + if (enable) + val |= BIT(4); + + /* + * Ask the PMIC to enable/disable this output when entering hibernate + * mode. + */ + return regmap_write(regmap, reg, val); +} + +static int act8865_set_suspend_enable(struct regulator_dev *rdev) +{ + return act8865_set_suspend_state(rdev, true); +} + +static int act8865_set_suspend_disable(struct regulator_dev *rdev) +{ + return act8865_set_suspend_state(rdev, false); +} + +static unsigned int act8865_of_map_mode(unsigned int mode) +{ + switch (mode) { + case ACT8865_REGULATOR_MODE_FIXED: + return REGULATOR_MODE_FAST; + case ACT8865_REGULATOR_MODE_NORMAL: + return REGULATOR_MODE_NORMAL; + case ACT8865_REGULATOR_MODE_LOWPOWER: + return REGULATOR_MODE_STANDBY; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int act8865_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct regmap *regmap = rdev->regmap; + int id = rdev_get_id(rdev); + int reg, val = 0; + + switch (id) { + case ACT8865_ID_DCDC1: + reg = ACT8865_DCDC1_CTRL; + break; + case ACT8865_ID_DCDC2: + reg = ACT8865_DCDC2_CTRL; + break; + case ACT8865_ID_DCDC3: + reg = ACT8865_DCDC3_CTRL; + break; + case ACT8865_ID_LDO1: + reg = ACT8865_LDO1_CTRL; + break; + case ACT8865_ID_LDO2: + reg = ACT8865_LDO2_CTRL; + break; + case ACT8865_ID_LDO3: + reg = ACT8865_LDO3_CTRL; + break; + case ACT8865_ID_LDO4: + reg = ACT8865_LDO4_CTRL; + break; + default: + return -EINVAL; + } + + switch (mode) { + case REGULATOR_MODE_FAST: + case REGULATOR_MODE_NORMAL: + if (id <= ACT8865_ID_DCDC3) + val = BIT(5); + break; + case REGULATOR_MODE_STANDBY: + if (id > ACT8865_ID_DCDC3) + val = BIT(5); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(regmap, reg, BIT(5), val); +} + +static unsigned int act8865_get_mode(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev->regmap; + int id = rdev_get_id(rdev); + int reg, ret, val = 0; + + switch (id) { + case ACT8865_ID_DCDC1: + reg = ACT8865_DCDC1_CTRL; + break; + case ACT8865_ID_DCDC2: + reg = ACT8865_DCDC2_CTRL; + break; + case ACT8865_ID_DCDC3: + reg = ACT8865_DCDC3_CTRL; + break; + case ACT8865_ID_LDO1: + reg = ACT8865_LDO1_CTRL; + break; + case ACT8865_ID_LDO2: + reg = ACT8865_LDO2_CTRL; + break; + case ACT8865_ID_LDO3: + reg = ACT8865_LDO3_CTRL; + break; + case ACT8865_ID_LDO4: + reg = ACT8865_LDO4_CTRL; + break; + default: + return -EINVAL; + } + + ret = regmap_read(regmap, reg, &val); + if (ret) + return ret; + + if (id <= ACT8865_ID_DCDC3 && (val & BIT(5))) + return REGULATOR_MODE_FAST; + else if (id > ACT8865_ID_DCDC3 && !(val & BIT(5))) + return REGULATOR_MODE_NORMAL; + else + return REGULATOR_MODE_STANDBY; +} + static const struct regulator_ops act8865_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, @@ -224,24 +406,44 @@ static const struct regulator_ops act8865_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, + .set_mode = act8865_set_mode, + .get_mode = act8865_get_mode, .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = act8865_set_suspend_enable, + .set_suspend_disable = act8865_set_suspend_disable, }; static const struct regulator_ops act8865_ldo_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_mode = act8865_set_mode, + .get_mode = act8865_get_mode, + .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = act8865_set_suspend_enable, + .set_suspend_disable = act8865_set_suspend_disable, + .set_pull_down = regulator_set_pull_down_regmap, +}; + +static const struct regulator_ops act8865_fixed_ldo_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, }; -#define ACT88xx_REG(_name, _family, _id, _vsel_reg, _supply) \ +#define ACT88xx_REG_(_name, _family, _id, _vsel_reg, _supply, _ops) \ [_family##_ID_##_id] = { \ .name = _name, \ .of_match = of_match_ptr(_name), \ + .of_map_mode = act8865_of_map_mode, \ .regulators_node = of_match_ptr("regulators"), \ .supply_name = _supply, \ .id = _family##_ID_##_id, \ .type = REGULATOR_VOLTAGE, \ - .ops = &act8865_ops, \ + .ops = _ops, \ .n_voltages = ACT8865_VOLTAGE_NUM, \ .linear_ranges = act8865_voltage_ranges, \ .n_linear_ranges = ARRAY_SIZE(act8865_voltage_ranges), \ @@ -249,9 +451,17 @@ static const struct regulator_ops act8865_ldo_ops = { .vsel_mask = ACT8865_VSEL_MASK, \ .enable_reg = _family##_##_id##_CTRL, \ .enable_mask = ACT8865_ENA, \ + .pull_down_reg = _family##_##_id##_CTRL, \ + .pull_down_mask = ACT8865_DIS, \ .owner = THIS_MODULE, \ } +#define ACT88xx_REG(_name, _family, _id, _vsel_reg, _supply) \ + ACT88xx_REG_(_name, _family, _id, _vsel_reg, _supply, &act8865_ops) + +#define ACT88xx_LDO(_name, _family, _id, _vsel_reg, _supply) \ + ACT88xx_REG_(_name, _family, _id, _vsel_reg, _supply, &act8865_ldo_ops) + static const struct regulator_desc act8600_regulators[] = { ACT88xx_REG("DCDC1", ACT8600, DCDC1, VSET, "vp1"), ACT88xx_REG("DCDC2", ACT8600, DCDC2, VSET, "vp2"), @@ -281,7 +491,7 @@ static const struct regulator_desc act8600_regulators[] = { .of_match = of_match_ptr("LDO_REG9"), .regulators_node = of_match_ptr("regulators"), .id = ACT8600_ID_LDO9, - .ops = &act8865_ldo_ops, + .ops = &act8865_fixed_ldo_ops, .type = REGULATOR_VOLTAGE, .n_voltages = 1, .fixed_uV = 3300000, @@ -294,7 +504,7 @@ static const struct regulator_desc act8600_regulators[] = { .of_match = of_match_ptr("LDO_REG10"), .regulators_node = of_match_ptr("regulators"), .id = ACT8600_ID_LDO10, - .ops = &act8865_ldo_ops, + .ops = &act8865_fixed_ldo_ops, .type = REGULATOR_VOLTAGE, .n_voltages = 1, .fixed_uV = 1200000, @@ -323,20 +533,20 @@ static const struct regulator_desc act8865_regulators[] = { ACT88xx_REG("DCDC_REG1", ACT8865, DCDC1, VSET1, "vp1"), ACT88xx_REG("DCDC_REG2", ACT8865, DCDC2, VSET1, "vp2"), ACT88xx_REG("DCDC_REG3", ACT8865, DCDC3, VSET1, "vp3"), - ACT88xx_REG("LDO_REG1", ACT8865, LDO1, VSET, "inl45"), - ACT88xx_REG("LDO_REG2", ACT8865, LDO2, VSET, "inl45"), - ACT88xx_REG("LDO_REG3", ACT8865, LDO3, VSET, "inl67"), - ACT88xx_REG("LDO_REG4", ACT8865, LDO4, VSET, "inl67"), + ACT88xx_LDO("LDO_REG1", ACT8865, LDO1, VSET, "inl45"), + ACT88xx_LDO("LDO_REG2", ACT8865, LDO2, VSET, "inl45"), + ACT88xx_LDO("LDO_REG3", ACT8865, LDO3, VSET, "inl67"), + ACT88xx_LDO("LDO_REG4", ACT8865, LDO4, VSET, "inl67"), }; static const struct regulator_desc act8865_alt_regulators[] = { ACT88xx_REG("DCDC_REG1", ACT8865, DCDC1, VSET2, "vp1"), ACT88xx_REG("DCDC_REG2", ACT8865, DCDC2, VSET2, "vp2"), ACT88xx_REG("DCDC_REG3", ACT8865, DCDC3, VSET2, "vp3"), - ACT88xx_REG("LDO_REG1", ACT8865, LDO1, VSET, "inl45"), - ACT88xx_REG("LDO_REG2", ACT8865, LDO2, VSET, "inl45"), - ACT88xx_REG("LDO_REG3", ACT8865, LDO3, VSET, "inl67"), - ACT88xx_REG("LDO_REG4", ACT8865, LDO4, VSET, "inl67"), + ACT88xx_LDO("LDO_REG1", ACT8865, LDO1, VSET, "inl45"), + ACT88xx_LDO("LDO_REG2", ACT8865, LDO2, VSET, "inl45"), + ACT88xx_LDO("LDO_REG3", ACT8865, LDO3, VSET, "inl67"), + ACT88xx_LDO("LDO_REG4", ACT8865, LDO4, VSET, "inl67"), }; #ifdef CONFIG_OF @@ -372,6 +582,75 @@ static void act8865_power_off(void) while (1); } +static int act8600_charger_get_status(struct regmap *map) +{ + unsigned int val; + int ret; + u8 state0, state1; + + ret = regmap_read(map, ACT8600_APCH_STAT, &val); + if (ret < 0) + return ret; + + state0 = val & ACT8600_APCH_CSTATE0; + state1 = val & ACT8600_APCH_CSTATE1; + + if (state0 && !state1) + return POWER_SUPPLY_STATUS_CHARGING; + if (!state0 && state1) + return POWER_SUPPLY_STATUS_NOT_CHARGING; + if (!state0 && !state1) + return POWER_SUPPLY_STATUS_DISCHARGING; + + return POWER_SUPPLY_STATUS_UNKNOWN; +} + +static int act8600_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct regmap *map = power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = act8600_charger_get_status(map); + if (ret < 0) + return ret; + + val->intval = ret; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property act8600_charger_properties[] = { + POWER_SUPPLY_PROP_STATUS, +}; + +static const struct power_supply_desc act8600_charger_desc = { + .name = "act8600-charger", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = act8600_charger_properties, + .num_properties = ARRAY_SIZE(act8600_charger_properties), + .get_property = act8600_charger_get_property, +}; + +static int act8600_charger_probe(struct device *dev, struct regmap *regmap) +{ + struct power_supply *charger; + struct power_supply_config cfg = { + .drv_data = regmap, + .of_node = dev->of_node, + }; + + charger = devm_power_supply_register(dev, &act8600_charger_desc, &cfg); + + return PTR_ERR_OR_ZERO(charger); +} + static int act8865_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { @@ -483,9 +762,20 @@ static int act8865_pmic_probe(struct i2c_client *client, } } + if (type == ACT8600) { + ret = act8600_charger_probe(dev, act8865->regmap); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to probe charger"); + return ret; + } + } + i2c_set_clientdata(client, act8865); - return 0; + /* Unlock expert registers for ACT8865. */ + return type != ACT8865 ? 0 : regmap_write(act8865->regmap, + ACT8865_SYS_UNLK_REGS, 0xef); } static const struct i2c_device_id act8865_ids[] = { diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c index 584284938ac9..d2f804dbc785 100644 --- a/drivers/regulator/act8945a-regulator.c +++ b/drivers/regulator/act8945a-regulator.c @@ -169,16 +169,16 @@ static int act8945a_set_mode(struct regulator_dev *rdev, unsigned int mode) reg = ACT8945A_DCDC3_CTRL; break; case ACT8945A_ID_LDO1: - reg = ACT8945A_LDO1_SUS; + reg = ACT8945A_LDO1_CTRL; break; case ACT8945A_ID_LDO2: - reg = ACT8945A_LDO2_SUS; + reg = ACT8945A_LDO2_CTRL; break; case ACT8945A_ID_LDO3: - reg = ACT8945A_LDO3_SUS; + reg = ACT8945A_LDO3_CTRL; break; case ACT8945A_ID_LDO4: - reg = ACT8945A_LDO4_SUS; + reg = ACT8945A_LDO4_CTRL; break; default: return -EINVAL; diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 152053361862..989506bd90b1 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -174,14 +174,14 @@ #define AXP803_DCDC5_1140mV_STEPS 35 #define AXP803_DCDC5_1140mV_END \ (AXP803_DCDC5_1140mV_START + AXP803_DCDC5_1140mV_STEPS) -#define AXP803_DCDC5_NUM_VOLTAGES 68 +#define AXP803_DCDC5_NUM_VOLTAGES 69 #define AXP803_DCDC6_600mV_START 0x00 #define AXP803_DCDC6_600mV_STEPS 50 #define AXP803_DCDC6_600mV_END \ (AXP803_DCDC6_600mV_START + AXP803_DCDC6_600mV_STEPS) #define AXP803_DCDC6_1120mV_START 0x33 -#define AXP803_DCDC6_1120mV_STEPS 14 +#define AXP803_DCDC6_1120mV_STEPS 20 #define AXP803_DCDC6_1120mV_END \ (AXP803_DCDC6_1120mV_START + AXP803_DCDC6_1120mV_STEPS) #define AXP803_DCDC6_NUM_VOLTAGES 72 @@ -240,7 +240,7 @@ #define AXP806_DCDCA_600mV_END \ (AXP806_DCDCA_600mV_START + AXP806_DCDCA_600mV_STEPS) #define AXP806_DCDCA_1120mV_START 0x33 -#define AXP806_DCDCA_1120mV_STEPS 14 +#define AXP806_DCDCA_1120mV_STEPS 20 #define AXP806_DCDCA_1120mV_END \ (AXP806_DCDCA_1120mV_START + AXP806_DCDCA_1120mV_STEPS) #define AXP806_DCDCA_NUM_VOLTAGES 72 @@ -774,8 +774,8 @@ static const struct regulator_linear_range axp806_dcdcd_ranges[] = { AXP806_DCDCD_600mV_END, 20000), REGULATOR_LINEAR_RANGE(1600000, - AXP806_DCDCD_600mV_START, - AXP806_DCDCD_600mV_END, + AXP806_DCDCD_1600mV_START, + AXP806_DCDCD_1600mV_END, 100000), }; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e0c0cf462004..afe94470b67f 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -381,12 +381,16 @@ static struct device_node *of_get_child_regulator(struct device_node *parent, if (!regnode) { regnode = of_get_child_regulator(child, prop_name); if (regnode) - return regnode; + goto err_node_put; } else { - return regnode; + goto err_node_put; } } return NULL; + +err_node_put: + of_node_put(child); + return regnode; } /** @@ -564,13 +568,15 @@ static ssize_t regulator_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - ssize_t ret; + int uV; regulator_lock(rdev); - ret = sprintf(buf, "%d\n", regulator_get_voltage_rdev(rdev)); + uV = regulator_get_voltage_rdev(rdev); regulator_unlock(rdev); - return ret; + if (uV < 0) + return uV; + return sprintf(buf, "%d\n", uV); } static DEVICE_ATTR(microvolts, 0444, regulator_uV_show, NULL); @@ -5640,7 +5646,7 @@ static int __init regulator_init(void) /* init early to allow our consumers to complete system booting */ core_initcall(regulator_init); -static int __init regulator_late_cleanup(struct device *dev, void *data) +static int regulator_late_cleanup(struct device *dev, void *data) { struct regulator_dev *rdev = dev_to_rdev(dev); const struct regulator_ops *ops = rdev->desc->ops; @@ -5689,18 +5695,9 @@ unlock: return 0; } -static int __init regulator_init_complete(void) +static void regulator_init_complete_work_function(struct work_struct *work) { /* - * Since DT doesn't provide an idiomatic mechanism for - * enabling full constraints and since it's much more natural - * with DT to provide them just assume that a DT enabled - * system has full constraints. - */ - if (of_have_populated_dt()) - has_full_constraints = true; - - /* * Regulators may had failed to resolve their input supplies * when were registered, either because the input supply was * not registered yet or because its parent device was not @@ -5717,6 +5714,35 @@ static int __init regulator_init_complete(void) */ class_for_each_device(®ulator_class, NULL, NULL, regulator_late_cleanup); +} + +static DECLARE_DELAYED_WORK(regulator_init_complete_work, + regulator_init_complete_work_function); + +static int __init regulator_init_complete(void) +{ + /* + * Since DT doesn't provide an idiomatic mechanism for + * enabling full constraints and since it's much more natural + * with DT to provide them just assume that a DT enabled + * system has full constraints. + */ + if (of_have_populated_dt()) + has_full_constraints = true; + + /* + * We punt completion for an arbitrary amount of time since + * systems like distros will load many drivers from userspace + * so consumers might not always be ready yet, this is + * particularly an issue with laptops where this might bounce + * the display off then on. Ideally we'd get a notification + * from userspace when this happens but we don't so just wait + * a bit and hope we waited long enough. It'd be better if + * we'd only do this on systems that need it, and a kernel + * command line option might be useful. + */ + schedule_delayed_work(®ulator_init_complete_work, + msecs_to_jiffies(30000)); return 0; } diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c index 2ffc64622451..56f3f72d7707 100644 --- a/drivers/regulator/da9062-regulator.c +++ b/drivers/regulator/da9062-regulator.c @@ -1032,10 +1032,8 @@ static int da9062_regulator_probe(struct platform_device *pdev) /* LDOs overcurrent event support */ irq = platform_get_irq_byname(pdev, "LDO_LIM"); - if (irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ.\n"); + if (irq < 0) return irq; - } regulators->irq_ldo_lim = irq; ret = devm_request_threaded_irq(&pdev->dev, irq, diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 02f816318fba..28b1b20f45bd 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -863,10 +863,8 @@ static int da9063_regulator_probe(struct platform_device *pdev) /* LDOs overcurrent event support */ irq = platform_get_irq_byname(pdev, "LDO_LIM"); - if (irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ.\n"); + if (irq < 0) return irq; - } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, da9063_ldo_lim_event, diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index 0309823d2c72..bf80748f1ccc 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -285,7 +285,7 @@ static struct da9211_pdata *da9211_parse_regulators_dt( pdata->reg_node[n] = da9211_matches[i].of_node; pdata->gpiod_ren[n] = devm_gpiod_get_from_of_node(dev, da9211_matches[i].of_node, - "enable", + "enable-gpios", 0, GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, "da9211-enable"); diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 999547dde99d..d90a6fd8cbc7 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -23,14 +23,63 @@ #include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/machine.h> +#include <linux/clk.h> + struct fixed_voltage_data { struct regulator_desc desc; struct regulator_dev *dev; + + struct clk *enable_clock; + unsigned int clk_enable_counter; }; +struct fixed_dev_type { + bool has_enable_clock; +}; + +static const struct fixed_dev_type fixed_voltage_data = { + .has_enable_clock = false, +}; + +static const struct fixed_dev_type fixed_clkenable_data = { + .has_enable_clock = true, +}; + +static int reg_clock_enable(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + int ret = 0; + + ret = clk_prepare_enable(priv->enable_clock); + if (ret) + return ret; + + priv->clk_enable_counter++; + + return ret; +} + +static int reg_clock_disable(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + + clk_disable_unprepare(priv->enable_clock); + priv->clk_enable_counter--; + + return 0; +} + +static int reg_clock_is_enabled(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + + return priv->clk_enable_counter > 0; +} + /** * of_get_fixed_voltage_config - extract fixed_voltage_config structure info @@ -84,10 +133,19 @@ of_get_fixed_voltage_config(struct device *dev, static struct regulator_ops fixed_voltage_ops = { }; +static struct regulator_ops fixed_voltage_clkenabled_ops = { + .enable = reg_clock_enable, + .disable = reg_clock_disable, + .is_enabled = reg_clock_is_enabled, +}; + static int reg_fixed_voltage_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct fixed_voltage_config *config; struct fixed_voltage_data *drvdata; + const struct fixed_dev_type *drvtype = + of_match_device(dev->driver->of_match_table, dev)->data; struct regulator_config cfg = { }; enum gpiod_flags gflags; int ret; @@ -118,7 +176,18 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) } drvdata->desc.type = REGULATOR_VOLTAGE; drvdata->desc.owner = THIS_MODULE; - drvdata->desc.ops = &fixed_voltage_ops; + + if (drvtype->has_enable_clock) { + drvdata->desc.ops = &fixed_voltage_clkenabled_ops; + + drvdata->enable_clock = devm_clk_get(dev, NULL); + if (IS_ERR(drvdata->enable_clock)) { + dev_err(dev, "Cant get enable-clock from devicetree\n"); + return -ENOENT; + } + } else { + drvdata->desc.ops = &fixed_voltage_ops; + } drvdata->desc.enable_time = config->startup_delay; @@ -191,8 +260,16 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) #if defined(CONFIG_OF) static const struct of_device_id fixed_of_match[] = { - { .compatible = "regulator-fixed", }, - {}, + { + .compatible = "regulator-fixed", + .data = &fixed_voltage_data, + }, + { + .compatible = "regulator-fixed-clock", + .data = &fixed_clkenable_data, + }, + { + }, }; MODULE_DEVICE_TABLE(of, fixed_of_match); #endif diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index 4986cc5064a1..ca3dc3f3bb29 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -860,3 +860,24 @@ int regulator_get_current_limit_regmap(struct regulator_dev *rdev) return -EINVAL; } EXPORT_SYMBOL_GPL(regulator_get_current_limit_regmap); + +/** + * regulator_bulk_set_supply_names - initialize the 'supply' fields in an array + * of regulator_bulk_data structs + * + * @consumers: array of regulator_bulk_data entries to initialize + * @supply_names: array of supply name strings + * @num_supplies: number of supply names to initialize + * + * Note: the 'consumers' array must be the size of 'num_supplies'. + */ +void regulator_bulk_set_supply_names(struct regulator_bulk_data *consumers, + const char *const *supply_names, + unsigned int num_supplies) +{ + unsigned int i; + + for (i = 0; i < num_supplies; i++) + consumers[i].supply = supply_names[i]; +} +EXPORT_SYMBOL_GPL(regulator_bulk_set_supply_names); diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c index 5647e2f97ff8..4b9f618b07e9 100644 --- a/drivers/regulator/lm363x-regulator.c +++ b/drivers/regulator/lm363x-regulator.c @@ -30,13 +30,13 @@ /* LM3632 */ #define LM3632_BOOST_VSEL_MAX 0x26 -#define LM3632_LDO_VSEL_MAX 0x29 +#define LM3632_LDO_VSEL_MAX 0x28 #define LM3632_VBOOST_MIN 4500000 #define LM3632_VLDO_MIN 4000000 /* LM36274 */ #define LM36274_BOOST_VSEL_MAX 0x3f -#define LM36274_LDO_VSEL_MAX 0x34 +#define LM36274_LDO_VSEL_MAX 0x32 #define LM36274_VOLTAGE_MIN 4000000 /* Common */ @@ -226,7 +226,7 @@ static const struct regulator_desc lm363x_regulator_desc[] = { .of_match = "vboost", .id = LM36274_BOOST, .ops = &lm363x_boost_voltage_table_ops, - .n_voltages = LM36274_BOOST_VSEL_MAX, + .n_voltages = LM36274_BOOST_VSEL_MAX + 1, .min_uV = LM36274_VOLTAGE_MIN, .uV_step = LM363X_STEP_50mV, .type = REGULATOR_VOLTAGE, @@ -239,7 +239,7 @@ static const struct regulator_desc lm363x_regulator_desc[] = { .of_match = "vpos", .id = LM36274_LDO_POS, .ops = &lm363x_regulator_voltage_table_ops, - .n_voltages = LM36274_LDO_VSEL_MAX, + .n_voltages = LM36274_LDO_VSEL_MAX + 1, .min_uV = LM36274_VOLTAGE_MIN, .uV_step = LM363X_STEP_50mV, .type = REGULATOR_VOLTAGE, @@ -254,7 +254,7 @@ static const struct regulator_desc lm363x_regulator_desc[] = { .of_match = "vneg", .id = LM36274_LDO_NEG, .ops = &lm363x_regulator_voltage_table_ops, - .n_voltages = LM36274_LDO_VSEL_MAX, + .n_voltages = LM36274_LDO_VSEL_MAX + 1, .min_uV = LM36274_VOLTAGE_MIN, .uV_step = LM363X_STEP_50mV, .type = REGULATOR_VOLTAGE, diff --git a/drivers/regulator/lp87565-regulator.c b/drivers/regulator/lp87565-regulator.c index 5d067f7c2116..4ae12ac1f4c6 100644 --- a/drivers/regulator/lp87565-regulator.c +++ b/drivers/regulator/lp87565-regulator.c @@ -65,7 +65,6 @@ static int lp87565_buck_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { int id = rdev_get_id(rdev); - struct lp87565 *lp87565 = rdev_get_drvdata(rdev); unsigned int reg; int ret; @@ -86,11 +85,11 @@ static int lp87565_buck_set_ramp_delay(struct regulator_dev *rdev, else reg = 0; - ret = regmap_update_bits(lp87565->regmap, regulators[id].ctrl2_reg, + ret = regmap_update_bits(rdev->regmap, regulators[id].ctrl2_reg, LP87565_BUCK_CTRL_2_SLEW_RATE, reg << __ffs(LP87565_BUCK_CTRL_2_SLEW_RATE)); if (ret) { - dev_err(lp87565->dev, "SLEW RATE write failed: %d\n", ret); + dev_err(&rdev->dev, "SLEW RATE write failed: %d\n", ret); return ret; } @@ -163,7 +162,7 @@ static int lp87565_regulator_probe(struct platform_device *pdev) struct lp87565 *lp87565 = dev_get_drvdata(pdev->dev.parent); struct regulator_config config = { }; struct regulator_dev *rdev; - int i, min_idx = LP87565_BUCK_0, max_idx = LP87565_BUCK_3; + int i, min_idx, max_idx; platform_set_drvdata(pdev, lp87565); @@ -182,9 +181,9 @@ static int lp87565_regulator_probe(struct platform_device *pdev) max_idx = LP87565_BUCK_3210; break; default: - dev_err(lp87565->dev, "Invalid lp config %d\n", - lp87565->dev_type); - return -EINVAL; + min_idx = LP87565_BUCK_0; + max_idx = LP87565_BUCK_3; + break; } for (i = min_idx; i <= max_idx; i++) { diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 1b00f3638996..00e9bb92c326 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -464,7 +464,7 @@ static int lp8788_config_ldo_enable_mode(struct platform_device *pdev, { struct lp8788 *lp = ldo->lp; enum lp8788_ext_ldo_en_id enable_id; - u8 en_mask[] = { + static const u8 en_mask[] = { [EN_ALDO1] = LP8788_EN_SEL_ALDO1_M, [EN_ALDO234] = LP8788_EN_SEL_ALDO234_M, [EN_ALDO5] = LP8788_EN_SEL_ALDO5_M, diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index 8020eb57374a..c8e579e99316 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c @@ -257,7 +257,7 @@ static int max77686_of_parse_cb(struct device_node *np, case MAX77686_BUCK9: case MAX77686_LDO20 ... MAX77686_LDO22: config->ena_gpiod = gpiod_get_from_of_node(np, - "maxim,ena", + "maxim,ena-gpios", 0, GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, "max77686-regulator"); diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index 4bca54446287..347043a5a9a7 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -485,7 +485,6 @@ static int max8660_probe(struct i2c_client *client, rdev = devm_regulator_register(&client->dev, &max8660_reg[id], &config); if (IS_ERR(rdev)) { - ret = PTR_ERR(rdev); dev_err(&client->dev, "failed to register %s\n", max8660_reg[id].name); return PTR_ERR(rdev); diff --git a/drivers/regulator/mt6358-regulator.c b/drivers/regulator/mt6358-regulator.c new file mode 100644 index 000000000000..ba42682e06f3 --- /dev/null +++ b/drivers/regulator/mt6358-regulator.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2019 MediaTek Inc. + +#include <linux/mfd/mt6358/registers.h> +#include <linux/mfd/mt6397/core.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/mt6358-regulator.h> +#include <linux/regulator/of_regulator.h> + +#define MT6358_BUCK_MODE_AUTO 0 +#define MT6358_BUCK_MODE_FORCE_PWM 1 + +/* + * MT6358 regulators' information + * + * @desc: standard fields of regulator description. + * @qi: Mask for query enable signal status of regulators + */ +struct mt6358_regulator_info { + struct regulator_desc desc; + u32 status_reg; + u32 qi; + const u32 *index_table; + unsigned int n_table; + u32 vsel_shift; + u32 da_vsel_reg; + u32 da_vsel_mask; + u32 da_vsel_shift; + u32 modeset_reg; + u32 modeset_mask; + u32 modeset_shift; +}; + +#define MT6358_BUCK(match, vreg, min, max, step, \ + volt_ranges, vosel_mask, _da_vsel_reg, _da_vsel_mask, \ + _da_vsel_shift, _modeset_reg, _modeset_shift) \ +[MT6358_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6358_volt_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6358_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .linear_ranges = volt_ranges, \ + .n_linear_ranges = ARRAY_SIZE(volt_ranges), \ + .vsel_reg = MT6358_BUCK_##vreg##_ELR0, \ + .vsel_mask = vosel_mask, \ + .enable_reg = MT6358_BUCK_##vreg##_CON0, \ + .enable_mask = BIT(0), \ + .of_map_mode = mt6358_map_mode, \ + }, \ + .status_reg = MT6358_BUCK_##vreg##_DBG1, \ + .qi = BIT(0), \ + .da_vsel_reg = _da_vsel_reg, \ + .da_vsel_mask = _da_vsel_mask, \ + .da_vsel_shift = _da_vsel_shift, \ + .modeset_reg = _modeset_reg, \ + .modeset_mask = BIT(_modeset_shift), \ + .modeset_shift = _modeset_shift \ +} + +#define MT6358_LDO(match, vreg, ldo_volt_table, \ + ldo_index_table, enreg, enbit, vosel, \ + vosel_mask, vosel_shift) \ +[MT6358_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6358_volt_table_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6358_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(ldo_volt_table), \ + .volt_table = ldo_volt_table, \ + .vsel_reg = vosel, \ + .vsel_mask = vosel_mask, \ + .enable_reg = enreg, \ + .enable_mask = BIT(enbit), \ + }, \ + .status_reg = MT6358_LDO_##vreg##_CON1, \ + .qi = BIT(15), \ + .index_table = ldo_index_table, \ + .n_table = ARRAY_SIZE(ldo_index_table), \ + .vsel_shift = vosel_shift, \ +} + +#define MT6358_LDO1(match, vreg, min, max, step, \ + volt_ranges, _da_vsel_reg, _da_vsel_mask, \ + _da_vsel_shift, vosel, vosel_mask) \ +[MT6358_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6358_volt_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6358_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .linear_ranges = volt_ranges, \ + .n_linear_ranges = ARRAY_SIZE(volt_ranges), \ + .vsel_reg = vosel, \ + .vsel_mask = vosel_mask, \ + .enable_reg = MT6358_LDO_##vreg##_CON0, \ + .enable_mask = BIT(0), \ + }, \ + .da_vsel_reg = _da_vsel_reg, \ + .da_vsel_mask = _da_vsel_mask, \ + .da_vsel_shift = _da_vsel_shift, \ + .status_reg = MT6358_LDO_##vreg##_DBG1, \ + .qi = BIT(0), \ +} + +#define MT6358_REG_FIXED(match, vreg, \ + enreg, enbit, volt) \ +[MT6358_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6358_volt_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6358_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = 1, \ + .enable_reg = enreg, \ + .enable_mask = BIT(enbit), \ + .min_uV = volt, \ + }, \ + .status_reg = MT6358_LDO_##vreg##_CON1, \ + .qi = BIT(15), \ +} + +static const struct regulator_linear_range buck_volt_range1[] = { + REGULATOR_LINEAR_RANGE(500000, 0, 0x7f, 6250), +}; + +static const struct regulator_linear_range buck_volt_range2[] = { + REGULATOR_LINEAR_RANGE(500000, 0, 0x7f, 12500), +}; + +static const struct regulator_linear_range buck_volt_range3[] = { + REGULATOR_LINEAR_RANGE(500000, 0, 0x3f, 50000), +}; + +static const struct regulator_linear_range buck_volt_range4[] = { + REGULATOR_LINEAR_RANGE(1000000, 0, 0x7f, 12500), +}; + +static const u32 vdram2_voltages[] = { + 600000, 1800000, +}; + +static const u32 vsim_voltages[] = { + 1700000, 1800000, 2700000, 3000000, 3100000, +}; + +static const u32 vibr_voltages[] = { + 1200000, 1300000, 1500000, 1800000, + 2000000, 2800000, 3000000, 3300000, +}; + +static const u32 vusb_voltages[] = { + 3000000, 3100000, +}; + +static const u32 vcamd_voltages[] = { + 900000, 1000000, 1100000, 1200000, + 1300000, 1500000, 1800000, +}; + +static const u32 vefuse_voltages[] = { + 1700000, 1800000, 1900000, +}; + +static const u32 vmch_vemc_voltages[] = { + 2900000, 3000000, 3300000, +}; + +static const u32 vcama_voltages[] = { + 1800000, 2500000, 2700000, + 2800000, 2900000, 3000000, +}; + +static const u32 vcn33_bt_wifi_voltages[] = { + 3300000, 3400000, 3500000, +}; + +static const u32 vmc_voltages[] = { + 1800000, 2900000, 3000000, 3300000, +}; + +static const u32 vldo28_voltages[] = { + 2800000, 3000000, +}; + +static const u32 vdram2_idx[] = { + 0, 12, +}; + +static const u32 vsim_idx[] = { + 3, 4, 8, 11, 12, +}; + +static const u32 vibr_idx[] = { + 0, 1, 2, 4, 5, 9, 11, 13, +}; + +static const u32 vusb_idx[] = { + 3, 4, +}; + +static const u32 vcamd_idx[] = { + 3, 4, 5, 6, 7, 9, 12, +}; + +static const u32 vefuse_idx[] = { + 11, 12, 13, +}; + +static const u32 vmch_vemc_idx[] = { + 2, 3, 5, +}; + +static const u32 vcama_idx[] = { + 0, 7, 9, 10, 11, 12, +}; + +static const u32 vcn33_bt_wifi_idx[] = { + 1, 2, 3, +}; + +static const u32 vmc_idx[] = { + 4, 10, 11, 13, +}; + +static const u32 vldo28_idx[] = { + 1, 3, +}; + +static unsigned int mt6358_map_mode(unsigned int mode) +{ + return mode == MT6358_BUCK_MODE_AUTO ? + REGULATOR_MODE_NORMAL : REGULATOR_MODE_FAST; +} + +static int mt6358_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + int idx, ret; + const u32 *pvol; + struct mt6358_regulator_info *info = rdev_get_drvdata(rdev); + + pvol = info->index_table; + + idx = pvol[selector]; + ret = regmap_update_bits(rdev->regmap, info->desc.vsel_reg, + info->desc.vsel_mask, + idx << info->vsel_shift); + + return ret; +} + +static int mt6358_get_voltage_sel(struct regulator_dev *rdev) +{ + int idx, ret; + u32 selector; + struct mt6358_regulator_info *info = rdev_get_drvdata(rdev); + const u32 *pvol; + + ret = regmap_read(rdev->regmap, info->desc.vsel_reg, &selector); + if (ret != 0) { + dev_info(&rdev->dev, + "Failed to get mt6358 %s vsel reg: %d\n", + info->desc.name, ret); + return ret; + } + + selector = (selector & info->desc.vsel_mask) >> info->vsel_shift; + pvol = info->index_table; + for (idx = 0; idx < info->desc.n_voltages; idx++) { + if (pvol[idx] == selector) + return idx; + } + + return -EINVAL; +} + +static int mt6358_get_buck_voltage_sel(struct regulator_dev *rdev) +{ + int ret, regval; + struct mt6358_regulator_info *info = rdev_get_drvdata(rdev); + + ret = regmap_read(rdev->regmap, info->da_vsel_reg, ®val); + if (ret != 0) { + dev_err(&rdev->dev, + "Failed to get mt6358 Buck %s vsel reg: %d\n", + info->desc.name, ret); + return ret; + } + + ret = (regval >> info->da_vsel_shift) & info->da_vsel_mask; + + return ret; +} + +static int mt6358_get_status(struct regulator_dev *rdev) +{ + int ret; + u32 regval; + struct mt6358_regulator_info *info = rdev_get_drvdata(rdev); + + ret = regmap_read(rdev->regmap, info->status_reg, ®val); + if (ret != 0) { + dev_info(&rdev->dev, "Failed to get enable reg: %d\n", ret); + return ret; + } + + return (regval & info->qi) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF; +} + +static int mt6358_regulator_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct mt6358_regulator_info *info = rdev_get_drvdata(rdev); + int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = MT6358_BUCK_MODE_FORCE_PWM; + break; + case REGULATOR_MODE_NORMAL: + val = MT6358_BUCK_MODE_AUTO; + break; + default: + return -EINVAL; + } + + dev_dbg(&rdev->dev, "mt6358 buck set_mode %#x, %#x, %#x, %#x\n", + info->modeset_reg, info->modeset_mask, + info->modeset_shift, val); + + val <<= info->modeset_shift; + + return regmap_update_bits(rdev->regmap, info->modeset_reg, + info->modeset_mask, val); +} + +static unsigned int mt6358_regulator_get_mode(struct regulator_dev *rdev) +{ + struct mt6358_regulator_info *info = rdev_get_drvdata(rdev); + int ret, regval; + + ret = regmap_read(rdev->regmap, info->modeset_reg, ®val); + if (ret != 0) { + dev_err(&rdev->dev, + "Failed to get mt6358 buck mode: %d\n", ret); + return ret; + } + + switch ((regval & info->modeset_mask) >> info->modeset_shift) { + case MT6358_BUCK_MODE_AUTO: + return REGULATOR_MODE_NORMAL; + case MT6358_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + default: + return -EINVAL; + } +} + +static const struct regulator_ops mt6358_volt_range_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = mt6358_get_buck_voltage_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6358_get_status, + .set_mode = mt6358_regulator_set_mode, + .get_mode = mt6358_regulator_get_mode, +}; + +static const struct regulator_ops mt6358_volt_table_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, + .set_voltage_sel = mt6358_set_voltage_sel, + .get_voltage_sel = mt6358_get_voltage_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6358_get_status, +}; + +static const struct regulator_ops mt6358_volt_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6358_get_status, +}; + +/* The array is indexed by id(MT6358_ID_XXX) */ +static struct mt6358_regulator_info mt6358_regulators[] = { + MT6358_BUCK("buck_vdram1", VDRAM1, 500000, 2087500, 12500, + buck_volt_range2, 0x7f, MT6358_BUCK_VDRAM1_DBG0, 0x7f, + 0, MT6358_VDRAM1_ANA_CON0, 8), + MT6358_BUCK("buck_vcore", VCORE, 500000, 1293750, 6250, + buck_volt_range1, 0x7f, MT6358_BUCK_VCORE_DBG0, 0x7f, + 0, MT6358_VCORE_VGPU_ANA_CON0, 1), + MT6358_BUCK("buck_vpa", VPA, 500000, 3650000, 50000, + buck_volt_range3, 0x3f, MT6358_BUCK_VPA_DBG0, 0x3f, 0, + MT6358_VPA_ANA_CON0, 3), + MT6358_BUCK("buck_vproc11", VPROC11, 500000, 1293750, 6250, + buck_volt_range1, 0x7f, MT6358_BUCK_VPROC11_DBG0, 0x7f, + 0, MT6358_VPROC_ANA_CON0, 1), + MT6358_BUCK("buck_vproc12", VPROC12, 500000, 1293750, 6250, + buck_volt_range1, 0x7f, MT6358_BUCK_VPROC12_DBG0, 0x7f, + 0, MT6358_VPROC_ANA_CON0, 2), + MT6358_BUCK("buck_vgpu", VGPU, 500000, 1293750, 6250, + buck_volt_range1, 0x7f, MT6358_BUCK_VGPU_ELR0, 0x7f, 0, + MT6358_VCORE_VGPU_ANA_CON0, 2), + MT6358_BUCK("buck_vs2", VS2, 500000, 2087500, 12500, + buck_volt_range2, 0x7f, MT6358_BUCK_VS2_DBG0, 0x7f, 0, + MT6358_VS2_ANA_CON0, 8), + MT6358_BUCK("buck_vmodem", VMODEM, 500000, 1293750, 6250, + buck_volt_range1, 0x7f, MT6358_BUCK_VMODEM_DBG0, 0x7f, + 0, MT6358_VMODEM_ANA_CON0, 8), + MT6358_BUCK("buck_vs1", VS1, 1000000, 2587500, 12500, + buck_volt_range4, 0x7f, MT6358_BUCK_VS1_DBG0, 0x7f, 0, + MT6358_VS1_ANA_CON0, 8), + MT6358_REG_FIXED("ldo_vrf12", VRF12, + MT6358_LDO_VRF12_CON0, 0, 1200000), + MT6358_REG_FIXED("ldo_vio18", VIO18, + MT6358_LDO_VIO18_CON0, 0, 1800000), + MT6358_REG_FIXED("ldo_vcamio", VCAMIO, + MT6358_LDO_VCAMIO_CON0, 0, 1800000), + MT6358_REG_FIXED("ldo_vcn18", VCN18, MT6358_LDO_VCN18_CON0, 0, 1800000), + MT6358_REG_FIXED("ldo_vfe28", VFE28, MT6358_LDO_VFE28_CON0, 0, 2800000), + MT6358_REG_FIXED("ldo_vcn28", VCN28, MT6358_LDO_VCN28_CON0, 0, 2800000), + MT6358_REG_FIXED("ldo_vxo22", VXO22, MT6358_LDO_VXO22_CON0, 0, 2200000), + MT6358_REG_FIXED("ldo_vaux18", VAUX18, + MT6358_LDO_VAUX18_CON0, 0, 1800000), + MT6358_REG_FIXED("ldo_vbif28", VBIF28, + MT6358_LDO_VBIF28_CON0, 0, 2800000), + MT6358_REG_FIXED("ldo_vio28", VIO28, MT6358_LDO_VIO28_CON0, 0, 2800000), + MT6358_REG_FIXED("ldo_va12", VA12, MT6358_LDO_VA12_CON0, 0, 1200000), + MT6358_REG_FIXED("ldo_vrf18", VRF18, MT6358_LDO_VRF18_CON0, 0, 1800000), + MT6358_REG_FIXED("ldo_vaud28", VAUD28, + MT6358_LDO_VAUD28_CON0, 0, 2800000), + MT6358_LDO("ldo_vdram2", VDRAM2, vdram2_voltages, vdram2_idx, + MT6358_LDO_VDRAM2_CON0, 0, MT6358_LDO_VDRAM2_ELR0, 0x10, 0), + MT6358_LDO("ldo_vsim1", VSIM1, vsim_voltages, vsim_idx, + MT6358_LDO_VSIM1_CON0, 0, MT6358_VSIM1_ANA_CON0, 0xf00, 8), + MT6358_LDO("ldo_vibr", VIBR, vibr_voltages, vibr_idx, + MT6358_LDO_VIBR_CON0, 0, MT6358_VIBR_ANA_CON0, 0xf00, 8), + MT6358_LDO("ldo_vusb", VUSB, vusb_voltages, vusb_idx, + MT6358_LDO_VUSB_CON0_0, 0, MT6358_VUSB_ANA_CON0, 0x700, 8), + MT6358_LDO("ldo_vcamd", VCAMD, vcamd_voltages, vcamd_idx, + MT6358_LDO_VCAMD_CON0, 0, MT6358_VCAMD_ANA_CON0, 0xf00, 8), + MT6358_LDO("ldo_vefuse", VEFUSE, vefuse_voltages, vefuse_idx, + MT6358_LDO_VEFUSE_CON0, 0, MT6358_VEFUSE_ANA_CON0, 0xf00, 8), + MT6358_LDO("ldo_vmch", VMCH, vmch_vemc_voltages, vmch_vemc_idx, + MT6358_LDO_VMCH_CON0, 0, MT6358_VMCH_ANA_CON0, 0x700, 8), + MT6358_LDO("ldo_vcama1", VCAMA1, vcama_voltages, vcama_idx, + MT6358_LDO_VCAMA1_CON0, 0, MT6358_VCAMA1_ANA_CON0, 0xf00, 8), + MT6358_LDO("ldo_vemc", VEMC, vmch_vemc_voltages, vmch_vemc_idx, + MT6358_LDO_VEMC_CON0, 0, MT6358_VEMC_ANA_CON0, 0x700, 8), + MT6358_LDO("ldo_vcn33_bt", VCN33_BT, vcn33_bt_wifi_voltages, + vcn33_bt_wifi_idx, MT6358_LDO_VCN33_CON0_0, + 0, MT6358_VCN33_ANA_CON0, 0x300, 8), + MT6358_LDO("ldo_vcn33_wifi", VCN33_WIFI, vcn33_bt_wifi_voltages, + vcn33_bt_wifi_idx, MT6358_LDO_VCN33_CON0_1, + 0, MT6358_VCN33_ANA_CON0, 0x300, 8), + MT6358_LDO("ldo_vcama2", VCAMA2, vcama_voltages, vcama_idx, + MT6358_LDO_VCAMA2_CON0, 0, MT6358_VCAMA2_ANA_CON0, 0xf00, 8), + MT6358_LDO("ldo_vmc", VMC, vmc_voltages, vmc_idx, + MT6358_LDO_VMC_CON0, 0, MT6358_VMC_ANA_CON0, 0xf00, 8), + MT6358_LDO("ldo_vldo28", VLDO28, vldo28_voltages, vldo28_idx, + MT6358_LDO_VLDO28_CON0_0, 0, + MT6358_VLDO28_ANA_CON0, 0x300, 8), + MT6358_LDO("ldo_vsim2", VSIM2, vsim_voltages, vsim_idx, + MT6358_LDO_VSIM2_CON0, 0, MT6358_VSIM2_ANA_CON0, 0xf00, 8), + MT6358_LDO1("ldo_vsram_proc11", VSRAM_PROC11, 500000, 1293750, 6250, + buck_volt_range1, MT6358_LDO_VSRAM_PROC11_DBG0, 0x7f, 8, + MT6358_LDO_VSRAM_CON0, 0x7f), + MT6358_LDO1("ldo_vsram_others", VSRAM_OTHERS, 500000, 1293750, 6250, + buck_volt_range1, MT6358_LDO_VSRAM_OTHERS_DBG0, 0x7f, 8, + MT6358_LDO_VSRAM_CON2, 0x7f), + MT6358_LDO1("ldo_vsram_gpu", VSRAM_GPU, 500000, 1293750, 6250, + buck_volt_range1, MT6358_LDO_VSRAM_GPU_DBG0, 0x7f, 8, + MT6358_LDO_VSRAM_CON3, 0x7f), + MT6358_LDO1("ldo_vsram_proc12", VSRAM_PROC12, 500000, 1293750, 6250, + buck_volt_range1, MT6358_LDO_VSRAM_PROC12_DBG0, 0x7f, 8, + MT6358_LDO_VSRAM_CON1, 0x7f), +}; + +static int mt6358_regulator_probe(struct platform_device *pdev) +{ + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = {}; + struct regulator_dev *rdev; + int i; + + for (i = 0; i < MT6358_MAX_REGULATOR; i++) { + config.dev = &pdev->dev; + config.driver_data = &mt6358_regulators[i]; + config.regmap = mt6397->regmap; + + rdev = devm_regulator_register(&pdev->dev, + &mt6358_regulators[i].desc, + &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", + mt6358_regulators[i].desc.name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id mt6358_platform_ids[] = { + {"mt6358-regulator", 0}, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, mt6358_platform_ids); + +static struct platform_driver mt6358_regulator_driver = { + .driver = { + .name = "mt6358-regulator", + }, + .probe = mt6358_regulator_probe, + .id_table = mt6358_platform_ids, +}; + +module_platform_driver(mt6358_regulator_driver); + +MODULE_AUTHOR("Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>"); +MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6358 PMIC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 397918ebba55..9112faa6a9a0 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -416,8 +416,10 @@ device_node *regulator_of_get_init_node(struct device *dev, if (!name) name = child->name; - if (!strcmp(desc->of_match, name)) + if (!strcmp(desc->of_match, name)) { + of_node_put(search); return of_node_get(child); + } } of_node_put(search); diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index b2c2d01d1637..db6c085da65e 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -50,6 +50,20 @@ enum rpmh_regulator_type { #define PMIC4_BOB_MODE_AUTO 2 #define PMIC4_BOB_MODE_PWM 3 +#define PMIC5_LDO_MODE_RETENTION 3 +#define PMIC5_LDO_MODE_LPM 4 +#define PMIC5_LDO_MODE_HPM 7 + +#define PMIC5_SMPS_MODE_RETENTION 3 +#define PMIC5_SMPS_MODE_PFM 4 +#define PMIC5_SMPS_MODE_AUTO 6 +#define PMIC5_SMPS_MODE_PWM 7 + +#define PMIC5_BOB_MODE_PASS 2 +#define PMIC5_BOB_MODE_PFM 4 +#define PMIC5_BOB_MODE_AUTO 6 +#define PMIC5_BOB_MODE_PWM 7 + /** * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations * @regulator_type: RPMh accelerator type used to manage this @@ -488,6 +502,14 @@ static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = { [REGULATOR_MODE_FAST] = -EINVAL, }; +static const int pmic_mode_map_pmic5_ldo[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC5_LDO_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC5_LDO_MODE_LPM, + [REGULATOR_MODE_NORMAL] = PMIC5_LDO_MODE_HPM, + [REGULATOR_MODE_FAST] = -EINVAL, +}; + static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode) { unsigned int mode; @@ -518,6 +540,14 @@ static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = { [REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM, }; +static const int pmic_mode_map_pmic5_smps[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC5_SMPS_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC5_SMPS_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC5_SMPS_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC5_SMPS_MODE_PWM, +}; + static unsigned int rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode) { @@ -552,6 +582,14 @@ static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = { [REGULATOR_MODE_FAST] = PMIC4_BOB_MODE_PWM, }; +static const int pmic_mode_map_pmic5_bob[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = -EINVAL, + [REGULATOR_MODE_IDLE] = PMIC5_BOB_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC5_BOB_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC5_BOB_MODE_PWM, +}; + static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode) { unsigned int mode; @@ -637,6 +675,72 @@ static const struct rpmh_vreg_hw_data pmic4_lvs = { /* LVS hardware does not support voltage or mode configuration. */ }; +static const struct rpmh_vreg_hw_data pmic5_pldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000), + .n_voltages = 256, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo_lv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(1504000, 0, 62, 8000), + .n_voltages = 63, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_nldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 123, 8000), + .n_voltages = 124, + .hpm_min_load_uA = 30000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_hfsmps510 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + .n_voltages = 216, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps510 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(300000, 0, 263, 4000), + .n_voltages = 264, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_hfsmps515 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(2800000, 0, 4, 1600), + .n_voltages = 5, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_bob = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_bypass_ops, + .voltage_range = REGULATOR_LINEAR_RANGE(300000, 0, 135, 32000), + .n_voltages = 136, + .pmic_mode_map = pmic_mode_map_pmic5_bob, + .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode, +}; + #define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ { \ .name = _name, \ @@ -705,6 +809,75 @@ static const struct rpmh_vreg_init_data pm8005_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pm8150_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l10"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l2-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l13-l6-l17"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l6-l17"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l13-l6-l17"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + {}, +}; + +static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l8"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l11"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l1-l8-l11"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l9-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l7-l11"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {}, +}; + +static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s6", &pmic5_pldo_lv, "vdd-l7"), + {}, +}; + static int rpmh_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -744,6 +917,22 @@ static int rpmh_regulator_probe(struct platform_device *pdev) static const struct of_device_id rpmh_regulator_match_table[] = { { + .compatible = "qcom,pm8005-rpmh-regulators", + .data = pm8005_vreg_data, + }, + { + .compatible = "qcom,pm8009-rpmh-regulators", + .data = pm8009_vreg_data, + }, + { + .compatible = "qcom,pm8150-rpmh-regulators", + .data = pm8150_vreg_data, + }, + { + .compatible = "qcom,pm8150l-rpmh-regulators", + .data = pm8150l_vreg_data, + }, + { .compatible = "qcom,pm8998-rpmh-regulators", .data = pm8998_vreg_data, }, @@ -751,10 +940,6 @@ static const struct of_device_id rpmh_regulator_match_table[] = { .compatible = "qcom,pmi8998-rpmh-regulators", .data = pmi8998_vreg_data, }, - { - .compatible = "qcom,pm8005-rpmh-regulators", - .data = pm8005_vreg_data, - }, {} }; MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index e7af0c53d449..61bd5ef0806c 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -606,7 +606,7 @@ static unsigned int rk8xx_regulator_of_map_mode(unsigned int mode) case 2: return REGULATOR_MODE_NORMAL; default: - return -EINVAL; + return REGULATOR_MODE_INVALID; } } diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 054baaadfdfd..5bc00884cf51 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -1226,7 +1226,7 @@ common_reg: goto out; } - if (s2mps11->ext_control_gpiod[i]) { + if (config.ena_gpiod) { ret = s2mps14_pmic_enable_ext_control(s2mps11, regulator); if (ret < 0) { diff --git a/drivers/regulator/slg51000-regulator.c b/drivers/regulator/slg51000-regulator.c index 04b732991d69..a0565daecace 100644 --- a/drivers/regulator/slg51000-regulator.c +++ b/drivers/regulator/slg51000-regulator.c @@ -205,7 +205,7 @@ static int slg51000_of_parse_cb(struct device_node *np, ena_gpiod = devm_gpiod_get_from_of_node(chip->dev, np, "enable-gpios", 0, gflags, "gpio-en-ldo"); - if (ena_gpiod) { + if (!IS_ERR(ena_gpiod)) { config->ena_gpiod = ena_gpiod; devm_gpiod_unhinge(chip->dev, config->ena_gpiod); } @@ -447,18 +447,19 @@ static int slg51000_i2c_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct slg51000 *chip; - struct gpio_desc *cs_gpiod = NULL; + struct gpio_desc *cs_gpiod; int error, ret; chip = devm_kzalloc(dev, sizeof(struct slg51000), GFP_KERNEL); if (!chip) return -ENOMEM; - cs_gpiod = devm_gpiod_get_from_of_node(dev, dev->of_node, - "dlg,cs-gpios", 0, - GPIOD_OUT_HIGH - | GPIOD_FLAGS_BIT_NONEXCLUSIVE, - "slg51000-cs"); + cs_gpiod = devm_gpiod_get_optional(dev, "dlg,cs", + GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(cs_gpiod)) + return PTR_ERR(cs_gpiod); + if (cs_gpiod) { dev_info(dev, "Found chip selector property\n"); chip->cs_gpiod = cs_gpiod; diff --git a/drivers/regulator/stm32-booster.c b/drivers/regulator/stm32-booster.c index 2a897666c650..03f162ffd144 100644 --- a/drivers/regulator/stm32-booster.c +++ b/drivers/regulator/stm32-booster.c @@ -20,7 +20,6 @@ #define STM32MP1_SYSCFG_EN_BOOSTER_MASK BIT(8) static const struct regulator_ops stm32h7_booster_ops = { - .list_voltage = regulator_list_voltage_linear, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -31,7 +30,6 @@ static const struct regulator_desc stm32h7_booster_desc = { .supply_name = "vdda", .n_voltages = 1, .type = REGULATOR_VOLTAGE, - .min_uV = 3300000, .fixed_uV = 3300000, .ramp_delay = 66000, /* up to 50us to stabilize */ .ops = &stm32h7_booster_ops, @@ -53,7 +51,6 @@ static int stm32mp1_booster_disable(struct regulator_dev *rdev) } static const struct regulator_ops stm32mp1_booster_ops = { - .list_voltage = regulator_list_voltage_linear, .enable = stm32mp1_booster_enable, .disable = stm32mp1_booster_disable, .is_enabled = regulator_is_enabled_regmap, @@ -64,7 +61,6 @@ static const struct regulator_desc stm32mp1_booster_desc = { .supply_name = "vdda", .n_voltages = 1, .type = REGULATOR_VOLTAGE, - .min_uV = 3300000, .fixed_uV = 3300000, .ramp_delay = 66000, .ops = &stm32mp1_booster_ops, diff --git a/drivers/regulator/sy8824x.c b/drivers/regulator/sy8824x.c new file mode 100644 index 000000000000..92adb4f3ee19 --- /dev/null +++ b/drivers/regulator/sy8824x.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// SY8824C/SY8824E regulator driver +// +// Copyright (C) 2019 Synaptics Incorporated +// +// Author: Jisheng Zhang <jszhang@kernel.org> + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> + +#define SY8824C_BUCK_EN (1 << 7) +#define SY8824C_MODE (1 << 6) + +struct sy8824_config { + /* registers */ + unsigned int vol_reg; + unsigned int mode_reg; + unsigned int enable_reg; + /* Voltage range and step(linear) */ + unsigned int vsel_min; + unsigned int vsel_step; + unsigned int vsel_count; +}; + +struct sy8824_device_info { + struct device *dev; + struct regulator_desc desc; + struct regulator_init_data *regulator; + const struct sy8824_config *cfg; +}; + +static int sy8824_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct sy8824_device_info *di = rdev_get_drvdata(rdev); + const struct sy8824_config *cfg = di->cfg; + + switch (mode) { + case REGULATOR_MODE_FAST: + regmap_update_bits(rdev->regmap, cfg->mode_reg, + SY8824C_MODE, SY8824C_MODE); + break; + case REGULATOR_MODE_NORMAL: + regmap_update_bits(rdev->regmap, cfg->mode_reg, + SY8824C_MODE, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static unsigned int sy8824_get_mode(struct regulator_dev *rdev) +{ + struct sy8824_device_info *di = rdev_get_drvdata(rdev); + const struct sy8824_config *cfg = di->cfg; + u32 val; + int ret = 0; + + ret = regmap_read(rdev->regmap, cfg->mode_reg, &val); + if (ret < 0) + return ret; + if (val & SY8824C_MODE) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static const struct regulator_ops sy8824_regulator_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .map_voltage = regulator_map_voltage_linear, + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_mode = sy8824_set_mode, + .get_mode = sy8824_get_mode, +}; + +static int sy8824_regulator_register(struct sy8824_device_info *di, + struct regulator_config *config) +{ + struct regulator_desc *rdesc = &di->desc; + const struct sy8824_config *cfg = di->cfg; + struct regulator_dev *rdev; + + rdesc->name = "sy8824-reg"; + rdesc->supply_name = "vin"; + rdesc->ops = &sy8824_regulator_ops; + rdesc->type = REGULATOR_VOLTAGE; + rdesc->n_voltages = cfg->vsel_count; + rdesc->enable_reg = cfg->enable_reg; + rdesc->enable_mask = SY8824C_BUCK_EN; + rdesc->min_uV = cfg->vsel_min; + rdesc->uV_step = cfg->vsel_step; + rdesc->vsel_reg = cfg->vol_reg; + rdesc->vsel_mask = cfg->vsel_count - 1; + rdesc->owner = THIS_MODULE; + + rdev = devm_regulator_register(di->dev, &di->desc, config); + return PTR_ERR_OR_ZERO(rdev); +} + +static const struct regmap_config sy8824_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int sy8824_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct sy8824_device_info *di; + struct regulator_config config = { }; + struct regmap *regmap; + int ret; + + di = devm_kzalloc(dev, sizeof(struct sy8824_device_info), GFP_KERNEL); + if (!di) + return -ENOMEM; + + di->regulator = of_get_regulator_init_data(dev, np, &di->desc); + if (!di->regulator) { + dev_err(dev, "Platform data not found!\n"); + return -EINVAL; + } + + di->dev = dev; + di->cfg = of_device_get_match_data(dev); + + regmap = devm_regmap_init_i2c(client, &sy8824_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to allocate regmap!\n"); + return PTR_ERR(regmap); + } + i2c_set_clientdata(client, di); + + config.dev = di->dev; + config.init_data = di->regulator; + config.regmap = regmap; + config.driver_data = di; + config.of_node = np; + + ret = sy8824_regulator_register(di, &config); + if (ret < 0) + dev_err(dev, "Failed to register regulator!\n"); + return ret; +} + +static const struct sy8824_config sy8824c_cfg = { + .vol_reg = 0x00, + .mode_reg = 0x00, + .enable_reg = 0x00, + .vsel_min = 762500, + .vsel_step = 12500, + .vsel_count = 64, +}; + +static const struct sy8824_config sy8824e_cfg = { + .vol_reg = 0x00, + .mode_reg = 0x00, + .enable_reg = 0x00, + .vsel_min = 700000, + .vsel_step = 12500, + .vsel_count = 64, +}; + +static const struct sy8824_config sy20276_cfg = { + .vol_reg = 0x00, + .mode_reg = 0x01, + .enable_reg = 0x01, + .vsel_min = 600000, + .vsel_step = 10000, + .vsel_count = 128, +}; + +static const struct sy8824_config sy20278_cfg = { + .vol_reg = 0x00, + .mode_reg = 0x01, + .enable_reg = 0x01, + .vsel_min = 762500, + .vsel_step = 12500, + .vsel_count = 64, +}; + +static const struct of_device_id sy8824_dt_ids[] = { + { + .compatible = "silergy,sy8824c", + .data = &sy8824c_cfg + }, + { + .compatible = "silergy,sy8824e", + .data = &sy8824e_cfg + }, + { + .compatible = "silergy,sy20276", + .data = &sy20276_cfg + }, + { + .compatible = "silergy,sy20278", + .data = &sy20278_cfg + }, + { } +}; +MODULE_DEVICE_TABLE(of, sy8824_dt_ids); + +static const struct i2c_device_id sy8824_id[] = { + { "sy8824", }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sy8824_id); + +static struct i2c_driver sy8824_regulator_driver = { + .driver = { + .name = "sy8824-regulator", + .of_match_table = of_match_ptr(sy8824_dt_ids), + }, + .probe = sy8824_i2c_probe, + .id_table = sy8824_id, +}; +module_i2c_driver(sy8824_regulator_driver); + +MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); +MODULE_DESCRIPTION("SY8824C/SY8824E regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps65132-regulator.c b/drivers/regulator/tps65132-regulator.c index 6e22f5ebba2e..e302bd01a084 100644 --- a/drivers/regulator/tps65132-regulator.c +++ b/drivers/regulator/tps65132-regulator.c @@ -138,7 +138,7 @@ static int tps65132_of_parse_cb(struct device_node *np, rpdata->en_gpiod = devm_fwnode_get_index_gpiod_from_child(tps->dev, "enable", 0, &np->fwnode, 0, "enable"); - if (IS_ERR(rpdata->en_gpiod)) { + if (IS_ERR_OR_NULL(rpdata->en_gpiod)) { ret = PTR_ERR(rpdata->en_gpiod); /* Ignore the error other than probe defer */ @@ -150,7 +150,7 @@ static int tps65132_of_parse_cb(struct device_node *np, rpdata->act_dis_gpiod = devm_fwnode_get_index_gpiod_from_child( tps->dev, "active-discharge", 0, &np->fwnode, 0, "active-discharge"); - if (IS_ERR(rpdata->act_dis_gpiod)) { + if (IS_ERR_OR_NULL(rpdata->act_dis_gpiod)) { ret = PTR_ERR(rpdata->act_dis_gpiod); /* Ignore the error other than probe defer */ diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 6fa15b2d6fb3..866b4dd01da9 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -359,6 +359,17 @@ static const u16 VINTANA2_VSEL_table[] = { 2500, 2750, }; +/* 600mV to 1450mV in 12.5 mV steps */ +static const struct regulator_linear_range VDD1_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 68, 12500) +}; + +/* 600mV to 1450mV in 12.5 mV steps, everything above = 1500mV */ +static const struct regulator_linear_range VDD2_ranges[] = { + REGULATOR_LINEAR_RANGE(600000, 0, 68, 12500), + REGULATOR_LINEAR_RANGE(1500000, 69, 69, 12500) +}; + static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -427,6 +438,8 @@ static int twl4030smps_get_voltage(struct regulator_dev *rdev) } static const struct regulator_ops twl4030smps_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage = twl4030smps_set_voltage, .get_voltage = twl4030smps_get_voltage, }; @@ -466,7 +479,8 @@ static const struct twlreg_info TWL4030_INFO_##label = { \ }, \ } -#define TWL4030_ADJUSTABLE_SMPS(label, offset, num, turnon_delay, remap_conf) \ +#define TWL4030_ADJUSTABLE_SMPS(label, offset, num, turnon_delay, remap_conf, \ + n_volt) \ static const struct twlreg_info TWL4030_INFO_##label = { \ .base = offset, \ .id = num, \ @@ -479,6 +493,9 @@ static const struct twlreg_info TWL4030_INFO_##label = { \ .owner = THIS_MODULE, \ .enable_time = turnon_delay, \ .of_map_mode = twl4030reg_map_mode, \ + .n_voltages = n_volt, \ + .n_linear_ranges = ARRAY_SIZE(label ## _ranges), \ + .linear_ranges = label ## _ranges, \ }, \ } @@ -518,8 +535,8 @@ TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9, 100, 0x00); TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10, 100, 0x08); TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12, 100, 0x08); TWL4030_ADJUSTABLE_LDO(VIO, 0x4b, 14, 1000, 0x08); -TWL4030_ADJUSTABLE_SMPS(VDD1, 0x55, 15, 1000, 0x08); -TWL4030_ADJUSTABLE_SMPS(VDD2, 0x63, 16, 1000, 0x08); +TWL4030_ADJUSTABLE_SMPS(VDD1, 0x55, 15, 1000, 0x08, 68); +TWL4030_ADJUSTABLE_SMPS(VDD2, 0x63, 16, 1000, 0x08, 69); /* VUSBCP is managed *only* by the USB subchip */ TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08); TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08); diff --git a/drivers/regulator/twl6030-regulator.c b/drivers/regulator/twl6030-regulator.c index 5fe208b381eb..b8100c3cedad 100644 --- a/drivers/regulator/twl6030-regulator.c +++ b/drivers/regulator/twl6030-regulator.c @@ -57,6 +57,9 @@ struct twlreg_info { #define VREG_BC_PROC 3 #define VREG_BC_CLK_RST 4 +/* TWL6030 LDO register values for VREG_VOLTAGE */ +#define TWL6030_VREG_VOLTAGE_WR_S BIT(7) + /* TWL6030 LDO register values for CFG_STATE */ #define TWL6030_CFG_STATE_OFF 0x00 #define TWL6030_CFG_STATE_ON 0x01 @@ -68,9 +71,10 @@ struct twlreg_info { #define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\ TWL6030_CFG_STATE_APP_SHIFT) -/* Flags for SMPS Voltage reading */ +/* Flags for SMPS Voltage reading and LDO reading*/ #define SMPS_OFFSET_EN BIT(0) #define SMPS_EXTENDED_EN BIT(1) +#define TWL_6030_WARM_RESET BIT(3) /* twl6032 SMPS EPROM values */ #define TWL6030_SMPS_OFFSET 0xB0 @@ -250,6 +254,9 @@ twl6030ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { struct twlreg_info *info = rdev_get_drvdata(rdev); + if (info->flags & TWL_6030_WARM_RESET) + selector |= TWL6030_VREG_VOLTAGE_WR_S; + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE, selector); } @@ -259,6 +266,9 @@ static int twl6030ldo_get_voltage_sel(struct regulator_dev *rdev) struct twlreg_info *info = rdev_get_drvdata(rdev); int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE); + if (info->flags & TWL_6030_WARM_RESET) + vsel &= ~TWL6030_VREG_VOLTAGE_WR_S; + return vsel; } @@ -665,14 +675,14 @@ static int twlreg_probe(struct platform_device *pdev) struct regulation_constraints *c; struct regulator_dev *rdev; struct regulator_config config = { }; + struct device_node *np = pdev->dev.of_node; template = of_device_get_match_data(&pdev->dev); if (!template) return -ENODEV; id = template->desc.id; - initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, - &template->desc); + initdata = of_get_regulator_init_data(&pdev->dev, np, &template->desc); if (!initdata) return -EINVAL; @@ -710,10 +720,13 @@ static int twlreg_probe(struct platform_device *pdev) break; } + if (of_get_property(np, "ti,retain-on-reset", NULL)) + info->flags |= TWL_6030_WARM_RESET; + config.dev = &pdev->dev; config.init_data = initdata; config.driver_data = info; - config.of_node = pdev->dev.of_node; + config.of_node = np; rdev = devm_regulator_register(&pdev->dev, &info->desc, &config); if (IS_ERR(rdev)) { diff --git a/drivers/regulator/uniphier-regulator.c b/drivers/regulator/uniphier-regulator.c index 9026d5a3e964..2311924c3103 100644 --- a/drivers/regulator/uniphier-regulator.c +++ b/drivers/regulator/uniphier-regulator.c @@ -186,6 +186,10 @@ static const struct of_device_id uniphier_regulator_match[] = { .data = &uniphier_pro4_usb3_data, }, { + .compatible = "socionext,uniphier-pro5-usb3-regulator", + .data = &uniphier_pro4_usb3_data, + }, + { .compatible = "socionext,uniphier-pxs2-usb3-regulator", .data = &uniphier_pxs2_usb3_data, }, diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 1b4ee570b712..4a8a5373cb35 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -1704,6 +1704,7 @@ static void ctcmpc_chx_attnbusy(fsm_instance *fsm, int event, void *arg) grp->changed_side = 2; break; } + /* Else, fall through */ case MPCG_STATE_XID0IOWAIX: case MPCG_STATE_XID7INITW: case MPCG_STATE_XID7INITX: diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index e02f295d38a9..1534420a0243 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -357,6 +357,7 @@ int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int)) /*fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);*/ if (callback) grp->send_qllc_disc = 1; + /* Else, fall through */ case MPCG_STATE_XID0IOWAIT: fsm_deltimer(&grp->timer); grp->outstanding_xid2 = 0; @@ -1469,6 +1470,7 @@ static void mpc_action_timeout(fsm_instance *fi, int event, void *arg) if ((fsm_getstate(rch->fsm) == CH_XID0_PENDING) && (fsm_getstate(wch->fsm) == CH_XID0_PENDING)) break; + /* Else, fall through */ default: fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); } @@ -2089,6 +2091,7 @@ static int mpc_send_qllc_discontact(struct net_device *dev) grp->estconnfunc = NULL; break; } + /* Else, fall through */ case MPCG_STATE_FLOWC: case MPCG_STATE_READY: grp->send_qllc_disc = 2; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index c7ee07ce3615..28db887d38ed 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -629,6 +629,7 @@ struct qeth_seqno { struct qeth_reply { struct list_head list; struct completion received; + spinlock_t lock; int (*callback)(struct qeth_card *, struct qeth_reply *, unsigned long); u32 seqno; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 4d0caeebc802..6502b148541e 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -544,6 +544,7 @@ static struct qeth_reply *qeth_alloc_reply(struct qeth_card *card) if (reply) { refcount_set(&reply->refcnt, 1); init_completion(&reply->received); + spin_lock_init(&reply->lock); } return reply; } @@ -799,6 +800,13 @@ static void qeth_issue_next_read_cb(struct qeth_card *card, if (!reply->callback) { rc = 0; + goto no_callback; + } + + spin_lock_irqsave(&reply->lock, flags); + if (reply->rc) { + /* Bail out when the requestor has already left: */ + rc = reply->rc; } else { if (cmd) { reply->offset = (u16)((char *)cmd - (char *)iob->data); @@ -807,7 +815,9 @@ static void qeth_issue_next_read_cb(struct qeth_card *card, rc = reply->callback(card, reply, (unsigned long)iob); } } + spin_unlock_irqrestore(&reply->lock, flags); +no_callback: if (rc <= 0) qeth_notify_reply(reply, rc); qeth_put_reply(reply); @@ -1749,6 +1759,16 @@ static int qeth_send_control_data(struct qeth_card *card, rc = (timeout == -ERESTARTSYS) ? -EINTR : -ETIME; qeth_dequeue_reply(card, reply); + + if (reply_cb) { + /* Wait until the callback for a late reply has completed: */ + spin_lock_irq(&reply->lock); + if (rc) + /* Zap any callback that's still pending: */ + reply->rc = rc; + spin_unlock_irq(&reply->lock); + } + if (!rc) rc = reply->rc; qeth_put_reply(reply); @@ -4354,6 +4374,10 @@ static int qeth_snmp_command(struct qeth_card *card, char __user *udata) get_user(req_len, &ureq->hdr.req_len)) return -EFAULT; + /* Sanitize user input, to avoid overflows in iob size calculation: */ + if (req_len > QETH_BUFSIZE) + return -EINVAL; + iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, req_len); if (!iob) return -ENOMEM; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index fd64bc3f4062..cbead3d1b2fd 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -333,7 +333,7 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, card->osn_info.data_cb(skb); break; } - /* else unknown */ + /* Else, fall through */ default: dev_kfree_skb_any(skb); QETH_CARD_TEXT(card, 3, "inbunkno"); diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index aea4fd73c862..6c68c2303638 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -603,6 +603,7 @@ static void fas216_handlesync(FAS216_Info *info, char *msg) msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); info->scsi.phase = PHASE_MSGOUT_EXPECT; + /* fall through */ case async: dev->period = info->ifcfg.asyncperiod / 4; @@ -915,6 +916,7 @@ static void fas216_disconnect_intr(FAS216_Info *info) fas216_done(info, DID_ABORT); break; } + /* else, fall through */ default: /* huh? */ printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n", @@ -1411,6 +1413,8 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ fas216_stoptransfer(info); + /* fall through */ + case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ @@ -1422,6 +1426,8 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ fas216_stoptransfer(info); + /* fall through */ + case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */ case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ @@ -1575,6 +1581,7 @@ static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned fas216_message(info); break; } + /* else, fall through */ default: fas216_log(info, 0, "internal phase %s for function done?" @@ -1957,6 +1964,7 @@ static void fas216_kick(FAS216_Info *info) switch (where_from) { case TYPE_QUEUE: fas216_allocate_tag(info, SCpnt); + /* fall through */ case TYPE_OTHER: fas216_start_command(info, SCpnt); break; diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index abcad097ff2f..f47b4b281b14 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -459,6 +459,7 @@ static void sas_discover_domain(struct work_struct *work) pr_notice("ATA device seen but CONFIG_SCSI_SAS_ATA=N so cannot attach\n"); /* Fall through */ #endif + /* Fall through - only for the #else condition above. */ default: error = -ENXIO; pr_err("unhandled device %d\n", dev->dev_type); diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 2c3bb8a966e5..bade2e025ecf 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -824,6 +824,7 @@ struct lpfc_hba { uint32_t cfg_cq_poll_threshold; uint32_t cfg_cq_max_proc_limit; uint32_t cfg_fcp_cpu_map; + uint32_t cfg_fcp_mq_threshold; uint32_t cfg_hdw_queue; uint32_t cfg_irq_chann; uint32_t cfg_suppress_rsp; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index ea62322ffe2b..d65558619ab0 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -5709,6 +5709,19 @@ LPFC_ATTR_RW(nvme_embed_cmd, 1, 0, 2, "Embed NVME Command in WQE"); /* + * lpfc_fcp_mq_threshold: Set the maximum number of Hardware Queues + * the driver will advertise it supports to the SCSI layer. + * + * 0 = Set nr_hw_queues by the number of CPUs or HW queues. + * 1,128 = Manually specify the maximum nr_hw_queue value to be set, + * + * Value range is [0,256]. Default value is 8. + */ +LPFC_ATTR_R(fcp_mq_threshold, LPFC_FCP_MQ_THRESHOLD_DEF, + LPFC_FCP_MQ_THRESHOLD_MIN, LPFC_FCP_MQ_THRESHOLD_MAX, + "Set the number of SCSI Queues advertised"); + +/* * lpfc_hdw_queue: Set the number of Hardware Queues the driver * will advertise it supports to the NVME and SCSI layers. This also * will map to the number of CQ/WQ pairs the driver will create. @@ -6030,6 +6043,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_cq_poll_threshold, &dev_attr_lpfc_cq_max_proc_limit, &dev_attr_lpfc_fcp_cpu_map, + &dev_attr_lpfc_fcp_mq_threshold, &dev_attr_lpfc_hdw_queue, &dev_attr_lpfc_irq_chann, &dev_attr_lpfc_suppress_rsp, @@ -7112,6 +7126,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) /* Initialize first burst. Target vs Initiator are different. */ lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb); lpfc_nvmet_fb_size_init(phba, lpfc_nvmet_fb_size); + lpfc_fcp_mq_threshold_init(phba, lpfc_fcp_mq_threshold); lpfc_hdw_queue_init(phba, lpfc_hdw_queue); lpfc_irq_chann_init(phba, lpfc_irq_chann); lpfc_enable_bbcr_init(phba, lpfc_enable_bbcr); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index faf43b1d3dbe..1ac98becb5ba 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4309,10 +4309,12 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) shost->max_cmd_len = 16; if (phba->sli_rev == LPFC_SLI_REV4) { - if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_HDWQ) - shost->nr_hw_queues = phba->cfg_hdw_queue; - else - shost->nr_hw_queues = phba->sli4_hba.num_present_cpu; + if (!phba->cfg_fcp_mq_threshold || + phba->cfg_fcp_mq_threshold > phba->cfg_hdw_queue) + phba->cfg_fcp_mq_threshold = phba->cfg_hdw_queue; + + shost->nr_hw_queues = min_t(int, 2 * num_possible_nodes(), + phba->cfg_fcp_mq_threshold); shost->dma_boundary = phba->sli4_hba.pc_sli4_params.sge_supp_len-1; @@ -10776,12 +10778,31 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) /* This loop sets up all CPUs that are affinitized with a * irq vector assigned to the driver. All affinitized CPUs * will get a link to that vectors IRQ and EQ. + * + * NULL affinity mask handling: + * If irq count is greater than one, log an error message. + * If the null mask is received for the first irq, find the + * first present cpu, and assign the eq index to ensure at + * least one EQ is assigned. */ for (idx = 0; idx < phba->cfg_irq_chann; idx++) { /* Get a CPU mask for all CPUs affinitized to this vector */ maskp = pci_irq_get_affinity(phba->pcidev, idx); - if (!maskp) - continue; + if (!maskp) { + if (phba->cfg_irq_chann > 1) + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3329 No affinity mask found " + "for vector %d (%d)\n", + idx, phba->cfg_irq_chann); + if (!idx) { + cpu = cpumask_first(cpu_present_mask); + cpup = &phba->sli4_hba.cpu_map[cpu]; + cpup->eq = idx; + cpup->irq = pci_irq_vector(phba->pcidev, idx); + cpup->flag |= LPFC_CPU_FIRST_IRQ; + } + break; + } i = 0; /* Loop through all CPUs associated with vector idx */ diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 3aeca387b22a..a81ef0293696 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -44,6 +44,11 @@ #define LPFC_HBA_HDWQ_MAX 128 #define LPFC_HBA_HDWQ_DEF 0 +/* FCP MQ queue count limiting */ +#define LPFC_FCP_MQ_THRESHOLD_MIN 0 +#define LPFC_FCP_MQ_THRESHOLD_MAX 256 +#define LPFC_FCP_MQ_THRESHOLD_DEF 8 + /* Common buffer size to accomidate SCSI and NVME IO buffers */ #define LPFC_COMMON_IO_BUF_SZ 768 diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 8d560c562e9c..6b7b390b2e52 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -2956,6 +2956,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) dma_free_coherent(&ha->pdev->dev, vha->gnl.size, vha->gnl.l, vha->gnl.ldma); + vha->gnl.l = NULL; + vfree(vha->scan.l); if (vha->qpair && vha->qpair->vp_idx == vha->vp_idx) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 2e58cff9d200..98e60a34afd9 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3440,6 +3440,12 @@ skip_dpc: return 0; probe_failed: + if (base_vha->gnl.l) { + dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size, + base_vha->gnl.l, base_vha->gnl.ldma); + base_vha->gnl.l = NULL; + } + if (base_vha->timer_active) qla2x00_stop_timer(base_vha); base_vha->flags.online = 0; @@ -3673,7 +3679,7 @@ qla2x00_remove_one(struct pci_dev *pdev) if (!atomic_read(&pdev->enable_cnt)) { dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma); - + base_vha->gnl.l = NULL; scsi_host_put(base_vha->host); kfree(ha); pci_set_drvdata(pdev, NULL); @@ -3713,6 +3719,8 @@ qla2x00_remove_one(struct pci_dev *pdev) dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma); + base_vha->gnl.l = NULL; + vfree(base_vha->scan.l); if (IS_QLAFX00(ha)) @@ -4816,6 +4824,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, "Alloc failed for scan database.\n"); dma_free_coherent(&ha->pdev->dev, vha->gnl.size, vha->gnl.l, vha->gnl.ldma); + vha->gnl.l = NULL; scsi_remove_host(vha->host); return NULL; } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index e274053109d0..029da74bb2f5 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -7062,6 +7062,9 @@ static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba, static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba, struct ufs_vreg *vreg) { + if (!vreg) + return 0; + return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA); } diff --git a/drivers/soc/ixp4xx/Kconfig b/drivers/soc/ixp4xx/Kconfig index de2e62c3310a..e3eb19b85fa4 100644 --- a/drivers/soc/ixp4xx/Kconfig +++ b/drivers/soc/ixp4xx/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +if ARCH_IXP4XX || COMPILE_TEST + menu "IXP4xx SoC drivers" config IXP4XX_QMGR @@ -15,3 +17,5 @@ config IXP4XX_NPE and is automatically selected by Ethernet and HSS drivers. endmenu + +endif diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index d5cf953b4337..7d622ea1274e 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -630,6 +630,9 @@ int geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len, struct geni_wrapper *wrapper = se->wrapper; u32 val; + if (!wrapper) + return -EINVAL; + *iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE); if (dma_mapping_error(wrapper->dev, *iova)) return -EIO; @@ -663,6 +666,9 @@ int geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len, struct geni_wrapper *wrapper = se->wrapper; u32 val; + if (!wrapper) + return -EINVAL; + *iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE); if (dma_mapping_error(wrapper->dev, *iova)) return -EIO; diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index bb77c220b6f8..ccc6d53fe788 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -141,7 +141,7 @@ static int __init am43xx_map_gic(void) } #ifdef CONFIG_SUSPEND -struct wkup_m3_wakeup_src rtc_wake_src(void) +static struct wkup_m3_wakeup_src rtc_wake_src(void) { u32 i; @@ -157,7 +157,7 @@ struct wkup_m3_wakeup_src rtc_wake_src(void) return rtc_ext_wakeup; } -int am33xx_rtc_only_idle(unsigned long wfi_flags) +static int am33xx_rtc_only_idle(unsigned long wfi_flags) { omap_rtc_power_off_program(&omap_rtc->dev); am33xx_do_wfi_sram(wfi_flags); @@ -252,7 +252,7 @@ static int am33xx_pm_begin(suspend_state_t state) if (state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) { nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0"); - if (nvmem) + if (!IS_ERR(nvmem)) nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, (void *)&rtc_magic_val); rtc_only_idle = 1; @@ -278,9 +278,12 @@ static void am33xx_pm_end(void) struct nvmem_device *nvmem; nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0"); + if (IS_ERR(nvmem)) + return; + m3_ipc->ops->finish_low_power(m3_ipc); if (rtc_only_idle) { - if (retrigger_irq) + if (retrigger_irq) { /* * 32 bits of Interrupt Set-Pending correspond to 32 * 32 interrupts. Compute the bit offset of the @@ -291,8 +294,10 @@ static void am33xx_pm_end(void) writel_relaxed(1 << (retrigger_irq & 31), gic_dist_base + GIC_INT_SET_PENDING_BASE + retrigger_irq / 32 * 4); - nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, - (void *)&val); + } + + nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, + (void *)&val); } rtc_only_idle = 0; @@ -415,7 +420,7 @@ static int am33xx_pm_rtc_setup(void) nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0"); - if (nvmem) { + if (!IS_ERR(nvmem)) { nvmem_device_read(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, (void *)&rtc_magic_val); if ((rtc_magic_val & 0xffff) != RTC_REG_BOOT_MAGIC) diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index 3a01cfd70fdc..f518273cfbe3 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -4,7 +4,7 @@ # menuconfig SOUNDWIRE - bool "SoundWire support" + tristate "SoundWire support" help SoundWire is a 2-Pin interface with data and clock line ratified by the MIPI Alliance. SoundWire is used for transporting data @@ -17,17 +17,12 @@ if SOUNDWIRE comment "SoundWire Devices" -config SOUNDWIRE_BUS - tristate - select REGMAP_SOUNDWIRE - config SOUNDWIRE_CADENCE tristate config SOUNDWIRE_INTEL tristate "Intel SoundWire Master driver" select SOUNDWIRE_CADENCE - select SOUNDWIRE_BUS depends on X86 && ACPI && SND_SOC help SoundWire Intel Master driver. diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index fd99a831b92a..45b7e5001653 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -5,7 +5,7 @@ #Bus Objs soundwire-bus-objs := bus_type.o bus.o slave.o mipi_disco.o stream.o -obj-$(CONFIG_SOUNDWIRE_BUS) += soundwire-bus.o +obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o #Cadence Objs soundwire-cadence-objs := cadence_master.o diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index ff4badc9b3de..60e8bdee5c75 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -81,8 +81,8 @@ #define CDNS_MCP_INTSET 0x4C -#define CDNS_SDW_SLAVE_STAT 0x50 -#define CDNS_MCP_SLAVE_STAT_MASK BIT(1, 0) +#define CDNS_MCP_SLAVE_STAT 0x50 +#define CDNS_MCP_SLAVE_STAT_MASK GENMASK(1, 0) #define CDNS_MCP_SLAVE_INTSTAT0 0x54 #define CDNS_MCP_SLAVE_INTSTAT1 0x58 @@ -96,8 +96,8 @@ #define CDNS_MCP_SLAVE_INTMASK0 0x5C #define CDNS_MCP_SLAVE_INTMASK1 0x60 -#define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(30, 0) -#define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(16, 0) +#define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(31, 0) +#define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(15, 0) #define CDNS_MCP_PORT_INTSTAT 0x64 #define CDNS_MCP_PDI_STAT 0x6C diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3a1d8f1170de..6ee514fd0920 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -433,6 +433,16 @@ config SPI_MT7621 help This selects a driver for the MediaTek MT7621 SPI Controller. +config SPI_NPCM_FIU + tristate "Nuvoton NPCM FLASH Interface Unit" + depends on ARCH_NPCM || COMPILE_TEST + depends on OF && HAS_IOMEM + help + This enables support for the Flash Interface Unit SPI controller + in master mode. + This driver does not support generic SPI. The implementation only + supports spi-mem interface. + config SPI_NPCM_PSPI tristate "Nuvoton NPCM PSPI Controller" depends on ARCH_NPCM || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 63dcab552bcb..adbebee93a75 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o +obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 6a7d7b553d95..fd8007ebb145 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -526,7 +526,6 @@ static int atmel_qspi_probe(struct platform_device *pdev) /* Request the IRQ */ irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "missing IRQ\n"); err = irq; goto disable_qspick; } diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index ea160f117f88..41d71ba7fd32 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -170,7 +170,6 @@ static int altera_spi_probe(struct platform_device *pdev) { struct altera_spi *hw; struct spi_master *master; - struct resource *res; int err = -ENODEV; master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); @@ -189,8 +188,7 @@ static int altera_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); /* find and map our resources */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hw->base = devm_ioremap_resource(&pdev->dev, res); + hw->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hw->base)) { err = PTR_ERR(hw->base); goto exit; diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 032888344822..e450ee17787f 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -817,7 +817,6 @@ static int a3700_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *of_node = dev->of_node; - struct resource *res; struct spi_master *master; struct a3700_spi *spi; u32 num_cs = 0; @@ -855,8 +854,7 @@ static int a3700_spi_probe(struct platform_device *pdev) spi->master = master; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spi->base = devm_ioremap_resource(dev, res); + spi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spi->base)) { ret = PTR_ERR(spi->base); goto error; @@ -864,7 +862,6 @@ static int a3700_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(dev, "could not get irq: %d\n", irq); ret = -ENXIO; goto error; } diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 032a615e4ccd..eb9a243e9526 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -139,7 +139,6 @@ static int ath79_spi_probe(struct platform_device *pdev) struct spi_master *master; struct ath79_spi *sp; struct ath79_spi_platform_data *pdata; - struct resource *r; unsigned long rate; int ret; @@ -169,8 +168,7 @@ static int ath79_spi_probe(struct platform_device *pdev) sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; sp->bitbang.flags = SPI_CS_HIGH; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sp->base = devm_ioremap_resource(&pdev->dev, r); + sp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sp->base)) { ret = PTR_ERR(sp->base); goto err_put_master; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index f00b367523cd..acf318e7330c 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -23,6 +23,7 @@ #include <linux/gpio/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> +#include <trace/events/spi.h> /* SPI register offsets */ #define SPI_CR 0x0000 @@ -1409,9 +1410,13 @@ static int atmel_spi_transfer_one_message(struct spi_master *master, msg->actual_length = 0; list_for_each_entry(xfer, &msg->transfers, transfer_list) { + trace_spi_transfer_start(msg, xfer); + ret = atmel_spi_one_transfer(master, msg, xfer); if (ret) goto msg_done; + + trace_spi_transfer_stop(msg, xfer); } if (as->use_pdc) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 3b1833e6c7ad..74842f6019ed 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -460,7 +460,6 @@ static int spi_engine_probe(struct platform_device *pdev) struct spi_engine *spi_engine; struct spi_master *master; unsigned int version; - struct resource *res; int irq; int ret; @@ -480,8 +479,7 @@ static int spi_engine_probe(struct platform_device *pdev) spin_lock_init(&spi_engine->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spi_engine->base = devm_ioremap_resource(&pdev->dev, res); + spi_engine->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spi_engine->base)) { ret = PTR_ERR(spi_engine->base); goto err_put_master; diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 902bdbfedea8..7a3531856491 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -343,7 +343,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, { int bpc = 0, bpp = 0; u8 command = op->cmd.opcode; - int width = op->cmd.buswidth ? op->cmd.buswidth : SPI_NBITS_SINGLE; + int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE; int addrlen = op->addr.nbytes; int flex_mode = 1; @@ -897,6 +897,7 @@ static int bcm_qspi_transfer_one(struct spi_master *master, read_from_hw(qspi, slots); } + bcm_qspi_enable_bspi(qspi); return 0; } @@ -981,7 +982,7 @@ static int bcm_qspi_exec_mem_op(struct spi_mem *mem, if (mspi_read) return bcm_qspi_mspi_exec_mem_op(spi, op); - ret = bcm_qspi_bspi_set_mode(qspi, op, -1); + ret = bcm_qspi_bspi_set_mode(qspi, op, 0); if (!ret) ret = bcm_qspi_bspi_exec_mem_op(spi, op); diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 6f243a90c844..b4070c0de3df 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -25,7 +25,9 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio/machine.h> /* FIXME: using chip internals */ +#include <linux/gpio/driver.h> /* FIXME: using chip internals */ #include <linux/of_irq.h> #include <linux/spi/spi.h> @@ -66,6 +68,7 @@ #define BCM2835_SPI_FIFO_SIZE 64 #define BCM2835_SPI_FIFO_SIZE_3_4 48 #define BCM2835_SPI_DMA_MIN_LENGTH 96 +#define BCM2835_SPI_NUM_CS 3 /* raise as necessary */ #define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ | SPI_NO_CS | SPI_3WIRE) @@ -92,7 +95,8 @@ MODULE_PARM_DESC(polling_limit_us, * @rx_prologue: bytes received without DMA if first RX sglist entry's * length is not a multiple of 4 (to overcome hardware limitation) * @tx_spillover: whether @tx_prologue spills over to second TX sglist entry - * @dma_pending: whether a DMA transfer is in progress + * @prepare_cs: precalculated CS register value for ->prepare_message() + * (uses slave-specific clock polarity and phase settings) * @debugfs_dir: the debugfs directory - neede to remove debugfs when * unloading the module * @count_transfer_polling: count of how often polling mode is used @@ -102,6 +106,19 @@ MODULE_PARM_DESC(polling_limit_us, * These are counted as well in @count_transfer_polling and * @count_transfer_irq * @count_transfer_dma: count how often dma mode is used + * @chip_select: SPI slave currently selected + * (used by bcm2835_spi_dma_tx_done() to write @clear_rx_cs) + * @tx_dma_active: whether a TX DMA descriptor is in progress + * @rx_dma_active: whether a RX DMA descriptor is in progress + * (used by bcm2835_spi_dma_tx_done() to handle a race) + * @fill_tx_desc: preallocated TX DMA descriptor used for RX-only transfers + * (cyclically copies from zero page to TX FIFO) + * @fill_tx_addr: bus address of zero page + * @clear_rx_desc: preallocated RX DMA descriptor used for TX-only transfers + * (cyclically clears RX FIFO by writing @clear_rx_cs to CS register) + * @clear_rx_addr: bus address of @clear_rx_cs + * @clear_rx_cs: precalculated CS register value to clear RX FIFO + * (uses slave-specific clock polarity and phase settings) */ struct bcm2835_spi { void __iomem *regs; @@ -115,13 +132,22 @@ struct bcm2835_spi { int tx_prologue; int rx_prologue; unsigned int tx_spillover; - unsigned int dma_pending; + u32 prepare_cs[BCM2835_SPI_NUM_CS]; struct dentry *debugfs_dir; u64 count_transfer_polling; u64 count_transfer_irq; u64 count_transfer_irq_after_polling; u64 count_transfer_dma; + + u8 chip_select; + unsigned int tx_dma_active; + unsigned int rx_dma_active; + struct dma_async_tx_descriptor *fill_tx_desc; + dma_addr_t fill_tx_addr; + struct dma_async_tx_descriptor *clear_rx_desc[BCM2835_SPI_NUM_CS]; + dma_addr_t clear_rx_addr; + u32 clear_rx_cs[BCM2835_SPI_NUM_CS] ____cacheline_aligned; }; #if defined(CONFIG_DEBUG_FS) @@ -319,6 +345,13 @@ static void bcm2835_spi_reset_hw(struct spi_controller *ctlr) BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_DMAEN | BCM2835_SPI_CS_TA); + /* + * Transmission sometimes breaks unless the DONE bit is written at the + * end of every transfer. The spec says it's a RO bit. Either the + * spec is wrong and the bit is actually of type RW1C, or it's a + * hardware erratum. + */ + cs |= BCM2835_SPI_CS_DONE; /* and reset RX/TX FIFOS */ cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX; @@ -448,14 +481,14 @@ static void bcm2835_spi_transfer_prologue(struct spi_controller *ctlr, bs->rx_prologue = 0; bs->tx_spillover = false; - if (!sg_is_last(&tfr->tx_sg.sgl[0])) + if (bs->tx_buf && !sg_is_last(&tfr->tx_sg.sgl[0])) bs->tx_prologue = sg_dma_len(&tfr->tx_sg.sgl[0]) & 3; - if (!sg_is_last(&tfr->rx_sg.sgl[0])) { + if (bs->rx_buf && !sg_is_last(&tfr->rx_sg.sgl[0])) { bs->rx_prologue = sg_dma_len(&tfr->rx_sg.sgl[0]) & 3; if (bs->rx_prologue > bs->tx_prologue) { - if (sg_is_last(&tfr->tx_sg.sgl[0])) { + if (!bs->tx_buf || sg_is_last(&tfr->tx_sg.sgl[0])) { bs->tx_prologue = bs->rx_prologue; } else { bs->tx_prologue += 4; @@ -477,7 +510,9 @@ static void bcm2835_spi_transfer_prologue(struct spi_controller *ctlr, bcm2835_wr_fifo_count(bs, bs->rx_prologue); bcm2835_wait_tx_fifo_empty(bs); bcm2835_rd_fifo_count(bs, bs->rx_prologue); - bcm2835_spi_reset_hw(ctlr); + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_CLEAR_RX + | BCM2835_SPI_CS_CLEAR_TX + | BCM2835_SPI_CS_DONE); dma_sync_single_for_device(ctlr->dma_rx->device->dev, sg_dma_address(&tfr->rx_sg.sgl[0]), @@ -487,6 +522,9 @@ static void bcm2835_spi_transfer_prologue(struct spi_controller *ctlr, sg_dma_len(&tfr->rx_sg.sgl[0]) -= bs->rx_prologue; } + if (!bs->tx_buf) + return; + /* * Write remaining TX prologue. Adjust first entry in TX sglist. * Also adjust second entry if prologue spills over to it. @@ -498,7 +536,8 @@ static void bcm2835_spi_transfer_prologue(struct spi_controller *ctlr, | BCM2835_SPI_CS_DMAEN); bcm2835_wr_fifo_count(bs, tx_remaining); bcm2835_wait_tx_fifo_empty(bs); - bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_CLEAR_TX); + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_CLEAR_TX + | BCM2835_SPI_CS_DONE); } if (likely(!bs->tx_spillover)) { @@ -531,6 +570,9 @@ static void bcm2835_spi_undo_prologue(struct bcm2835_spi *bs) sg_dma_len(&tfr->rx_sg.sgl[0]) += bs->rx_prologue; } + if (!bs->tx_buf) + goto out; + if (likely(!bs->tx_spillover)) { sg_dma_address(&tfr->tx_sg.sgl[0]) -= bs->tx_prologue; sg_dma_len(&tfr->tx_sg.sgl[0]) += bs->tx_prologue; @@ -539,32 +581,85 @@ static void bcm2835_spi_undo_prologue(struct bcm2835_spi *bs) sg_dma_address(&tfr->tx_sg.sgl[1]) -= 4; sg_dma_len(&tfr->tx_sg.sgl[1]) += 4; } +out: + bs->tx_prologue = 0; } -static void bcm2835_spi_dma_done(void *data) +/** + * bcm2835_spi_dma_rx_done() - callback for DMA RX channel + * @data: SPI master controller + * + * Used for bidirectional and RX-only transfers. + */ +static void bcm2835_spi_dma_rx_done(void *data) { struct spi_controller *ctlr = data; struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); - /* reset fifo and HW */ - bcm2835_spi_reset_hw(ctlr); - - /* and terminate tx-dma as we do not have an irq for it + /* terminate tx-dma as we do not have an irq for it * because when the rx dma will terminate and this callback * is called the tx-dma must have finished - can't get to this * situation otherwise... */ - if (cmpxchg(&bs->dma_pending, true, false)) { - dmaengine_terminate_async(ctlr->dma_tx); - bcm2835_spi_undo_prologue(bs); - } + dmaengine_terminate_async(ctlr->dma_tx); + bs->tx_dma_active = false; + bs->rx_dma_active = false; + bcm2835_spi_undo_prologue(bs); + + /* reset fifo and HW */ + bcm2835_spi_reset_hw(ctlr); /* and mark as completed */; complete(&ctlr->xfer_completion); } +/** + * bcm2835_spi_dma_tx_done() - callback for DMA TX channel + * @data: SPI master controller + * + * Used for TX-only transfers. + */ +static void bcm2835_spi_dma_tx_done(void *data) +{ + struct spi_controller *ctlr = data; + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); + + /* busy-wait for TX FIFO to empty */ + while (!(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE)) + bcm2835_wr(bs, BCM2835_SPI_CS, + bs->clear_rx_cs[bs->chip_select]); + + bs->tx_dma_active = false; + smp_wmb(); + + /* + * In case of a very short transfer, RX DMA may not have been + * issued yet. The onus is then on bcm2835_spi_transfer_one_dma() + * to terminate it immediately after issuing. + */ + if (cmpxchg(&bs->rx_dma_active, true, false)) + dmaengine_terminate_async(ctlr->dma_rx); + + bcm2835_spi_undo_prologue(bs); + bcm2835_spi_reset_hw(ctlr); + complete(&ctlr->xfer_completion); +} + +/** + * bcm2835_spi_prepare_sg() - prepare and submit DMA descriptor for sglist + * @ctlr: SPI master controller + * @spi: SPI slave + * @tfr: SPI transfer + * @bs: BCM2835 SPI controller + * @is_tx: whether to submit DMA descriptor for TX or RX sglist + * + * Prepare and submit a DMA descriptor for the TX or RX sglist of @tfr. + * Return 0 on success or a negative error number. + */ static int bcm2835_spi_prepare_sg(struct spi_controller *ctlr, + struct spi_device *spi, struct spi_transfer *tfr, + struct bcm2835_spi *bs, bool is_tx) { struct dma_chan *chan; @@ -581,8 +676,7 @@ static int bcm2835_spi_prepare_sg(struct spi_controller *ctlr, chan = ctlr->dma_tx; nents = tfr->tx_sg.nents; sgl = tfr->tx_sg.sgl; - flags = 0 /* no tx interrupt */; - + flags = tfr->rx_buf ? 0 : DMA_PREP_INTERRUPT; } else { dir = DMA_DEV_TO_MEM; chan = ctlr->dma_rx; @@ -595,10 +689,17 @@ static int bcm2835_spi_prepare_sg(struct spi_controller *ctlr, if (!desc) return -EINVAL; - /* set callback for rx */ + /* + * Completion is signaled by the RX channel for bidirectional and + * RX-only transfers; else by the TX channel for TX-only transfers. + */ if (!is_tx) { - desc->callback = bcm2835_spi_dma_done; + desc->callback = bcm2835_spi_dma_rx_done; + desc->callback_param = ctlr; + } else if (!tfr->rx_buf) { + desc->callback = bcm2835_spi_dma_tx_done; desc->callback_param = ctlr; + bs->chip_select = spi->chip_select; } /* submit it to DMA-engine */ @@ -607,12 +708,60 @@ static int bcm2835_spi_prepare_sg(struct spi_controller *ctlr, return dma_submit_error(cookie); } +/** + * bcm2835_spi_transfer_one_dma() - perform SPI transfer using DMA engine + * @ctlr: SPI master controller + * @spi: SPI slave + * @tfr: SPI transfer + * @cs: CS register + * + * For *bidirectional* transfers (both tx_buf and rx_buf are non-%NULL), set up + * the TX and RX DMA channel to copy between memory and FIFO register. + * + * For *TX-only* transfers (rx_buf is %NULL), copying the RX FIFO's contents to + * memory is pointless. However not reading the RX FIFO isn't an option either + * because transmission is halted once it's full. As a workaround, cyclically + * clear the RX FIFO by setting the CLEAR_RX bit in the CS register. + * + * The CS register value is precalculated in bcm2835_spi_setup(). Normally + * this is called only once, on slave registration. A DMA descriptor to write + * this value is preallocated in bcm2835_dma_init(). All that's left to do + * when performing a TX-only transfer is to submit this descriptor to the RX + * DMA channel. Latency is thereby minimized. The descriptor does not + * generate any interrupts while running. It must be terminated once the + * TX DMA channel is done. + * + * Clearing the RX FIFO is paced by the DREQ signal. The signal is asserted + * when the RX FIFO becomes half full, i.e. 32 bytes. (Tuneable with the DC + * register.) Reading 32 bytes from the RX FIFO would normally require 8 bus + * accesses, whereas clearing it requires only 1 bus access. So an 8-fold + * reduction in bus traffic and thus energy consumption is achieved. + * + * For *RX-only* transfers (tx_buf is %NULL), fill the TX FIFO by cyclically + * copying from the zero page. The DMA descriptor to do this is preallocated + * in bcm2835_dma_init(). It must be terminated once the RX DMA channel is + * done and can then be reused. + * + * The BCM2835 DMA driver autodetects when a transaction copies from the zero + * page and utilizes the DMA controller's ability to synthesize zeroes instead + * of copying them from memory. This reduces traffic on the memory bus. The + * feature is not available on so-called "lite" channels, but normally TX DMA + * is backed by a full-featured channel. + * + * Zero-filling the TX FIFO is paced by the DREQ signal. Unfortunately the + * BCM2835 SPI controller continues to assert DREQ even after the DLEN register + * has been counted down to zero (hardware erratum). Thus, when the transfer + * has finished, the DMA engine zero-fills the TX FIFO until it is half full. + * (Tuneable with the DC register.) So up to 9 gratuitous bus accesses are + * performed at the end of an RX-only transfer. + */ static int bcm2835_spi_transfer_one_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *tfr, u32 cs) { struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); + dma_cookie_t cookie; int ret; /* update usage statistics */ @@ -625,16 +774,15 @@ static int bcm2835_spi_transfer_one_dma(struct spi_controller *ctlr, bcm2835_spi_transfer_prologue(ctlr, tfr, bs, cs); /* setup tx-DMA */ - ret = bcm2835_spi_prepare_sg(ctlr, tfr, true); + if (bs->tx_buf) { + ret = bcm2835_spi_prepare_sg(ctlr, spi, tfr, bs, true); + } else { + cookie = dmaengine_submit(bs->fill_tx_desc); + ret = dma_submit_error(cookie); + } if (ret) goto err_reset_hw; - /* start TX early */ - dma_async_issue_pending(ctlr->dma_tx); - - /* mark as dma pending */ - bs->dma_pending = 1; - /* set the DMA length */ bcm2835_wr(bs, BCM2835_SPI_DLEN, bs->tx_len); @@ -642,20 +790,43 @@ static int bcm2835_spi_transfer_one_dma(struct spi_controller *ctlr, bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA | BCM2835_SPI_CS_DMAEN); + bs->tx_dma_active = true; + smp_wmb(); + + /* start TX early */ + dma_async_issue_pending(ctlr->dma_tx); + /* setup rx-DMA late - to run transfers while * mapping of the rx buffers still takes place * this saves 10us or more. */ - ret = bcm2835_spi_prepare_sg(ctlr, tfr, false); + if (bs->rx_buf) { + ret = bcm2835_spi_prepare_sg(ctlr, spi, tfr, bs, false); + } else { + cookie = dmaengine_submit(bs->clear_rx_desc[spi->chip_select]); + ret = dma_submit_error(cookie); + } if (ret) { /* need to reset on errors */ dmaengine_terminate_sync(ctlr->dma_tx); - bs->dma_pending = false; + bs->tx_dma_active = false; goto err_reset_hw; } /* start rx dma late */ dma_async_issue_pending(ctlr->dma_rx); + bs->rx_dma_active = true; + smp_mb(); + + /* + * In case of a very short TX-only transfer, bcm2835_spi_dma_tx_done() + * may run before RX DMA is issued. Terminate RX DMA if so. + */ + if (!bs->rx_buf && !bs->tx_dma_active && + cmpxchg(&bs->rx_dma_active, true, false)) { + dmaengine_terminate_async(ctlr->dma_rx); + bcm2835_spi_reset_hw(ctlr); + } /* wait for wakeup in framework */ return 1; @@ -678,26 +849,52 @@ static bool bcm2835_spi_can_dma(struct spi_controller *ctlr, return true; } -static void bcm2835_dma_release(struct spi_controller *ctlr) +static void bcm2835_dma_release(struct spi_controller *ctlr, + struct bcm2835_spi *bs) { + int i; + if (ctlr->dma_tx) { dmaengine_terminate_sync(ctlr->dma_tx); + + if (bs->fill_tx_desc) + dmaengine_desc_free(bs->fill_tx_desc); + + if (bs->fill_tx_addr) + dma_unmap_page_attrs(ctlr->dma_tx->device->dev, + bs->fill_tx_addr, sizeof(u32), + DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + dma_release_channel(ctlr->dma_tx); ctlr->dma_tx = NULL; } + if (ctlr->dma_rx) { dmaengine_terminate_sync(ctlr->dma_rx); + + for (i = 0; i < BCM2835_SPI_NUM_CS; i++) + if (bs->clear_rx_desc[i]) + dmaengine_desc_free(bs->clear_rx_desc[i]); + + if (bs->clear_rx_addr) + dma_unmap_single(ctlr->dma_rx->device->dev, + bs->clear_rx_addr, + sizeof(bs->clear_rx_cs), + DMA_TO_DEVICE); + dma_release_channel(ctlr->dma_rx); ctlr->dma_rx = NULL; } } -static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev) +static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, + struct bcm2835_spi *bs) { struct dma_slave_config slave_config; const __be32 *addr; dma_addr_t dma_reg_base; - int ret; + int ret, i; /* base address in dma-space */ addr = of_get_address(ctlr->dev.of_node, 0, NULL, NULL); @@ -719,7 +916,11 @@ static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev) goto err_release; } - /* configure DMAs */ + /* + * The TX DMA channel either copies a transfer's TX buffer to the FIFO + * or, in case of an RX-only transfer, cyclically copies from the zero + * page to the FIFO using a preallocated, reusable descriptor. + */ slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -727,17 +928,74 @@ static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev) if (ret) goto err_config; + bs->fill_tx_addr = dma_map_page_attrs(ctlr->dma_tx->device->dev, + ZERO_PAGE(0), 0, sizeof(u32), + DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(ctlr->dma_tx->device->dev, bs->fill_tx_addr)) { + dev_err(dev, "cannot map zero page - not using DMA mode\n"); + bs->fill_tx_addr = 0; + goto err_release; + } + + bs->fill_tx_desc = dmaengine_prep_dma_cyclic(ctlr->dma_tx, + bs->fill_tx_addr, + sizeof(u32), 0, + DMA_MEM_TO_DEV, 0); + if (!bs->fill_tx_desc) { + dev_err(dev, "cannot prepare fill_tx_desc - not using DMA mode\n"); + goto err_release; + } + + ret = dmaengine_desc_set_reuse(bs->fill_tx_desc); + if (ret) { + dev_err(dev, "cannot reuse fill_tx_desc - not using DMA mode\n"); + goto err_release; + } + + /* + * The RX DMA channel is used bidirectionally: It either reads the + * RX FIFO or, in case of a TX-only transfer, cyclically writes a + * precalculated value to the CS register to clear the RX FIFO. + */ slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_CS); + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ret = dmaengine_slave_config(ctlr->dma_rx, &slave_config); if (ret) goto err_config; + bs->clear_rx_addr = dma_map_single(ctlr->dma_rx->device->dev, + bs->clear_rx_cs, + sizeof(bs->clear_rx_cs), + DMA_TO_DEVICE); + if (dma_mapping_error(ctlr->dma_rx->device->dev, bs->clear_rx_addr)) { + dev_err(dev, "cannot map clear_rx_cs - not using DMA mode\n"); + bs->clear_rx_addr = 0; + goto err_release; + } + + for (i = 0; i < BCM2835_SPI_NUM_CS; i++) { + bs->clear_rx_desc[i] = dmaengine_prep_dma_cyclic(ctlr->dma_rx, + bs->clear_rx_addr + i * sizeof(u32), + sizeof(u32), 0, + DMA_MEM_TO_DEV, 0); + if (!bs->clear_rx_desc[i]) { + dev_err(dev, "cannot prepare clear_rx_desc - not using DMA mode\n"); + goto err_release; + } + + ret = dmaengine_desc_set_reuse(bs->clear_rx_desc[i]); + if (ret) { + dev_err(dev, "cannot reuse clear_rx_desc - not using DMA mode\n"); + goto err_release; + } + } + /* all went well, so set can_dma */ ctlr->can_dma = bcm2835_spi_can_dma; - /* need to do TX AND RX DMA, so we need dummy buffers */ - ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; return; @@ -745,7 +1003,7 @@ err_config: dev_err(dev, "issue configuring dma: %d - not using DMA mode\n", ret); err_release: - bcm2835_dma_release(ctlr); + bcm2835_dma_release(ctlr, bs); err: return; } @@ -812,7 +1070,7 @@ static int bcm2835_spi_transfer_one(struct spi_controller *ctlr, struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); unsigned long spi_hz, clk_hz, cdiv, spi_used_hz; unsigned long hz_per_byte, byte_limit; - u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + u32 cs = bs->prepare_cs[spi->chip_select]; /* set clock */ spi_hz = tfr->speed_hz; @@ -834,17 +1092,8 @@ static int bcm2835_spi_transfer_one(struct spi_controller *ctlr, bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv); /* handle all the 3-wire mode */ - if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf)) + if (spi->mode & SPI_3WIRE && tfr->rx_buf) cs |= BCM2835_SPI_CS_REN; - else - cs &= ~BCM2835_SPI_CS_REN; - - /* - * The driver always uses software-controlled GPIO Chip Select. - * Set the hardware-controlled native Chip Select to an invalid - * value to prevent it from interfering. - */ - cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; /* set transmit buffers and length */ bs->tx_buf = tfr->tx_buf; @@ -881,7 +1130,6 @@ static int bcm2835_spi_prepare_message(struct spi_controller *ctlr, { struct spi_device *spi = msg->spi; struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); - u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); int ret; if (ctlr->can_dma) { @@ -896,14 +1144,11 @@ static int bcm2835_spi_prepare_message(struct spi_controller *ctlr, return ret; } - cs &= ~(BCM2835_SPI_CS_CPOL | BCM2835_SPI_CS_CPHA); - - if (spi->mode & SPI_CPOL) - cs |= BCM2835_SPI_CS_CPOL; - if (spi->mode & SPI_CPHA) - cs |= BCM2835_SPI_CS_CPHA; - - bcm2835_wr(bs, BCM2835_SPI_CS, cs); + /* + * Set up clock polarity before spi_transfer_one_message() asserts + * chip select to avoid a gratuitous clock signal edge. + */ + bcm2835_wr(bs, BCM2835_SPI_CS, bs->prepare_cs[spi->chip_select]); return 0; } @@ -914,11 +1159,12 @@ static void bcm2835_spi_handle_err(struct spi_controller *ctlr, struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); /* if an error occurred and we have an active dma, then terminate */ - if (cmpxchg(&bs->dma_pending, true, false)) { - dmaengine_terminate_sync(ctlr->dma_tx); - dmaengine_terminate_sync(ctlr->dma_rx); - bcm2835_spi_undo_prologue(bs); - } + dmaengine_terminate_sync(ctlr->dma_tx); + bs->tx_dma_active = false; + dmaengine_terminate_sync(ctlr->dma_rx); + bs->rx_dma_active = false; + bcm2835_spi_undo_prologue(bs); + /* and reset */ bcm2835_spi_reset_hw(ctlr); } @@ -930,14 +1176,50 @@ static int chip_match_name(struct gpio_chip *chip, void *data) static int bcm2835_spi_setup(struct spi_device *spi) { - int err; + struct spi_controller *ctlr = spi->controller; + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); struct gpio_chip *chip; + enum gpio_lookup_flags lflags; + u32 cs; + + /* + * Precalculate SPI slave's CS register value for ->prepare_message(): + * The driver always uses software-controlled GPIO chip select, hence + * set the hardware-controlled native chip select to an invalid value + * to prevent it from interfering. + */ + cs = BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; + if (spi->mode & SPI_CPOL) + cs |= BCM2835_SPI_CS_CPOL; + if (spi->mode & SPI_CPHA) + cs |= BCM2835_SPI_CS_CPHA; + bs->prepare_cs[spi->chip_select] = cs; + + /* + * Precalculate SPI slave's CS register value to clear RX FIFO + * in case of a TX-only DMA transfer. + */ + if (ctlr->dma_rx) { + bs->clear_rx_cs[spi->chip_select] = cs | + BCM2835_SPI_CS_TA | + BCM2835_SPI_CS_DMAEN | + BCM2835_SPI_CS_CLEAR_RX; + dma_sync_single_for_device(ctlr->dma_rx->device->dev, + bs->clear_rx_addr, + sizeof(bs->clear_rx_cs), + DMA_TO_DEVICE); + } + /* * sanity checking the native-chipselects */ if (spi->mode & SPI_NO_CS) return 0; - if (gpio_is_valid(spi->cs_gpio)) + /* + * The SPI core has successfully requested the CS GPIO line from the + * device tree, so we are done. + */ + if (spi->cs_gpiod) return 0; if (spi->chip_select > 1) { /* error in the case of native CS requested with CS > 1 @@ -948,29 +1230,43 @@ static int bcm2835_spi_setup(struct spi_device *spi) "setup: only two native chip-selects are supported\n"); return -EINVAL; } - /* now translate native cs to GPIO */ + + /* + * Translate native CS to GPIO + * + * FIXME: poking around in the gpiolib internals like this is + * not very good practice. Find a way to locate the real problem + * and fix it. Why is the GPIO descriptor in spi->cs_gpiod + * sometimes not assigned correctly? Erroneous device trees? + */ /* get the gpio chip for the base */ chip = gpiochip_find("pinctrl-bcm2835", chip_match_name); if (!chip) return 0; - /* and calculate the real CS */ - spi->cs_gpio = chip->base + 8 - spi->chip_select; + /* + * Retrieve the corresponding GPIO line used for CS. + * The inversion semantics will be handled by the GPIO core + * code, so we pass GPIOS_OUT_LOW for "unasserted" and + * the correct flag for inversion semantics. The SPI_CS_HIGH + * on spi->mode cannot be checked for polarity in this case + * as the flag use_gpio_descriptors enforces SPI_CS_HIGH. + */ + if (of_property_read_bool(spi->dev.of_node, "spi-cs-high")) + lflags = GPIO_ACTIVE_HIGH; + else + lflags = GPIO_ACTIVE_LOW; + spi->cs_gpiod = gpiochip_request_own_desc(chip, 8 - spi->chip_select, + DRV_NAME, + lflags, + GPIOD_OUT_LOW); + if (IS_ERR(spi->cs_gpiod)) + return PTR_ERR(spi->cs_gpiod); /* and set up the "mode" and level */ - dev_info(&spi->dev, "setting up native-CS%i as GPIO %i\n", - spi->chip_select, spi->cs_gpio); - - /* set up GPIO as output and pull to the correct level */ - err = gpio_direction_output(spi->cs_gpio, - (spi->mode & SPI_CS_HIGH) ? 0 : 1); - if (err) { - dev_err(&spi->dev, - "could not set CS%i gpio %i as output: %i", - spi->chip_select, spi->cs_gpio, err); - return err; - } + dev_info(&spi->dev, "setting up native-CS%i to use GPIO\n", + spi->chip_select); return 0; } @@ -979,18 +1275,19 @@ static int bcm2835_spi_probe(struct platform_device *pdev) { struct spi_controller *ctlr; struct bcm2835_spi *bs; - struct resource *res; int err; - ctlr = spi_alloc_master(&pdev->dev, sizeof(*bs)); + ctlr = spi_alloc_master(&pdev->dev, ALIGN(sizeof(*bs), + dma_get_cache_alignment())); if (!ctlr) return -ENOMEM; platform_set_drvdata(pdev, ctlr); + ctlr->use_gpio_descriptors = true; ctlr->mode_bits = BCM2835_SPI_MODE_BITS; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); - ctlr->num_chipselect = 3; + ctlr->num_chipselect = BCM2835_SPI_NUM_CS; ctlr->setup = bcm2835_spi_setup; ctlr->transfer_one = bcm2835_spi_transfer_one; ctlr->handle_err = bcm2835_spi_handle_err; @@ -999,8 +1296,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) bs = spi_controller_get_devdata(ctlr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bs->regs = devm_ioremap_resource(&pdev->dev, res); + bs->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bs->regs)) { err = PTR_ERR(bs->regs); goto out_controller_put; @@ -1015,14 +1311,13 @@ static int bcm2835_spi_probe(struct platform_device *pdev) bs->irq = platform_get_irq(pdev, 0); if (bs->irq <= 0) { - dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); err = bs->irq ? bs->irq : -ENODEV; goto out_controller_put; } clk_prepare_enable(bs->clk); - bcm2835_dma_init(ctlr, &pdev->dev); + bcm2835_dma_init(ctlr, &pdev->dev, bs); /* initialise the hardware with the default polarities */ bcm2835_wr(bs, BCM2835_SPI_CS, @@ -1066,7 +1361,7 @@ static int bcm2835_spi_remove(struct platform_device *pdev) clk_disable_unprepare(bs->clk); - bcm2835_dma_release(ctlr); + bcm2835_dma_release(ctlr, bs); return 0; } diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index bb57035c5770..a2162ff56a12 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -491,7 +491,6 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct bcm2835aux_spi *bs; - struct resource *res; unsigned long clk_hz; int err; @@ -524,8 +523,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) bs = spi_master_get_devdata(master); /* the main area */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bs->regs = devm_ioremap_resource(&pdev->dev, res); + bs->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bs->regs)) { err = PTR_ERR(bs->regs); goto out_master_put; @@ -540,7 +538,6 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) bs->irq = platform_get_irq(pdev, 0); if (bs->irq <= 0) { - dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); err = bs->irq ? bs->irq : -ENODEV; goto out_master_put; } diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 9a06ffdb73b8..c6836a931dbf 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -330,7 +330,6 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) { struct spi_master *master; struct bcm63xx_hsspi *bs; - struct resource *res_mem; void __iomem *regs; struct device *dev = &pdev->dev; struct clk *clk, *pll_clk = NULL; @@ -338,13 +337,10 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no irq: %d\n", irq); + if (irq < 0) return irq; - } - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev, res_mem); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index df1c94a131e6..fdd7eaa0b8ed 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -520,10 +520,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no irq: %d\n", irq); + if (irq < 0) return irq; - } clk = devm_clk_get(dev, "spi"); if (IS_ERR(clk)) { diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 7c41e4e82849..c36587b42e95 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -474,7 +474,6 @@ static int cdns_spi_probe(struct platform_device *pdev) int ret = 0, irq; struct spi_master *master; struct cdns_spi *xspi; - struct resource *res; u32 num_cs; master = spi_alloc_master(&pdev->dev, sizeof(*xspi)); @@ -485,8 +484,7 @@ static int cdns_spi_probe(struct platform_device *pdev) master->dev.of_node = pdev->dev.of_node; platform_set_drvdata(pdev, master); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - xspi->regs = devm_ioremap_resource(&pdev->dev, res); + xspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xspi->regs)) { ret = PTR_ERR(xspi->regs); goto remove_master; @@ -540,7 +538,6 @@ static int cdns_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq <= 0) { ret = -ENXIO; - dev_err(&pdev->dev, "irq number is invalid\n"); goto clk_dis_all; } diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c index ee4703e84622..1a2de6ce9064 100644 --- a/drivers/spi/spi-cavium-octeon.c +++ b/drivers/spi/spi-cavium-octeon.c @@ -18,7 +18,6 @@ static int octeon_spi_probe(struct platform_device *pdev) { - struct resource *res_mem; void __iomem *reg_base; struct spi_master *master; struct octeon_spi *p; @@ -30,8 +29,7 @@ static int octeon_spi_probe(struct platform_device *pdev) p = spi_master_get_devdata(master); platform_set_drvdata(pdev, master); - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg_base = devm_ioremap_resource(&pdev->dev, res_mem); + reg_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg_base)) { err = PTR_ERR(reg_base); goto fail; diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 4daba12ec843..5e900f228919 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -91,7 +91,6 @@ static int spi_clps711x_probe(struct platform_device *pdev) { struct spi_clps711x_data *hw; struct spi_master *master; - struct resource *res; int irq, ret; irq = platform_get_irq(pdev, 0); @@ -125,8 +124,7 @@ static int spi_clps711x_probe(struct platform_device *pdev) goto err_out; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hw->syncio = devm_ioremap_resource(&pdev->dev, res); + hw->syncio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hw->syncio)) { ret = PTR_ERR(hw->syncio); goto err_out; diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 5ff48ab2f534..f80e06c87fbe 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -339,7 +339,6 @@ static int mcfqspi_probe(struct platform_device *pdev) { struct spi_master *master; struct mcfqspi *mcfqspi; - struct resource *res; struct mcfqspi_platform_data *pdata; int status; @@ -362,8 +361,7 @@ static int mcfqspi_probe(struct platform_device *pdev) mcfqspi = spi_master_get_devdata(master); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mcfqspi->iobase = devm_ioremap_resource(&pdev->dev, res); + mcfqspi->iobase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mcfqspi->iobase)) { status = PTR_ERR(mcfqspi->iobase); goto fail0; diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 18c06568805e..bd46fca3f094 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -79,14 +79,12 @@ static int dw_spi_mscc_init(struct platform_device *pdev, const char *cpu_syscon, u32 if_si_owner_offset) { struct dw_spi_mscc *dwsmscc; - struct resource *res; dwsmscc = devm_kzalloc(&pdev->dev, sizeof(*dwsmscc), GFP_KERNEL); if (!dwsmscc) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - dwsmscc->spi_mst = devm_ioremap_resource(&pdev->dev, res); + dwsmscc->spi_mst = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(dwsmscc->spi_mst)) { dev_err(&pdev->dev, "SPI_MST region map failed\n"); return PTR_ERR(dwsmscc->spi_mst); @@ -138,7 +136,6 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) struct dw_spi_mmio *dwsmmio); struct dw_spi_mmio *dwsmmio; struct dw_spi *dws; - struct resource *mem; int ret; int num_cs; @@ -150,18 +147,15 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dws = &dwsmmio->dws; /* Get basic io resource and map it */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dws->regs = devm_ioremap_resource(&pdev->dev, mem); + dws->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dws->regs)) { dev_err(&pdev->dev, "SPI region map failed\n"); return PTR_ERR(dws->regs); } dws->irq = platform_get_irq(pdev, 0); - if (dws->irq < 0) { - dev_err(&pdev->dev, "no irq resource?\n"); + if (dws->irq < 0) return dws->irq; /* -ENXIO */ - } dwsmmio->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dwsmmio->clk)) @@ -172,8 +166,10 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) /* Optional clock needed to access the registers */ dwsmmio->pclk = devm_clk_get_optional(&pdev->dev, "pclk"); - if (IS_ERR(dwsmmio->pclk)) - return PTR_ERR(dwsmmio->pclk); + if (IS_ERR(dwsmmio->pclk)) { + ret = PTR_ERR(dwsmmio->pclk); + goto out_clk; + } ret = clk_prepare_enable(dwsmmio->pclk); if (ret) goto out_clk; diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 9651679ee7f7..140644913e6c 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -19,6 +19,7 @@ struct spi_pci_desc { int (*setup)(struct dw_spi *); u16 num_cs; u16 bus_num; + u32 max_freq; }; static struct spi_pci_desc spi_pci_mid_desc_1 = { @@ -33,6 +34,12 @@ static struct spi_pci_desc spi_pci_mid_desc_2 = { .bus_num = 1, }; +static struct spi_pci_desc spi_pci_ehl_desc = { + .num_cs = 1, + .bus_num = -1, + .max_freq = 100000000, +}; + static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct dw_spi *dws; @@ -65,6 +72,7 @@ static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (desc) { dws->num_cs = desc->num_cs; dws->bus_num = desc->bus_num; + dws->max_freq = desc->max_freq; if (desc->setup) { ret = desc->setup(dws); @@ -98,16 +106,14 @@ static void spi_pci_remove(struct pci_dev *pdev) #ifdef CONFIG_PM_SLEEP static int spi_suspend(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct dw_spi *dws = pci_get_drvdata(pdev); + struct dw_spi *dws = dev_get_drvdata(dev); return dw_spi_suspend_host(dws); } static int spi_resume(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct dw_spi *dws = pci_get_drvdata(pdev); + struct dw_spi *dws = dev_get_drvdata(dev); return dw_spi_resume_host(dws); } @@ -125,8 +131,14 @@ static const struct pci_device_id pci_ids[] = { { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc_1}, /* Intel MID platform SPI controller 2 */ { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&spi_pci_mid_desc_2}, + /* Intel Elkhart Lake PSE SPI controllers */ + { PCI_VDEVICE(INTEL, 0x4b84), (kernel_ulong_t)&spi_pci_ehl_desc}, + { PCI_VDEVICE(INTEL, 0x4b85), (kernel_ulong_t)&spi_pci_ehl_desc}, + { PCI_VDEVICE(INTEL, 0x4b86), (kernel_ulong_t)&spi_pci_ehl_desc}, + { PCI_VDEVICE(INTEL, 0x4b87), (kernel_ulong_t)&spi_pci_ehl_desc}, {}, }; +MODULE_DEVICE_TABLE(pci, pci_ids); static struct pci_driver dw_spi_driver = { .name = DRIVER_NAME, diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index eb1f2142a335..64d4c441b641 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -400,10 +400,8 @@ static int efm32_spi_probe(struct platform_device *pdev) } ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "failed to get rx irq (%d)\n", ret); + if (ret <= 0) goto err; - } ddata->rxirq = ret; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 4034e3ec0ba2..4e1ccd4e52b6 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -656,10 +656,8 @@ static int ep93xx_spi_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get irq resources\n"); + if (irq < 0) return -EBUSY; - } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index e967ac564761..858f0544289e 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -305,12 +305,10 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) } if (mspi->flags & SPI_CPM1) { - struct resource *res; void *pram; - res = platform_get_resource(to_platform_device(dev), - IORESOURCE_MEM, 1); - pram = devm_ioremap_resource(dev, res); + pram = devm_platform_ioremap_resource(to_platform_device(dev), + 1); if (IS_ERR(pram)) mspi->pram = NULL; else diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 53335ccc98f6..bec758e978fb 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -9,26 +9,16 @@ #include <linux/delay.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> -#include <linux/err.h> -#include <linux/errno.h> #include <linux/interrupt.h> -#include <linux/io.h> #include <linux/kernel.h> -#include <linux/math64.h> #include <linux/module.h> -#include <linux/of.h> #include <linux/of_device.h> #include <linux/pinctrl/consumer.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> -#include <linux/sched.h> #include <linux/spi/spi.h> #include <linux/spi/spi-fsl-dspi.h> -#include <linux/spi/spi_bitbang.h> -#include <linux/time.h> -#define DRIVER_NAME "fsl-dspi" +#define DRIVER_NAME "fsl-dspi" #ifdef CONFIG_M5441x #define DSPI_FIFO_SIZE 16 @@ -37,101 +27,97 @@ #endif #define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024) -#define SPI_MCR 0x00 -#define SPI_MCR_MASTER (1 << 31) -#define SPI_MCR_PCSIS (0x3F << 16) -#define SPI_MCR_CLR_TXF (1 << 11) -#define SPI_MCR_CLR_RXF (1 << 10) -#define SPI_MCR_XSPI (1 << 3) - -#define SPI_TCR 0x08 -#define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16) - -#define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4)) -#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27) -#define SPI_CTAR_CPOL(x) ((x) << 26) -#define SPI_CTAR_CPHA(x) ((x) << 25) -#define SPI_CTAR_LSBFE(x) ((x) << 24) -#define SPI_CTAR_PCSSCK(x) (((x) & 0x00000003) << 22) -#define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20) -#define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18) -#define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16) -#define SPI_CTAR_CSSCK(x) (((x) & 0x0000000f) << 12) -#define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8) -#define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4) -#define SPI_CTAR_BR(x) ((x) & 0x0000000f) -#define SPI_CTAR_SCALE_BITS 0xf - -#define SPI_CTAR0_SLAVE 0x0c - -#define SPI_SR 0x2c -#define SPI_SR_EOQF 0x10000000 -#define SPI_SR_TCFQF 0x80000000 -#define SPI_SR_CLEAR 0x9aaf0000 - -#define SPI_RSER_TFFFE BIT(25) -#define SPI_RSER_TFFFD BIT(24) -#define SPI_RSER_RFDFE BIT(17) -#define SPI_RSER_RFDFD BIT(16) - -#define SPI_RSER 0x30 -#define SPI_RSER_EOQFE 0x10000000 -#define SPI_RSER_TCFQE 0x80000000 - -#define SPI_PUSHR 0x34 -#define SPI_PUSHR_CMD_CONT (1 << 15) -#define SPI_PUSHR_CONT (SPI_PUSHR_CMD_CONT << 16) -#define SPI_PUSHR_CMD_CTAS(x) (((x) & 0x0003) << 12) -#define SPI_PUSHR_CTAS(x) (SPI_PUSHR_CMD_CTAS(x) << 16) -#define SPI_PUSHR_CMD_EOQ (1 << 11) -#define SPI_PUSHR_EOQ (SPI_PUSHR_CMD_EOQ << 16) -#define SPI_PUSHR_CMD_CTCNT (1 << 10) -#define SPI_PUSHR_CTCNT (SPI_PUSHR_CMD_CTCNT << 16) -#define SPI_PUSHR_CMD_PCS(x) ((1 << x) & 0x003f) -#define SPI_PUSHR_PCS(x) (SPI_PUSHR_CMD_PCS(x) << 16) -#define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff) - -#define SPI_PUSHR_SLAVE 0x34 - -#define SPI_POPR 0x38 -#define SPI_POPR_RXDATA(x) ((x) & 0x0000ffff) - -#define SPI_TXFR0 0x3c -#define SPI_TXFR1 0x40 -#define SPI_TXFR2 0x44 -#define SPI_TXFR3 0x48 -#define SPI_RXFR0 0x7c -#define SPI_RXFR1 0x80 -#define SPI_RXFR2 0x84 -#define SPI_RXFR3 0x88 - -#define SPI_CTARE(x) (0x11c + (((x) & 0x3) * 4)) -#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16) -#define SPI_CTARE_DTCP(x) ((x) & 0x7ff) - -#define SPI_SREX 0x13c - -#define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1) -#define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf) -#define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) -#define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) - -#define SPI_FRAME_EBITS(bits) SPI_CTARE_FMSZE(((bits) - 1) >> 4) -#define SPI_FRAME_EBITS_MASK SPI_CTARE_FMSZE(1) +#define SPI_MCR 0x00 +#define SPI_MCR_MASTER BIT(31) +#define SPI_MCR_PCSIS (0x3F << 16) +#define SPI_MCR_CLR_TXF BIT(11) +#define SPI_MCR_CLR_RXF BIT(10) +#define SPI_MCR_XSPI BIT(3) + +#define SPI_TCR 0x08 +#define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16) + +#define SPI_CTAR(x) (0x0c + (((x) & GENMASK(1, 0)) * 4)) +#define SPI_CTAR_FMSZ(x) (((x) << 27) & GENMASK(30, 27)) +#define SPI_CTAR_CPOL BIT(26) +#define SPI_CTAR_CPHA BIT(25) +#define SPI_CTAR_LSBFE BIT(24) +#define SPI_CTAR_PCSSCK(x) (((x) << 22) & GENMASK(23, 22)) +#define SPI_CTAR_PASC(x) (((x) << 20) & GENMASK(21, 20)) +#define SPI_CTAR_PDT(x) (((x) << 18) & GENMASK(19, 18)) +#define SPI_CTAR_PBR(x) (((x) << 16) & GENMASK(17, 16)) +#define SPI_CTAR_CSSCK(x) (((x) << 12) & GENMASK(15, 12)) +#define SPI_CTAR_ASC(x) (((x) << 8) & GENMASK(11, 8)) +#define SPI_CTAR_DT(x) (((x) << 4) & GENMASK(7, 4)) +#define SPI_CTAR_BR(x) ((x) & GENMASK(3, 0)) +#define SPI_CTAR_SCALE_BITS 0xf + +#define SPI_CTAR0_SLAVE 0x0c + +#define SPI_SR 0x2c +#define SPI_SR_TCFQF BIT(31) +#define SPI_SR_EOQF BIT(28) +#define SPI_SR_TFUF BIT(27) +#define SPI_SR_TFFF BIT(25) +#define SPI_SR_CMDTCF BIT(23) +#define SPI_SR_SPEF BIT(21) +#define SPI_SR_RFOF BIT(19) +#define SPI_SR_TFIWF BIT(18) +#define SPI_SR_RFDF BIT(17) +#define SPI_SR_CMDFFF BIT(16) +#define SPI_SR_CLEAR (SPI_SR_TCFQF | SPI_SR_EOQF | \ + SPI_SR_TFUF | SPI_SR_TFFF | \ + SPI_SR_CMDTCF | SPI_SR_SPEF | \ + SPI_SR_RFOF | SPI_SR_TFIWF | \ + SPI_SR_RFDF | SPI_SR_CMDFFF) + +#define SPI_RSER_TFFFE BIT(25) +#define SPI_RSER_TFFFD BIT(24) +#define SPI_RSER_RFDFE BIT(17) +#define SPI_RSER_RFDFD BIT(16) + +#define SPI_RSER 0x30 +#define SPI_RSER_TCFQE BIT(31) +#define SPI_RSER_EOQFE BIT(28) + +#define SPI_PUSHR 0x34 +#define SPI_PUSHR_CMD_CONT BIT(15) +#define SPI_PUSHR_CMD_CTAS(x) (((x) << 12 & GENMASK(14, 12))) +#define SPI_PUSHR_CMD_EOQ BIT(11) +#define SPI_PUSHR_CMD_CTCNT BIT(10) +#define SPI_PUSHR_CMD_PCS(x) (BIT(x) & GENMASK(5, 0)) + +#define SPI_PUSHR_SLAVE 0x34 + +#define SPI_POPR 0x38 + +#define SPI_TXFR0 0x3c +#define SPI_TXFR1 0x40 +#define SPI_TXFR2 0x44 +#define SPI_TXFR3 0x48 +#define SPI_RXFR0 0x7c +#define SPI_RXFR1 0x80 +#define SPI_RXFR2 0x84 +#define SPI_RXFR3 0x88 + +#define SPI_CTARE(x) (0x11c + (((x) & GENMASK(1, 0)) * 4)) +#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16) +#define SPI_CTARE_DTCP(x) ((x) & 0x7ff) + +#define SPI_SREX 0x13c + +#define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1) +#define SPI_FRAME_EBITS(bits) SPI_CTARE_FMSZE(((bits) - 1) >> 4) /* Register offsets for regmap_pushr */ -#define PUSHR_CMD 0x0 -#define PUSHR_TX 0x2 +#define PUSHR_CMD 0x0 +#define PUSHR_TX 0x2 -#define SPI_CS_INIT 0x01 -#define SPI_CS_ASSERT 0x02 -#define SPI_CS_DROP 0x04 - -#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) +#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) struct chip_data { - u32 ctar_val; - u16 void_write_data; + u32 ctar_val; + u16 void_write_data; }; enum dspi_trans_mode { @@ -141,75 +127,75 @@ enum dspi_trans_mode { }; struct fsl_dspi_devtype_data { - enum dspi_trans_mode trans_mode; - u8 max_clock_factor; - bool xspi_mode; + enum dspi_trans_mode trans_mode; + u8 max_clock_factor; + bool xspi_mode; }; static const struct fsl_dspi_devtype_data vf610_data = { - .trans_mode = DSPI_DMA_MODE, - .max_clock_factor = 2, + .trans_mode = DSPI_DMA_MODE, + .max_clock_factor = 2, }; static const struct fsl_dspi_devtype_data ls1021a_v1_data = { - .trans_mode = DSPI_TCFQ_MODE, - .max_clock_factor = 8, - .xspi_mode = true, + .trans_mode = DSPI_TCFQ_MODE, + .max_clock_factor = 8, + .xspi_mode = true, }; static const struct fsl_dspi_devtype_data ls2085a_data = { - .trans_mode = DSPI_TCFQ_MODE, - .max_clock_factor = 8, + .trans_mode = DSPI_TCFQ_MODE, + .max_clock_factor = 8, }; static const struct fsl_dspi_devtype_data coldfire_data = { - .trans_mode = DSPI_EOQ_MODE, - .max_clock_factor = 8, + .trans_mode = DSPI_EOQ_MODE, + .max_clock_factor = 8, }; struct fsl_dspi_dma { /* Length of transfer in words of DSPI_FIFO_SIZE */ - u32 curr_xfer_len; - - u32 *tx_dma_buf; - struct dma_chan *chan_tx; - dma_addr_t tx_dma_phys; - struct completion cmd_tx_complete; - struct dma_async_tx_descriptor *tx_desc; - - u32 *rx_dma_buf; - struct dma_chan *chan_rx; - dma_addr_t rx_dma_phys; - struct completion cmd_rx_complete; - struct dma_async_tx_descriptor *rx_desc; + u32 curr_xfer_len; + + u32 *tx_dma_buf; + struct dma_chan *chan_tx; + dma_addr_t tx_dma_phys; + struct completion cmd_tx_complete; + struct dma_async_tx_descriptor *tx_desc; + + u32 *rx_dma_buf; + struct dma_chan *chan_rx; + dma_addr_t rx_dma_phys; + struct completion cmd_rx_complete; + struct dma_async_tx_descriptor *rx_desc; }; struct fsl_dspi { - struct spi_master *master; - struct platform_device *pdev; - - struct regmap *regmap; - struct regmap *regmap_pushr; - int irq; - struct clk *clk; - - struct spi_transfer *cur_transfer; - struct spi_message *cur_msg; - struct chip_data *cur_chip; - size_t len; - const void *tx; - void *rx; - void *rx_end; - u16 void_write_data; - u16 tx_cmd; - u8 bits_per_word; - u8 bytes_per_word; - const struct fsl_dspi_devtype_data *devtype_data; - - wait_queue_head_t waitq; - u32 waitflags; - - struct fsl_dspi_dma *dma; + struct spi_controller *ctlr; + struct platform_device *pdev; + + struct regmap *regmap; + struct regmap *regmap_pushr; + int irq; + struct clk *clk; + + struct spi_transfer *cur_transfer; + struct spi_message *cur_msg; + struct chip_data *cur_chip; + size_t len; + const void *tx; + void *rx; + void *rx_end; + u16 void_write_data; + u16 tx_cmd; + u8 bits_per_word; + u8 bytes_per_word; + const struct fsl_dspi_devtype_data *devtype_data; + + wait_queue_head_t waitq; + u32 waitflags; + + struct fsl_dspi_dma *dma; }; static u32 dspi_pop_tx(struct fsl_dspi *dspi) @@ -233,7 +219,7 @@ static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi) { u16 cmd = dspi->tx_cmd, data = dspi_pop_tx(dspi); - if (spi_controller_is_slave(dspi->master)) + if (spi_controller_is_slave(dspi->ctlr)) return data; if (dspi->len > 0) @@ -246,7 +232,7 @@ static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata) if (!dspi->rx) return; - /* Mask of undefined bits */ + /* Mask off undefined bits */ rxdata &= (1 << dspi->bits_per_word) - 1; if (dspi->bytes_per_word == 1) @@ -282,8 +268,8 @@ static void dspi_rx_dma_callback(void *arg) static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) { - struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; + struct fsl_dspi_dma *dma = dspi->dma; int time_left; int i; @@ -332,13 +318,13 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) dma_async_issue_pending(dma->chan_rx); dma_async_issue_pending(dma->chan_tx); - if (spi_controller_is_slave(dspi->master)) { + if (spi_controller_is_slave(dspi->ctlr)) { wait_for_completion_interruptible(&dspi->dma->cmd_rx_complete); return 0; } time_left = wait_for_completion_timeout(&dspi->dma->cmd_tx_complete, - DMA_COMPLETION_TIMEOUT); + DMA_COMPLETION_TIMEOUT); if (time_left == 0) { dev_err(dev, "DMA tx timeout\n"); dmaengine_terminate_all(dma->chan_tx); @@ -347,7 +333,7 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) } time_left = wait_for_completion_timeout(&dspi->dma->cmd_rx_complete, - DMA_COMPLETION_TIMEOUT); + DMA_COMPLETION_TIMEOUT); if (time_left == 0) { dev_err(dev, "DMA rx timeout\n"); dmaengine_terminate_all(dma->chan_tx); @@ -360,9 +346,9 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) static int dspi_dma_xfer(struct fsl_dspi *dspi) { - struct fsl_dspi_dma *dma = dspi->dma; - struct device *dev = &dspi->pdev->dev; struct spi_message *message = dspi->cur_msg; + struct device *dev = &dspi->pdev->dev; + struct fsl_dspi_dma *dma = dspi->dma; int curr_remaining_bytes; int bytes_per_buffer; int ret = 0; @@ -397,9 +383,9 @@ exit: static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr) { - struct fsl_dspi_dma *dma; - struct dma_slave_config cfg; struct device *dev = &dspi->pdev->dev; + struct dma_slave_config cfg; + struct fsl_dspi_dma *dma; int ret; dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); @@ -421,14 +407,14 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr) } dma->tx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE, - &dma->tx_dma_phys, GFP_KERNEL); + &dma->tx_dma_phys, GFP_KERNEL); if (!dma->tx_dma_buf) { ret = -ENOMEM; goto err_tx_dma_buf; } dma->rx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE, - &dma->rx_dma_phys, GFP_KERNEL); + &dma->rx_dma_phys, GFP_KERNEL); if (!dma->rx_dma_buf) { ret = -ENOMEM; goto err_rx_dma_buf; @@ -485,30 +471,31 @@ static void dspi_release_dma(struct fsl_dspi *dspi) struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; - if (dma) { - if (dma->chan_tx) { - dma_unmap_single(dev, dma->tx_dma_phys, - DSPI_DMA_BUFSIZE, DMA_TO_DEVICE); - dma_release_channel(dma->chan_tx); - } + if (!dma) + return; - if (dma->chan_rx) { - dma_unmap_single(dev, dma->rx_dma_phys, - DSPI_DMA_BUFSIZE, DMA_FROM_DEVICE); - dma_release_channel(dma->chan_rx); - } + if (dma->chan_tx) { + dma_unmap_single(dev, dma->tx_dma_phys, + DSPI_DMA_BUFSIZE, DMA_TO_DEVICE); + dma_release_channel(dma->chan_tx); + } + + if (dma->chan_rx) { + dma_unmap_single(dev, dma->rx_dma_phys, + DSPI_DMA_BUFSIZE, DMA_FROM_DEVICE); + dma_release_channel(dma->chan_rx); } } static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, - unsigned long clkrate) + unsigned long clkrate) { /* Valid baud rate pre-scaler values */ int pbr_tbl[4] = {2, 3, 5, 7}; int brs[16] = { 2, 4, 6, 8, - 16, 32, 64, 128, - 256, 512, 1024, 2048, - 4096, 8192, 16384, 32768 }; + 16, 32, 64, 128, + 256, 512, 1024, 2048, + 4096, 8192, 16384, 32768 }; int scale_needed, scale, minscale = INT_MAX; int i, j; @@ -538,15 +525,15 @@ static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, } static void ns_delay_scale(char *psc, char *sc, int delay_ns, - unsigned long clkrate) + unsigned long clkrate) { - int pscale_tbl[4] = {1, 3, 5, 7}; int scale_needed, scale, minscale = INT_MAX; - int i, j; + int pscale_tbl[4] = {1, 3, 5, 7}; u32 remainder; + int i, j; scale_needed = div_u64_rem((u64)delay_ns * clkrate, NSEC_PER_SEC, - &remainder); + &remainder); if (remainder) scale_needed++; @@ -601,7 +588,7 @@ static void dspi_tcfq_write(struct fsl_dspi *dspi) */ u32 data = dspi_pop_tx(dspi); - if (dspi->cur_chip->ctar_val & SPI_CTAR_LSBFE(1)) { + if (dspi->cur_chip->ctar_val & SPI_CTAR_LSBFE) { /* LSB */ tx_fifo_write(dspi, data & 0xFFFF); tx_fifo_write(dspi, data >> 16); @@ -655,19 +642,90 @@ static void dspi_eoq_read(struct fsl_dspi *dspi) { int fifo_size = DSPI_FIFO_SIZE; - /* Read one FIFO entry at and push to rx buffer */ + /* Read one FIFO entry and push to rx buffer */ while ((dspi->rx < dspi->rx_end) && fifo_size--) dspi_push_rx(dspi, fifo_read(dspi)); } -static int dspi_transfer_one_message(struct spi_master *master, - struct spi_message *message) +static int dspi_rxtx(struct fsl_dspi *dspi) { - struct fsl_dspi *dspi = spi_master_get_devdata(master); + struct spi_message *msg = dspi->cur_msg; + enum dspi_trans_mode trans_mode; + u16 spi_tcnt; + u32 spi_tcr; + + /* Get transfer counter (in number of SPI transfers). It was + * reset to 0 when transfer(s) were started. + */ + regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); + spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); + /* Update total number of bytes that were transferred */ + msg->actual_length += spi_tcnt * dspi->bytes_per_word; + + trans_mode = dspi->devtype_data->trans_mode; + if (trans_mode == DSPI_EOQ_MODE) + dspi_eoq_read(dspi); + else if (trans_mode == DSPI_TCFQ_MODE) + dspi_tcfq_read(dspi); + + if (!dspi->len) + /* Success! */ + return 0; + + if (trans_mode == DSPI_EOQ_MODE) + dspi_eoq_write(dspi); + else if (trans_mode == DSPI_TCFQ_MODE) + dspi_tcfq_write(dspi); + + return -EINPROGRESS; +} + +static int dspi_poll(struct fsl_dspi *dspi) +{ + int tries = 1000; + u32 spi_sr; + + do { + regmap_read(dspi->regmap, SPI_SR, &spi_sr); + regmap_write(dspi->regmap, SPI_SR, spi_sr); + + if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) + break; + } while (--tries); + + if (!tries) + return -ETIMEDOUT; + + return dspi_rxtx(dspi); +} + +static irqreturn_t dspi_interrupt(int irq, void *dev_id) +{ + struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; + u32 spi_sr; + + regmap_read(dspi->regmap, SPI_SR, &spi_sr); + regmap_write(dspi->regmap, SPI_SR, spi_sr); + + if (!(spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF))) + return IRQ_NONE; + + if (dspi_rxtx(dspi) == 0) { + dspi->waitflags = 1; + wake_up_interruptible(&dspi->waitq); + } + + return IRQ_HANDLED; +} + +static int dspi_transfer_one_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr); struct spi_device *spi = message->spi; + enum dspi_trans_mode trans_mode; struct spi_transfer *transfer; int status = 0; - enum dspi_trans_mode trans_mode; message->actual_length = 0; @@ -677,7 +735,7 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->cur_chip = spi_get_ctldata(spi); /* Prepare command word for CMD FIFO */ dspi->tx_cmd = SPI_PUSHR_CMD_CTAS(0) | - SPI_PUSHR_CMD_PCS(spi->chip_select); + SPI_PUSHR_CMD_PCS(spi->chip_select); if (list_is_last(&dspi->cur_transfer->transfer_list, &dspi->cur_msg->transfers)) { /* Leave PCS activated after last transfer when @@ -718,8 +776,8 @@ static int dspi_transfer_one_message(struct spi_master *master, SPI_FRAME_BITS(transfer->bits_per_word)); if (dspi->devtype_data->xspi_mode) regmap_write(dspi->regmap, SPI_CTARE(0), - SPI_FRAME_EBITS(transfer->bits_per_word) - | SPI_CTARE_DTCP(1)); + SPI_FRAME_EBITS(transfer->bits_per_word) | + SPI_CTARE_DTCP(1)); trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { @@ -733,8 +791,8 @@ static int dspi_transfer_one_message(struct spi_master *master, break; case DSPI_DMA_MODE: regmap_write(dspi->regmap, SPI_RSER, - SPI_RSER_TFFFE | SPI_RSER_TFFFD | - SPI_RSER_RFDFE | SPI_RSER_RFDFD); + SPI_RSER_TFFFE | SPI_RSER_TFFFD | + SPI_RSER_RFDFE | SPI_RSER_RFDFD); status = dspi_dma_xfer(dspi); break; default: @@ -744,13 +802,18 @@ static int dspi_transfer_one_message(struct spi_master *master, goto out; } - if (trans_mode != DSPI_DMA_MODE) { - if (wait_event_interruptible(dspi->waitq, - dspi->waitflags)) - dev_err(&dspi->pdev->dev, - "wait transfer complete fail!\n"); + if (!dspi->irq) { + do { + status = dspi_poll(dspi); + } while (status == -EINPROGRESS); + } else if (trans_mode != DSPI_DMA_MODE) { + status = wait_event_interruptible(dspi->waitq, + dspi->waitflags); dspi->waitflags = 0; } + if (status) + dev_err(&dspi->pdev->dev, + "Waiting for transfer to complete failed!\n"); if (transfer->delay_usecs) udelay(transfer->delay_usecs); @@ -758,19 +821,19 @@ static int dspi_transfer_one_message(struct spi_master *master, out: message->status = status; - spi_finalize_current_message(master); + spi_finalize_current_message(ctlr); return status; } static int dspi_setup(struct spi_device *spi) { - struct chip_data *chip; - struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - struct fsl_dspi_platform_data *pdata; - u32 cs_sck_delay = 0, sck_cs_delay = 0; + struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller); unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0; + u32 cs_sck_delay = 0, sck_cs_delay = 0; + struct fsl_dspi_platform_data *pdata; unsigned char pasc = 0, asc = 0; + struct chip_data *chip; unsigned long clkrate; /* Only alloc on first setup */ @@ -785,10 +848,10 @@ static int dspi_setup(struct spi_device *spi) if (!pdata) { of_property_read_u32(spi->dev.of_node, "fsl,spi-cs-sck-delay", - &cs_sck_delay); + &cs_sck_delay); of_property_read_u32(spi->dev.of_node, "fsl,spi-sck-cs-delay", - &sck_cs_delay); + &sck_cs_delay); } else { cs_sck_delay = pdata->cs_sck_delay; sck_cs_delay = pdata->sck_cs_delay; @@ -805,18 +868,22 @@ static int dspi_setup(struct spi_device *spi) /* Set After SCK delay scale values */ ns_delay_scale(&pasc, &asc, sck_cs_delay, clkrate); - chip->ctar_val = SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) - | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0); - - if (!spi_controller_is_slave(dspi->master)) { - chip->ctar_val |= SPI_CTAR_LSBFE(spi->mode & - SPI_LSB_FIRST ? 1 : 0) - | SPI_CTAR_PCSSCK(pcssck) - | SPI_CTAR_CSSCK(cssck) - | SPI_CTAR_PASC(pasc) - | SPI_CTAR_ASC(asc) - | SPI_CTAR_PBR(pbr) - | SPI_CTAR_BR(br); + chip->ctar_val = 0; + if (spi->mode & SPI_CPOL) + chip->ctar_val |= SPI_CTAR_CPOL; + if (spi->mode & SPI_CPHA) + chip->ctar_val |= SPI_CTAR_CPHA; + + if (!spi_controller_is_slave(dspi->ctlr)) { + chip->ctar_val |= SPI_CTAR_PCSSCK(pcssck) | + SPI_CTAR_CSSCK(cssck) | + SPI_CTAR_PASC(pasc) | + SPI_CTAR_ASC(asc) | + SPI_CTAR_PBR(pbr) | + SPI_CTAR_BR(br); + + if (spi->mode & SPI_LSB_FIRST) + chip->ctar_val |= SPI_CTAR_LSBFE; } spi_set_ctldata(spi, chip); @@ -829,68 +896,11 @@ static void dspi_cleanup(struct spi_device *spi) struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n", - spi->master->bus_num, spi->chip_select); + spi->controller->bus_num, spi->chip_select); kfree(chip); } -static irqreturn_t dspi_interrupt(int irq, void *dev_id) -{ - struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; - struct spi_message *msg = dspi->cur_msg; - enum dspi_trans_mode trans_mode; - u32 spi_sr, spi_tcr; - u16 spi_tcnt; - - regmap_read(dspi->regmap, SPI_SR, &spi_sr); - regmap_write(dspi->regmap, SPI_SR, spi_sr); - - - if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) { - /* Get transfer counter (in number of SPI transfers). It was - * reset to 0 when transfer(s) were started. - */ - regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); - spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); - /* Update total number of bytes that were transferred */ - msg->actual_length += spi_tcnt * dspi->bytes_per_word; - - trans_mode = dspi->devtype_data->trans_mode; - switch (trans_mode) { - case DSPI_EOQ_MODE: - dspi_eoq_read(dspi); - break; - case DSPI_TCFQ_MODE: - dspi_tcfq_read(dspi); - break; - default: - dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", - trans_mode); - return IRQ_HANDLED; - } - - if (!dspi->len) { - dspi->waitflags = 1; - wake_up_interruptible(&dspi->waitq); - } else { - switch (trans_mode) { - case DSPI_EOQ_MODE: - dspi_eoq_write(dspi); - break; - case DSPI_TCFQ_MODE: - dspi_tcfq_write(dspi); - break; - default: - dev_err(&dspi->pdev->dev, - "unsupported trans_mode %u\n", - trans_mode); - } - } - } - - return IRQ_HANDLED; -} - static const struct of_device_id fsl_dspi_dt_ids[] = { { .compatible = "fsl,vf610-dspi", .data = &vf610_data, }, { .compatible = "fsl,ls1021a-v1.0-dspi", .data = &ls1021a_v1_data, }, @@ -902,10 +912,10 @@ MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids); #ifdef CONFIG_PM_SLEEP static int dspi_suspend(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct fsl_dspi *dspi = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr); - spi_master_suspend(master); + spi_controller_suspend(ctlr); clk_disable_unprepare(dspi->clk); pinctrl_pm_select_sleep_state(dev); @@ -915,8 +925,8 @@ static int dspi_suspend(struct device *dev) static int dspi_resume(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct fsl_dspi *dspi = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr); int ret; pinctrl_pm_select_default_state(dev); @@ -924,7 +934,7 @@ static int dspi_resume(struct device *dev) ret = clk_prepare_enable(dspi->clk); if (ret) return ret; - spi_master_resume(master); + spi_controller_resume(ctlr); return 0; } @@ -939,16 +949,16 @@ static const struct regmap_range dspi_volatile_ranges[] = { }; static const struct regmap_access_table dspi_volatile_table = { - .yes_ranges = dspi_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges), + .yes_ranges = dspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges), }; static const struct regmap_config dspi_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0x88, - .volatile_table = &dspi_volatile_table, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x88, + .volatile_table = &dspi_volatile_table, }; static const struct regmap_range dspi_xspi_volatile_ranges[] = { @@ -959,33 +969,34 @@ static const struct regmap_range dspi_xspi_volatile_ranges[] = { }; static const struct regmap_access_table dspi_xspi_volatile_table = { - .yes_ranges = dspi_xspi_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges), + .yes_ranges = dspi_xspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges), }; static const struct regmap_config dspi_xspi_regmap_config[] = { { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0x13c, - .volatile_table = &dspi_xspi_volatile_table, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x13c, + .volatile_table = &dspi_xspi_volatile_table, }, { - .name = "pushr", - .reg_bits = 16, - .val_bits = 16, - .reg_stride = 2, - .max_register = 0x2, + .name = "pushr", + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .max_register = 0x2, }, }; static void dspi_init(struct fsl_dspi *dspi) { - unsigned int mcr = SPI_MCR_PCSIS | - (dspi->devtype_data->xspi_mode ? SPI_MCR_XSPI : 0); + unsigned int mcr = SPI_MCR_PCSIS; - if (!spi_controller_is_slave(dspi->master)) + if (dspi->devtype_data->xspi_mode) + mcr |= SPI_MCR_XSPI; + if (!spi_controller_is_slave(dspi->ctlr)) mcr |= SPI_MCR_MASTER; regmap_write(dspi->regmap, SPI_MCR, mcr); @@ -998,34 +1009,33 @@ static void dspi_init(struct fsl_dspi *dspi) static int dspi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct spi_master *master; + const struct regmap_config *regmap_config; + struct fsl_dspi_platform_data *pdata; + struct spi_controller *ctlr; + int ret, cs_num, bus_num; struct fsl_dspi *dspi; struct resource *res; - const struct regmap_config *regmap_config; void __iomem *base; - struct fsl_dspi_platform_data *pdata; - int ret = 0, cs_num, bus_num; - master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); - if (!master) + ctlr = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); + if (!ctlr) return -ENOMEM; - dspi = spi_master_get_devdata(master); + dspi = spi_controller_get_devdata(ctlr); dspi->pdev = pdev; - dspi->master = master; + dspi->ctlr = ctlr; - master->transfer = NULL; - master->setup = dspi_setup; - master->transfer_one_message = dspi_transfer_one_message; - master->dev.of_node = pdev->dev.of_node; + ctlr->setup = dspi_setup; + ctlr->transfer_one_message = dspi_transfer_one_message; + ctlr->dev.of_node = pdev->dev.of_node; - master->cleanup = dspi_cleanup; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + ctlr->cleanup = dspi_cleanup; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; pdata = dev_get_platdata(&pdev->dev); if (pdata) { - master->num_chipselect = pdata->cs_num; - master->bus_num = pdata->bus_num; + ctlr->num_chipselect = pdata->cs_num; + ctlr->bus_num = pdata->bus_num; dspi->devtype_data = &coldfire_data; } else { @@ -1033,38 +1043,38 @@ static int dspi_probe(struct platform_device *pdev) ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num); if (ret < 0) { dev_err(&pdev->dev, "can't get spi-num-chipselects\n"); - goto out_master_put; + goto out_ctlr_put; } - master->num_chipselect = cs_num; + ctlr->num_chipselect = cs_num; ret = of_property_read_u32(np, "bus-num", &bus_num); if (ret < 0) { dev_err(&pdev->dev, "can't get bus-num\n"); - goto out_master_put; + goto out_ctlr_put; } - master->bus_num = bus_num; + ctlr->bus_num = bus_num; if (of_property_read_bool(np, "spi-slave")) - master->slave = true; + ctlr->slave = true; dspi->devtype_data = of_device_get_match_data(&pdev->dev); if (!dspi->devtype_data) { dev_err(&pdev->dev, "can't get devtype_data\n"); ret = -EFAULT; - goto out_master_put; + goto out_ctlr_put; } } if (dspi->devtype_data->xspi_mode) - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { ret = PTR_ERR(base); - goto out_master_put; + goto out_ctlr_put; } if (dspi->devtype_data->xspi_mode) @@ -1076,7 +1086,7 @@ static int dspi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to init regmap: %ld\n", PTR_ERR(dspi->regmap)); ret = PTR_ERR(dspi->regmap); - goto out_master_put; + goto out_ctlr_put; } if (dspi->devtype_data->xspi_mode) { @@ -1088,7 +1098,7 @@ static int dspi_probe(struct platform_device *pdev) "failed to init pushr regmap: %ld\n", PTR_ERR(dspi->regmap_pushr)); ret = PTR_ERR(dspi->regmap_pushr); - goto out_master_put; + goto out_ctlr_put; } } @@ -1096,18 +1106,20 @@ static int dspi_probe(struct platform_device *pdev) if (IS_ERR(dspi->clk)) { ret = PTR_ERR(dspi->clk); dev_err(&pdev->dev, "unable to get clock\n"); - goto out_master_put; + goto out_ctlr_put; } ret = clk_prepare_enable(dspi->clk); if (ret) - goto out_master_put; + goto out_ctlr_put; dspi_init(dspi); + dspi->irq = platform_get_irq(pdev, 0); - if (dspi->irq < 0) { - dev_err(&pdev->dev, "can't get platform irq\n"); - ret = dspi->irq; - goto out_clk_put; + if (dspi->irq <= 0) { + dev_info(&pdev->dev, + "can't get platform irq, using poll mode\n"); + dspi->irq = 0; + goto poll_mode; } ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, @@ -1117,6 +1129,9 @@ static int dspi_probe(struct platform_device *pdev) goto out_clk_put; } + init_waitqueue_head(&dspi->waitq); + +poll_mode: if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) { ret = dspi_request_dma(dspi, res->start); if (ret < 0) { @@ -1125,15 +1140,14 @@ static int dspi_probe(struct platform_device *pdev) } } - master->max_speed_hz = + ctlr->max_speed_hz = clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor; - init_waitqueue_head(&dspi->waitq); - platform_set_drvdata(pdev, master); + platform_set_drvdata(pdev, ctlr); - ret = spi_register_master(master); + ret = spi_register_controller(ctlr); if (ret != 0) { - dev_err(&pdev->dev, "Problem registering DSPI master\n"); + dev_err(&pdev->dev, "Problem registering DSPI ctlr\n"); goto out_clk_put; } @@ -1141,32 +1155,32 @@ static int dspi_probe(struct platform_device *pdev) out_clk_put: clk_disable_unprepare(dspi->clk); -out_master_put: - spi_master_put(master); +out_ctlr_put: + spi_controller_put(ctlr); return ret; } static int dspi_remove(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); - struct fsl_dspi *dspi = spi_master_get_devdata(master); + struct spi_controller *ctlr = platform_get_drvdata(pdev); + struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr); /* Disconnect from the SPI framework */ dspi_release_dma(dspi); clk_disable_unprepare(dspi->clk); - spi_unregister_master(dspi->master); + spi_unregister_controller(dspi->ctlr); return 0; } static struct platform_driver fsl_dspi_driver = { - .driver.name = DRIVER_NAME, - .driver.of_match_table = fsl_dspi_dt_ids, - .driver.owner = THIS_MODULE, - .driver.pm = &dspi_pm, - .probe = dspi_probe, - .remove = dspi_remove, + .driver.name = DRIVER_NAME, + .driver.of_match_table = fsl_dspi_dt_ids, + .driver.owner = THIS_MODULE, + .driver.pm = &dspi_pm, + .probe = dspi_probe, + .remove = dspi_remove, }; module_platform_driver(fsl_dspi_driver); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 3576167283dc..015a1abb6a84 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -91,9 +91,6 @@ static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg) struct mpc8xxx_spi_probe_info { struct fsl_spi_platform_data pdata; - int ngpios; - int *gpios; - bool *alow_flags; __be32 __iomem *immr_spi_cs; }; diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index 41a49b93ca60..c02e24c01136 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -206,7 +206,7 @@ static const struct fsl_qspi_devtype_data imx6sx_data = { }; static const struct fsl_qspi_devtype_data imx7d_data = { - .rxfifo = SZ_512, + .rxfifo = SZ_128, .txfifo = SZ_512, .ahb_buf_size = SZ_1K, .quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_4X_INT_CLK, @@ -860,10 +860,8 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* find the irq */ ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "failed to get the irq: %d\n", ret); + if (ret < 0) goto err_disable_clk; - } ret = devm_request_irq(dev, ret, fsl_qspi_irq_handler, 0, pdev->name, q); diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 1d9b33aa1a3b..4b80ace1d137 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -18,7 +18,7 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/fsl_devices.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> @@ -28,7 +28,6 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> @@ -481,32 +480,6 @@ static int fsl_spi_setup(struct spi_device *spi) return retval; } - if (mpc8xxx_spi->type == TYPE_GRLIB) { - if (gpio_is_valid(spi->cs_gpio)) { - int desel; - - retval = gpio_request(spi->cs_gpio, - dev_name(&spi->dev)); - if (retval) - return retval; - - desel = !(spi->mode & SPI_CS_HIGH); - retval = gpio_direction_output(spi->cs_gpio, desel); - if (retval) { - gpio_free(spi->cs_gpio); - return retval; - } - } else if (spi->cs_gpio != -ENOENT) { - if (spi->cs_gpio < 0) - return spi->cs_gpio; - return -EINVAL; - } - /* When spi->cs_gpio == -ENOENT, a hole in the phandle list - * indicates to use native chipselect if present, or allow for - * an always selected chip - */ - } - /* Initialize chipselect - might be active for SPI_CS_HIGH mode */ fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); @@ -515,12 +488,8 @@ static int fsl_spi_setup(struct spi_device *spi) static void fsl_spi_cleanup(struct spi_device *spi) { - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi); - if (mpc8xxx_spi->type == TYPE_GRLIB && gpio_is_valid(spi->cs_gpio)) - gpio_free(spi->cs_gpio); - kfree(cs); spi_set_ctldata(spi, NULL); } @@ -586,8 +555,8 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) u32 slvsel; u16 cs = spi->chip_select; - if (gpio_is_valid(spi->cs_gpio)) { - gpio_set_value(spi->cs_gpio, on); + if (spi->cs_gpiod) { + gpiod_set_value(spi->cs_gpiod, on); } else if (cs < mpc8xxx_spi->native_chipselects) { slvsel = mpc8xxx_spi_read_reg(®_base->slvsel); slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs)); @@ -718,139 +687,19 @@ err: static void fsl_spi_cs_control(struct spi_device *spi, bool on) { - struct device *dev = spi->dev.parent->parent; - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); - u16 cs = spi->chip_select; - - if (cs < pinfo->ngpios) { - int gpio = pinfo->gpios[cs]; - bool alow = pinfo->alow_flags[cs]; - - gpio_set_value(gpio, on ^ alow); + if (spi->cs_gpiod) { + gpiod_set_value(spi->cs_gpiod, on); } else { - if (WARN_ON_ONCE(cs > pinfo->ngpios || !pinfo->immr_spi_cs)) + struct device *dev = spi->dev.parent->parent; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); + struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); + + if (WARN_ON_ONCE(!pinfo->immr_spi_cs)) return; iowrite32be(on ? SPI_BOOT_SEL_BIT : 0, pinfo->immr_spi_cs); } } -static int of_fsl_spi_get_chipselects(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); - bool spisel_boot = IS_ENABLED(CONFIG_FSL_SOC) && - of_property_read_bool(np, "fsl,spisel_boot"); - int ngpios; - int i = 0; - int ret; - - ngpios = of_gpio_count(np); - ngpios = max(ngpios, 0); - if (ngpios == 0 && !spisel_boot) { - /* - * SPI w/o chip-select line. One SPI device is still permitted - * though. - */ - pdata->max_chipselect = 1; - return 0; - } - - pinfo->ngpios = ngpios; - pinfo->gpios = kmalloc_array(ngpios, sizeof(*pinfo->gpios), - GFP_KERNEL); - if (!pinfo->gpios) - return -ENOMEM; - memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios)); - - pinfo->alow_flags = kcalloc(ngpios, sizeof(*pinfo->alow_flags), - GFP_KERNEL); - if (!pinfo->alow_flags) { - ret = -ENOMEM; - goto err_alloc_flags; - } - - for (; i < ngpios; i++) { - int gpio; - enum of_gpio_flags flags; - - gpio = of_get_gpio_flags(np, i, &flags); - if (!gpio_is_valid(gpio)) { - dev_err(dev, "invalid gpio #%d: %d\n", i, gpio); - ret = gpio; - goto err_loop; - } - - ret = gpio_request(gpio, dev_name(dev)); - if (ret) { - dev_err(dev, "can't request gpio #%d: %d\n", i, ret); - goto err_loop; - } - - pinfo->gpios[i] = gpio; - pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW; - - ret = gpio_direction_output(pinfo->gpios[i], - pinfo->alow_flags[i]); - if (ret) { - dev_err(dev, - "can't set output direction for gpio #%d: %d\n", - i, ret); - goto err_loop; - } - } - -#if IS_ENABLED(CONFIG_FSL_SOC) - if (spisel_boot) { - pinfo->immr_spi_cs = ioremap(get_immrbase() + IMMR_SPI_CS_OFFSET, 4); - if (!pinfo->immr_spi_cs) { - ret = -ENOMEM; - i = ngpios - 1; - goto err_loop; - } - } -#endif - - pdata->max_chipselect = ngpios + spisel_boot; - pdata->cs_control = fsl_spi_cs_control; - - return 0; - -err_loop: - while (i >= 0) { - if (gpio_is_valid(pinfo->gpios[i])) - gpio_free(pinfo->gpios[i]); - i--; - } - - kfree(pinfo->alow_flags); - pinfo->alow_flags = NULL; -err_alloc_flags: - kfree(pinfo->gpios); - pinfo->gpios = NULL; - return ret; -} - -static int of_fsl_spi_free_chipselects(struct device *dev) -{ - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); - int i; - - if (!pinfo->gpios) - return 0; - - for (i = 0; i < pdata->max_chipselect; i++) { - if (gpio_is_valid(pinfo->gpios[i])) - gpio_free(pinfo->gpios[i]); - } - - kfree(pinfo->gpios); - kfree(pinfo->alow_flags); - return 0; -} - static int of_fsl_spi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; @@ -866,9 +715,21 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) type = fsl_spi_get_type(&ofdev->dev); if (type == TYPE_FSL) { - ret = of_fsl_spi_get_chipselects(dev); - if (ret) - goto err; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); +#if IS_ENABLED(CONFIG_FSL_SOC) + struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); + bool spisel_boot = of_property_read_bool(np, "fsl,spisel_boot"); + + if (spisel_boot) { + pinfo->immr_spi_cs = ioremap(get_immrbase() + IMMR_SPI_CS_OFFSET, 4); + if (!pinfo->immr_spi_cs) { + ret = -ENOMEM; + goto err; + } + } +#endif + + pdata->cs_control = fsl_spi_cs_control; } ret = of_address_to_resource(np, 0, &mem); @@ -891,8 +752,6 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) err: irq_dispose_mapping(irq); - if (type == TYPE_FSL) - of_fsl_spi_free_chipselects(dev); return ret; } @@ -902,8 +761,6 @@ static int of_fsl_spi_remove(struct platform_device *ofdev) struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); fsl_spi_cpm_free(mpc8xxx_spi); - if (mpc8xxx_spi->type == TYPE_FSL) - of_fsl_spi_free_chipselects(&ofdev->dev); return 0; } diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 5f0b0d5bfef4..6f3d64a1a2b3 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -534,18 +534,14 @@ static int spi_geni_probe(struct platform_device *pdev) int ret, irq; struct spi_master *spi; struct spi_geni_master *mas; - struct resource *res; void __iomem *base; struct clk *clk; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Err getting IRQ %d\n", irq); + if (irq < 0) return irq; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index eca9d52ecf65..1d3e23ec20a6 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -290,10 +290,7 @@ static int spi_gpio_request(struct device *dev, struct spi_gpio *spi_gpio) return PTR_ERR(spi_gpio->miso); spi_gpio->sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW); - if (IS_ERR(spi_gpio->sck)) - return PTR_ERR(spi_gpio->sck); - - return 0; + return PTR_ERR_OR_ZERO(spi_gpio->sck); } #ifdef CONFIG_OF @@ -410,6 +407,12 @@ static int spi_gpio_probe(struct platform_device *pdev) bb = &spi_gpio->bitbang; bb->master = master; + /* + * There is some additional business, apart from driving the CS GPIO + * line, that we need to do on selection. This makes the local + * callback for chipselect always get called. + */ + master->flags |= SPI_MASTER_GPIO_SS; bb->chipselect = spi_gpio_chipselect; bb->set_line_direction = spi_gpio_set_direction; diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c index 8f01858c0ae6..9dfe8b04e688 100644 --- a/drivers/spi/spi-lantiq-ssc.c +++ b/drivers/spi/spi-lantiq-ssc.c @@ -819,22 +819,16 @@ static int lantiq_ssc_probe(struct platform_device *pdev) } rx_irq = platform_get_irq_byname(pdev, LTQ_SPI_RX_IRQ_NAME); - if (rx_irq < 0) { - dev_err(dev, "failed to get %s\n", LTQ_SPI_RX_IRQ_NAME); + if (rx_irq < 0) return -ENXIO; - } tx_irq = platform_get_irq_byname(pdev, LTQ_SPI_TX_IRQ_NAME); - if (tx_irq < 0) { - dev_err(dev, "failed to get %s\n", LTQ_SPI_TX_IRQ_NAME); + if (tx_irq < 0) return -ENXIO; - } err_irq = platform_get_irq_byname(pdev, LTQ_SPI_ERR_IRQ_NAME); - if (err_irq < 0) { - dev_err(dev, "failed to get %s\n", LTQ_SPI_ERR_IRQ_NAME); + if (err_irq < 0) return -ENXIO; - } master = spi_alloc_master(dev, sizeof(struct lantiq_ssc_spi)); if (!master) diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c index f50779fd329c..2d436541d6c2 100644 --- a/drivers/spi/spi-lp8841-rtc.c +++ b/drivers/spi/spi-lp8841-rtc.c @@ -185,7 +185,6 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) int ret; struct spi_master *master; struct spi_lp8841_rtc *data; - void *iomem; master = spi_alloc_master(&pdev->dev, sizeof(*data)); if (!master) @@ -207,8 +206,7 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) data = spi_master_get_devdata(master); - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->iomem = devm_ioremap_resource(&pdev->dev, iomem); + data->iomem = devm_platform_ioremap_resource(pdev, 0); ret = PTR_ERR_OR_ZERO(data->iomem); if (ret) { dev_err(&pdev->dev, "failed to get IO address\n"); diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index 7fe4488ace57..f3f10443f9e2 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -503,7 +503,6 @@ static int meson_spicc_probe(struct platform_device *pdev) { struct spi_master *master; struct meson_spicc_device *spicc; - struct resource *res; int ret, irq, rate; master = spi_alloc_master(&pdev->dev, sizeof(*spicc)); @@ -517,8 +516,7 @@ static int meson_spicc_probe(struct platform_device *pdev) spicc->pdev = pdev; platform_set_drvdata(pdev, spicc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spicc->base = devm_ioremap_resource(&pdev->dev, res); + spicc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spicc->base)) { dev_err(&pdev->dev, "io resource mapping failed\n"); ret = PTR_ERR(spicc->base); diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index f7fe9b13d122..c7b039980291 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -286,7 +286,6 @@ static int meson_spifc_probe(struct platform_device *pdev) { struct spi_master *master; struct meson_spifc *spifc; - struct resource *res; void __iomem *base; unsigned int rate; int ret = 0; @@ -300,8 +299,7 @@ static int meson_spifc_probe(struct platform_device *pdev) spifc = spi_master_get_devdata(master); spifc->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(spifc->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { ret = PTR_ERR(base); goto out_err; diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 45d8a7048b6c..6888a4dcff6d 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -17,6 +17,7 @@ #include <linux/platform_data/spi-mt65xx.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> +#include <linux/dma-mapping.h> #define SPI_CFG0_REG 0x0000 #define SPI_CFG1_REG 0x0004 @@ -28,6 +29,8 @@ #define SPI_STATUS0_REG 0x001c #define SPI_PAD_SEL_REG 0x0024 #define SPI_CFG2_REG 0x0028 +#define SPI_TX_SRC_REG_64 0x002c +#define SPI_RX_DST_REG_64 0x0030 #define SPI_CFG0_SCK_HIGH_OFFSET 0 #define SPI_CFG0_SCK_LOW_OFFSET 8 @@ -73,6 +76,10 @@ #define MTK_SPI_MAX_FIFO_SIZE 32U #define MTK_SPI_PACKET_SIZE 1024 +#define MTK_SPI_32BITS_MASK (0xffffffff) + +#define DMA_ADDR_EXT_BITS (36) +#define DMA_ADDR_DEF_BITS (32) struct mtk_spi_compatible { bool need_pad_sel; @@ -80,6 +87,8 @@ struct mtk_spi_compatible { bool must_tx; /* some IC design adjust cfg register to enhance time accuracy */ bool enhance_timing; + /* some IC support DMA addr extension */ + bool dma_ext; }; struct mtk_spi { @@ -102,6 +111,13 @@ static const struct mtk_spi_compatible mt2712_compat = { .must_tx = true, }; +static const struct mtk_spi_compatible mt6765_compat = { + .need_pad_sel = true, + .must_tx = true, + .enhance_timing = true, + .dma_ext = true, +}; + static const struct mtk_spi_compatible mt7622_compat = { .must_tx = true, .enhance_timing = true, @@ -137,6 +153,9 @@ static const struct of_device_id mtk_spi_of_match[] = { { .compatible = "mediatek,mt6589-spi", .data = (void *)&mtk_common_compat, }, + { .compatible = "mediatek,mt6765-spi", + .data = (void *)&mt6765_compat, + }, { .compatible = "mediatek,mt7622-spi", .data = (void *)&mt7622_compat, }, @@ -371,10 +390,25 @@ static void mtk_spi_setup_dma_addr(struct spi_master *master, { struct mtk_spi *mdata = spi_master_get_devdata(master); - if (mdata->tx_sgl) - writel(xfer->tx_dma, mdata->base + SPI_TX_SRC_REG); - if (mdata->rx_sgl) - writel(xfer->rx_dma, mdata->base + SPI_RX_DST_REG); + if (mdata->tx_sgl) { + writel((u32)(xfer->tx_dma & MTK_SPI_32BITS_MASK), + mdata->base + SPI_TX_SRC_REG); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (mdata->dev_comp->dma_ext) + writel((u32)(xfer->tx_dma >> 32), + mdata->base + SPI_TX_SRC_REG_64); +#endif + } + + if (mdata->rx_sgl) { + writel((u32)(xfer->rx_dma & MTK_SPI_32BITS_MASK), + mdata->base + SPI_RX_DST_REG); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + if (mdata->dev_comp->dma_ext) + writel((u32)(xfer->rx_dma >> 32), + mdata->base + SPI_RX_DST_REG_64); +#endif + } } static int mtk_spi_fifo_transfer(struct spi_master *master, @@ -586,7 +620,7 @@ static int mtk_spi_probe(struct platform_device *pdev) struct mtk_spi *mdata; const struct of_device_id *of_id; struct resource *res; - int i, irq, ret; + int i, irq, ret, addr_bits; master = spi_alloc_master(&pdev->dev, sizeof(*mdata)); if (!master) { @@ -664,7 +698,6 @@ static int mtk_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "failed to get irq (%d)\n", irq); ret = irq; goto err_put_master; } @@ -753,6 +786,15 @@ static int mtk_spi_probe(struct platform_device *pdev) } } + if (mdata->dev_comp->dma_ext) + addr_bits = DMA_ADDR_EXT_BITS; + else + addr_bits = DMA_ADDR_DEF_BITS; + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(addr_bits)); + if (ret) + dev_notice(&pdev->dev, "SPI dma_set_mask(%d) failed, ret:%d\n", + addr_bits, ret); + return 0; err_disable_runtime_pm: diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index ff85982464d2..2c3b7a2a1ec7 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -327,7 +327,6 @@ static int mt7621_spi_probe(struct platform_device *pdev) struct spi_controller *master; struct mt7621_spi *rs; void __iomem *base; - struct resource *r; int status = 0; struct clk *clk; int ret; @@ -336,8 +335,7 @@ static int mt7621_spi_probe(struct platform_device *pdev) if (!match) return -EINVAL; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, r); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 7bf53cfc25d6..996c1c8a9c71 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -532,7 +532,6 @@ static int mxs_spi_probe(struct platform_device *pdev) struct spi_master *master; struct mxs_spi *spi; struct mxs_ssp *ssp; - struct resource *iores; struct clk *clk; void __iomem *base; int devid, clk_freq; @@ -545,12 +544,11 @@ static int mxs_spi_probe(struct platform_device *pdev) */ const int clk_freq_default = 160000000; - iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); if (irq_err < 0) return irq_err; - base = devm_ioremap_resource(&pdev->dev, iores); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c new file mode 100644 index 000000000000..cb52fd8008d0 --- /dev/null +++ b/drivers/spi/spi-npcm-fiu.c @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Nuvoton Technology corporation. + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/vmalloc.h> +#include <linux/regmap.h> +#include <linux/of_device.h> +#include <linux/spi/spi-mem.h> +#include <linux/mfd/syscon.h> + +/* NPCM7xx GCR module */ +#define NPCM7XX_INTCR3_OFFSET 0x9C +#define NPCM7XX_INTCR3_FIU_FIX BIT(6) + +/* Flash Interface Unit (FIU) Registers */ +#define NPCM_FIU_DRD_CFG 0x00 +#define NPCM_FIU_DWR_CFG 0x04 +#define NPCM_FIU_UMA_CFG 0x08 +#define NPCM_FIU_UMA_CTS 0x0C +#define NPCM_FIU_UMA_CMD 0x10 +#define NPCM_FIU_UMA_ADDR 0x14 +#define NPCM_FIU_PRT_CFG 0x18 +#define NPCM_FIU_UMA_DW0 0x20 +#define NPCM_FIU_UMA_DW1 0x24 +#define NPCM_FIU_UMA_DW2 0x28 +#define NPCM_FIU_UMA_DW3 0x2C +#define NPCM_FIU_UMA_DR0 0x30 +#define NPCM_FIU_UMA_DR1 0x34 +#define NPCM_FIU_UMA_DR2 0x38 +#define NPCM_FIU_UMA_DR3 0x3C +#define NPCM_FIU_MAX_REG_LIMIT 0x80 + +/* FIU Direct Read Configuration Register */ +#define NPCM_FIU_DRD_CFG_LCK BIT(31) +#define NPCM_FIU_DRD_CFG_R_BURST GENMASK(25, 24) +#define NPCM_FIU_DRD_CFG_ADDSIZ GENMASK(17, 16) +#define NPCM_FIU_DRD_CFG_DBW GENMASK(13, 12) +#define NPCM_FIU_DRD_CFG_ACCTYPE GENMASK(9, 8) +#define NPCM_FIU_DRD_CFG_RDCMD GENMASK(7, 0) +#define NPCM_FIU_DRD_ADDSIZ_SHIFT 16 +#define NPCM_FIU_DRD_DBW_SHIFT 12 +#define NPCM_FIU_DRD_ACCTYPE_SHIFT 8 + +/* FIU Direct Write Configuration Register */ +#define NPCM_FIU_DWR_CFG_LCK BIT(31) +#define NPCM_FIU_DWR_CFG_W_BURST GENMASK(25, 24) +#define NPCM_FIU_DWR_CFG_ADDSIZ GENMASK(17, 16) +#define NPCM_FIU_DWR_CFG_ABPCK GENMASK(11, 10) +#define NPCM_FIU_DWR_CFG_DBPCK GENMASK(9, 8) +#define NPCM_FIU_DWR_CFG_WRCMD GENMASK(7, 0) +#define NPCM_FIU_DWR_ADDSIZ_SHIFT 16 +#define NPCM_FIU_DWR_ABPCK_SHIFT 10 +#define NPCM_FIU_DWR_DBPCK_SHIFT 8 + +/* FIU UMA Configuration Register */ +#define NPCM_FIU_UMA_CFG_LCK BIT(31) +#define NPCM_FIU_UMA_CFG_CMMLCK BIT(30) +#define NPCM_FIU_UMA_CFG_RDATSIZ GENMASK(28, 24) +#define NPCM_FIU_UMA_CFG_DBSIZ GENMASK(23, 21) +#define NPCM_FIU_UMA_CFG_WDATSIZ GENMASK(20, 16) +#define NPCM_FIU_UMA_CFG_ADDSIZ GENMASK(13, 11) +#define NPCM_FIU_UMA_CFG_CMDSIZ BIT(10) +#define NPCM_FIU_UMA_CFG_RDBPCK GENMASK(9, 8) +#define NPCM_FIU_UMA_CFG_DBPCK GENMASK(7, 6) +#define NPCM_FIU_UMA_CFG_WDBPCK GENMASK(5, 4) +#define NPCM_FIU_UMA_CFG_ADBPCK GENMASK(3, 2) +#define NPCM_FIU_UMA_CFG_CMBPCK GENMASK(1, 0) +#define NPCM_FIU_UMA_CFG_ADBPCK_SHIFT 2 +#define NPCM_FIU_UMA_CFG_WDBPCK_SHIFT 4 +#define NPCM_FIU_UMA_CFG_DBPCK_SHIFT 6 +#define NPCM_FIU_UMA_CFG_RDBPCK_SHIFT 8 +#define NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT 11 +#define NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT 16 +#define NPCM_FIU_UMA_CFG_DBSIZ_SHIFT 21 +#define NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT 24 + +/* FIU UMA Control and Status Register */ +#define NPCM_FIU_UMA_CTS_RDYIE BIT(25) +#define NPCM_FIU_UMA_CTS_RDYST BIT(24) +#define NPCM_FIU_UMA_CTS_SW_CS BIT(16) +#define NPCM_FIU_UMA_CTS_DEV_NUM GENMASK(9, 8) +#define NPCM_FIU_UMA_CTS_EXEC_DONE BIT(0) +#define NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT 8 + +/* FIU UMA Command Register */ +#define NPCM_FIU_UMA_CMD_DUM3 GENMASK(31, 24) +#define NPCM_FIU_UMA_CMD_DUM2 GENMASK(23, 16) +#define NPCM_FIU_UMA_CMD_DUM1 GENMASK(15, 8) +#define NPCM_FIU_UMA_CMD_CMD GENMASK(7, 0) + +/* FIU UMA Address Register */ +#define NPCM_FIU_UMA_ADDR_UMA_ADDR GENMASK(31, 0) +#define NPCM_FIU_UMA_ADDR_AB3 GENMASK(31, 24) +#define NPCM_FIU_UMA_ADDR_AB2 GENMASK(23, 16) +#define NPCM_FIU_UMA_ADDR_AB1 GENMASK(15, 8) +#define NPCM_FIU_UMA_ADDR_AB0 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 0-3 Register */ +#define NPCM_FIU_UMA_DW0_WB3 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW0_WB2 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW0_WB1 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW0_WB0 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 4-7 Register */ +#define NPCM_FIU_UMA_DW1_WB7 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW1_WB6 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW1_WB5 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW1_WB4 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 8-11 Register */ +#define NPCM_FIU_UMA_DW2_WB11 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW2_WB10 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW2_WB9 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW2_WB8 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 12-15 Register */ +#define NPCM_FIU_UMA_DW3_WB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW3_WB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW3_WB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW3_WB12 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 0-3 Register */ +#define NPCM_FIU_UMA_DR0_RB3 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR0_RB2 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR0_RB1 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR0_RB0 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 4-7 Register */ +#define NPCM_FIU_UMA_DR1_RB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR1_RB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR1_RB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR1_RB12 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 8-11 Register */ +#define NPCM_FIU_UMA_DR2_RB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR2_RB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR2_RB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR2_RB12 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 12-15 Register */ +#define NPCM_FIU_UMA_DR3_RB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR3_RB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR3_RB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR3_RB12 GENMASK(7, 0) + +/* FIU Read Mode */ +enum { + DRD_SINGLE_WIRE_MODE = 0, + DRD_DUAL_IO_MODE = 1, + DRD_QUAD_IO_MODE = 2, + DRD_SPI_X_MODE = 3, +}; + +enum { + DWR_ABPCK_BIT_PER_CLK = 0, + DWR_ABPCK_2_BIT_PER_CLK = 1, + DWR_ABPCK_4_BIT_PER_CLK = 2, +}; + +enum { + DWR_DBPCK_BIT_PER_CLK = 0, + DWR_DBPCK_2_BIT_PER_CLK = 1, + DWR_DBPCK_4_BIT_PER_CLK = 2, +}; + +#define NPCM_FIU_DRD_16_BYTE_BURST 0x3000000 +#define NPCM_FIU_DWR_16_BYTE_BURST 0x3000000 + +#define MAP_SIZE_128MB 0x8000000 +#define MAP_SIZE_16MB 0x1000000 +#define MAP_SIZE_8MB 0x800000 + +#define NUM_BITS_IN_BYTE 8 +#define FIU_DRD_MAX_DUMMY_NUMBER 3 +#define NPCM_MAX_CHIP_NUM 4 +#define CHUNK_SIZE 16 +#define UMA_MICRO_SEC_TIMEOUT 150 + +enum { + FIU0 = 0, + FIU3, + FIUX, +}; + +struct npcm_fiu_info { + char *name; + u32 fiu_id; + u32 max_map_size; + u32 max_cs; +}; + +struct fiu_data { + const struct npcm_fiu_info *npcm_fiu_data_info; + int fiu_max; +}; + +static const struct npcm_fiu_info npxm7xx_fiu_info[] = { + {.name = "FIU0", .fiu_id = FIU0, + .max_map_size = MAP_SIZE_128MB, .max_cs = 2}, + {.name = "FIU3", .fiu_id = FIU3, + .max_map_size = MAP_SIZE_128MB, .max_cs = 4}, + {.name = "FIUX", .fiu_id = FIUX, + .max_map_size = MAP_SIZE_16MB, .max_cs = 2} }; + +static const struct fiu_data npxm7xx_fiu_data = { + .npcm_fiu_data_info = npxm7xx_fiu_info, + .fiu_max = 3, +}; + +struct npcm_fiu_spi; + +struct npcm_fiu_chip { + void __iomem *flash_region_mapped_ptr; + struct npcm_fiu_spi *fiu; + unsigned long clkrate; + u32 chipselect; +}; + +struct npcm_fiu_spi { + struct npcm_fiu_chip chip[NPCM_MAX_CHIP_NUM]; + const struct npcm_fiu_info *info; + struct spi_mem_op drd_op; + struct resource *res_mem; + struct regmap *regmap; + unsigned long clkrate; + struct device *dev; + struct clk *clk; + bool spix_mode; +}; + +static const struct regmap_config npcm_mtd_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = NPCM_FIU_MAX_REG_LIMIT, +}; + +static void npcm_fiu_set_drd(struct npcm_fiu_spi *fiu, + const struct spi_mem_op *op) +{ + regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_ACCTYPE, + ilog2(op->addr.buswidth) << + NPCM_FIU_DRD_ACCTYPE_SHIFT); + fiu->drd_op.addr.buswidth = op->addr.buswidth; + regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_DBW, + ((op->dummy.nbytes * ilog2(op->addr.buswidth)) + / NUM_BITS_IN_BYTE) << NPCM_FIU_DRD_DBW_SHIFT); + fiu->drd_op.dummy.nbytes = op->dummy.nbytes; + regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_RDCMD, op->cmd.opcode); + fiu->drd_op.cmd.opcode = op->cmd.opcode; + regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_ADDSIZ, + (op->addr.nbytes - 3) << NPCM_FIU_DRD_ADDSIZ_SHIFT); + fiu->drd_op.addr.nbytes = op->addr.nbytes; +} + +static ssize_t npcm_fiu_direct_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct npcm_fiu_spi *fiu = + spi_controller_get_devdata(desc->mem->spi->master); + struct npcm_fiu_chip *chip = &fiu->chip[desc->mem->spi->chip_select]; + void __iomem *src = (void __iomem *)(chip->flash_region_mapped_ptr + + offs); + u8 *buf_rx = buf; + u32 i; + + if (fiu->spix_mode) { + for (i = 0 ; i < len ; i++) + *(buf_rx + i) = ioread8(src + i); + } else { + if (desc->info.op_tmpl.addr.buswidth != fiu->drd_op.addr.buswidth || + desc->info.op_tmpl.dummy.nbytes != fiu->drd_op.dummy.nbytes || + desc->info.op_tmpl.cmd.opcode != fiu->drd_op.cmd.opcode || + desc->info.op_tmpl.addr.nbytes != fiu->drd_op.addr.nbytes) + npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); + + memcpy_fromio(buf_rx, src, len); + } + + return len; +} + +static ssize_t npcm_fiu_direct_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) +{ + struct npcm_fiu_spi *fiu = + spi_controller_get_devdata(desc->mem->spi->master); + struct npcm_fiu_chip *chip = &fiu->chip[desc->mem->spi->chip_select]; + void __iomem *dst = (void __iomem *)(chip->flash_region_mapped_ptr + + offs); + const u8 *buf_tx = buf; + u32 i; + + if (fiu->spix_mode) + for (i = 0 ; i < len ; i++) + iowrite8(*(buf_tx + i), dst + i); + else + memcpy_toio(dst, buf_tx, len); + + return len; +} + +static int npcm_fiu_uma_read(struct spi_mem *mem, + const struct spi_mem_op *op, u32 addr, + bool is_address_size, u8 *data, u32 data_size) +{ + struct npcm_fiu_spi *fiu = + spi_controller_get_devdata(mem->spi->master); + u32 uma_cfg = BIT(10); + u32 data_reg[4]; + int ret; + u32 val; + u32 i; + + regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_DEV_NUM, + (mem->spi->chip_select << + NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); + regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD, + NPCM_FIU_UMA_CMD_CMD, op->cmd.opcode); + + if (is_address_size) { + uma_cfg |= ilog2(op->cmd.buswidth); + uma_cfg |= ilog2(op->addr.buswidth) + << NPCM_FIU_UMA_CFG_ADBPCK_SHIFT; + uma_cfg |= ilog2(op->dummy.buswidth) + << NPCM_FIU_UMA_CFG_DBPCK_SHIFT; + uma_cfg |= ilog2(op->data.buswidth) + << NPCM_FIU_UMA_CFG_RDBPCK_SHIFT; + uma_cfg |= op->dummy.nbytes << NPCM_FIU_UMA_CFG_DBSIZ_SHIFT; + uma_cfg |= op->addr.nbytes << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT; + regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, addr); + } else { + regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, 0x0); + } + + uma_cfg |= data_size << NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT; + regmap_write(fiu->regmap, NPCM_FIU_UMA_CFG, uma_cfg); + regmap_write_bits(fiu->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_EXEC_DONE, + NPCM_FIU_UMA_CTS_EXEC_DONE); + ret = regmap_read_poll_timeout(fiu->regmap, NPCM_FIU_UMA_CTS, val, + (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, + UMA_MICRO_SEC_TIMEOUT); + if (ret) + return ret; + + if (data_size) { + for (i = 0; i < DIV_ROUND_UP(data_size, 4); i++) + regmap_read(fiu->regmap, NPCM_FIU_UMA_DR0 + (i * 4), + &data_reg[i]); + memcpy(data, data_reg, data_size); + } + + return 0; +} + +static int npcm_fiu_uma_write(struct spi_mem *mem, + const struct spi_mem_op *op, u8 cmd, + bool is_address_size, u8 *data, u32 data_size) +{ + struct npcm_fiu_spi *fiu = + spi_controller_get_devdata(mem->spi->master); + u32 uma_cfg = BIT(10); + u32 data_reg[4] = {0}; + u32 val; + u32 i; + + regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_DEV_NUM, + (mem->spi->chip_select << + NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); + + regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD, + NPCM_FIU_UMA_CMD_CMD, cmd); + + if (data_size) { + memcpy(data_reg, data, data_size); + for (i = 0; i < DIV_ROUND_UP(data_size, 4); i++) + regmap_write(fiu->regmap, NPCM_FIU_UMA_DW0 + (i * 4), + data_reg[i]); + } + + if (is_address_size) { + uma_cfg |= ilog2(op->cmd.buswidth); + uma_cfg |= ilog2(op->addr.buswidth) << + NPCM_FIU_UMA_CFG_ADBPCK_SHIFT; + uma_cfg |= ilog2(op->data.buswidth) << + NPCM_FIU_UMA_CFG_WDBPCK_SHIFT; + uma_cfg |= op->addr.nbytes << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT; + regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, op->addr.val); + } else { + regmap_write(fiu->regmap, NPCM_FIU_UMA_ADDR, 0x0); + } + + uma_cfg |= (data_size << NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT); + regmap_write(fiu->regmap, NPCM_FIU_UMA_CFG, uma_cfg); + + regmap_write_bits(fiu->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_EXEC_DONE, + NPCM_FIU_UMA_CTS_EXEC_DONE); + + return regmap_read_poll_timeout(fiu->regmap, NPCM_FIU_UMA_CTS, val, + (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, + UMA_MICRO_SEC_TIMEOUT); +} + +static int npcm_fiu_manualwrite(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct npcm_fiu_spi *fiu = + spi_controller_get_devdata(mem->spi->master); + u8 *data = (u8 *)op->data.buf.out; + u32 num_data_chunks; + u32 remain_data; + u32 idx = 0; + int ret; + + num_data_chunks = op->data.nbytes / CHUNK_SIZE; + remain_data = op->data.nbytes % CHUNK_SIZE; + + regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_DEV_NUM, + (mem->spi->chip_select << + NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); + regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_SW_CS, 0); + + ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, true, NULL, 0); + if (ret) + return ret; + + /* Starting the data writing loop in multiples of 8 */ + for (idx = 0; idx < num_data_chunks; ++idx) { + ret = npcm_fiu_uma_write(mem, op, data[0], false, + &data[1], CHUNK_SIZE - 1); + if (ret) + return ret; + + data += CHUNK_SIZE; + } + + /* Handling chunk remains */ + if (remain_data > 0) { + ret = npcm_fiu_uma_write(mem, op, data[0], false, + &data[1], remain_data - 1); + if (ret) + return ret; + } + + regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_SW_CS, NPCM_FIU_UMA_CTS_SW_CS); + + return 0; +} + +static int npcm_fiu_read(struct spi_mem *mem, const struct spi_mem_op *op) +{ + u8 *data = op->data.buf.in; + int i, readlen, currlen; + u8 *buf_ptr; + u32 addr; + int ret; + + i = 0; + currlen = op->data.nbytes; + + do { + addr = ((u32)op->addr.val + i); + if (currlen < 16) + readlen = currlen; + else + readlen = 16; + + buf_ptr = data + i; + ret = npcm_fiu_uma_read(mem, op, addr, true, buf_ptr, + readlen); + if (ret) + return ret; + + i += readlen; + currlen -= 16; + } while (currlen > 0); + + return 0; +} + +static void npcm_fiux_set_direct_wr(struct npcm_fiu_spi *fiu) +{ + regmap_write(fiu->regmap, NPCM_FIU_DWR_CFG, + NPCM_FIU_DWR_16_BYTE_BURST); + regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, + NPCM_FIU_DWR_CFG_ABPCK, + DWR_ABPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_ABPCK_SHIFT); + regmap_update_bits(fiu->regmap, NPCM_FIU_DWR_CFG, + NPCM_FIU_DWR_CFG_DBPCK, + DWR_DBPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_DBPCK_SHIFT); +} + +static void npcm_fiux_set_direct_rd(struct npcm_fiu_spi *fiu) +{ + u32 rx_dummy = 0; + + regmap_write(fiu->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_16_BYTE_BURST); + regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_ACCTYPE, + DRD_SPI_X_MODE << NPCM_FIU_DRD_ACCTYPE_SHIFT); + regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_DBW, + rx_dummy << NPCM_FIU_DRD_DBW_SHIFT); +} + +static int npcm_fiu_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct npcm_fiu_spi *fiu = + spi_controller_get_devdata(mem->spi->master); + struct npcm_fiu_chip *chip = &fiu->chip[mem->spi->chip_select]; + int ret = 0; + u8 *buf; + + dev_dbg(fiu->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, op->addr.val, + op->data.nbytes); + + if (fiu->spix_mode || op->addr.nbytes > 4) + return -ENOTSUPP; + + if (fiu->clkrate != chip->clkrate) { + ret = clk_set_rate(fiu->clk, chip->clkrate); + if (ret < 0) + dev_warn(fiu->dev, "Failed setting %lu frequency, stay at %lu frequency\n", + chip->clkrate, fiu->clkrate); + else + fiu->clkrate = chip->clkrate; + } + + if (op->data.dir == SPI_MEM_DATA_IN) { + if (!op->addr.nbytes) { + buf = op->data.buf.in; + ret = npcm_fiu_uma_read(mem, op, op->addr.val, false, + buf, op->data.nbytes); + } else { + ret = npcm_fiu_read(mem, op); + } + } else { + if (!op->addr.nbytes && !op->data.nbytes) + ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, + NULL, 0); + if (op->addr.nbytes && !op->data.nbytes) { + int i; + u8 buf_addr[4]; + u32 addr = op->addr.val; + + for (i = op->addr.nbytes - 1; i >= 0; i--) { + buf_addr[i] = addr & 0xff; + addr >>= 8; + } + ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, + buf_addr, op->addr.nbytes); + } + if (!op->addr.nbytes && op->data.nbytes) + ret = npcm_fiu_uma_write(mem, op, op->cmd.opcode, false, + (u8 *)op->data.buf.out, + op->data.nbytes); + if (op->addr.nbytes && op->data.nbytes) + ret = npcm_fiu_manualwrite(mem, op); + } + + return ret; +} + +static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ + struct npcm_fiu_spi *fiu = + spi_controller_get_devdata(desc->mem->spi->master); + struct npcm_fiu_chip *chip = &fiu->chip[desc->mem->spi->chip_select]; + struct regmap *gcr_regmap; + + if (!fiu->res_mem) { + dev_warn(fiu->dev, "Reserved memory not defined, direct read disabled\n"); + desc->nodirmap = true; + return 0; + } + + if (!fiu->spix_mode && + desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { + desc->nodirmap = true; + return 0; + } + + if (!chip->flash_region_mapped_ptr) { + chip->flash_region_mapped_ptr = + devm_ioremap_nocache(fiu->dev, (fiu->res_mem->start + + (fiu->info->max_map_size * + desc->mem->spi->chip_select)), + (u32)desc->info.length); + if (!chip->flash_region_mapped_ptr) { + dev_warn(fiu->dev, "Error mapping memory region, direct read disabled\n"); + desc->nodirmap = true; + return 0; + } + } + + if (of_device_is_compatible(fiu->dev->of_node, "nuvoton,npcm750-fiu")) { + gcr_regmap = + syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); + if (IS_ERR(gcr_regmap)) { + dev_warn(fiu->dev, "Didn't find nuvoton,npcm750-gcr, direct read disabled\n"); + desc->nodirmap = true; + return 0; + } + regmap_update_bits(gcr_regmap, NPCM7XX_INTCR3_OFFSET, + NPCM7XX_INTCR3_FIU_FIX, + NPCM7XX_INTCR3_FIU_FIX); + } + + if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) { + if (!fiu->spix_mode) + npcm_fiu_set_drd(fiu, &desc->info.op_tmpl); + else + npcm_fiux_set_direct_rd(fiu); + + } else { + npcm_fiux_set_direct_wr(fiu); + } + + return 0; +} + +static int npcm_fiu_setup(struct spi_device *spi) +{ + struct spi_controller *ctrl = spi->master; + struct npcm_fiu_spi *fiu = spi_controller_get_devdata(ctrl); + struct npcm_fiu_chip *chip; + + chip = &fiu->chip[spi->chip_select]; + chip->fiu = fiu; + chip->chipselect = spi->chip_select; + chip->clkrate = spi->max_speed_hz; + + fiu->clkrate = clk_get_rate(fiu->clk); + + return 0; +} + +static const struct spi_controller_mem_ops npcm_fiu_mem_ops = { + .exec_op = npcm_fiu_exec_op, + .dirmap_create = npcm_fiu_dirmap_create, + .dirmap_read = npcm_fiu_direct_read, + .dirmap_write = npcm_fiu_direct_write, +}; + +static const struct of_device_id npcm_fiu_dt_ids[] = { + { .compatible = "nuvoton,npcm750-fiu", .data = &npxm7xx_fiu_data }, + { /* sentinel */ } +}; + +static int npcm_fiu_probe(struct platform_device *pdev) +{ + const struct fiu_data *fiu_data_match; + const struct of_device_id *match; + struct device *dev = &pdev->dev; + struct spi_controller *ctrl; + struct npcm_fiu_spi *fiu; + void __iomem *regbase; + struct resource *res; + int ret; + int id; + + ctrl = spi_alloc_master(dev, sizeof(*fiu)); + if (!ctrl) + return -ENOMEM; + + fiu = spi_controller_get_devdata(ctrl); + + match = of_match_device(npcm_fiu_dt_ids, dev); + if (!match || !match->data) { + dev_err(dev, "No compatible OF match\n"); + return -ENODEV; + } + + fiu_data_match = match->data; + id = of_alias_get_id(dev->of_node, "fiu"); + if (id < 0 || id >= fiu_data_match->fiu_max) { + dev_err(dev, "Invalid platform device id: %d\n", id); + return -EINVAL; + } + + fiu->info = &fiu_data_match->npcm_fiu_data_info[id]; + + platform_set_drvdata(pdev, fiu); + fiu->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); + regbase = devm_ioremap_resource(dev, res); + if (IS_ERR(regbase)) + return PTR_ERR(regbase); + + fiu->regmap = devm_regmap_init_mmio(dev, regbase, + &npcm_mtd_regmap_config); + if (IS_ERR(fiu->regmap)) { + dev_err(dev, "Failed to create regmap\n"); + return PTR_ERR(fiu->regmap); + } + + fiu->res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "memory"); + fiu->clk = devm_clk_get(dev, NULL); + if (IS_ERR(fiu->clk)) + return PTR_ERR(fiu->clk); + + fiu->spix_mode = of_property_read_bool(dev->of_node, + "nuvoton,spix-mode"); + + platform_set_drvdata(pdev, fiu); + clk_prepare_enable(fiu->clk); + + ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD + | SPI_TX_DUAL | SPI_TX_QUAD; + ctrl->setup = npcm_fiu_setup; + ctrl->bus_num = -1; + ctrl->mem_ops = &npcm_fiu_mem_ops; + ctrl->num_chipselect = fiu->info->max_cs; + ctrl->dev.of_node = dev->of_node; + + ret = devm_spi_register_master(dev, ctrl); + if (ret) + return ret; + + return 0; +} + +static int npcm_fiu_remove(struct platform_device *pdev) +{ + struct npcm_fiu_spi *fiu = platform_get_drvdata(pdev); + + clk_disable_unprepare(fiu->clk); + return 0; +} + +MODULE_DEVICE_TABLE(of, npcm_fiu_dt_ids); + +static struct platform_driver npcm_fiu_driver = { + .driver = { + .name = "NPCM-FIU", + .bus = &platform_bus_type, + .of_match_table = npcm_fiu_dt_ids, + }, + .probe = npcm_fiu_probe, + .remove = npcm_fiu_remove, +}; +module_platform_driver(npcm_fiu_driver); + +MODULE_DESCRIPTION("Nuvoton FLASH Interface Unit SPI Controller Driver"); +MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index 734a2b956959..b191d57d1dc0 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -341,7 +341,6 @@ static int npcm_pspi_probe(struct platform_device *pdev) { struct npcm_pspi *priv; struct spi_master *master; - struct resource *res; unsigned long clk_hz; struct device_node *np = pdev->dev.of_node; int num_cs, i; @@ -368,8 +367,7 @@ static int npcm_pspi_probe(struct platform_device *pdev) priv->is_save_param = false; priv->id = pdev->id; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto out_master_put; @@ -388,7 +386,6 @@ static int npcm_pspi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "failed to get IRQ\n"); ret = irq; goto out_disable_clk; } diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index 37e2034ad4d5..61400358f4be 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -327,7 +327,6 @@ static int nuc900_spi_probe(struct platform_device *pdev) { struct nuc900_spi *hw; struct spi_master *master; - struct resource *res; int err = 0; master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi)); @@ -358,8 +357,7 @@ static int nuc900_spi_probe(struct platform_device *pdev) hw->bitbang.chipselect = nuc900_spi_chipsel; hw->bitbang.txrx_bufs = nuc900_spi_txrx; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hw->regs = devm_ioremap_resource(&pdev->dev, res); + hw->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hw->regs)) { err = PTR_ERR(hw->regs); goto err_pdata; @@ -367,7 +365,6 @@ static int nuc900_spi_probe(struct platform_device *pdev) hw->irq = platform_get_irq(pdev, 0); if (hw->irq < 0) { - dev_err(&pdev->dev, "No IRQ specified\n"); err = -ENOENT; goto err_pdata; } diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 8894f98cc99c..501b923f2c27 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -1007,10 +1007,8 @@ static int nxp_fspi_probe(struct platform_device *pdev) /* find the irq */ ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "failed to get the irq: %d\n", ret); + if (ret < 0) goto err_disable_clk; - } ret = devm_request_irq(dev, ret, nxp_fspi_irq_handler, 0, pdev->name, f); diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index bbc4ba66571f..e2331eb7b47a 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -240,7 +240,6 @@ static int tiny_spi_probe(struct platform_device *pdev) struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev); struct tiny_spi *hw; struct spi_master *master; - struct resource *res; unsigned int i; int err = -ENODEV; @@ -264,8 +263,7 @@ static int tiny_spi_probe(struct platform_device *pdev) hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; /* find and map our resources */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hw->base = devm_ioremap_resource(&pdev->dev, res); + hw->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hw->base)) { err = PTR_ERR(hw->base); goto exit; diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c index b635526ad414..86ad17597f5f 100644 --- a/drivers/spi/spi-pic32-sqi.c +++ b/drivers/spi/spi-pic32-sqi.c @@ -570,7 +570,6 @@ static int pic32_sqi_probe(struct platform_device *pdev) { struct spi_master *master; struct pic32_sqi *sqi; - struct resource *reg; int ret; master = spi_alloc_master(&pdev->dev, sizeof(*sqi)); @@ -580,8 +579,7 @@ static int pic32_sqi_probe(struct platform_device *pdev) sqi = spi_master_get_devdata(master); sqi->master = master; - reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sqi->regs = devm_ioremap_resource(&pdev->dev, reg); + sqi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sqi->regs)) { ret = PTR_ERR(sqi->regs); goto err_free_master; @@ -590,7 +588,6 @@ static int pic32_sqi_probe(struct platform_device *pdev) /* irq */ sqi->irq = platform_get_irq(pdev, 0); if (sqi->irq < 0) { - dev_err(&pdev->dev, "no irq found\n"); ret = sqi->irq; goto err_free_master; } diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 10cebeaa1e6b..69f517ec59c6 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -711,22 +711,16 @@ static int pic32_spi_hw_probe(struct platform_device *pdev, /* get irq resources: err-irq, rx-irq, tx-irq */ pic32s->fault_irq = platform_get_irq_byname(pdev, "fault"); - if (pic32s->fault_irq < 0) { - dev_err(&pdev->dev, "fault-irq not found\n"); + if (pic32s->fault_irq < 0) return pic32s->fault_irq; - } pic32s->rx_irq = platform_get_irq_byname(pdev, "rx"); - if (pic32s->rx_irq < 0) { - dev_err(&pdev->dev, "rx-irq not found\n"); + if (pic32s->rx_irq < 0) return pic32s->rx_irq; - } pic32s->tx_irq = platform_get_irq_byname(pdev, "tx"); - if (pic32s->tx_irq < 0) { - dev_err(&pdev->dev, "tx-irq not found\n"); + if (pic32s->tx_irq < 0) return pic32s->tx_irq; - } /* get clock */ pic32s->clk = devm_clk_get(&pdev->dev, "mck0"); diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index fc7ab4b26880..bb6a14d1ab0f 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1457,6 +1457,14 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { PCI_VDEVICE(INTEL, 0x02aa), LPSS_CNL_SSP }, { PCI_VDEVICE(INTEL, 0x02ab), LPSS_CNL_SSP }, { PCI_VDEVICE(INTEL, 0x02fb), LPSS_CNL_SSP }, + /* TGL-LP */ + { PCI_VDEVICE(INTEL, 0xa0aa), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0xa0ab), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0xa0de), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0xa0df), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0xa0fb), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0xa0fd), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0xa0fe), LPSS_CNL_SSP }, { }, }; @@ -1831,14 +1839,16 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) status = devm_spi_register_controller(&pdev->dev, controller); if (status != 0) { dev_err(&pdev->dev, "problem registering spi controller\n"); - goto out_error_clock_enabled; + goto out_error_pm_runtime_enabled; } return status; -out_error_clock_enabled: +out_error_pm_runtime_enabled: pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); + +out_error_clock_enabled: clk_disable_unprepare(ssp->clk); out_error_dma_irq_alloc: diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index e0f061139c8f..250fd60e1678 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -424,7 +424,6 @@ static int qcom_qspi_probe(struct platform_device *pdev) { int ret; struct device *dev; - struct resource *res; struct spi_master *master; struct qcom_qspi *ctrl; @@ -440,8 +439,7 @@ static int qcom_qspi_probe(struct platform_device *pdev) spin_lock_init(&ctrl->lock); ctrl->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctrl->base = devm_ioremap_resource(dev, res); + ctrl->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctrl->base)) { ret = PTR_ERR(ctrl->base); goto exit_probe_master_put; @@ -454,10 +452,8 @@ static int qcom_qspi_probe(struct platform_device *pdev) goto exit_probe_master_put; ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "Failed to get irq %d\n", ret); + if (ret < 0) goto exit_probe_master_put; - } ret = devm_request_irq(dev, ret, qcom_qspi_irq, IRQF_TRIGGER_HIGH, dev_name(dev), ctrl); if (ret) { diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 51f03d977ad6..4c9620e0d18c 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -135,12 +135,10 @@ static int rb4xx_spi_probe(struct platform_device *pdev) struct spi_master *master; struct clk *ahb_clk; struct rb4xx_spi *rbspi; - struct resource *r; int err; void __iomem *spi_base; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spi_base = devm_ioremap_resource(&pdev->dev, r); + spi_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spi_base)) return PTR_ERR(spi_base); diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 48d8dff05a3a..2d6e37f25e2d 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -487,7 +487,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) struct s3c2410_spi_info *pdata; struct s3c24xx_spi *hw; struct spi_master *master; - struct resource *res; int err = 0; master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); @@ -536,8 +535,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); /* find and map our resources */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hw->regs = devm_ioremap_resource(&pdev->dev, res); + hw->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hw->regs)) { err = PTR_ERR(hw->regs); goto err_no_pdata; @@ -545,7 +543,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) hw->irq = platform_get_irq(pdev, 0); if (hw->irq < 0) { - dev_err(&pdev->dev, "No IRQ specified\n"); err = -ENOENT; goto err_no_pdata; } diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index b50bdbc27e58..8f134735291f 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1297,7 +1297,6 @@ static void sh_msiof_release_dma(struct sh_msiof_spi_priv *p) static int sh_msiof_spi_probe(struct platform_device *pdev) { - struct resource *r; struct spi_controller *ctlr; const struct sh_msiof_chipdata *chipdata; struct sh_msiof_spi_info *info; @@ -1346,13 +1345,11 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) i = platform_get_irq(pdev, 0); if (i < 0) { - dev_err(&pdev->dev, "cannot get IRQ\n"); ret = i; goto err1; } - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - p->mapbase = devm_ioremap_resource(&pdev->dev, r); + p->mapbase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p->mapbase)) { ret = PTR_ERR(p->mapbase); goto err1; diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index f1ee58208216..20bdae5fdf3b 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -437,10 +437,8 @@ static int spi_sh_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "platform_get_irq error: %d\n", irq); + if (irq < 0) return irq; - } master = spi_alloc_master(&pdev->dev, sizeof(struct spi_sh_data)); if (master == NULL) { diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c index 93ec2c6cdbfd..35254bdc42c4 100644 --- a/drivers/spi/spi-sifive.c +++ b/drivers/spi/spi-sifive.c @@ -292,7 +292,6 @@ sifive_spi_transfer_one(struct spi_master *master, struct spi_device *device, static int sifive_spi_probe(struct platform_device *pdev) { struct sifive_spi *spi; - struct resource *res; int ret, irq, num_cs; u32 cs_bits, max_bits_per_word; struct spi_master *master; @@ -307,8 +306,7 @@ static int sifive_spi_probe(struct platform_device *pdev) init_completion(&spi->done); platform_set_drvdata(pdev, master); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spi->regs = devm_ioremap_resource(&pdev->dev, res); + spi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spi->regs)) { ret = PTR_ERR(spi->regs); goto put_master; @@ -323,7 +321,6 @@ static int sifive_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "Unable to find interrupt\n"); ret = irq; goto put_master; } diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 71b882ab31b9..e1e639191557 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -1070,7 +1070,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) { struct sirfsoc_spi *sspi; struct spi_master *master; - struct resource *mem_res; const struct sirf_spi_comp_data *spi_comp_data; int irq; int ret; @@ -1097,8 +1096,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) sspi->fifo_level_chk_mask = (sspi->fifo_size / 4) - 1; sspi->dat_max_frm_len = spi_comp_data->dat_max_frm_len; sspi->fifo_size = spi_comp_data->fifo_size; - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sspi->base = devm_ioremap_resource(&pdev->dev, mem_res); + sspi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sspi->base)) { ret = PTR_ERR(sspi->base); goto free_master; diff --git a/drivers/spi/spi-slave-mt27xx.c b/drivers/spi/spi-slave-mt27xx.c index d1075433f6a6..61bc43b0fe57 100644 --- a/drivers/spi/spi-slave-mt27xx.c +++ b/drivers/spi/spi-slave-mt27xx.c @@ -410,7 +410,6 @@ static int mtk_spi_slave_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "failed to get irq (%d)\n", irq); ret = irq; goto err_put_ctlr; } diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index df5960bddfe6..9a051286f120 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -86,6 +86,7 @@ #define BIT_WDG_EN BIT(2) /* Definition of PMIC reset status register */ +#define HWRST_STATUS_SECURITY 0x02 #define HWRST_STATUS_RECOVERY 0x20 #define HWRST_STATUS_NORMAL 0x40 #define HWRST_STATUS_ALARM 0x50 @@ -97,6 +98,8 @@ #define HWRST_STATUS_AUTODLOADER 0xa0 #define HWRST_STATUS_IQMODE 0xb0 #define HWRST_STATUS_SPRDISK 0xc0 +#define HWRST_STATUS_FACTORYTEST 0xe0 +#define HWRST_STATUS_WATCHDOG 0xf0 /* Use default timeout 50 ms that converts to watchdog values */ #define WDG_LOAD_VAL ((50 * 1000) / 32768) @@ -162,14 +165,16 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val) int read_timeout = ADI_READ_TIMEOUT; unsigned long flags; u32 val, rd_addr; - int ret; - - ret = hwspin_lock_timeout_irqsave(sadi->hwlock, - ADI_HWSPINLOCK_TIMEOUT, - &flags); - if (ret) { - dev_err(sadi->dev, "get the hw lock failed\n"); - return ret; + int ret = 0; + + if (sadi->hwlock) { + ret = hwspin_lock_timeout_irqsave(sadi->hwlock, + ADI_HWSPINLOCK_TIMEOUT, + &flags); + if (ret) { + dev_err(sadi->dev, "get the hw lock failed\n"); + return ret; + } } /* @@ -216,7 +221,8 @@ static int sprd_adi_read(struct sprd_adi *sadi, u32 reg_paddr, u32 *read_val) *read_val = val & RD_VALUE_MASK; out: - hwspin_unlock_irqrestore(sadi->hwlock, &flags); + if (sadi->hwlock) + hwspin_unlock_irqrestore(sadi->hwlock, &flags); return ret; } @@ -227,12 +233,14 @@ static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val) unsigned long flags; int ret; - ret = hwspin_lock_timeout_irqsave(sadi->hwlock, - ADI_HWSPINLOCK_TIMEOUT, - &flags); - if (ret) { - dev_err(sadi->dev, "get the hw lock failed\n"); - return ret; + if (sadi->hwlock) { + ret = hwspin_lock_timeout_irqsave(sadi->hwlock, + ADI_HWSPINLOCK_TIMEOUT, + &flags); + if (ret) { + dev_err(sadi->dev, "get the hw lock failed\n"); + return ret; + } } ret = sprd_adi_drain_fifo(sadi); @@ -258,7 +266,8 @@ static int sprd_adi_write(struct sprd_adi *sadi, u32 reg_paddr, u32 val) } out: - hwspin_unlock_irqrestore(sadi->hwlock, &flags); + if (sadi->hwlock) + hwspin_unlock_irqrestore(sadi->hwlock, &flags); return ret; } @@ -307,6 +316,18 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr, return 0; } +static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi) +{ +#ifdef CONFIG_SPRD_WATCHDOG + u32 val; + + /* Set default watchdog reboot mode */ + sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val); + val |= HWRST_STATUS_WATCHDOG; + sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val); +#endif +} + static int sprd_adi_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd) { @@ -336,11 +357,16 @@ static int sprd_adi_restart_handler(struct notifier_block *this, reboot_mode = HWRST_STATUS_IQMODE; else if (!strncmp(cmd, "sprdisk", 7)) reboot_mode = HWRST_STATUS_SPRDISK; + else if (!strncmp(cmd, "tospanic", 8)) + reboot_mode = HWRST_STATUS_SECURITY; + else if (!strncmp(cmd, "factorytest", 11)) + reboot_mode = HWRST_STATUS_FACTORYTEST; else reboot_mode = HWRST_STATUS_NORMAL; /* Record the reboot mode */ sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val); + val &= ~HWRST_STATUS_WATCHDOG; val |= reboot_mode; sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val); @@ -380,9 +406,6 @@ static void sprd_adi_hw_init(struct sprd_adi *sadi) const __be32 *list; u32 tmp; - /* Address bits select default 12 bits */ - writel_relaxed(0, sadi->base + REG_ADI_CTRL0); - /* Set all channels as default priority */ writel_relaxed(0, sadi->base + REG_ADI_CHN_PRIL); writel_relaxed(0, sadi->base + REG_ADI_CHN_PRIH); @@ -459,19 +482,30 @@ static int sprd_adi_probe(struct platform_device *pdev) sadi->slave_pbase = res->start + ADI_SLAVE_OFFSET; sadi->ctlr = ctlr; sadi->dev = &pdev->dev; - ret = of_hwspin_lock_get_id_byname(np, "adi"); - if (ret < 0) { - dev_err(&pdev->dev, "can not get the hardware spinlock\n"); - goto put_ctlr; - } - - sadi->hwlock = devm_hwspin_lock_request_specific(&pdev->dev, ret); - if (!sadi->hwlock) { - ret = -ENXIO; - goto put_ctlr; + ret = of_hwspin_lock_get_id(np, 0); + if (ret > 0 || (IS_ENABLED(CONFIG_HWSPINLOCK) && ret == 0)) { + sadi->hwlock = + devm_hwspin_lock_request_specific(&pdev->dev, ret); + if (!sadi->hwlock) { + ret = -ENXIO; + goto put_ctlr; + } + } else { + switch (ret) { + case -ENOENT: + dev_info(&pdev->dev, "no hardware spinlock supplied\n"); + break; + default: + dev_err(&pdev->dev, + "failed to find hwlock id, %d\n", ret); + /* fall-through */ + case -EPROBE_DEFER: + goto put_ctlr; + } } sprd_adi_hw_init(sadi); + sprd_adi_set_wdt_rst_mode(sadi); ctlr->dev.of_node = pdev->dev.of_node; ctlr->bus_num = pdev->id; diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index 1b7eebb72c07..8c9021b7f7a9 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -843,10 +843,8 @@ static int sprd_spi_irq_init(struct platform_device *pdev, struct sprd_spi *ss) int ret; ss->irq = platform_get_irq(pdev, 0); - if (ss->irq < 0) { - dev_err(&pdev->dev, "failed to get irq resource\n"); + if (ss->irq < 0) return ss->irq; - } ret = devm_request_irq(&pdev->dev, ss->irq, sprd_spi_handle_irq, 0, pdev->name, ss); diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 840a6bf81336..0c24c494f386 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -298,7 +298,6 @@ static int spi_st_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct spi_master *master; - struct resource *res; struct spi_st *spi_st; int irq, ret = 0; u32 var; @@ -331,8 +330,7 @@ static int spi_st_probe(struct platform_device *pdev) init_completion(&spi_st->done); /* Get resources */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spi_st->base = devm_ioremap_resource(&pdev->dev, res); + spi_st->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spi_st->base)) { ret = PTR_ERR(spi_st->base); goto clk_disable; diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 655e4afbfb2a..9ac6f9fe13cf 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -570,11 +570,8 @@ static int stm32_qspi_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - if (irq != -EPROBE_DEFER) - dev_err(dev, "IRQ error missing or invalid\n"); + if (irq < 0) return irq; - } ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0, dev_name(dev), qspi); diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 5194bc07fd60..cbfac6596fad 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -428,7 +428,6 @@ static int sun4i_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct sun4i_spi *sspi; - struct resource *res; int ret = 0, irq; master = spi_alloc_master(&pdev->dev, sizeof(struct sun4i_spi)); @@ -440,8 +439,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); + sspi->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sspi->base_addr)) { ret = PTR_ERR(sspi->base_addr); goto err_free_master; @@ -449,7 +447,6 @@ static int sun4i_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "No spi IRQ specified\n"); ret = -ENXIO; goto err_free_master; } diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index ee2bdaf5b856..ec7967be9e2f 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -435,7 +435,6 @@ static int sun6i_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct sun6i_spi *sspi; - struct resource *res; int ret = 0, irq; master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); @@ -447,8 +446,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); + sspi->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sspi->base_addr)) { ret = PTR_ERR(sspi->base_addr); goto err_free_master; @@ -456,7 +454,6 @@ static int sun6i_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "No spi IRQ specified\n"); ret = -ENXIO; goto err_free_master; } diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c index f99abd85c50a..ae17c99cce03 100644 --- a/drivers/spi/spi-synquacer.c +++ b/drivers/spi/spi-synquacer.c @@ -670,7 +670,6 @@ static int synquacer_spi_probe(struct platform_device *pdev) rx_irq = platform_get_irq(pdev, 0); if (rx_irq <= 0) { - dev_err(&pdev->dev, "get rx_irq failed (%d)\n", rx_irq); ret = rx_irq; goto put_spi; } @@ -685,7 +684,6 @@ static int synquacer_spi_probe(struct platform_device *pdev) tx_irq = platform_get_irq(pdev, 1); if (tx_irq <= 0) { - dev_err(&pdev->dev, "get tx_irq failed (%d)\n", tx_irq); ret = tx_irq; goto put_spi; } diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index cd714a4f52c6..a841a7250d14 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -419,7 +419,6 @@ static int tegra_sflash_probe(struct platform_device *pdev) { struct spi_master *master; struct tegra_sflash_data *tsd; - struct resource *r; int ret; const struct of_device_id *match; @@ -451,8 +450,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) &master->max_speed_hz)) master->max_speed_hz = 25000000; /* 25MHz */ - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tsd->base = devm_ioremap_resource(&pdev->dev, r); + tsd->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(tsd->base)) { ret = PTR_ERR(tsd->base); goto exit_free_master; diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 6ca600702470..3cb65371ae3b 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -717,7 +717,6 @@ static int ti_qspi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "no irq resource?\n"); ret = irq; goto free_master; } diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index b32c77df5d49..47cde1864630 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/clk.h> +#include <linux/delay.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -16,6 +17,7 @@ #include <asm/unaligned.h> #define SSI_TIMEOUT_MS 2000 +#define SSI_POLL_TIMEOUT_US 200 #define SSI_MAX_CLK_DIVIDER 254 #define SSI_MIN_CLK_DIVIDER 4 @@ -214,6 +216,7 @@ static void uniphier_spi_setup_transfer(struct spi_device *spi, if (!priv->is_save_param || priv->mode != spi->mode) { uniphier_spi_set_mode(spi); priv->mode = spi->mode; + priv->is_save_param = false; } if (!priv->is_save_param || priv->bits_per_word != t->bits_per_word) { @@ -226,8 +229,7 @@ static void uniphier_spi_setup_transfer(struct spi_device *spi, priv->speed_hz = t->speed_hz; } - if (!priv->is_save_param) - priv->is_save_param = true; + priv->is_save_param = true; /* reset FIFOs */ val = SSI_FC_TXFFL | SSI_FC_RXFFL; @@ -290,21 +292,23 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv) static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) { - unsigned int tx_count; + unsigned int fifo_threshold, fill_bytes; u32 val; - tx_count = DIV_ROUND_UP(priv->tx_bytes, + fifo_threshold = DIV_ROUND_UP(priv->rx_bytes, bytes_per_word(priv->bits_per_word)); - tx_count = min(tx_count, SSI_FIFO_DEPTH); + fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); + + fill_bytes = fifo_threshold - (priv->rx_bytes - priv->tx_bytes); /* set fifo threshold */ val = readl(priv->base + SSI_FC); val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); - val |= FIELD_PREP(SSI_FC_TXFTH_MASK, tx_count); - val |= FIELD_PREP(SSI_FC_RXFTH_MASK, tx_count); + val |= FIELD_PREP(SSI_FC_TXFTH_MASK, fifo_threshold); + val |= FIELD_PREP(SSI_FC_RXFTH_MASK, fifo_threshold); writel(val, priv->base + SSI_FC); - while (tx_count--) + while (fill_bytes--) uniphier_spi_send(priv); } @@ -323,20 +327,14 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) writel(val, priv->base + SSI_FPS); } -static int uniphier_spi_transfer_one(struct spi_master *master, - struct spi_device *spi, - struct spi_transfer *t) +static int uniphier_spi_transfer_one_irq(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) { struct uniphier_spi_priv *priv = spi_master_get_devdata(master); struct device *dev = master->dev.parent; unsigned long time_left; - /* Terminate and return success for 0 byte length transfer */ - if (!t->len) - return 0; - - uniphier_spi_setup_transfer(spi, t); - reinit_completion(&priv->xfer_done); uniphier_spi_fill_tx_fifo(priv); @@ -356,6 +354,59 @@ static int uniphier_spi_transfer_one(struct spi_master *master, return priv->error; } +static int uniphier_spi_transfer_one_poll(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int loop = SSI_POLL_TIMEOUT_US * 10; + + while (priv->tx_bytes) { + uniphier_spi_fill_tx_fifo(priv); + + while ((priv->rx_bytes - priv->tx_bytes) > 0) { + while (!(readl(priv->base + SSI_SR) & SSI_SR_RNE) + && loop--) + ndelay(100); + + if (loop == -1) + goto irq_transfer; + + uniphier_spi_recv(priv); + } + } + + return 0; + +irq_transfer: + return uniphier_spi_transfer_one_irq(master, spi, t); +} + +static int uniphier_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + unsigned long threshold; + + /* Terminate and return success for 0 byte length transfer */ + if (!t->len) + return 0; + + uniphier_spi_setup_transfer(spi, t); + + /* + * If the transfer operation will take longer than + * SSI_POLL_TIMEOUT_US, it should use irq. + */ + threshold = DIV_ROUND_UP(SSI_POLL_TIMEOUT_US * priv->speed_hz, + USEC_PER_SEC * BITS_PER_BYTE); + if (t->len > threshold) + return uniphier_spi_transfer_one_irq(master, spi, t); + else + return uniphier_spi_transfer_one_poll(master, spi, t); +} + static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master) { struct uniphier_spi_priv *priv = spi_master_get_devdata(master); @@ -419,7 +470,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) { struct uniphier_spi_priv *priv; struct spi_master *master; - struct resource *res; unsigned long clk_rate; int irq; int ret; @@ -434,8 +484,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->master = master; priv->is_save_param = false; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto out_master_put; @@ -454,7 +503,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "failed to get IRQ\n"); ret = irq; goto out_disable_clk; } diff --git a/drivers/spi/spi-xlp.c b/drivers/spi/spi-xlp.c index 1dc479fab98c..797ac0ea8fa3 100644 --- a/drivers/spi/spi-xlp.c +++ b/drivers/spi/spi-xlp.c @@ -370,7 +370,6 @@ static int xlp_spi_probe(struct platform_device *pdev) { struct spi_master *master; struct xlp_spi_priv *xspi; - struct resource *res; struct clk *clk; int irq, err; @@ -378,16 +377,13 @@ static int xlp_spi_probe(struct platform_device *pdev) if (!xspi) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - xspi->base = devm_ioremap_resource(&pdev->dev, res); + xspi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xspi->base)) return PTR_ERR(xspi->base); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no IRQ resource found: %d\n", irq); + if (irq < 0) return irq; - } err = devm_request_irq(&pdev->dev, irq, xlp_spi_interrupt, 0, pdev->name, xspi); if (err) { diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c index c6bee67decb5..5cf6993ddce5 100644 --- a/drivers/spi/spi-zynq-qspi.c +++ b/drivers/spi/spi-zynq-qspi.c @@ -620,7 +620,6 @@ static int zynq_qspi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct zynq_qspi *xqspi; - struct resource *res; u32 num_cs; ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); @@ -630,8 +629,7 @@ static int zynq_qspi_probe(struct platform_device *pdev) xqspi = spi_controller_get_devdata(ctlr); xqspi->dev = dev; platform_set_drvdata(pdev, xqspi); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - xqspi->regs = devm_ioremap_resource(&pdev->dev, res); + xqspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xqspi->regs)) { ret = PTR_ERR(xqspi->regs); goto remove_master; @@ -671,7 +669,6 @@ static int zynq_qspi_probe(struct platform_device *pdev) xqspi->irq = platform_get_irq(pdev, 0); if (xqspi->irq <= 0) { ret = -ENXIO; - dev_err(&pdev->dev, "irq resource not found\n"); goto remove_master; } ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq, @@ -695,7 +692,7 @@ static int zynq_qspi_probe(struct platform_device *pdev) ctlr->setup = zynq_qspi_setup_op; ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; ctlr->dev.of_node = np; - ret = spi_register_controller(ctlr); + ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_master failed\n"); goto clk_dis_all; diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 07a83ca164c2..60c4de4e4485 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -1016,7 +1016,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) int ret = 0; struct spi_master *master; struct zynqmp_qspi *xqspi; - struct resource *res; struct device *dev = &pdev->dev; eemi_ops = zynqmp_pm_get_eemi_ops(); @@ -1031,8 +1030,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) master->dev.of_node = pdev->dev.of_node; platform_set_drvdata(pdev, master); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - xqspi->regs = devm_ioremap_resource(&pdev->dev, res); + xqspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xqspi->regs)) { ret = PTR_ERR(xqspi->regs); goto remove_master; @@ -1077,7 +1075,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) xqspi->irq = platform_get_irq(pdev, 0); if (xqspi->irq <= 0) { ret = -ENXIO; - dev_err(dev, "irq resource not found\n"); goto clk_dis_all; } ret = devm_request_irq(&pdev->dev, xqspi->irq, zynqmp_qspi_irq, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 75ac046cae52..f8b4654a57d3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1265,8 +1265,9 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); */ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread) { - unsigned long flags; + struct spi_message *msg; bool was_busy = false; + unsigned long flags; int ret; /* Lock queue */ @@ -1325,10 +1326,10 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread) } /* Extract head of queue */ - ctlr->cur_msg = - list_first_entry(&ctlr->queue, struct spi_message, queue); + msg = list_first_entry(&ctlr->queue, struct spi_message, queue); + ctlr->cur_msg = msg; - list_del_init(&ctlr->cur_msg->queue); + list_del_init(&msg->queue); if (ctlr->busy) was_busy = true; else @@ -1361,7 +1362,7 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread) if (ctlr->auto_runtime_pm) pm_runtime_put(ctlr->dev.parent); - ctlr->cur_msg->status = ret; + msg->status = ret; spi_finalize_current_message(ctlr); mutex_unlock(&ctlr->io_mutex); @@ -1369,28 +1370,28 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread) } } - trace_spi_message_start(ctlr->cur_msg); + trace_spi_message_start(msg); if (ctlr->prepare_message) { - ret = ctlr->prepare_message(ctlr, ctlr->cur_msg); + ret = ctlr->prepare_message(ctlr, msg); if (ret) { dev_err(&ctlr->dev, "failed to prepare message: %d\n", ret); - ctlr->cur_msg->status = ret; + msg->status = ret; spi_finalize_current_message(ctlr); goto out; } ctlr->cur_msg_prepared = true; } - ret = spi_map_msg(ctlr, ctlr->cur_msg); + ret = spi_map_msg(ctlr, msg); if (ret) { - ctlr->cur_msg->status = ret; + msg->status = ret; spi_finalize_current_message(ctlr); goto out; } - ret = ctlr->transfer_one_message(ctlr, ctlr->cur_msg); + ret = ctlr->transfer_one_message(ctlr, msg); if (ret) { dev_err(&ctlr->dev, "failed to transfer one message from queue\n"); @@ -1434,7 +1435,7 @@ static void spi_pump_messages(struct kthread_work *work) */ static void spi_set_thread_rt(struct spi_controller *ctlr) { - struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + struct sched_param param = { .sched_priority = MAX_RT_PRIO / 2 }; dev_info(&ctlr->dev, "will run message pump with realtime priority\n"); @@ -2105,8 +2106,8 @@ static int match_true(struct device *dev, void *data) return 1; } -static ssize_t spi_slave_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t slave_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct spi_controller *ctlr = container_of(dev, struct spi_controller, dev); @@ -2117,9 +2118,8 @@ static ssize_t spi_slave_show(struct device *dev, child ? to_spi_device(child)->modalias : NULL); } -static ssize_t spi_slave_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t slave_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct spi_controller *ctlr = container_of(dev, struct spi_controller, dev); @@ -2157,7 +2157,7 @@ static ssize_t spi_slave_store(struct device *dev, return count; } -static DEVICE_ATTR(slave, 0644, spi_slave_show, spi_slave_store); +static DEVICE_ATTR_RW(slave); static struct attribute *spi_slave_attrs[] = { &dev_attr_slave.attr, @@ -2188,8 +2188,10 @@ extern struct class spi_slave_class; /* dummy */ * __spi_alloc_controller - allocate an SPI master or slave controller * @dev: the controller, possibly using the platform_bus * @size: how much zeroed driver-private data to allocate; the pointer to this - * memory is in the driver_data field of the returned device, - * accessible with spi_controller_get_devdata(). + * memory is in the driver_data field of the returned device, accessible + * with spi_controller_get_devdata(); the memory is cacheline aligned; + * drivers granting DMA access to portions of their private data need to + * round up @size using ALIGN(size, dma_get_cache_alignment()). * @slave: flag indicating whether to allocate an SPI master (false) or SPI * slave (true) controller * Context: can sleep @@ -2211,11 +2213,12 @@ struct spi_controller *__spi_alloc_controller(struct device *dev, unsigned int size, bool slave) { struct spi_controller *ctlr; + size_t ctlr_size = ALIGN(sizeof(*ctlr), dma_get_cache_alignment()); if (!dev) return NULL; - ctlr = kzalloc(size + sizeof(*ctlr), GFP_KERNEL); + ctlr = kzalloc(size + ctlr_size, GFP_KERNEL); if (!ctlr) return NULL; @@ -2229,14 +2232,14 @@ struct spi_controller *__spi_alloc_controller(struct device *dev, ctlr->dev.class = &spi_master_class; ctlr->dev.parent = dev; pm_suspend_ignore_children(&ctlr->dev, true); - spi_controller_set_devdata(ctlr, &ctlr[1]); + spi_controller_set_devdata(ctlr, (void *)ctlr + ctlr_size); return ctlr; } EXPORT_SYMBOL_GPL(__spi_alloc_controller); #ifdef CONFIG_OF -static int of_spi_register_master(struct spi_controller *ctlr) +static int of_spi_get_gpio_numbers(struct spi_controller *ctlr) { int nb, i, *cs; struct device_node *np = ctlr->dev.of_node; @@ -2269,7 +2272,7 @@ static int of_spi_register_master(struct spi_controller *ctlr) return 0; } #else -static int of_spi_register_master(struct spi_controller *ctlr) +static int of_spi_get_gpio_numbers(struct spi_controller *ctlr) { return 0; } @@ -2456,7 +2459,7 @@ int spi_register_controller(struct spi_controller *ctlr) ctlr->mode_bits |= SPI_CS_HIGH; } else { /* Legacy code path for GPIOs from DT */ - status = of_spi_register_master(ctlr); + status = of_spi_get_gpio_numbers(ctlr); if (status) return status; } diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index fd4995fb676e..f85ec5b16b65 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -8,11 +8,14 @@ #include <linux/list.h> #include <linux/slab.h> #include <linux/swap.h> +#include <linux/sched/signal.h> #include "ion.h" static inline struct page *ion_page_pool_alloc_pages(struct ion_page_pool *pool) { + if (fatal_signal_pending(current)) + return NULL; return alloc_pages(pool->gfp_mask, pool->order); } diff --git a/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c index 2edf3ee91300..caf4d4df4bd3 100644 --- a/drivers/staging/comedi/drivers/dt3000.c +++ b/drivers/staging/comedi/drivers/dt3000.c @@ -342,9 +342,9 @@ static irqreturn_t dt3k_interrupt(int irq, void *d) static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec, unsigned int flags) { - int divider, base, prescale; + unsigned int divider, base, prescale; - /* This function needs improvment */ + /* This function needs improvement */ /* Don't know if divider==0 works. */ for (prescale = 0; prescale < 16; prescale++) { @@ -358,7 +358,7 @@ static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec, divider = (*nanosec) / base; break; case CMDF_ROUND_UP: - divider = (*nanosec) / base; + divider = DIV_ROUND_UP(*nanosec, base); break; } if (divider < 65536) { @@ -368,7 +368,7 @@ static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec, } prescale = 15; - base = timer_base * (1 << prescale); + base = timer_base * (prescale + 1); divider = 65535; *nanosec = divider * base; return (prescale << 16) | (divider); diff --git a/drivers/staging/fbtft/fb_bd663474.c b/drivers/staging/fbtft/fb_bd663474.c index b6c6d66e4eb1..e2c7646588f8 100644 --- a/drivers/staging/fbtft/fb_bd663474.c +++ b/drivers/staging/fbtft/fb_bd663474.c @@ -24,7 +24,7 @@ static int init_display(struct fbtft_par *par) { - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ par->fbtftops.reset(par); diff --git a/drivers/staging/fbtft/fb_ili9163.c b/drivers/staging/fbtft/fb_ili9163.c index d609a2b67db9..fd32376700e2 100644 --- a/drivers/staging/fbtft/fb_ili9163.c +++ b/drivers/staging/fbtft/fb_ili9163.c @@ -77,7 +77,7 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ write_reg(par, MIPI_DCS_SOFT_RESET); /* software reset */ diff --git a/drivers/staging/fbtft/fb_ili9325.c b/drivers/staging/fbtft/fb_ili9325.c index b090e7ab6fdd..85e54a10ed72 100644 --- a/drivers/staging/fbtft/fb_ili9325.c +++ b/drivers/staging/fbtft/fb_ili9325.c @@ -85,7 +85,7 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ bt &= 0x07; diff --git a/drivers/staging/fbtft/fb_s6d1121.c b/drivers/staging/fbtft/fb_s6d1121.c index b3d0701880fe..5a129b1352cc 100644 --- a/drivers/staging/fbtft/fb_s6d1121.c +++ b/drivers/staging/fbtft/fb_s6d1121.c @@ -29,7 +29,7 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ /* Initialization sequence from Lib_UTFT */ diff --git a/drivers/staging/fbtft/fb_ssd1289.c b/drivers/staging/fbtft/fb_ssd1289.c index bbf75f795234..88a5b6925901 100644 --- a/drivers/staging/fbtft/fb_ssd1289.c +++ b/drivers/staging/fbtft/fb_ssd1289.c @@ -28,7 +28,7 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ write_reg(par, 0x00, 0x0001); diff --git a/drivers/staging/fbtft/fb_ssd1331.c b/drivers/staging/fbtft/fb_ssd1331.c index 4cfe9f8535d0..37622c9462aa 100644 --- a/drivers/staging/fbtft/fb_ssd1331.c +++ b/drivers/staging/fbtft/fb_ssd1331.c @@ -81,7 +81,7 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) va_start(args, len); *buf = (u8)va_arg(args, unsigned int); - if (!par->gpio.dc) + if (par->gpio.dc) gpiod_set_value(par->gpio.dc, 0); ret = par->fbtftops.write(par, par->buf, sizeof(u8)); if (ret < 0) { @@ -104,7 +104,7 @@ static void write_reg8_bus8(struct fbtft_par *par, int len, ...) return; } } - if (!par->gpio.dc) + if (par->gpio.dc) gpiod_set_value(par->gpio.dc, 1); va_end(args); } diff --git a/drivers/staging/fbtft/fb_upd161704.c b/drivers/staging/fbtft/fb_upd161704.c index 564a38e34440..c77832ae5e5b 100644 --- a/drivers/staging/fbtft/fb_upd161704.c +++ b/drivers/staging/fbtft/fb_upd161704.c @@ -26,7 +26,7 @@ static int init_display(struct fbtft_par *par) { par->fbtftops.reset(par); - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ /* Initialization sequence from Lib_UTFT */ diff --git a/drivers/staging/fbtft/fbtft-bus.c b/drivers/staging/fbtft/fbtft-bus.c index 2ea814d0dca5..63c65dd67b17 100644 --- a/drivers/staging/fbtft/fbtft-bus.c +++ b/drivers/staging/fbtft/fbtft-bus.c @@ -135,7 +135,7 @@ int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len) remain = len / 2; vmem16 = (u16 *)(par->info->screen_buffer + offset); - if (!par->gpio.dc) + if (par->gpio.dc) gpiod_set_value(par->gpio.dc, 1); /* non buffered write */ diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index 7cbc1bdd2d8a..cf5700a2ea66 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -76,21 +76,18 @@ static int fbtft_request_one_gpio(struct fbtft_par *par, struct gpio_desc **gpiop) { struct device *dev = par->info->device; - struct device_node *node = dev->of_node; int ret = 0; - if (of_find_property(node, name, NULL)) { - *gpiop = devm_gpiod_get_index(dev, dev->driver->name, index, - GPIOD_OUT_HIGH); - if (IS_ERR(*gpiop)) { - ret = PTR_ERR(*gpiop); - dev_err(dev, - "Failed to request %s GPIO:%d\n", name, ret); - return ret; - } - fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' GPIO\n", - __func__, name); + *gpiop = devm_gpiod_get_index_optional(dev, name, index, + GPIOD_OUT_HIGH); + if (IS_ERR(*gpiop)) { + ret = PTR_ERR(*gpiop); + dev_err(dev, + "Failed to request %s GPIO: %d\n", name, ret); + return ret; } + fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' GPIO\n", + __func__, name); return ret; } @@ -103,34 +100,34 @@ static int fbtft_request_gpios_dt(struct fbtft_par *par) if (!par->info->device->of_node) return -EINVAL; - ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset); + ret = fbtft_request_one_gpio(par, "reset", 0, &par->gpio.reset); if (ret) return ret; - ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc); + ret = fbtft_request_one_gpio(par, "dc", 0, &par->gpio.dc); if (ret) return ret; - ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd); + ret = fbtft_request_one_gpio(par, "rd", 0, &par->gpio.rd); if (ret) return ret; - ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr); + ret = fbtft_request_one_gpio(par, "wr", 0, &par->gpio.wr); if (ret) return ret; - ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs); + ret = fbtft_request_one_gpio(par, "cs", 0, &par->gpio.cs); if (ret) return ret; - ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch); + ret = fbtft_request_one_gpio(par, "latch", 0, &par->gpio.latch); if (ret) return ret; for (i = 0; i < 16; i++) { - ret = fbtft_request_one_gpio(par, "db-gpios", i, + ret = fbtft_request_one_gpio(par, "db", i, &par->gpio.db[i]); if (ret) return ret; - ret = fbtft_request_one_gpio(par, "led-gpios", i, + ret = fbtft_request_one_gpio(par, "led", i, &par->gpio.led[i]); if (ret) return ret; - ret = fbtft_request_one_gpio(par, "aux-gpios", i, + ret = fbtft_request_one_gpio(par, "aux", i, &par->gpio.aux[i]); if (ret) return ret; @@ -234,9 +231,9 @@ static void fbtft_reset(struct fbtft_par *par) if (!par->gpio.reset) return; fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__); - gpiod_set_value_cansleep(par->gpio.reset, 0); - usleep_range(20, 40); gpiod_set_value_cansleep(par->gpio.reset, 1); + usleep_range(20, 40); + gpiod_set_value_cansleep(par->gpio.reset, 0); msleep(120); } @@ -921,7 +918,7 @@ static int fbtft_init_display_dt(struct fbtft_par *par) return -EINVAL; par->fbtftops.reset(par); - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ while (p) { @@ -1012,7 +1009,7 @@ int fbtft_init_display(struct fbtft_par *par) } par->fbtftops.reset(par); - if (!par->gpio.cs) + if (par->gpio.cs) gpiod_set_value(par->gpio.cs, 0); /* Activate chip */ i = 0; diff --git a/drivers/staging/gasket/apex_driver.c b/drivers/staging/gasket/apex_driver.c index 2be45ee9d061..464648ee2036 100644 --- a/drivers/staging/gasket/apex_driver.c +++ b/drivers/staging/gasket/apex_driver.c @@ -532,7 +532,7 @@ static ssize_t sysfs_show(struct device *device, struct device_attribute *attr, break; case ATTR_KERNEL_HIB_SIMPLE_PAGE_TABLE_SIZE: ret = scnprintf(buf, PAGE_SIZE, "%u\n", - gasket_page_table_num_entries( + gasket_page_table_num_simple_entries( gasket_dev->page_table[0])); break; case ATTR_KERNEL_HIB_NUM_ACTIVE_PAGES: diff --git a/drivers/staging/unisys/visornic/visornic_main.c b/drivers/staging/unisys/visornic/visornic_main.c index 9d4f1dab0968..40dd573e73c3 100644 --- a/drivers/staging/unisys/visornic/visornic_main.c +++ b/drivers/staging/unisys/visornic/visornic_main.c @@ -1750,7 +1750,8 @@ static int visornic_poll(struct napi_struct *napi, int budget) } /* poll_for_irq - checks the status of the response queue - * @v: Void pointer to the visronic devdata struct. + * @t: pointer to the 'struct timer_list' from which we can retrieve the + * the visornic devdata struct. * * Main function of the vnic_incoming thread. Periodically check the response * queue and drain it if needed. diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c index d72fdd333050..736eedef23b6 100644 --- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c +++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c @@ -1969,6 +1969,7 @@ void wilc_deinit_host_int(struct net_device *net) priv->p2p_listen_state = false; + flush_workqueue(vif->wilc->hif_workqueue); mutex_destroy(&priv->scan_req_lock); ret = wilc_deinit(vif); diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 04eda111920e..661bb9358364 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1132,14 +1132,16 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * struct se_cmd *se_cmd = cmd->se_cmd; struct tcmu_dev *udev = cmd->tcmu_dev; bool read_len_valid = false; - uint32_t read_len = se_cmd->data_length; + uint32_t read_len; /* * cmd has been completed already from timeout, just reclaim * data area space and free cmd */ - if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) + if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { + WARN_ON_ONCE(se_cmd); goto out; + } list_del_init(&cmd->queue_entry); @@ -1152,6 +1154,7 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * goto done; } + read_len = se_cmd->data_length; if (se_cmd->data_direction == DMA_FROM_DEVICE && (entry->hdr.uflags & TCMU_UFLAG_READ_LEN) && entry->rsp.read_len) { read_len_valid = true; @@ -1307,6 +1310,7 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data) */ scsi_status = SAM_STAT_CHECK_CONDITION; list_del_init(&cmd->queue_entry); + cmd->se_cmd = NULL; } else { list_del_init(&cmd->queue_entry); idr_remove(&udev->commands, id); @@ -2022,6 +2026,7 @@ static void tcmu_reset_ring(struct tcmu_dev *udev, u8 err_level) idr_remove(&udev->commands, i); if (!test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { + WARN_ON(!cmd->se_cmd); list_del_init(&cmd->queue_entry); if (err_level == 1) { /* diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index bfe5e9e034ec..c7d51b51898f 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -277,10 +277,14 @@ static void kgdboc_pre_exp_handler(void) /* Increment the module count when the debugger is active */ if (!kgdb_connected) try_module_get(THIS_MODULE); + + atomic_inc(&ignore_console_lock_warning); } static void kgdboc_post_exp_handler(void) { + atomic_dec(&ignore_console_lock_warning); + /* decrement the module count when the debugger detaches */ if (!kgdb_connected) module_put(THIS_MODULE); diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index b5abfe89190c..df8812c30640 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -454,9 +454,11 @@ err_clk: imx_disable_unprepare_clks(dev); disable_hsic_regulator: if (data->hsic_pad_regulator) - ret = regulator_disable(data->hsic_pad_regulator); + /* don't overwrite original ret (cf. EPROBE_DEFER) */ + regulator_disable(data->hsic_pad_regulator); if (pdata.flags & CI_HDRC_PMQOS) pm_qos_remove_request(&data->pm_qos_req); + data->ci_pdev = NULL; return ret; } @@ -469,14 +471,17 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); } - ci_hdrc_remove_device(data->ci_pdev); + if (data->ci_pdev) + ci_hdrc_remove_device(data->ci_pdev); if (data->override_phy_control) usb_phy_shutdown(data->phy); - imx_disable_unprepare_clks(&pdev->dev); - if (data->plat_data->flags & CI_HDRC_PMQOS) - pm_qos_remove_request(&data->pm_qos_req); - if (data->hsic_pad_regulator) - regulator_disable(data->hsic_pad_regulator); + if (data->ci_pdev) { + imx_disable_unprepare_clks(&pdev->dev); + if (data->plat_data->flags & CI_HDRC_PMQOS) + pm_qos_remove_request(&data->pm_qos_req); + if (data->hsic_pad_regulator) + regulator_disable(data->hsic_pad_regulator); + } return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 6a5ee8e6da10..67ad40b0a05b 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -709,12 +709,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget); unsigned long flags; - spin_lock_irqsave(&ci->lock, flags); - ci->gadget.speed = USB_SPEED_UNKNOWN; - ci->remote_wakeup = 0; - ci->suspended = 0; - spin_unlock_irqrestore(&ci->lock, flags); - /* flush all endpoints */ gadget_for_each_ep(ep, gadget) { usb_ep_fifo_flush(ep); @@ -732,6 +726,12 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) ci->status = NULL; } + spin_lock_irqsave(&ci->lock, flags); + ci->gadget.speed = USB_SPEED_UNKNOWN; + ci->remote_wakeup = 0; + ci->suspended = 0; + spin_unlock_irqrestore(&ci->lock, flags); + return 0; } @@ -1303,6 +1303,10 @@ static int ep_disable(struct usb_ep *ep) return -EBUSY; spin_lock_irqsave(hwep->lock, flags); + if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) { + spin_unlock_irqrestore(hwep->lock, flags); + return 0; + } /* only internal SW should disable ctrl endpts */ @@ -1392,6 +1396,10 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, return -EINVAL; spin_lock_irqsave(hwep->lock, flags); + if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) { + spin_unlock_irqrestore(hwep->lock, flags); + return 0; + } retval = _ep_queue(ep, req, gfp_flags); spin_unlock_irqrestore(hwep->lock, flags); return retval; @@ -1415,8 +1423,8 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) return -EINVAL; spin_lock_irqsave(hwep->lock, flags); - - hw_ep_flush(hwep->ci, hwep->num, hwep->dir); + if (hwep->ci->gadget.speed != USB_SPEED_UNKNOWN) + hw_ep_flush(hwep->ci, hwep->num, hwep->dir); list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) { dma_pool_free(hwep->td_pool, node->ptr, node->dma); @@ -1487,6 +1495,10 @@ static void ep_fifo_flush(struct usb_ep *ep) } spin_lock_irqsave(hwep->lock, flags); + if (hwep->ci->gadget.speed == USB_SPEED_UNKNOWN) { + spin_unlock_irqrestore(hwep->lock, flags); + return; + } hw_ep_flush(hwep->ci, hwep->num, hwep->dir); @@ -1559,6 +1571,10 @@ static int ci_udc_wakeup(struct usb_gadget *_gadget) int ret = 0; spin_lock_irqsave(&ci->lock, flags); + if (ci->gadget.speed == USB_SPEED_UNKNOWN) { + spin_unlock_irqrestore(&ci->lock, flags); + return 0; + } if (!ci->remote_wakeup) { ret = -EOPNOTSUPP; goto out; diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 183b41753c98..62f4fb9b362f 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1301,10 +1301,6 @@ made_compressed_probe: tty_port_init(&acm->port); acm->port.ops = &acm_port_ops; - minor = acm_alloc_minor(acm); - if (minor < 0) - goto alloc_fail1; - ctrlsize = usb_endpoint_maxp(epctrl); readsize = usb_endpoint_maxp(epread) * (quirks == SINGLE_RX_URB ? 1 : 2); @@ -1312,6 +1308,13 @@ made_compressed_probe: acm->writesize = usb_endpoint_maxp(epwrite) * 20; acm->control = control_interface; acm->data = data_interface; + + usb_get_intf(acm->control); /* undone in destruct() */ + + minor = acm_alloc_minor(acm); + if (minor < 0) + goto alloc_fail1; + acm->minor = minor; acm->dev = usb_dev; if (h.usb_cdc_acm_descriptor) @@ -1458,7 +1461,6 @@ skip_countries: usb_driver_claim_interface(&acm_driver, data_interface, acm); usb_set_intfdata(data_interface, acm); - usb_get_intf(control_interface); tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor, &control_interface->dev); if (IS_ERR(tty_dev)) { diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index a7824a51f86d..70afb2ca1eab 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -587,10 +587,20 @@ static int wdm_flush(struct file *file, fl_owner_t id) { struct wdm_device *desc = file->private_data; - wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags)); + wait_event(desc->wait, + /* + * needs both flags. We cannot do with one + * because resetting it would cause a race + * with write() yet we need to signal + * a disconnect + */ + !test_bit(WDM_IN_USE, &desc->flags) || + test_bit(WDM_DISCONNECTING, &desc->flags)); /* cannot dereference desc->intf if WDM_DISCONNECTING */ - if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags)) + if (test_bit(WDM_DISCONNECTING, &desc->flags)) + return -ENODEV; + if (desc->werr < 0) dev_err(&desc->intf->dev, "Error in flush path: %d\n", desc->werr); @@ -974,8 +984,6 @@ static void wdm_disconnect(struct usb_interface *intf) spin_lock_irqsave(&desc->iuspin, flags); set_bit(WDM_DISCONNECTING, &desc->flags); set_bit(WDM_READ, &desc->flags); - /* to terminate pending flushes */ - clear_bit(WDM_IN_USE, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); wake_up_all(&desc->wait); mutex_lock(&desc->rlock); diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 4942122b2346..36858ddd8d9b 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2362,8 +2362,11 @@ static int usbtmc_probe(struct usb_interface *intf, goto err_put; } + retcode = -EINVAL; data->bulk_in = bulk_in->bEndpointAddress; data->wMaxPacketSize = usb_endpoint_maxp(bulk_in); + if (!data->wMaxPacketSize) + goto err_put; dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", data->bulk_in); data->bulk_out = bulk_out->bEndpointAddress; diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 1359b78a624e..6cf22c27f2d2 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -66,9 +66,7 @@ int hcd_buffer_create(struct usb_hcd *hcd) char name[16]; int i, size; - if (!IS_ENABLED(CONFIG_HAS_DMA) || - (!is_device_dma_capable(hcd->self.sysdev) && - !hcd->localmem_pool)) + if (hcd->localmem_pool || !hcd_uses_dma(hcd)) return 0; for (i = 0; i < HCD_BUFFER_POOLS; i++) { @@ -129,8 +127,7 @@ void *hcd_buffer_alloc( return gen_pool_dma_alloc(hcd->localmem_pool, size, dma); /* some USB hosts just use PIO */ - if (!IS_ENABLED(CONFIG_HAS_DMA) || - !is_device_dma_capable(bus->sysdev)) { + if (!hcd_uses_dma(hcd)) { *dma = ~(dma_addr_t) 0; return kmalloc(size, mem_flags); } @@ -160,8 +157,7 @@ void hcd_buffer_free( return; } - if (!IS_ENABLED(CONFIG_HAS_DMA) || - !is_device_dma_capable(bus->sysdev)) { + if (!hcd_uses_dma(hcd)) { kfree(addr); return; } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index b265ab5405f9..9063ede411ae 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1812,8 +1812,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb return 0; error: - if (as && as->usbm) - dec_usb_memory_use_count(as->usbm, &as->usbm->urb_use_count); kfree(isopkt); kfree(dr); if (as) diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 65de6f73b672..558890ada0e5 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -193,9 +193,10 @@ int usb_register_dev(struct usb_interface *intf, intf->minor = minor; break; } - up_write(&minor_rwsem); - if (intf->minor < 0) + if (intf->minor < 0) { + up_write(&minor_rwsem); return -EXFULL; + } /* create a usb class device for this usb interface */ snprintf(name, sizeof(name), class_driver->name, minor - minor_base); @@ -203,12 +204,11 @@ int usb_register_dev(struct usb_interface *intf, MKDEV(USB_MAJOR, minor), class_driver, "%s", kbasename(name)); if (IS_ERR(intf->usb_dev)) { - down_write(&minor_rwsem); usb_minors[minor] = NULL; intf->minor = -1; - up_write(&minor_rwsem); retval = PTR_ERR(intf->usb_dev); } + up_write(&minor_rwsem); return retval; } EXPORT_SYMBOL_GPL(usb_register_dev); @@ -234,12 +234,12 @@ void usb_deregister_dev(struct usb_interface *intf, return; dev_dbg(&intf->dev, "removing %d minor\n", intf->minor); + device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; up_write(&minor_rwsem); - device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); intf->usb_dev = NULL; intf->minor = -1; destroy_usb_class(); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 03432467b05f..7537681355f6 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -216,17 +216,18 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) /* EHCI, OHCI */ hcd->rsrc_start = pci_resource_start(dev, 0); hcd->rsrc_len = pci_resource_len(dev, 0); - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - driver->description)) { + if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start, + hcd->rsrc_len, driver->description)) { dev_dbg(&dev->dev, "controller already in use\n"); retval = -EBUSY; goto put_hcd; } - hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + hcd->regs = devm_ioremap_nocache(&dev->dev, hcd->rsrc_start, + hcd->rsrc_len); if (hcd->regs == NULL) { dev_dbg(&dev->dev, "error mapping memory\n"); retval = -EFAULT; - goto release_mem_region; + goto put_hcd; } } else { @@ -240,8 +241,8 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) hcd->rsrc_start = pci_resource_start(dev, region); hcd->rsrc_len = pci_resource_len(dev, region); - if (request_region(hcd->rsrc_start, hcd->rsrc_len, - driver->description)) + if (devm_request_region(&dev->dev, hcd->rsrc_start, + hcd->rsrc_len, driver->description)) break; } if (region == PCI_ROM_RESOURCE) { @@ -275,20 +276,13 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } if (retval != 0) - goto unmap_registers; + goto put_hcd; device_wakeup_enable(hcd->self.controller); if (pci_dev_run_wake(dev)) pm_runtime_put_noidle(&dev->dev); return retval; -unmap_registers: - if (driver->flags & HCD_MEMORY) { - iounmap(hcd->regs); -release_mem_region: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - } else - release_region(hcd->rsrc_start, hcd->rsrc_len); put_hcd: usb_put_hcd(hcd); disable_pci: @@ -347,14 +341,6 @@ void usb_hcd_pci_remove(struct pci_dev *dev) dev_set_drvdata(&dev->dev, NULL); up_read(&companions_rwsem); } - - if (hcd->driver->flags & HCD_MEMORY) { - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - } else { - release_region(hcd->rsrc_start, hcd->rsrc_len); - } - usb_put_hcd(hcd); pci_disable_device(dev); } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9320787ac2e6..8592c0344fe8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -103,11 +103,6 @@ static DEFINE_SPINLOCK(hcd_urb_unlink_lock); /* wait queue for synchronous unlinks */ DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); -static inline int is_root_hub(struct usb_device *udev) -{ - return (udev->parent == NULL); -} - /*-------------------------------------------------------------------------*/ /* @@ -880,101 +875,6 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } - -/* - * Show & store the current value of authorized_default - */ -static ssize_t authorized_default_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_device *rh_usb_dev = to_usb_device(dev); - struct usb_bus *usb_bus = rh_usb_dev->bus; - struct usb_hcd *hcd; - - hcd = bus_to_hcd(usb_bus); - return snprintf(buf, PAGE_SIZE, "%u\n", hcd->dev_policy); -} - -static ssize_t authorized_default_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - ssize_t result; - unsigned val; - struct usb_device *rh_usb_dev = to_usb_device(dev); - struct usb_bus *usb_bus = rh_usb_dev->bus; - struct usb_hcd *hcd; - - hcd = bus_to_hcd(usb_bus); - result = sscanf(buf, "%u\n", &val); - if (result == 1) { - hcd->dev_policy = val <= USB_DEVICE_AUTHORIZE_INTERNAL ? - val : USB_DEVICE_AUTHORIZE_ALL; - result = size; - } else { - result = -EINVAL; - } - return result; -} -static DEVICE_ATTR_RW(authorized_default); - -/* - * interface_authorized_default_show - show default authorization status - * for USB interfaces - * - * note: interface_authorized_default is the default value - * for initializing the authorized attribute of interfaces - */ -static ssize_t interface_authorized_default_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_device *usb_dev = to_usb_device(dev); - struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); - - return sprintf(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd)); -} - -/* - * interface_authorized_default_store - store default authorization status - * for USB interfaces - * - * note: interface_authorized_default is the default value - * for initializing the authorized attribute of interfaces - */ -static ssize_t interface_authorized_default_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct usb_device *usb_dev = to_usb_device(dev); - struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); - int rc = count; - bool val; - - if (strtobool(buf, &val) != 0) - return -EINVAL; - - if (val) - set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); - else - clear_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); - - return rc; -} -static DEVICE_ATTR_RW(interface_authorized_default); - -/* Group all the USB bus attributes */ -static struct attribute *usb_bus_attrs[] = { - &dev_attr_authorized_default.attr, - &dev_attr_interface_authorized_default.attr, - NULL, -}; - -static const struct attribute_group usb_bus_attr_group = { - .name = NULL, /* we want them in the same directory */ - .attrs = usb_bus_attrs, -}; - - - /*-------------------------------------------------------------------------*/ /** @@ -1512,7 +1412,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, if (usb_endpoint_xfer_control(&urb->ep->desc)) { if (hcd->self.uses_pio_for_control) return ret; - if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) { + if (hcd_uses_dma(hcd)) { if (is_vmalloc_addr(urb->setup_packet)) { WARN_ONCE(1, "setup packet is not dma capable\n"); return -EAGAIN; @@ -1546,7 +1446,7 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_buffer_length != 0 && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { - if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) { + if (hcd_uses_dma(hcd)) { if (urb->num_sgs) { int n; @@ -2894,32 +2794,11 @@ int usb_add_hcd(struct usb_hcd *hcd, if (retval != 0) goto err_register_root_hub; - retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group); - if (retval < 0) { - printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n", - retval); - goto error_create_attr_group; - } if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) usb_hcd_poll_rh_status(hcd); return retval; -error_create_attr_group: - clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); - if (HC_IS_RUNNING(hcd->state)) - hcd->state = HC_STATE_QUIESCING; - spin_lock_irq(&hcd_root_hub_lock); - hcd->rh_registered = 0; - spin_unlock_irq(&hcd_root_hub_lock); - -#ifdef CONFIG_PM - cancel_work_sync(&hcd->wakeup_work); -#endif - cancel_work_sync(&hcd->died_work); - mutex_lock(&usb_bus_idr_lock); - usb_disconnect(&rhdev); /* Sets rhdev to NULL */ - mutex_unlock(&usb_bus_idr_lock); err_register_root_hub: hcd->rh_pollable = 0; clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -2963,8 +2842,6 @@ void usb_remove_hcd(struct usb_hcd *hcd) dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); usb_get_dev(rhdev); - sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group); - clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING (hcd->state)) hcd->state = HC_STATE_QUIESCING; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index e844bb7b5676..5adf489428aa 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -2218,14 +2218,14 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, (struct usb_cdc_dmm_desc *)buffer; break; case USB_CDC_MDLM_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_desc *)) + if (elength < sizeof(struct usb_cdc_mdlm_desc)) goto next_desc; if (desc) return -EINVAL; desc = (struct usb_cdc_mdlm_desc *)buffer; break; case USB_CDC_MDLM_DETAIL_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *)) + if (elength < sizeof(struct usb_cdc_mdlm_detail_desc)) goto next_desc; if (detail) return -EINVAL; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 7e88fdfe3cf5..f19694e69f5c 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -15,6 +15,7 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/usb.h> +#include <linux/usb/hcd.h> #include <linux/usb/quirks.h> #include <linux/of.h> #include "usb.h" @@ -922,6 +923,116 @@ static struct bin_attribute dev_bin_attr_descriptors = { .size = 18 + 65535, /* dev descr + max-size raw descriptor */ }; +/* + * Show & store the current value of authorized_default + */ +static ssize_t authorized_default_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_device *rh_usb_dev = to_usb_device(dev); + struct usb_bus *usb_bus = rh_usb_dev->bus; + struct usb_hcd *hcd; + + hcd = bus_to_hcd(usb_bus); + return snprintf(buf, PAGE_SIZE, "%u\n", hcd->dev_policy); +} + +static ssize_t authorized_default_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + ssize_t result; + unsigned int val; + struct usb_device *rh_usb_dev = to_usb_device(dev); + struct usb_bus *usb_bus = rh_usb_dev->bus; + struct usb_hcd *hcd; + + hcd = bus_to_hcd(usb_bus); + result = sscanf(buf, "%u\n", &val); + if (result == 1) { + hcd->dev_policy = val <= USB_DEVICE_AUTHORIZE_INTERNAL ? + val : USB_DEVICE_AUTHORIZE_ALL; + result = size; + } else { + result = -EINVAL; + } + return result; +} +static DEVICE_ATTR_RW(authorized_default); + +/* + * interface_authorized_default_show - show default authorization status + * for USB interfaces + * + * note: interface_authorized_default is the default value + * for initializing the authorized attribute of interfaces + */ +static ssize_t interface_authorized_default_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_device *usb_dev = to_usb_device(dev); + struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); + + return sprintf(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd)); +} + +/* + * interface_authorized_default_store - store default authorization status + * for USB interfaces + * + * note: interface_authorized_default is the default value + * for initializing the authorized attribute of interfaces + */ +static ssize_t interface_authorized_default_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_device *usb_dev = to_usb_device(dev); + struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); + int rc = count; + bool val; + + if (strtobool(buf, &val) != 0) + return -EINVAL; + + if (val) + set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); + else + clear_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); + + return rc; +} +static DEVICE_ATTR_RW(interface_authorized_default); + +/* Group all the USB bus attributes */ +static struct attribute *usb_bus_attrs[] = { + &dev_attr_authorized_default.attr, + &dev_attr_interface_authorized_default.attr, + NULL, +}; + +static const struct attribute_group usb_bus_attr_group = { + .name = NULL, /* we want them in the same directory */ + .attrs = usb_bus_attrs, +}; + + +static int add_default_authorized_attributes(struct device *dev) +{ + int rc = 0; + + if (is_usb_device(dev)) + rc = sysfs_create_group(&dev->kobj, &usb_bus_attr_group); + + return rc; +} + +static void remove_default_authorized_attributes(struct device *dev) +{ + if (is_usb_device(dev)) { + sysfs_remove_group(&dev->kobj, &usb_bus_attr_group); + } +} + int usb_create_sysfs_dev_files(struct usb_device *udev) { struct device *dev = &udev->dev; @@ -938,7 +1049,14 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) retval = add_power_attributes(dev); if (retval) goto error; + + if (is_root_hub(udev)) { + retval = add_default_authorized_attributes(dev); + if (retval) + goto error; + } return retval; + error: usb_remove_sysfs_dev_files(udev); return retval; @@ -948,6 +1066,9 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) { struct device *dev = &udev->dev; + if (is_root_hub(udev)) + remove_default_authorized_attributes(dev); + remove_power_attributes(dev); remove_persist_attributes(dev); device_remove_bin_file(dev, &dev_bin_attr_descriptors); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index bd8d01f85a13..0c9fde5ad052 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -153,6 +153,11 @@ static inline int is_usb_port(const struct device *dev) return dev->type == &usb_port_device_type; } +static inline int is_root_hub(struct usb_device *udev) +{ + return (udev->parent == NULL); +} + /* Do the same for device drivers and interface drivers. */ static inline int is_usb_device_driver(struct device_driver *drv) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index ee144ff8af5b..111787a137ee 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4608,7 +4608,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, buf = urb->transfer_buffer; - if (hcd->self.uses_dma) { + if (hcd_uses_dma(hcd)) { if (!buf && (urb->transfer_dma & 3)) { dev_err(hsotg->dev, "%s: unaligned transfer with no transfer_buffer", diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 9118b42c70b6..76883ff4f5bb 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1976,6 +1976,7 @@ void composite_disconnect(struct usb_gadget *gadget) * disconnect callbacks? */ spin_lock_irqsave(&cdev->lock, flags); + cdev->suspended = 0; if (cdev->config) reset_config(cdev); if (cdev->driver->disconnect) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 29cc5693e05c..7c96c4665178 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -261,7 +261,7 @@ struct fsg_common; struct fsg_common { struct usb_gadget *gadget; struct usb_composite_dev *cdev; - struct fsg_dev *fsg, *new_fsg; + struct fsg_dev *fsg; wait_queue_head_t io_wait; wait_queue_head_t fsg_wait; @@ -290,6 +290,7 @@ struct fsg_common { unsigned int bulk_out_maxpacket; enum fsg_state state; /* For exception handling */ unsigned int exception_req_tag; + void *exception_arg; enum data_direction data_dir; u32 data_size; @@ -391,7 +392,8 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) /* These routines may be called in process context or in_irq */ -static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +static void __raise_exception(struct fsg_common *common, enum fsg_state new_state, + void *arg) { unsigned long flags; @@ -404,6 +406,7 @@ static void raise_exception(struct fsg_common *common, enum fsg_state new_state) if (common->state <= new_state) { common->exception_req_tag = common->ep0_req_tag; common->state = new_state; + common->exception_arg = arg; if (common->thread_task) send_sig_info(SIGUSR1, SEND_SIG_PRIV, common->thread_task); @@ -411,6 +414,10 @@ static void raise_exception(struct fsg_common *common, enum fsg_state new_state) spin_unlock_irqrestore(&common->lock, flags); } +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ + __raise_exception(common, new_state, NULL); +} /*-------------------------------------------------------------------------*/ @@ -2285,16 +2292,16 @@ reset: static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct fsg_dev *fsg = fsg_from_func(f); - fsg->common->new_fsg = fsg; - raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + + __raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, fsg); return USB_GADGET_DELAYED_STATUS; } static void fsg_disable(struct usb_function *f) { struct fsg_dev *fsg = fsg_from_func(f); - fsg->common->new_fsg = NULL; - raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + + __raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, NULL); } @@ -2307,6 +2314,7 @@ static void handle_exception(struct fsg_common *common) enum fsg_state old_state; struct fsg_lun *curlun; unsigned int exception_req_tag; + struct fsg_dev *new_fsg; /* * Clear the existing signals. Anything but SIGUSR1 is converted @@ -2360,6 +2368,7 @@ static void handle_exception(struct fsg_common *common) common->next_buffhd_to_fill = &common->buffhds[0]; common->next_buffhd_to_drain = &common->buffhds[0]; exception_req_tag = common->exception_req_tag; + new_fsg = common->exception_arg; old_state = common->state; common->state = FSG_STATE_NORMAL; @@ -2413,8 +2422,8 @@ static void handle_exception(struct fsg_common *common) break; case FSG_STATE_CONFIG_CHANGE: - do_set_interface(common, common->new_fsg); - if (common->new_fsg) + do_set_interface(common, new_fsg); + if (new_fsg) usb_composite_setup_continue(common->cdev); break; @@ -2989,8 +2998,7 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) DBG(fsg, "unbind\n"); if (fsg->common->fsg == fsg) { - fsg->common->new_fsg = NULL; - raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + __raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, NULL); /* FIXME: make interruptible or killable somehow? */ wait_event(common->fsg_wait, common->fsg != fsg); } diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 5f1b14f3e5a0..bb6af6b5ac97 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -2265,7 +2265,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) default: break; } - + break; case USB_REQ_SET_ADDRESS: if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 87062d22134d..1f4c3fbd1df8 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -19,6 +19,7 @@ #include <linux/pm_runtime.h> #include <linux/sizes.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/sys_soc.h> #include <linux/uaccess.h> #include <linux/usb/ch9.h> @@ -2450,9 +2451,9 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr, if (usb3->forced_b_device) return -EBUSY; - if (!strncmp(buf, "host", strlen("host"))) + if (sysfs_streq(buf, "host")) new_mode_is_host = true; - else if (!strncmp(buf, "peripheral", strlen("peripheral"))) + else if (sysfs_streq(buf, "peripheral")) new_mode_is_host = false; else return -EINVAL; diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 77cc36efae95..0dbfa5c10703 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1629,6 +1629,10 @@ static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* see what we found out */ temp = check_reset_complete(fotg210, wIndex, status_reg, fotg210_readl(fotg210, status_reg)); + + /* restart schedule */ + fotg210->command |= CMD_RUN; + fotg210_writel(fotg210, fotg210->command, &fotg210->regs->command); } if (!(temp & (PORT_RESUME|PORT_RESET))) { diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index b457fdaff297..1fe3deec35cf 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -419,8 +419,7 @@ static void ohci_usb_reset (struct ohci_hcd *ohci) * other cases where the next software may expect clean state from the * "firmware". this is bus-neutral, unlike shutdown() methods. */ -static void -ohci_shutdown (struct usb_hcd *hcd) +static void _ohci_shutdown(struct usb_hcd *hcd) { struct ohci_hcd *ohci; @@ -436,6 +435,16 @@ ohci_shutdown (struct usb_hcd *hcd) ohci->rh_state = OHCI_RH_HALTED; } +static void ohci_shutdown(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + _ohci_shutdown(hcd); + spin_unlock_irqrestore(&ohci->lock, flags); +} + /*-------------------------------------------------------------------------* * HC functions *-------------------------------------------------------------------------*/ @@ -760,7 +769,7 @@ static void io_watchdog_func(struct timer_list *t) died: usb_hc_died(ohci_to_hcd(ohci)); ohci_dump(ohci); - ohci_shutdown(ohci_to_hcd(ohci)); + _ohci_shutdown(ohci_to_hcd(ohci)); goto done; } else { /* No write back because the done queue was empty */ diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 671bce18782c..2b0ccd150209 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -104,7 +104,7 @@ static int xhci_rcar_is_gen2(struct device *dev) return of_device_is_compatible(node, "renesas,xhci-r8a7790") || of_device_is_compatible(node, "renesas,xhci-r8a7791") || of_device_is_compatible(node, "renesas,xhci-r8a7793") || - of_device_is_compatible(node, "renensas,rcar-gen2-xhci"); + of_device_is_compatible(node, "renesas,rcar-gen2-xhci"); } static int xhci_rcar_is_gen3(struct device *dev) @@ -238,10 +238,15 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd) * pointers. So, this driver clears the AC64 bit of xhci->hcc_params * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in * xhci_gen_setup(). + * + * And, since the firmware/internal CPU control the USBSTS.STS_HALT + * and the process speed is down when the roothub port enters U3, + * long delay for the handshake of STS_HALT is neeed in xhci_suspend(). */ if (xhci_rcar_is_gen2(hcd->self.controller) || - xhci_rcar_is_gen3(hcd->self.controller)) - xhci->quirks |= XHCI_NO_64BIT_SUPPORT; + xhci_rcar_is_gen3(hcd->self.controller)) { + xhci->quirks |= XHCI_NO_64BIT_SUPPORT | XHCI_SLOW_SUSPEND; + } if (!xhci_rcar_wait_for_pll_active(hcd)) return -ETIMEDOUT; diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index dafc65911fc0..2ff7c911fbd0 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1194,6 +1194,16 @@ static int tegra_xusb_probe(struct platform_device *pdev) tegra_xusb_config(tegra, regs); + /* + * The XUSB Falcon microcontroller can only address 40 bits, so set + * the DMA mask accordingly. + */ + err = dma_set_mask_and_coherent(tegra->dev, DMA_BIT_MASK(40)); + if (err < 0) { + dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); + goto put_rpm; + } + err = tegra_xusb_load_firmware(tegra); if (err < 0) { dev_err(&pdev->dev, "failed to load firmware: %d\n", err); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 248cd7a8b163..03d1e552769b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3089,8 +3089,18 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, return; udev = (struct usb_device *) host_ep->hcpriv; vdev = xhci->devs[udev->slot_id]; + + /* + * vdev may be lost due to xHC restore error and re-initialization + * during S3/S4 resume. A new vdev will be allocated later by + * xhci_discover_or_reset_device() + */ + if (!udev->slot_id || !vdev) + return; ep_index = xhci_get_endpoint_index(&host_ep->desc); ep = &vdev->eps[ep_index]; + if (!ep) + return; /* Bail out if toggle is already being cleared by a endpoint reset */ if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) { diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index ba05dd80a020..f5bed9f29e56 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -866,19 +866,20 @@ static void iowarrior_disconnect(struct usb_interface *interface) dev = usb_get_intfdata(interface); mutex_lock(&iowarrior_open_disc_lock); usb_set_intfdata(interface, NULL); + /* prevent device read, write and ioctl */ + dev->present = 0; minor = dev->minor; + mutex_unlock(&iowarrior_open_disc_lock); + /* give back our minor - this will call close() locks need to be dropped at this point*/ - /* give back our minor */ usb_deregister_dev(interface, &iowarrior_class); mutex_lock(&dev->mutex); /* prevent device read, write and ioctl */ - dev->present = 0; mutex_unlock(&dev->mutex); - mutex_unlock(&iowarrior_open_disc_lock); if (dev->opened) { /* There is a process that holds a filedescriptor to the device , diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 27e9c78a791e..a32d61a79ab8 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -51,6 +51,7 @@ struct rio_usb_data { char *obuf, *ibuf; /* transfer buffers */ char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ wait_queue_head_t wait_q; /* for timeouts */ + struct mutex lock; /* general race avoidance */ }; static DEFINE_MUTEX(rio500_mutex); @@ -62,8 +63,10 @@ static int open_rio(struct inode *inode, struct file *file) /* against disconnect() */ mutex_lock(&rio500_mutex); + mutex_lock(&(rio->lock)); if (rio->isopen || !rio->present) { + mutex_unlock(&(rio->lock)); mutex_unlock(&rio500_mutex); return -EBUSY; } @@ -71,6 +74,7 @@ static int open_rio(struct inode *inode, struct file *file) init_waitqueue_head(&rio->wait_q); + mutex_unlock(&(rio->lock)); dev_info(&rio->rio_dev->dev, "Rio opened.\n"); mutex_unlock(&rio500_mutex); @@ -84,6 +88,7 @@ static int close_rio(struct inode *inode, struct file *file) /* against disconnect() */ mutex_lock(&rio500_mutex); + mutex_lock(&(rio->lock)); rio->isopen = 0; if (!rio->present) { @@ -95,6 +100,7 @@ static int close_rio(struct inode *inode, struct file *file) } else { dev_info(&rio->rio_dev->dev, "Rio closed.\n"); } + mutex_unlock(&(rio->lock)); mutex_unlock(&rio500_mutex); return 0; } @@ -109,7 +115,7 @@ static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg) int retries; int retval=0; - mutex_lock(&rio500_mutex); + mutex_lock(&(rio->lock)); /* Sanity check to make sure rio is connected, powered, etc */ if (rio->present == 0 || rio->rio_dev == NULL) { retval = -ENODEV; @@ -253,7 +259,7 @@ static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg) err_out: - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return retval; } @@ -273,12 +279,12 @@ write_rio(struct file *file, const char __user *buffer, int errn = 0; int intr; - intr = mutex_lock_interruptible(&rio500_mutex); + intr = mutex_lock_interruptible(&(rio->lock)); if (intr) return -EINTR; /* Sanity check to make sure rio is connected, powered, etc */ if (rio->present == 0 || rio->rio_dev == NULL) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return -ENODEV; } @@ -301,7 +307,7 @@ write_rio(struct file *file, const char __user *buffer, goto error; } if (signal_pending(current)) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return bytes_written ? bytes_written : -EINTR; } @@ -339,12 +345,12 @@ write_rio(struct file *file, const char __user *buffer, buffer += copy_size; } while (count > 0); - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return bytes_written ? bytes_written : -EIO; error: - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return errn; } @@ -361,12 +367,12 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) char *ibuf; int intr; - intr = mutex_lock_interruptible(&rio500_mutex); + intr = mutex_lock_interruptible(&(rio->lock)); if (intr) return -EINTR; /* Sanity check to make sure rio is connected, powered, etc */ if (rio->present == 0 || rio->rio_dev == NULL) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return -ENODEV; } @@ -377,11 +383,11 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) while (count > 0) { if (signal_pending(current)) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return read_count ? read_count : -EINTR; } if (!rio->rio_dev) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return -ENODEV; } this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; @@ -399,7 +405,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) count = this_read = partial; } else if (result == -ETIMEDOUT || result == 15) { /* FIXME: 15 ??? */ if (!maxretry--) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); dev_err(&rio->rio_dev->dev, "read_rio: maxretry timeout\n"); return -ETIME; @@ -409,19 +415,19 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) finish_wait(&rio->wait_q, &wait); continue; } else if (result != -EREMOTEIO) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); dev_err(&rio->rio_dev->dev, "Read Whoops - result:%d partial:%u this_read:%u\n", result, partial, this_read); return -EIO; } else { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return (0); } if (this_read) { if (copy_to_user(buffer, ibuf, this_read)) { - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return -EFAULT; } count -= this_read; @@ -429,7 +435,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) buffer += this_read; } } - mutex_unlock(&rio500_mutex); + mutex_unlock(&(rio->lock)); return read_count; } @@ -494,6 +500,8 @@ static int probe_rio(struct usb_interface *intf, } dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf); + mutex_init(&(rio->lock)); + usb_set_intfdata (intf, rio); rio->present = 1; bail_out: @@ -511,10 +519,12 @@ static void disconnect_rio(struct usb_interface *intf) if (rio) { usb_deregister_dev(intf, &usb_rio_class); + mutex_lock(&(rio->lock)); if (rio->isopen) { rio->isopen = 0; /* better let it finish - the release will do whats needed */ rio->rio_dev = NULL; + mutex_unlock(&(rio->lock)); mutex_unlock(&rio500_mutex); return; } @@ -524,6 +534,7 @@ static void disconnect_rio(struct usb_interface *intf) dev_info(&intf->dev, "USB Rio disconnected.\n"); rio->present = 0; + mutex_unlock(&(rio->lock)); } mutex_unlock(&rio500_mutex); } diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 7b306aa22d25..6715a128e6c8 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -92,7 +92,6 @@ static void yurex_delete(struct kref *kref) dev_dbg(&dev->interface->dev, "%s\n", __func__); - usb_put_dev(dev->udev); if (dev->cntl_urb) { usb_kill_urb(dev->cntl_urb); kfree(dev->cntl_req); @@ -108,6 +107,7 @@ static void yurex_delete(struct kref *kref) dev->int_buffer, dev->urb->transfer_dma); usb_free_urb(dev->urb); } + usb_put_dev(dev->udev); kfree(dev); } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c1582fbd1150..38e920ac7f82 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -968,6 +968,11 @@ static const struct usb_device_id option_ids[] = { { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7B) }, { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x06, 0x7C) }, + /* Motorola devices */ + { USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x2a70, 0xff, 0xff, 0xff) }, /* mdm6600 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x2e0a, 0xff, 0xff, 0xff) }, /* mdm9600 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x4281, 0x0a, 0x00, 0xfc) }, /* mdm ram dl */ + { USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x900e, 0xff, 0xff, 0xff) }, /* mdm qc dl */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, @@ -1549,6 +1554,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G v2 */ .driver_info = RSVD(2) }, { USB_DEVICE_INTERFACE_CLASS(ZTE_VENDOR_ID, 0x1476, 0xff) }, /* GosunCn ZTE WeLink ME3630 (ECM/NCM mode) */ + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1481, 0xff, 0x00, 0x00) }, /* ZTE MF871A */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) }, @@ -1952,11 +1958,15 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */ .driver_info = RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e3d, 0xff), /* D-Link DWM-222 A2 */ + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */ { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2031, 0xff), /* Olicard 600 */ .driver_info = RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2060, 0xff), /* BroadMobi BM818 */ + .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) }, /* OLICARD300 - MT6225 */ { USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) }, { USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) }, diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index cc794e25a0b6..1d9ce9cbc831 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -38,7 +38,7 @@ MODULE_LICENSE("GPL"); static int auto_delink_en = 1; module_param(auto_delink_en, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(auto_delink_en, "enable auto delink"); +MODULE_PARM_DESC(auto_delink_en, "auto delink mode (0=firmware, 1=software [default])"); #ifdef CONFIG_REALTEK_AUTOPM static int ss_en = 1; @@ -996,12 +996,15 @@ static int init_realtek_cr(struct us_data *us) goto INIT_FAIL; } - if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) || - CHECK_FW_VER(chip, 0x5901)) - SET_AUTO_DELINK(chip); - if (STATUS_LEN(chip) == 16) { - if (SUPPORT_AUTO_DELINK(chip)) + if (CHECK_PID(chip, 0x0138) || CHECK_PID(chip, 0x0158) || + CHECK_PID(chip, 0x0159)) { + if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) || + CHECK_FW_VER(chip, 0x5901)) SET_AUTO_DELINK(chip); + if (STATUS_LEN(chip) == 16) { + if (SUPPORT_AUTO_DELINK(chip)) + SET_AUTO_DELINK(chip); + } } #ifdef CONFIG_REALTEK_AUTOPM if (ss_en) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ea0d27a94afe..1cd9b6305b06 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2100,7 +2100,7 @@ UNUSUAL_DEV( 0x14cd, 0x6600, 0x0201, 0x0201, US_FL_IGNORE_RESIDUE ), /* Reported by Michael Büsch <m@bues.ch> */ -UNUSUAL_DEV( 0x152d, 0x0567, 0x0114, 0x0116, +UNUSUAL_DEV( 0x152d, 0x0567, 0x0114, 0x0117, "JMicron", "USB to ATA/ATAPI Bridge", USB_SC_DEVICE, USB_PR_DEVICE, NULL, diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index fba32d84e578..bcfdb55fd198 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -379,7 +379,8 @@ static enum tcpm_state tcpm_default_state(struct tcpm_port *port) return SNK_UNATTACHED; else if (port->try_role == TYPEC_SOURCE) return SRC_UNATTACHED; - else if (port->tcpc->config->default_role == TYPEC_SINK) + else if (port->tcpc->config && + port->tcpc->config->default_role == TYPEC_SINK) return SNK_UNATTACHED; /* Fall through to return SRC_UNATTACHED */ } else if (port->port_type == TYPEC_PORT_SNK) { @@ -586,7 +587,20 @@ static void tcpm_debugfs_init(struct tcpm_port *port) static void tcpm_debugfs_exit(struct tcpm_port *port) { + int i; + + mutex_lock(&port->logbuffer_lock); + for (i = 0; i < LOG_BUFFER_ENTRIES; i++) { + kfree(port->logbuffer[i]); + port->logbuffer[i] = NULL; + } + mutex_unlock(&port->logbuffer_lock); + debugfs_remove(port->dentry); + if (list_empty(&rootdir->d_subdirs)) { + debugfs_remove(rootdir); + rootdir = NULL; + } } #else @@ -1095,7 +1109,8 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, break; case CMD_ATTENTION: /* Attention command does not have response */ - typec_altmode_attention(adev, p[1]); + if (adev) + typec_altmode_attention(adev, p[1]); return 0; default: break; @@ -1147,20 +1162,26 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, } break; case CMD_ENTER_MODE: - typec_altmode_update_active(pdev, true); - - if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) { - response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE); - response[0] |= VDO_OPOS(adev->mode); - return 1; + if (adev && pdev) { + typec_altmode_update_active(pdev, true); + + if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) { + response[0] = VDO(adev->svid, 1, + CMD_EXIT_MODE); + response[0] |= VDO_OPOS(adev->mode); + return 1; + } } return 0; case CMD_EXIT_MODE: - typec_altmode_update_active(pdev, false); + if (adev && pdev) { + typec_altmode_update_active(pdev, false); - /* Back to USB Operation */ - WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, - NULL)); + /* Back to USB Operation */ + WARN_ON(typec_altmode_notify(adev, + TYPEC_STATE_USB, + NULL)); + } break; default: break; @@ -1170,8 +1191,10 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, switch (cmd) { case CMD_ENTER_MODE: /* Back to USB Operation */ - WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, - NULL)); + if (adev) + WARN_ON(typec_altmode_notify(adev, + TYPEC_STATE_USB, + NULL)); break; default: break; @@ -1182,7 +1205,8 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, } /* Informing the alternate mode drivers about everything */ - typec_altmode_vdm(adev, p[0], &p[1], cnt); + if (adev) + typec_altmode_vdm(adev, p[0], &p[1], cnt); return rlen; } @@ -1422,7 +1446,7 @@ static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, else if ((pdo_min_voltage(pdo[i]) == pdo_min_voltage(pdo[i - 1])) && (pdo_max_voltage(pdo[i]) == - pdo_min_voltage(pdo[i - 1]))) + pdo_max_voltage(pdo[i - 1]))) return PDO_ERR_DUPE_PDO; break; /* @@ -4114,7 +4138,7 @@ static int tcpm_try_role(const struct typec_capability *cap, int role) mutex_lock(&port->lock); if (tcpc->try_role) ret = tcpc->try_role(tcpc, role); - if (!ret && !tcpc->config->try_role_hw) + if (!ret && (!tcpc->config || !tcpc->config->try_role_hw)) port->try_role = role; port->try_src_count = 0; port->try_snk_count = 0; @@ -4701,7 +4725,7 @@ static int tcpm_copy_caps(struct tcpm_port *port, port->typec_caps.prefer_role = tcfg->default_role; port->typec_caps.type = tcfg->type; port->typec_caps.data = tcfg->data; - port->self_powered = port->tcpc->config->self_powered; + port->self_powered = tcfg->self_powered; return 0; } diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index f7a79a23ebed..8e9f8fba55af 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1018,7 +1018,7 @@ release_fw: ******************************************************************************/ static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode) { - int err; + int err = 0; while (flash_mode != FLASH_NOT_NEEDED) { err = do_flash(uc, flash_mode); diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 054391f30fa8..ad830abe1021 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -650,12 +650,13 @@ unpin_exit: } static long vfio_sync_unpin(struct vfio_dma *dma, struct vfio_domain *domain, - struct list_head *regions) + struct list_head *regions, + struct iommu_iotlb_gather *iotlb_gather) { long unlocked = 0; struct vfio_regions *entry, *next; - iommu_tlb_sync(domain->domain); + iommu_tlb_sync(domain->domain, iotlb_gather); list_for_each_entry_safe(entry, next, regions, list) { unlocked += vfio_unpin_pages_remote(dma, @@ -685,18 +686,19 @@ static size_t unmap_unpin_fast(struct vfio_domain *domain, struct vfio_dma *dma, dma_addr_t *iova, size_t len, phys_addr_t phys, long *unlocked, struct list_head *unmapped_list, - int *unmapped_cnt) + int *unmapped_cnt, + struct iommu_iotlb_gather *iotlb_gather) { size_t unmapped = 0; struct vfio_regions *entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (entry) { - unmapped = iommu_unmap_fast(domain->domain, *iova, len); + unmapped = iommu_unmap_fast(domain->domain, *iova, len, + iotlb_gather); if (!unmapped) { kfree(entry); } else { - iommu_tlb_range_add(domain->domain, *iova, unmapped); entry->iova = *iova; entry->phys = phys; entry->len = unmapped; @@ -712,8 +714,8 @@ static size_t unmap_unpin_fast(struct vfio_domain *domain, * or in case of errors. */ if (*unmapped_cnt >= VFIO_IOMMU_TLB_SYNC_MAX || !unmapped) { - *unlocked += vfio_sync_unpin(dma, domain, - unmapped_list); + *unlocked += vfio_sync_unpin(dma, domain, unmapped_list, + iotlb_gather); *unmapped_cnt = 0; } @@ -744,6 +746,7 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, dma_addr_t iova = dma->iova, end = dma->iova + dma->size; struct vfio_domain *domain, *d; LIST_HEAD(unmapped_region_list); + struct iommu_iotlb_gather iotlb_gather; int unmapped_region_cnt = 0; long unlocked = 0; @@ -768,6 +771,7 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, cond_resched(); } + iommu_iotlb_gather_init(&iotlb_gather); while (iova < end) { size_t unmapped, len; phys_addr_t phys, next; @@ -796,7 +800,8 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, */ unmapped = unmap_unpin_fast(domain, dma, &iova, len, phys, &unlocked, &unmapped_region_list, - &unmapped_region_cnt); + &unmapped_region_cnt, + &iotlb_gather); if (!unmapped) { unmapped = unmap_unpin_slow(domain, dma, &iova, len, phys, &unlocked); @@ -807,8 +812,10 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, dma->iommu_mapped = false; - if (unmapped_region_cnt) - unlocked += vfio_sync_unpin(dma, domain, &unmapped_region_list); + if (unmapped_region_cnt) { + unlocked += vfio_sync_unpin(dma, domain, &unmapped_region_list, + &iotlb_gather); + } if (do_accounting) { vfio_lock_acct(dma, -unlocked, true); diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index 9e90e969af55..7804869c6a31 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -22,6 +22,12 @@ * Using this limit prevents one virtqueue from starving others. */ #define VHOST_TEST_WEIGHT 0x80000 +/* Max number of packets transferred before requeueing the job. + * Using this limit prevents one virtqueue from starving others with + * pkts. + */ +#define VHOST_TEST_PKT_WEIGHT 256 + enum { VHOST_TEST_VQ = 0, VHOST_TEST_VQ_MAX = 1, @@ -80,10 +86,8 @@ static void handle_vq(struct vhost_test *n) } vhost_add_used_and_signal(&n->dev, vq, head, 0); total_len += len; - if (unlikely(total_len >= VHOST_TEST_WEIGHT)) { - vhost_poll_queue(&vq->poll); + if (unlikely(vhost_exceeds_weight(vq, 0, total_len))) break; - } } mutex_unlock(&vq->mutex); @@ -115,7 +119,8 @@ static int vhost_test_open(struct inode *inode, struct file *f) dev = &n->dev; vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ]; n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick; - vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX); + vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX, UIO_MAXIOV, + VHOST_TEST_PKT_WEIGHT, VHOST_TEST_WEIGHT); f->private_data = n; diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 0536f8526359..36ca2cf419bf 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -203,7 +203,6 @@ EXPORT_SYMBOL_GPL(vhost_poll_init); int vhost_poll_start(struct vhost_poll *poll, struct file *file) { __poll_t mask; - int ret = 0; if (poll->wqh) return 0; @@ -213,10 +212,10 @@ int vhost_poll_start(struct vhost_poll *poll, struct file *file) vhost_poll_wakeup(&poll->wait, 0, 0, poll_to_key(mask)); if (mask & EPOLLERR) { vhost_poll_stop(poll); - ret = -EINVAL; + return -EINVAL; } - return ret; + return 0; } EXPORT_SYMBOL_GPL(vhost_poll_start); @@ -298,160 +297,6 @@ static void vhost_vq_meta_reset(struct vhost_dev *d) __vhost_vq_meta_reset(d->vqs[i]); } -#if VHOST_ARCH_CAN_ACCEL_UACCESS -static void vhost_map_unprefetch(struct vhost_map *map) -{ - kfree(map->pages); - map->pages = NULL; - map->npages = 0; - map->addr = NULL; -} - -static void vhost_uninit_vq_maps(struct vhost_virtqueue *vq) -{ - struct vhost_map *map[VHOST_NUM_ADDRS]; - int i; - - spin_lock(&vq->mmu_lock); - for (i = 0; i < VHOST_NUM_ADDRS; i++) { - map[i] = rcu_dereference_protected(vq->maps[i], - lockdep_is_held(&vq->mmu_lock)); - if (map[i]) - rcu_assign_pointer(vq->maps[i], NULL); - } - spin_unlock(&vq->mmu_lock); - - synchronize_rcu(); - - for (i = 0; i < VHOST_NUM_ADDRS; i++) - if (map[i]) - vhost_map_unprefetch(map[i]); - -} - -static void vhost_reset_vq_maps(struct vhost_virtqueue *vq) -{ - int i; - - vhost_uninit_vq_maps(vq); - for (i = 0; i < VHOST_NUM_ADDRS; i++) - vq->uaddrs[i].size = 0; -} - -static bool vhost_map_range_overlap(struct vhost_uaddr *uaddr, - unsigned long start, - unsigned long end) -{ - if (unlikely(!uaddr->size)) - return false; - - return !(end < uaddr->uaddr || start > uaddr->uaddr - 1 + uaddr->size); -} - -static void vhost_invalidate_vq_start(struct vhost_virtqueue *vq, - int index, - unsigned long start, - unsigned long end) -{ - struct vhost_uaddr *uaddr = &vq->uaddrs[index]; - struct vhost_map *map; - int i; - - if (!vhost_map_range_overlap(uaddr, start, end)) - return; - - spin_lock(&vq->mmu_lock); - ++vq->invalidate_count; - - map = rcu_dereference_protected(vq->maps[index], - lockdep_is_held(&vq->mmu_lock)); - if (map) { - if (uaddr->write) { - for (i = 0; i < map->npages; i++) - set_page_dirty(map->pages[i]); - } - rcu_assign_pointer(vq->maps[index], NULL); - } - spin_unlock(&vq->mmu_lock); - - if (map) { - synchronize_rcu(); - vhost_map_unprefetch(map); - } -} - -static void vhost_invalidate_vq_end(struct vhost_virtqueue *vq, - int index, - unsigned long start, - unsigned long end) -{ - if (!vhost_map_range_overlap(&vq->uaddrs[index], start, end)) - return; - - spin_lock(&vq->mmu_lock); - --vq->invalidate_count; - spin_unlock(&vq->mmu_lock); -} - -static int vhost_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct vhost_dev *dev = container_of(mn, struct vhost_dev, - mmu_notifier); - int i, j; - - if (!mmu_notifier_range_blockable(range)) - return -EAGAIN; - - for (i = 0; i < dev->nvqs; i++) { - struct vhost_virtqueue *vq = dev->vqs[i]; - - for (j = 0; j < VHOST_NUM_ADDRS; j++) - vhost_invalidate_vq_start(vq, j, - range->start, - range->end); - } - - return 0; -} - -static void vhost_invalidate_range_end(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct vhost_dev *dev = container_of(mn, struct vhost_dev, - mmu_notifier); - int i, j; - - for (i = 0; i < dev->nvqs; i++) { - struct vhost_virtqueue *vq = dev->vqs[i]; - - for (j = 0; j < VHOST_NUM_ADDRS; j++) - vhost_invalidate_vq_end(vq, j, - range->start, - range->end); - } -} - -static const struct mmu_notifier_ops vhost_mmu_notifier_ops = { - .invalidate_range_start = vhost_invalidate_range_start, - .invalidate_range_end = vhost_invalidate_range_end, -}; - -static void vhost_init_maps(struct vhost_dev *dev) -{ - struct vhost_virtqueue *vq; - int i, j; - - dev->mmu_notifier.ops = &vhost_mmu_notifier_ops; - - for (i = 0; i < dev->nvqs; ++i) { - vq = dev->vqs[i]; - for (j = 0; j < VHOST_NUM_ADDRS; j++) - RCU_INIT_POINTER(vq->maps[j], NULL); - } -} -#endif - static void vhost_vq_reset(struct vhost_dev *dev, struct vhost_virtqueue *vq) { @@ -480,11 +325,7 @@ static void vhost_vq_reset(struct vhost_dev *dev, vq->busyloop_timeout = 0; vq->umem = NULL; vq->iotlb = NULL; - vq->invalidate_count = 0; __vhost_vq_meta_reset(vq); -#if VHOST_ARCH_CAN_ACCEL_UACCESS - vhost_reset_vq_maps(vq); -#endif } static int vhost_worker(void *data) @@ -634,9 +475,7 @@ void vhost_dev_init(struct vhost_dev *dev, INIT_LIST_HEAD(&dev->read_list); INIT_LIST_HEAD(&dev->pending_list); spin_lock_init(&dev->iotlb_lock); -#if VHOST_ARCH_CAN_ACCEL_UACCESS - vhost_init_maps(dev); -#endif + for (i = 0; i < dev->nvqs; ++i) { vq = dev->vqs[i]; @@ -645,7 +484,6 @@ void vhost_dev_init(struct vhost_dev *dev, vq->heads = NULL; vq->dev = dev; mutex_init(&vq->mutex); - spin_lock_init(&vq->mmu_lock); vhost_vq_reset(dev, vq); if (vq->handle_kick) vhost_poll_init(&vq->poll, vq->handle_kick, @@ -725,18 +563,7 @@ long vhost_dev_set_owner(struct vhost_dev *dev) if (err) goto err_cgroup; -#if VHOST_ARCH_CAN_ACCEL_UACCESS - err = mmu_notifier_register(&dev->mmu_notifier, dev->mm); - if (err) - goto err_mmu_notifier; -#endif - return 0; - -#if VHOST_ARCH_CAN_ACCEL_UACCESS -err_mmu_notifier: - vhost_dev_free_iovecs(dev); -#endif err_cgroup: kthread_stop(worker); dev->worker = NULL; @@ -827,107 +654,6 @@ static void vhost_clear_msg(struct vhost_dev *dev) spin_unlock(&dev->iotlb_lock); } -#if VHOST_ARCH_CAN_ACCEL_UACCESS -static void vhost_setup_uaddr(struct vhost_virtqueue *vq, - int index, unsigned long uaddr, - size_t size, bool write) -{ - struct vhost_uaddr *addr = &vq->uaddrs[index]; - - addr->uaddr = uaddr; - addr->size = size; - addr->write = write; -} - -static void vhost_setup_vq_uaddr(struct vhost_virtqueue *vq) -{ - vhost_setup_uaddr(vq, VHOST_ADDR_DESC, - (unsigned long)vq->desc, - vhost_get_desc_size(vq, vq->num), - false); - vhost_setup_uaddr(vq, VHOST_ADDR_AVAIL, - (unsigned long)vq->avail, - vhost_get_avail_size(vq, vq->num), - false); - vhost_setup_uaddr(vq, VHOST_ADDR_USED, - (unsigned long)vq->used, - vhost_get_used_size(vq, vq->num), - true); -} - -static int vhost_map_prefetch(struct vhost_virtqueue *vq, - int index) -{ - struct vhost_map *map; - struct vhost_uaddr *uaddr = &vq->uaddrs[index]; - struct page **pages; - int npages = DIV_ROUND_UP(uaddr->size, PAGE_SIZE); - int npinned; - void *vaddr, *v; - int err; - int i; - - spin_lock(&vq->mmu_lock); - - err = -EFAULT; - if (vq->invalidate_count) - goto err; - - err = -ENOMEM; - map = kmalloc(sizeof(*map), GFP_ATOMIC); - if (!map) - goto err; - - pages = kmalloc_array(npages, sizeof(struct page *), GFP_ATOMIC); - if (!pages) - goto err_pages; - - err = EFAULT; - npinned = __get_user_pages_fast(uaddr->uaddr, npages, - uaddr->write, pages); - if (npinned > 0) - release_pages(pages, npinned); - if (npinned != npages) - goto err_gup; - - for (i = 0; i < npinned; i++) - if (PageHighMem(pages[i])) - goto err_gup; - - vaddr = v = page_address(pages[0]); - - /* For simplicity, fallback to userspace address if VA is not - * contigious. - */ - for (i = 1; i < npinned; i++) { - v += PAGE_SIZE; - if (v != page_address(pages[i])) - goto err_gup; - } - - map->addr = vaddr + (uaddr->uaddr & (PAGE_SIZE - 1)); - map->npages = npages; - map->pages = pages; - - rcu_assign_pointer(vq->maps[index], map); - /* No need for a synchronize_rcu(). This function should be - * called by dev->worker so we are serialized with all - * readers. - */ - spin_unlock(&vq->mmu_lock); - - return 0; - -err_gup: - kfree(pages); -err_pages: - kfree(map); -err: - spin_unlock(&vq->mmu_lock); - return err; -} -#endif - void vhost_dev_cleanup(struct vhost_dev *dev) { int i; @@ -957,16 +683,8 @@ void vhost_dev_cleanup(struct vhost_dev *dev) kthread_stop(dev->worker); dev->worker = NULL; } - if (dev->mm) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - mmu_notifier_unregister(&dev->mmu_notifier, dev->mm); -#endif + if (dev->mm) mmput(dev->mm); - } -#if VHOST_ARCH_CAN_ACCEL_UACCESS - for (i = 0; i < dev->nvqs; i++) - vhost_uninit_vq_maps(dev->vqs[i]); -#endif dev->mm = NULL; } EXPORT_SYMBOL_GPL(vhost_dev_cleanup); @@ -1195,26 +913,6 @@ static inline void __user *__vhost_get_user(struct vhost_virtqueue *vq, static inline int vhost_put_avail_event(struct vhost_virtqueue *vq) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_used *used; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_USED]); - if (likely(map)) { - used = map->addr; - *((__virtio16 *)&used->ring[vq->num]) = - cpu_to_vhost16(vq, vq->avail_idx); - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_put_user(vq, cpu_to_vhost16(vq, vq->avail_idx), vhost_avail_event(vq)); } @@ -1223,27 +921,6 @@ static inline int vhost_put_used(struct vhost_virtqueue *vq, struct vring_used_elem *head, int idx, int count) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_used *used; - size_t size; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_USED]); - if (likely(map)) { - used = map->addr; - size = count * sizeof(*head); - memcpy(used->ring + idx, head, size); - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_copy_to_user(vq, vq->used->ring + idx, head, count * sizeof(*head)); } @@ -1251,25 +928,6 @@ static inline int vhost_put_used(struct vhost_virtqueue *vq, static inline int vhost_put_used_flags(struct vhost_virtqueue *vq) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_used *used; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_USED]); - if (likely(map)) { - used = map->addr; - used->flags = cpu_to_vhost16(vq, vq->used_flags); - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_put_user(vq, cpu_to_vhost16(vq, vq->used_flags), &vq->used->flags); } @@ -1277,25 +935,6 @@ static inline int vhost_put_used_flags(struct vhost_virtqueue *vq) static inline int vhost_put_used_idx(struct vhost_virtqueue *vq) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_used *used; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_USED]); - if (likely(map)) { - used = map->addr; - used->idx = cpu_to_vhost16(vq, vq->last_used_idx); - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_put_user(vq, cpu_to_vhost16(vq, vq->last_used_idx), &vq->used->idx); } @@ -1341,50 +980,12 @@ static void vhost_dev_unlock_vqs(struct vhost_dev *d) static inline int vhost_get_avail_idx(struct vhost_virtqueue *vq, __virtio16 *idx) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_avail *avail; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_AVAIL]); - if (likely(map)) { - avail = map->addr; - *idx = avail->idx; - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_get_avail(vq, *idx, &vq->avail->idx); } static inline int vhost_get_avail_head(struct vhost_virtqueue *vq, __virtio16 *head, int idx) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_avail *avail; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_AVAIL]); - if (likely(map)) { - avail = map->addr; - *head = avail->ring[idx & (vq->num - 1)]; - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_get_avail(vq, *head, &vq->avail->ring[idx & (vq->num - 1)]); } @@ -1392,98 +993,24 @@ static inline int vhost_get_avail_head(struct vhost_virtqueue *vq, static inline int vhost_get_avail_flags(struct vhost_virtqueue *vq, __virtio16 *flags) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_avail *avail; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_AVAIL]); - if (likely(map)) { - avail = map->addr; - *flags = avail->flags; - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_get_avail(vq, *flags, &vq->avail->flags); } static inline int vhost_get_used_event(struct vhost_virtqueue *vq, __virtio16 *event) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_avail *avail; - - if (!vq->iotlb) { - rcu_read_lock(); - map = rcu_dereference(vq->maps[VHOST_ADDR_AVAIL]); - if (likely(map)) { - avail = map->addr; - *event = (__virtio16)avail->ring[vq->num]; - rcu_read_unlock(); - return 0; - } - rcu_read_unlock(); - } -#endif - return vhost_get_avail(vq, *event, vhost_used_event(vq)); } static inline int vhost_get_used_idx(struct vhost_virtqueue *vq, __virtio16 *idx) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_used *used; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_USED]); - if (likely(map)) { - used = map->addr; - *idx = used->idx; - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_get_used(vq, *idx, &vq->used->idx); } static inline int vhost_get_desc(struct vhost_virtqueue *vq, struct vring_desc *desc, int idx) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - struct vhost_map *map; - struct vring_desc *d; - - if (!vq->iotlb) { - rcu_read_lock(); - - map = rcu_dereference(vq->maps[VHOST_ADDR_DESC]); - if (likely(map)) { - d = map->addr; - *desc = *(d + idx); - rcu_read_unlock(); - return 0; - } - - rcu_read_unlock(); - } -#endif - return vhost_copy_from_user(vq, desc, vq->desc + idx, sizeof(*desc)); } @@ -1824,32 +1351,12 @@ static bool iotlb_access_ok(struct vhost_virtqueue *vq, return true; } -#if VHOST_ARCH_CAN_ACCEL_UACCESS -static void vhost_vq_map_prefetch(struct vhost_virtqueue *vq) -{ - struct vhost_map __rcu *map; - int i; - - for (i = 0; i < VHOST_NUM_ADDRS; i++) { - rcu_read_lock(); - map = rcu_dereference(vq->maps[i]); - rcu_read_unlock(); - if (unlikely(!map)) - vhost_map_prefetch(vq, i); - } -} -#endif - int vq_meta_prefetch(struct vhost_virtqueue *vq) { unsigned int num = vq->num; - if (!vq->iotlb) { -#if VHOST_ARCH_CAN_ACCEL_UACCESS - vhost_vq_map_prefetch(vq); -#endif + if (!vq->iotlb) return 1; - } return iotlb_access_ok(vq, VHOST_ACCESS_RO, (u64)(uintptr_t)vq->desc, vhost_get_desc_size(vq, num), VHOST_ADDR_DESC) && @@ -2060,16 +1567,6 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d, mutex_lock(&vq->mutex); -#if VHOST_ARCH_CAN_ACCEL_UACCESS - /* Unregister MMU notifer to allow invalidation callback - * can access vq->uaddrs[] without holding a lock. - */ - if (d->mm) - mmu_notifier_unregister(&d->mmu_notifier, d->mm); - - vhost_uninit_vq_maps(vq); -#endif - switch (ioctl) { case VHOST_SET_VRING_NUM: r = vhost_vring_set_num(d, vq, argp); @@ -2081,13 +1578,6 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d, BUG(); } -#if VHOST_ARCH_CAN_ACCEL_UACCESS - vhost_setup_vq_uaddr(vq); - - if (d->mm) - mmu_notifier_register(&d->mmu_notifier, d->mm); -#endif - mutex_unlock(&vq->mutex); return r; @@ -2688,7 +2178,7 @@ static int get_indirect(struct vhost_virtqueue *vq, /* If this is an input descriptor, increment that count. */ if (access == VHOST_ACCESS_WO) { *in_num += ret; - if (unlikely(log)) { + if (unlikely(log && ret)) { log[*log_num].addr = vhost64_to_cpu(vq, desc.addr); log[*log_num].len = vhost32_to_cpu(vq, desc.len); ++*log_num; @@ -2829,7 +2319,7 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq, /* If this is an input descriptor, * increment that count. */ *in_num += ret; - if (unlikely(log)) { + if (unlikely(log && ret)) { log[*log_num].addr = vhost64_to_cpu(vq, desc.addr); log[*log_num].len = vhost32_to_cpu(vq, desc.len); ++*log_num; diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 42a8c2a13ab1..e9ed2722b633 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -12,9 +12,6 @@ #include <linux/virtio_config.h> #include <linux/virtio_ring.h> #include <linux/atomic.h> -#include <linux/pagemap.h> -#include <linux/mmu_notifier.h> -#include <asm/cacheflush.h> struct vhost_work; typedef void (*vhost_work_fn_t)(struct vhost_work *work); @@ -83,24 +80,6 @@ enum vhost_uaddr_type { VHOST_NUM_ADDRS = 3, }; -struct vhost_map { - int npages; - void *addr; - struct page **pages; -}; - -struct vhost_uaddr { - unsigned long uaddr; - size_t size; - bool write; -}; - -#if defined(CONFIG_MMU_NOTIFIER) && ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 0 -#define VHOST_ARCH_CAN_ACCEL_UACCESS 0 -#else -#define VHOST_ARCH_CAN_ACCEL_UACCESS 0 -#endif - /* The virtqueue structure describes a queue attached to a device. */ struct vhost_virtqueue { struct vhost_dev *dev; @@ -111,22 +90,7 @@ struct vhost_virtqueue { struct vring_desc __user *desc; struct vring_avail __user *avail; struct vring_used __user *used; - -#if VHOST_ARCH_CAN_ACCEL_UACCESS - /* Read by memory accessors, modified by meta data - * prefetching, MMU notifier and vring ioctl(). - * Synchonrized through mmu_lock (writers) and RCU (writers - * and readers). - */ - struct vhost_map __rcu *maps[VHOST_NUM_ADDRS]; - /* Read by MMU notifier, modified by vring ioctl(), - * synchronized through MMU notifier - * registering/unregistering. - */ - struct vhost_uaddr uaddrs[VHOST_NUM_ADDRS]; -#endif const struct vhost_umem_node *meta_iotlb[VHOST_NUM_ADDRS]; - struct file *kick; struct eventfd_ctx *call_ctx; struct eventfd_ctx *error_ctx; @@ -181,8 +145,6 @@ struct vhost_virtqueue { bool user_be; #endif u32 busyloop_timeout; - spinlock_t mmu_lock; - int invalidate_count; }; struct vhost_msg_node { @@ -196,9 +158,6 @@ struct vhost_msg_node { struct vhost_dev { struct mm_struct *mm; -#ifdef CONFIG_MMU_NOTIFIER - struct mmu_notifier mmu_notifier; -#endif struct mutex mutex; struct vhost_virtqueue **vqs; int nvqs; diff --git a/drivers/video/fbdev/acornfb.c b/drivers/video/fbdev/acornfb.c index 92f23e3bc27a..7cacae5a8797 100644 --- a/drivers/video/fbdev/acornfb.c +++ b/drivers/video/fbdev/acornfb.c @@ -858,6 +858,7 @@ static void acornfb_parse_dram(char *opt) case 'M': case 'm': size *= 1024; + /* Fall through */ case 'K': case 'k': size *= 1024; diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c index 90eca64e3144..702cca59bda1 100644 --- a/drivers/video/fbdev/omap/omapfb_main.c +++ b/drivers/video/fbdev/omap/omapfb_main.c @@ -447,6 +447,7 @@ static int set_color_mode(struct omapfb_plane_struct *plane, return 0; case 12: var->bits_per_pixel = 16; + /* fall through */ case 16: if (plane->fbdev->panel->bpp == 12) plane->color_mode = OMAPFB_COLOR_RGB444; @@ -1534,20 +1535,27 @@ static void omapfb_free_resources(struct omapfb_device *fbdev, int state) case OMAPFB_ACTIVE: for (i = 0; i < fbdev->mem_desc.region_cnt; i++) unregister_framebuffer(fbdev->fb_info[i]); + /* fall through */ case 7: omapfb_unregister_sysfs(fbdev); + /* fall through */ case 6: if (fbdev->panel->disable) fbdev->panel->disable(fbdev->panel); + /* fall through */ case 5: omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED); + /* fall through */ case 4: planes_cleanup(fbdev); + /* fall through */ case 3: ctrl_cleanup(fbdev); + /* fall through */ case 2: if (fbdev->panel->cleanup) fbdev->panel->cleanup(fbdev->panel); + /* fall through */ case 1: dev_set_drvdata(fbdev->dev, NULL); kfree(fbdev); diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index c8be1c4f5b55..bdc08244a648 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -566,13 +566,17 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, unmap_release: err_idx = i; - i = head; + + if (indirect) + i = 0; + else + i = head; for (n = 0; n < total_sg; n++) { if (i == err_idx) break; vring_unmap_one_split(vq, &desc[i]); - i = virtio16_to_cpu(_vq->vdev, vq->split.vring.desc[i].next); + i = virtio16_to_cpu(_vq->vdev, desc[i].next); } if (indirect) diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index b9b2d06b3879..668a1c704f28 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -235,6 +235,7 @@ static long ar7_wdt_ioctl(struct file *file, ar7_wdt_update_margin(new_margin); ar7_wdt_kick(1); spin_unlock(&wdt_lock); + /* Fall through */ case WDIOC_GETTIMEOUT: if (put_user(margin, (int *)arg)) diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c index 1b2cf5b95a89..c3c93e00b320 100644 --- a/drivers/watchdog/pcwd.c +++ b/drivers/watchdog/pcwd.c @@ -651,7 +651,7 @@ static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; pcwd_keepalive(); - /* Fall */ + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(heartbeat, argp); diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index 41a2a11535a6..b35f7be20c00 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -134,7 +134,7 @@ static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return -EINVAL; riowd_timeout = (new_margin + 59) / 60; riowd_writereg(p, riowd_timeout, WDTO_INDEX); - /* Fall */ + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(riowd_timeout * 60, (int __user *)argp); diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c index 5a6ced7a7e8f..202fc8d8ca5f 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c @@ -202,6 +202,7 @@ static long sbwdog_ioctl(struct file *file, unsigned int cmd, timeout = time; sbwdog_set(user_dog, timeout); sbwdog_pet(user_dog); + /* Fall through */ case WDIOC_GETTIMEOUT: /* diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c index efd7996694de..46268309ee9b 100644 --- a/drivers/watchdog/scx200_wdt.c +++ b/drivers/watchdog/scx200_wdt.c @@ -186,6 +186,7 @@ static long scx200_wdt_ioctl(struct file *file, unsigned int cmd, margin = new_margin; scx200_wdt_update_margin(); scx200_wdt_ping(); + /* Fall through */ case WDIOC_GETTIMEOUT: if (put_user(margin, p)) return -EFAULT; diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index 0650100fad00..7d278b37e083 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c @@ -389,7 +389,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (wdt_set_heartbeat(new_heartbeat)) return -EINVAL; wdt_ping(); - /* Fall */ + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); default: diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c index 4eacfb1ce1ac..eb729d704836 100644 --- a/drivers/watchdog/wdt285.c +++ b/drivers/watchdog/wdt285.c @@ -168,7 +168,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, soft_margin = new_margin; reload = soft_margin * (mem_fclk_21285 / 256); watchdog_ping(); - /* Fall */ + /* Fall through */ case WDIOC_GETTIMEOUT: ret = put_user(soft_margin, int_arg); break; diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c index 567005d7598e..5c52c73e1839 100644 --- a/drivers/watchdog/wdt977.c +++ b/drivers/watchdog/wdt977.c @@ -398,7 +398,7 @@ static long wdt977_ioctl(struct file *file, unsigned int cmd, return -EINVAL; wdt977_keepalive(); - /* Fall */ + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, uarg.i); diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index ae1df496bf38..adcabd9473eb 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -386,8 +386,8 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, */ trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force); - map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, size, dir, - attrs); + map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, + size, size, dir, attrs); if (map == (phys_addr_t)DMA_MAPPING_ERROR) return DMA_MAPPING_ERROR; @@ -397,7 +397,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, * Ensure that the address returned is DMA'ble */ if (unlikely(!dma_capable(dev, dev_addr, size))) { - swiotlb_tbl_unmap_single(dev, map, size, dir, + swiotlb_tbl_unmap_single(dev, map, size, size, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC); return DMA_MAPPING_ERROR; } @@ -433,7 +433,7 @@ static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr, /* NOTE: We use dev_addr here, not paddr! */ if (is_xen_swiotlb_buffer(dev_addr)) - swiotlb_tbl_unmap_single(hwdev, paddr, size, dir, attrs); + swiotlb_tbl_unmap_single(hwdev, paddr, size, size, dir, attrs); } static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr, |