Merge tag 'ntb-4.20' of git://github.com/jonmason/ntb
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 4 Nov 2018 16:12:44 +0000 (08:12 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 4 Nov 2018 16:12:44 +0000 (08:12 -0800)
Pull NTB updates from Jon Mason:
 "Fairly minor changes and bug fixes:

  NTB IDT thermal changes and hook into hwmon, ntb_netdev clean-up of
  private struct, and a few bug fixes"

* tag 'ntb-4.20' of git://github.com/jonmason/ntb:
  ntb: idt: Alter the driver info comments
  ntb: idt: Discard temperature sensor IRQ handler
  ntb: idt: Add basic hwmon sysfs interface
  ntb: idt: Alter temperature read method
  ntb_netdev: Simplify remove with client device drvdata
  NTB: transport: Try harder to alloc an aligned MW buffer
  ntb: ntb_transport: Mark expected switch fall-throughs
  ntb: idt: Set PCIe bus address to BARLIMITx
  NTB: ntb_hw_idt: replace IS_ERR_OR_NULL with regular NULL checks
  ntb: intel: fix return value for ndev_vec_mask()
  ntb_netdev: fix sleep time mismatch

drivers/net/ntb_netdev.c
drivers/ntb/hw/idt/Kconfig
drivers/ntb/hw/idt/ntb_hw_idt.c
drivers/ntb/hw/idt/ntb_hw_idt.h
drivers/ntb/hw/intel/ntb_hw_gen1.c
drivers/ntb/ntb_transport.c

index b12023b..a5bab61 100644 (file)
@@ -71,7 +71,6 @@ static unsigned int tx_start = 10;
 static unsigned int tx_stop = 5;
 
 struct ntb_netdev {
-       struct list_head list;
        struct pci_dev *pdev;
        struct net_device *ndev;
        struct ntb_transport_qp *qp;
@@ -81,8 +80,6 @@ struct ntb_netdev {
 #define        NTB_TX_TIMEOUT_MS       1000
 #define        NTB_RXQ_SIZE            100
 
-static LIST_HEAD(dev_list);
-
 static void ntb_netdev_event_handler(void *data, int link_is_up)
 {
        struct net_device *ndev = data;
@@ -236,7 +233,7 @@ static void ntb_netdev_tx_timer(struct timer_list *t)
        struct net_device *ndev = dev->ndev;
 
        if (ntb_transport_tx_free_entry(dev->qp) < tx_stop) {
-               mod_timer(&dev->tx_timer, jiffies + msecs_to_jiffies(tx_time));
+               mod_timer(&dev->tx_timer, jiffies + usecs_to_jiffies(tx_time));
        } else {
                /* Make sure anybody stopping the queue after this sees the new
                 * value of ntb_transport_tx_free_entry()
@@ -452,7 +449,7 @@ static int ntb_netdev_probe(struct device *client_dev)
        if (rc)
                goto err1;
 
-       list_add(&dev->list, &dev_list);
+       dev_set_drvdata(client_dev, ndev);
        dev_info(&pdev->dev, "%s created\n", ndev->name);
        return 0;
 
@@ -465,27 +462,8 @@ err:
 
 static void ntb_netdev_remove(struct device *client_dev)
 {
-       struct ntb_dev *ntb;
-       struct net_device *ndev;
-       struct pci_dev *pdev;
-       struct ntb_netdev *dev;
-       bool found = false;
-
-       ntb = dev_ntb(client_dev->parent);
-       pdev = ntb->pdev;
-
-       list_for_each_entry(dev, &dev_list, list) {
-               if (dev->pdev == pdev) {
-                       found = true;
-                       break;
-               }
-       }
-       if (!found)
-               return;
-
-       list_del(&dev->list);
-
-       ndev = dev->ndev;
+       struct net_device *ndev = dev_get_drvdata(client_dev);
+       struct ntb_netdev *dev = netdev_priv(ndev);
 
        unregister_netdev(ndev);
        ntb_transport_free_queue(dev->qp);
index b360e56..f8948cf 100644 (file)
@@ -1,6 +1,7 @@
 config NTB_IDT
        tristate "IDT PCIe-switch Non-Transparent Bridge support"
        depends on PCI
+       select HWMON
        help
         This driver supports NTB of cappable IDT PCIe-switches.
 
@@ -23,9 +24,7 @@ config NTB_IDT
         BAR settings of peer NT-functions, the BAR setups can't be done over
         kernel PCI fixups. That's why the alternative pre-initialization
         techniques like BIOS using SMBus interface or EEPROM should be
-        utilized. Additionally if one needs to have temperature sensor
-        information printed to system log, the corresponding registers must
-        be initialized within BIOS/EEPROM as well.
+        utilized.
 
         If unsure, say N.
 
index dbe72f1..1dede87 100644 (file)
@@ -4,7 +4,7 @@
  *
  *   GPL LICENSE SUMMARY
  *
- *   Copyright (C) 2016 T-Platforms All Rights Reserved.
+ *   Copyright (C) 2016-2018 T-Platforms JSC All Rights Reserved.
  *
  *   This program is free software; you can redistribute it and/or modify it
  *   under the terms and conditions of the GNU General Public License,
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/pci.h>
 #include <linux/aer.h>
 #include <linux/slab.h>
 #include <linux/list.h>
 #include <linux/debugfs.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include <linux/ntb.h>
 
 #include "ntb_hw_idt.h"
@@ -1105,9 +1108,9 @@ static struct idt_mw_cfg *idt_scan_mws(struct idt_ntb_dev *ndev, int port,
        }
 
        /* Allocate memory for memory window descriptors */
-       ret_mws = devm_kcalloc(&ndev->ntb.pdev->dev, *mw_cnt,
-                               sizeof(*ret_mws), GFP_KERNEL);
-       if (IS_ERR_OR_NULL(ret_mws))
+       ret_mws = devm_kcalloc(&ndev->ntb.pdev->dev, *mw_cnt, sizeof(*ret_mws),
+                              GFP_KERNEL);
+       if (!ret_mws)
                return ERR_PTR(-ENOMEM);
 
        /* Copy the info of detected memory windows */
@@ -1320,7 +1323,7 @@ static int idt_ntb_peer_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx,
                idt_nt_write(ndev, bar->ltbase, (u32)addr);
                idt_nt_write(ndev, bar->utbase, (u32)(addr >> 32));
                /* Set the custom BAR aperture limit */
-               limit = pci_resource_start(ntb->pdev, mw_cfg->bar) + size;
+               limit = pci_bus_address(ntb->pdev, mw_cfg->bar) + size;
                idt_nt_write(ndev, bar->limit, (u32)limit);
                if (IS_FLD_SET(BARSETUP_TYPE, data, 64))
                        idt_nt_write(ndev, (bar + 1)->limit, (limit >> 32));
@@ -1821,61 +1824,284 @@ static int idt_ntb_peer_msg_write(struct ntb_dev *ntb, int pidx, int midx,
  *                      7. Temperature sensor operations
  *
  *    IDT PCIe-switch has an embedded temperature sensor, which can be used to
- * warn a user-space of possible chip overheating. Since workload temperature
- * can be different on different platforms, temperature thresholds as well as
- * general sensor settings must be setup in the framework of BIOS/EEPROM
- * initializations. It includes the actual sensor enabling as well.
+ * check current chip core temperature. Since a workload environment can be
+ * different on different platforms, an offset and ADC/filter settings can be
+ * specified. Although the offset configuration is only exposed to the sysfs
+ * hwmon interface at the moment. The rest of the settings can be adjusted
+ * for instance by the BIOS/EEPROM firmware.
  *=============================================================================
  */
 
+/*
+ * idt_get_deg() - convert millidegree Celsius value to just degree
+ * @mdegC:     IN - millidegree Celsius value
+ *
+ * Return: Degree corresponding to the passed millidegree value
+ */
+static inline s8 idt_get_deg(long mdegC)
+{
+       return mdegC / 1000;
+}
+
+/*
+ * idt_get_frac() - retrieve 0/0.5 fraction of the millidegree Celsius value
+ * @mdegC:     IN - millidegree Celsius value
+ *
+ * Return: 0/0.5 degree fraction of the passed millidegree value
+ */
+static inline u8 idt_get_deg_frac(long mdegC)
+{
+       return (mdegC % 1000) >= 500 ? 5 : 0;
+}
+
+/*
+ * idt_get_temp_fmt() - convert millidegree Celsius value to 0:7:1 format
+ * @mdegC:     IN - millidegree Celsius value
+ *
+ * Return: 0:7:1 format acceptable by the IDT temperature sensor
+ */
+static inline u8 idt_temp_get_fmt(long mdegC)
+{
+       return (idt_get_deg(mdegC) << 1) | (idt_get_deg_frac(mdegC) ? 1 : 0);
+}
+
+/*
+ * idt_get_temp_sval() - convert temp sample to signed millidegree Celsius
+ * @data:      IN - shifted to LSB 8-bits temperature sample
+ *
+ * Return: signed millidegree Celsius
+ */
+static inline long idt_get_temp_sval(u32 data)
+{
+       return ((s8)data / 2) * 1000 + (data & 0x1 ? 500 : 0);
+}
+
+/*
+ * idt_get_temp_sval() - convert temp sample to unsigned millidegree Celsius
+ * @data:      IN - shifted to LSB 8-bits temperature sample
+ *
+ * Return: unsigned millidegree Celsius
+ */
+static inline long idt_get_temp_uval(u32 data)
+{
+       return (data / 2) * 1000 + (data & 0x1 ? 500 : 0);
+}
+
 /*
  * idt_read_temp() - read temperature from chip sensor
  * @ntb:       NTB device context.
- * @val:       OUT - integer value of temperature
- * @frac:      OUT - fraction
+ * @type:      IN - type of the temperature value to read
+ * @val:       OUT - integer value of temperature in millidegree Celsius
  */
-static void idt_read_temp(struct idt_ntb_dev *ndev, unsigned char *val,
-                         unsigned char *frac)
+static void idt_read_temp(struct idt_ntb_dev *ndev,
+                         const enum idt_temp_val type, long *val)
 {
        u32 data;
 
-       /* Read the data from TEMP field of the TMPSTS register */
-       data = idt_sw_read(ndev, IDT_SW_TMPSTS);
-       data = GET_FIELD(TMPSTS_TEMP, data);
-       /* TEMP field has one fractional bit and seven integer bits */
-       *val = data >> 1;
-       *frac = ((data & 0x1) ? 5 : 0);
+       /* Alter the temperature field in accordance with the passed type */
+       switch (type) {
+       case IDT_TEMP_CUR:
+               data = GET_FIELD(TMPSTS_TEMP,
+                                idt_sw_read(ndev, IDT_SW_TMPSTS));
+               break;
+       case IDT_TEMP_LOW:
+               data = GET_FIELD(TMPSTS_LTEMP,
+                                idt_sw_read(ndev, IDT_SW_TMPSTS));
+               break;
+       case IDT_TEMP_HIGH:
+               data = GET_FIELD(TMPSTS_HTEMP,
+                                idt_sw_read(ndev, IDT_SW_TMPSTS));
+               break;
+       case IDT_TEMP_OFFSET:
+               /* This is the only field with signed 0:7:1 format */
+               data = GET_FIELD(TMPADJ_OFFSET,
+                                idt_sw_read(ndev, IDT_SW_TMPADJ));
+               *val = idt_get_temp_sval(data);
+               return;
+       default:
+               data = GET_FIELD(TMPSTS_TEMP,
+                                idt_sw_read(ndev, IDT_SW_TMPSTS));
+               break;
+       }
+
+       /* The rest of the fields accept unsigned 0:7:1 format */
+       *val = idt_get_temp_uval(data);
 }
 
 /*
- * idt_temp_isr() - temperature sensor alarm events ISR
- * @ndev:      IDT NTB hardware driver descriptor
- * @ntint_sts: NT-function interrupt status
+ * idt_write_temp() - write temperature to the chip sensor register
+ * @ntb:       NTB device context.
+ * @type:      IN - type of the temperature value to change
+ * @val:       IN - integer value of temperature in millidegree Celsius
+ */
+static void idt_write_temp(struct idt_ntb_dev *ndev,
+                          const enum idt_temp_val type, const long val)
+{
+       unsigned int reg;
+       u32 data;
+       u8 fmt;
+
+       /* Retrieve the properly formatted temperature value */
+       fmt = idt_temp_get_fmt(val);
+
+       mutex_lock(&ndev->hwmon_mtx);
+       switch (type) {
+       case IDT_TEMP_LOW:
+               reg = IDT_SW_TMPALARM;
+               data = SET_FIELD(TMPALARM_LTEMP, idt_sw_read(ndev, reg), fmt) &
+                       ~IDT_TMPALARM_IRQ_MASK;
+               break;
+       case IDT_TEMP_HIGH:
+               reg = IDT_SW_TMPALARM;
+               data = SET_FIELD(TMPALARM_HTEMP, idt_sw_read(ndev, reg), fmt) &
+                       ~IDT_TMPALARM_IRQ_MASK;
+               break;
+       case IDT_TEMP_OFFSET:
+               reg = IDT_SW_TMPADJ;
+               data = SET_FIELD(TMPADJ_OFFSET, idt_sw_read(ndev, reg), fmt);
+               break;
+       default:
+               goto inval_spin_unlock;
+       }
+
+       idt_sw_write(ndev, reg, data);
+
+inval_spin_unlock:
+       mutex_unlock(&ndev->hwmon_mtx);
+}
+
+/*
+ * idt_sysfs_show_temp() - printout corresponding temperature value
+ * @dev:       Pointer to the NTB device structure
+ * @da:                Sensor device attribute structure
+ * @buf:       Buffer to print temperature out
  *
- * It handles events of temperature crossing alarm thresholds. Since reading
- * of TMPALARM register clears it up, the function doesn't analyze the
- * read value, instead the current temperature value just warningly printed to
- * log.
- * The method is called from PCIe ISR bottom-half routine.
+ * Return: Number of written symbols or negative error
  */
-static void idt_temp_isr(struct idt_ntb_dev *ndev, u32 ntint_sts)
+static ssize_t idt_sysfs_show_temp(struct device *dev,
+                                  struct device_attribute *da, char *buf)
 {
-       unsigned char val, frac;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+       struct idt_ntb_dev *ndev = dev_get_drvdata(dev);
+       enum idt_temp_val type = attr->index;
+       long mdeg;
 
-       /* Read the current temperature value */
-       idt_read_temp(ndev, &val, &frac);
+       idt_read_temp(ndev, type, &mdeg);
+       return sprintf(buf, "%ld\n", mdeg);
+}
 
-       /* Read the temperature alarm to clean the alarm status out */
-       /*(void)idt_sw_read(ndev, IDT_SW_TMPALARM);*/
+/*
+ * idt_sysfs_set_temp() - set corresponding temperature value
+ * @dev:       Pointer to the NTB device structure
+ * @da:                Sensor device attribute structure
+ * @buf:       Buffer to print temperature out
+ * @count:     Size of the passed buffer
+ *
+ * Return: Number of written symbols or negative error
+ */
+static ssize_t idt_sysfs_set_temp(struct device *dev,
+                                 struct device_attribute *da, const char *buf,
+                                 size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+       struct idt_ntb_dev *ndev = dev_get_drvdata(dev);
+       enum idt_temp_val type = attr->index;
+       long mdeg;
+       int ret;
 
-       /* Clean the corresponding interrupt bit */
-       idt_nt_write(ndev, IDT_NT_NTINTSTS, IDT_NTINTSTS_TMPSENSOR);
+       ret = kstrtol(buf, 10, &mdeg);
+       if (ret)
+               return ret;
+
+       /* Clamp the passed value in accordance with the type */
+       if (type == IDT_TEMP_OFFSET)
+               mdeg = clamp_val(mdeg, IDT_TEMP_MIN_OFFSET,
+                                IDT_TEMP_MAX_OFFSET);
+       else
+               mdeg = clamp_val(mdeg, IDT_TEMP_MIN_MDEG, IDT_TEMP_MAX_MDEG);
+
+       idt_write_temp(ndev, type, mdeg);
+
+       return count;
+}
+
+/*
+ * idt_sysfs_reset_hist() - reset temperature history
+ * @dev:       Pointer to the NTB device structure
+ * @da:                Sensor device attribute structure
+ * @buf:       Buffer to print temperature out
+ * @count:     Size of the passed buffer
+ *
+ * Return: Number of written symbols or negative error
+ */
+static ssize_t idt_sysfs_reset_hist(struct device *dev,
+                                   struct device_attribute *da,
+                                   const char *buf, size_t count)
+{
+       struct idt_ntb_dev *ndev = dev_get_drvdata(dev);
+
+       /* Just set the maximal value to the lowest temperature field and
+        * minimal value to the highest temperature field
+        */
+       idt_write_temp(ndev, IDT_TEMP_LOW, IDT_TEMP_MAX_MDEG);
+       idt_write_temp(ndev, IDT_TEMP_HIGH, IDT_TEMP_MIN_MDEG);
 
-       dev_dbg(&ndev->ntb.pdev->dev,
-               "Temp sensor IRQ detected %#08x", ntint_sts);
+       return count;
+}
+
+/*
+ * Hwmon IDT sysfs attributes
+ */
+static SENSOR_DEVICE_ATTR(temp1_input, 0444, idt_sysfs_show_temp, NULL,
+                         IDT_TEMP_CUR);
+static SENSOR_DEVICE_ATTR(temp1_lowest, 0444, idt_sysfs_show_temp, NULL,
+                         IDT_TEMP_LOW);
+static SENSOR_DEVICE_ATTR(temp1_highest, 0444, idt_sysfs_show_temp, NULL,
+                         IDT_TEMP_HIGH);
+static SENSOR_DEVICE_ATTR(temp1_offset, 0644, idt_sysfs_show_temp,
+                         idt_sysfs_set_temp, IDT_TEMP_OFFSET);
+static DEVICE_ATTR(temp1_reset_history, 0200, NULL, idt_sysfs_reset_hist);
 
-       /* Print temperature value to log */
-       dev_warn(&ndev->ntb.pdev->dev, "Temperature %hhu.%hhu", val, frac);
+/*
+ * Hwmon IDT sysfs attributes group
+ */
+static struct attribute *idt_temp_attrs[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_lowest.dev_attr.attr,
+       &sensor_dev_attr_temp1_highest.dev_attr.attr,
+       &sensor_dev_attr_temp1_offset.dev_attr.attr,
+       &dev_attr_temp1_reset_history.attr,
+       NULL
+};
+ATTRIBUTE_GROUPS(idt_temp);
+
+/*
+ * idt_init_temp() - initialize temperature sensor interface
+ * @ndev:      IDT NTB hardware driver descriptor
+ *
+ * Simple sensor initializarion method is responsible for device switching
+ * on and resource management based hwmon interface registration. Note, that
+ * since the device is shared we won't disable it on remove, but leave it
+ * working until the system is powered off.
+ */
+static void idt_init_temp(struct idt_ntb_dev *ndev)
+{
+       struct device *hwmon;
+
+       /* Enable sensor if it hasn't been already */
+       idt_sw_write(ndev, IDT_SW_TMPCTL, 0x0);
+
+       /* Initialize hwmon interface fields */
+       mutex_init(&ndev->hwmon_mtx);
+
+       hwmon = devm_hwmon_device_register_with_groups(&ndev->ntb.pdev->dev,
+               ndev->swcfg->name, ndev, idt_temp_groups);
+       if (IS_ERR(hwmon)) {
+               dev_err(&ndev->ntb.pdev->dev, "Couldn't create hwmon device");
+               return;
+       }
+
+       dev_dbg(&ndev->ntb.pdev->dev, "Temperature HWmon interface registered");
 }
 
 /*=============================================================================
@@ -1931,7 +2157,7 @@ static int idt_init_isr(struct idt_ntb_dev *ndev)
                goto err_free_vectors;
        }
 
-       /* Unmask Message/Doorbell/SE/Temperature interrupts */
+       /* Unmask Message/Doorbell/SE interrupts */
        ntint_mask = idt_nt_read(ndev, IDT_NT_NTINTMSK) & ~IDT_NTINTMSK_ALL;
        idt_nt_write(ndev, IDT_NT_NTINTMSK, ntint_mask);
 
@@ -1946,7 +2172,6 @@ err_free_vectors:
        return ret;
 }
 
-
 /*
  * idt_deinit_ist() - deinitialize PCIe interrupt handler
  * @ndev:      IDT NTB hardware driver descriptor
@@ -2007,12 +2232,6 @@ static irqreturn_t idt_thread_isr(int irq, void *devid)
                handled = true;
        }
 
-       /* Handle temperature sensor interrupt */
-       if (ntint_sts & IDT_NTINTSTS_TMPSENSOR) {
-               idt_temp_isr(ndev, ntint_sts);
-               handled = true;
-       }
-
        dev_dbg(&ndev->ntb.pdev->dev, "IDT IRQs 0x%08x handled", ntint_sts);
 
        return handled ? IRQ_HANDLED : IRQ_NONE;
@@ -2123,9 +2342,9 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf,
                                   size_t count, loff_t *offp)
 {
        struct idt_ntb_dev *ndev = filp->private_data;
-       unsigned char temp, frac, idx, pidx, cnt;
+       unsigned char idx, pidx, cnt;
+       unsigned long irqflags, mdeg;
        ssize_t ret = 0, off = 0;
-       unsigned long irqflags;
        enum ntb_speed speed;
        enum ntb_width width;
        char *strbuf;
@@ -2274,9 +2493,10 @@ static ssize_t idt_dbgfs_info_read(struct file *filp, char __user *ubuf,
        off += scnprintf(strbuf + off, size - off, "\n");
 
        /* Current temperature */
-       idt_read_temp(ndev, &temp, &frac);
+       idt_read_temp(ndev, IDT_TEMP_CUR, &mdeg);
        off += scnprintf(strbuf + off, size - off,
-               "Switch temperature\t\t- %hhu.%hhuC\n", temp, frac);
+               "Switch temperature\t\t- %hhd.%hhuC\n",
+               idt_get_deg(mdeg), idt_get_deg_frac(mdeg));
 
        /* Copy the buffer to the User Space */
        ret = simple_read_from_buffer(ubuf, count, offp, strbuf, off);
@@ -2390,7 +2610,7 @@ static struct idt_ntb_dev *idt_create_dev(struct pci_dev *pdev,
 
        /* Allocate memory for the IDT PCIe-device descriptor */
        ndev = devm_kzalloc(&pdev->dev, sizeof(*ndev), GFP_KERNEL);
-       if (IS_ERR_OR_NULL(ndev)) {
+       if (!ndev) {
                dev_err(&pdev->dev, "Memory allocation failed for descriptor");
                return ERR_PTR(-ENOMEM);
        }
@@ -2571,6 +2791,9 @@ static int idt_pci_probe(struct pci_dev *pdev,
        /* Initialize Messaging subsystem */
        idt_init_msg(ndev);
 
+       /* Initialize hwmon interface */
+       idt_init_temp(ndev);
+
        /* Initialize IDT interrupts handler */
        ret = idt_init_isr(ndev);
        if (ret != 0)
index 856fd18..2f1aa12 100644 (file)
@@ -4,7 +4,7 @@
  *
  *   GPL LICENSE SUMMARY
  *
- *   Copyright (C) 2016 T-Platforms All Rights Reserved.
+ *   Copyright (C) 2016-2018 T-Platforms JSC All Rights Reserved.
  *
  *   This program is free software; you can redistribute it and/or modify it
  *   under the terms and conditions of the GNU General Public License,
@@ -47,9 +47,9 @@
 #include <linux/pci_ids.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/ntb.h>
 
-
 /*
  * Macro is used to create the struct pci_device_id that matches
  * the supported IDT PCIe-switches
  * @IDT_NTINTMSK_DBELL:                Doorbell interrupt mask bit
  * @IDT_NTINTMSK_SEVENT:       Switch Event interrupt mask bit
  * @IDT_NTINTMSK_TMPSENSOR:    Temperature sensor interrupt mask bit
- * @IDT_NTINTMSK_ALL:          All the useful interrupts mask
+ * @IDT_NTINTMSK_ALL:          NTB-related interrupts mask
  */
 #define IDT_NTINTMSK_MSG               0x00000001U
 #define IDT_NTINTMSK_DBELL             0x00000002U
 #define IDT_NTINTMSK_SEVENT            0x00000008U
 #define IDT_NTINTMSK_TMPSENSOR         0x00000080U
 #define IDT_NTINTMSK_ALL \
-       (IDT_NTINTMSK_MSG | IDT_NTINTMSK_DBELL | \
-        IDT_NTINTMSK_SEVENT | IDT_NTINTMSK_TMPSENSOR)
+       (IDT_NTINTMSK_MSG | IDT_NTINTMSK_DBELL | IDT_NTINTMSK_SEVENT)
 
 /*
  * NTGSIGNAL register fields related constants
 #define IDT_SWPxMSGCTL_PART_MASK       0x00000070U
 #define IDT_SWPxMSGCTL_PART_FLD                4
 
+/*
+ * TMPCTL register fields related constants
+ * @IDT_TMPCTL_LTH_MASK:       Low temperature threshold field mask
+ * @IDT_TMPCTL_LTH_FLD:                Low temperature threshold field offset
+ * @IDT_TMPCTL_MTH_MASK:       Middle temperature threshold field mask
+ * @IDT_TMPCTL_MTH_FLD:                Middle temperature threshold field offset
+ * @IDT_TMPCTL_HTH_MASK:       High temperature threshold field mask
+ * @IDT_TMPCTL_HTH_FLD:                High temperature threshold field offset
+ * @IDT_TMPCTL_PDOWN:          Temperature sensor power down
+ */
+#define IDT_TMPCTL_LTH_MASK            0x000000FFU
+#define IDT_TMPCTL_LTH_FLD             0
+#define IDT_TMPCTL_MTH_MASK            0x0000FF00U
+#define IDT_TMPCTL_MTH_FLD             8
+#define IDT_TMPCTL_HTH_MASK            0x00FF0000U
+#define IDT_TMPCTL_HTH_FLD             16
+#define IDT_TMPCTL_PDOWN               0x80000000U
+
 /*
  * TMPSTS register fields related constants
  * @IDT_TMPSTS_TEMP_MASK:      Current temperature field mask
  * @IDT_TMPSTS_TEMP_FLD:       Current temperature field offset
+ * @IDT_TMPSTS_LTEMP_MASK:     Lowest temperature field mask
+ * @IDT_TMPSTS_LTEMP_FLD:      Lowest temperature field offset
+ * @IDT_TMPSTS_HTEMP_MASK:     Highest temperature field mask
+ * @IDT_TMPSTS_HTEMP_FLD:      Highest temperature field offset
  */
 #define IDT_TMPSTS_TEMP_MASK           0x000000FFU
 #define IDT_TMPSTS_TEMP_FLD            0
+#define IDT_TMPSTS_LTEMP_MASK          0x0000FF00U
+#define IDT_TMPSTS_LTEMP_FLD           8
+#define IDT_TMPSTS_HTEMP_MASK          0x00FF0000U
+#define IDT_TMPSTS_HTEMP_FLD           16
+
+/*
+ * TMPALARM register fields related constants
+ * @IDT_TMPALARM_LTEMP_MASK:   Lowest temperature field mask
+ * @IDT_TMPALARM_LTEMP_FLD:    Lowest temperature field offset
+ * @IDT_TMPALARM_HTEMP_MASK:   Highest temperature field mask
+ * @IDT_TMPALARM_HTEMP_FLD:    Highest temperature field offset
+ * @IDT_TMPALARM_IRQ_MASK:     Alarm IRQ status mask
+ */
+#define IDT_TMPALARM_LTEMP_MASK                0x0000FF00U
+#define IDT_TMPALARM_LTEMP_FLD         8
+#define IDT_TMPALARM_HTEMP_MASK                0x00FF0000U
+#define IDT_TMPALARM_HTEMP_FLD         16
+#define IDT_TMPALARM_IRQ_MASK          0x3F000000U
+
+/*
+ * TMPADJ register fields related constants
+ * @IDT_TMPADJ_OFFSET_MASK:    Temperature value offset field mask
+ * @IDT_TMPADJ_OFFSET_FLD:     Temperature value offset field offset
+ */
+#define IDT_TMPADJ_OFFSET_MASK         0x000000FFU
+#define IDT_TMPADJ_OFFSET_FLD          0
 
 /*
  * Helper macro to get/set the corresponding field value
 #define IDT_TRANS_ALIGN                4
 #define IDT_DIR_SIZE_ALIGN     1
 
+/*
+ * IDT PCIe-switch temperature sensor value limits
+ * @IDT_TEMP_MIN_MDEG: Minimal integer value of temperature
+ * @IDT_TEMP_MAX_MDEG: Maximal integer value of temperature
+ * @IDT_TEMP_MIN_OFFSET:Minimal integer value of temperature offset
+ * @IDT_TEMP_MAX_OFFSET:Maximal integer value of temperature offset
+ */
+#define IDT_TEMP_MIN_MDEG      0
+#define IDT_TEMP_MAX_MDEG      127500
+#define IDT_TEMP_MIN_OFFSET    -64000
+#define IDT_TEMP_MAX_OFFSET    63500
+
+/*
+ * Temperature sensor values enumeration
+ * @IDT_TEMP_CUR:      Current temperature
+ * @IDT_TEMP_LOW:      Lowest historical temperature
+ * @IDT_TEMP_HIGH:     Highest historical temperature
+ * @IDT_TEMP_OFFSET:   Current temperature offset
+ */
+enum idt_temp_val {
+       IDT_TEMP_CUR,
+       IDT_TEMP_LOW,
+       IDT_TEMP_HIGH,
+       IDT_TEMP_OFFSET
+};
+
 /*
  * IDT Memory Windows type. Depending on the device settings, IDT supports
  * Direct Address Translation MW registers and Lookup Table registers
@@ -1044,6 +1117,8 @@ struct idt_ntb_peer {
  * @msg_mask_lock:     Message mask register lock
  * @gasa_lock:         GASA registers access lock
  *
+ * @hwmon_mtx:         Temperature sensor interface update mutex
+ *
  * @dbgfs_info:                DebugFS info node
  */
 struct idt_ntb_dev {
@@ -1071,6 +1146,8 @@ struct idt_ntb_dev {
        spinlock_t msg_mask_lock;
        spinlock_t gasa_lock;
 
+       struct mutex hwmon_mtx;
+
        struct dentry *dbgfs_info;
 };
 #define to_ndev_ntb(__ntb) container_of(__ntb, struct idt_ntb_dev, ntb)
index 6aa5732..2ad263f 100644 (file)
@@ -265,7 +265,7 @@ static inline int ndev_db_clear_mask(struct intel_ntb_dev *ndev, u64 db_bits,
        return 0;
 }
 
-static inline int ndev_vec_mask(struct intel_ntb_dev *ndev, int db_vector)
+static inline u64 ndev_vec_mask(struct intel_ntb_dev *ndev, int db_vector)
 {
        u64 shift, mask;
 
index 9398959..3bfdb45 100644 (file)
@@ -194,6 +194,8 @@ struct ntb_transport_mw {
        void __iomem *vbase;
        size_t xlat_size;
        size_t buff_size;
+       size_t alloc_size;
+       void *alloc_addr;
        void *virt_addr;
        dma_addr_t dma_addr;
 };
@@ -672,13 +674,59 @@ static void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw)
                return;
 
        ntb_mw_clear_trans(nt->ndev, PIDX, num_mw);
-       dma_free_coherent(&pdev->dev, mw->buff_size,
-                         mw->virt_addr, mw->dma_addr);
+       dma_free_coherent(&pdev->dev, mw->alloc_size,
+                         mw->alloc_addr, mw->dma_addr);
        mw->xlat_size = 0;
        mw->buff_size = 0;
+       mw->alloc_size = 0;
+       mw->alloc_addr = NULL;
        mw->virt_addr = NULL;
 }
 
+static int ntb_alloc_mw_buffer(struct ntb_transport_mw *mw,
+                              struct device *dma_dev, size_t align)
+{
+       dma_addr_t dma_addr;
+       void *alloc_addr, *virt_addr;
+       int rc;
+
+       alloc_addr = dma_alloc_coherent(dma_dev, mw->alloc_size,
+                                       &dma_addr, GFP_KERNEL);
+       if (!alloc_addr) {
+               dev_err(dma_dev, "Unable to alloc MW buff of size %zu\n",
+                       mw->alloc_size);
+               return -ENOMEM;
+       }
+       virt_addr = alloc_addr;
+
+       /*
+        * we must ensure that the memory address allocated is BAR size
+        * aligned in order for the XLAT register to take the value. This
+        * is a requirement of the hardware. It is recommended to setup CMA
+        * for BAR sizes equal or greater than 4MB.
+        */
+       if (!IS_ALIGNED(dma_addr, align)) {
+               if (mw->alloc_size > mw->buff_size) {
+                       virt_addr = PTR_ALIGN(alloc_addr, align);
+                       dma_addr = ALIGN(dma_addr, align);
+               } else {
+                       rc = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       mw->alloc_addr = alloc_addr;
+       mw->virt_addr = virt_addr;
+       mw->dma_addr = dma_addr;
+
+       return 0;
+
+err:
+       dma_free_coherent(dma_dev, mw->alloc_size, alloc_addr, dma_addr);
+
+       return rc;
+}
+
 static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw,
                      resource_size_t size)
 {
@@ -710,28 +758,20 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw,
        /* Alloc memory for receiving data.  Must be aligned */
        mw->xlat_size = xlat_size;
        mw->buff_size = buff_size;
+       mw->alloc_size = buff_size;
 
-       mw->virt_addr = dma_alloc_coherent(&pdev->dev, buff_size,
-                                          &mw->dma_addr, GFP_KERNEL);
-       if (!mw->virt_addr) {
-               mw->xlat_size = 0;
-               mw->buff_size = 0;
-               dev_err(&pdev->dev, "Unable to alloc MW buff of size %zu\n",
-                       buff_size);
-               return -ENOMEM;
-       }
-
-       /*
-        * we must ensure that the memory address allocated is BAR size
-        * aligned in order for the XLAT register to take the value. This
-        * is a requirement of the hardware. It is recommended to setup CMA
-        * for BAR sizes equal or greater than 4MB.
-        */
-       if (!IS_ALIGNED(mw->dma_addr, xlat_align)) {
-               dev_err(&pdev->dev, "DMA memory %pad is not aligned\n",
-                       &mw->dma_addr);
-               ntb_free_mw(nt, num_mw);
-               return -ENOMEM;
+       rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align);
+       if (rc) {
+               mw->alloc_size *= 2;
+               rc = ntb_alloc_mw_buffer(mw, &pdev->dev, xlat_align);
+               if (rc) {
+                       dev_err(&pdev->dev,
+                               "Unable to alloc aligned MW buff\n");
+                       mw->xlat_size = 0;
+                       mw->buff_size = 0;
+                       mw->alloc_size = 0;
+                       return rc;
+               }
        }
 
        /* Notify HW the memory location of the receive buffer */
@@ -1278,6 +1318,7 @@ static void ntb_rx_copy_callback(void *data,
                case DMA_TRANS_READ_FAILED:
                case DMA_TRANS_WRITE_FAILED:
                        entry->errors++;
+                       /* fall through */
                case DMA_TRANS_ABORTED:
                {
                        struct ntb_transport_qp *qp = entry->qp;
@@ -1533,6 +1574,7 @@ static void ntb_tx_copy_callback(void *data,
                case DMA_TRANS_READ_FAILED:
                case DMA_TRANS_WRITE_FAILED:
                        entry->errors++;
+                       /* fall through */
                case DMA_TRANS_ABORTED:
                {
                        void __iomem *offset =