diff options
-rw-r--r-- | drivers/net/ntb_netdev.c | 30 | ||||
-rw-r--r-- | drivers/ntb/hw/idt/Kconfig | 5 | ||||
-rw-r--r-- | drivers/ntb/hw/idt/ntb_hw_idt.c | 327 | ||||
-rw-r--r-- | drivers/ntb/hw/idt/ntb_hw_idt.h | 87 | ||||
-rw-r--r-- | drivers/ntb/hw/intel/ntb_hw_gen1.c | 2 | ||||
-rw-r--r-- | drivers/ntb/ntb_transport.c | 88 |
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 = |