aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-11-04 08:12:44 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2018-11-04 08:12:44 -0800
commit04578e8441c5420259a71eff9fede535858bfc6a (patch)
treedddc3123bffdd48c872ed05af5cbad80368b4029
parent71e56028173bc84f01456a5679d8be9d681b49f1 (diff)
parenta662315d8ad9e687fe648b6eea9bd35017f565dd (diff)
downloadlinux-04578e8441c5420259a71eff9fede535858bfc6a.tar.gz
Merge tag 'ntb-4.20' of git://github.com/jonmason/ntb
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
-rw-r--r--drivers/net/ntb_netdev.c30
-rw-r--r--drivers/ntb/hw/idt/Kconfig5
-rw-r--r--drivers/ntb/hw/idt/ntb_hw_idt.c327
-rw-r--r--drivers/ntb/hw/idt/ntb_hw_idt.h87
-rw-r--r--drivers/ntb/hw/intel/ntb_hw_gen1.c2
-rw-r--r--drivers/ntb/ntb_transport.c88
6 files changed, 429 insertions, 110 deletions
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index b12023bc2cab..a5bab614ff84 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -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);
diff --git a/drivers/ntb/hw/idt/Kconfig b/drivers/ntb/hw/idt/Kconfig
index b360e5613b9f..f8948cf515ce 100644
--- a/drivers/ntb/hw/idt/Kconfig
+++ b/drivers/ntb/hw/idt/Kconfig
@@ -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.
diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c
index dbe72f116017..1dede87dd54f 100644
--- a/drivers/ntb/hw/idt/ntb_hw_idt.c
+++ b/drivers/ntb/hw/idt/ntb_hw_idt.c
@@ -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,
@@ -49,11 +49,14 @@
#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)
diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.h b/drivers/ntb/hw/idt/ntb_hw_idt.h
index 856fd182f6f4..2f1aa121b0cf 100644
--- a/drivers/ntb/hw/idt/ntb_hw_idt.h
+++ b/drivers/ntb/hw/idt/ntb_hw_idt.h
@@ -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
@@ -688,15 +688,14 @@
* @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
@@ -886,12 +885,60 @@
#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
@@ -951,6 +998,32 @@
#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
* @IDT_MW_DIR: Direct address translation
@@ -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)
diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c
index 6aa573227279..2ad263f708da 100644
--- a/drivers/ntb/hw/intel/ntb_hw_gen1.c
+++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c
@@ -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;
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 939895966476..3bfdb4562408 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -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 =