staging:gasket: gasket-apex updates
Update gasket framework version.
Add unique chip id sysfs node.
Add dma_bit_mask module parameter.
Enable DFS and thermal shutdown by default.
Add DFS support.
Add thermal sysfs nodes.
Fix pci remove/rescan for kernel module build.
Various clean ups and fixes.
Change-Id: Ie7f0bc1c33397468f83c6bd606bf433d3dd6b01c
Signed-off-by: Leonid Lobachev <leonidl@google.com>
diff --git a/drivers/staging/gasket/apex.h b/drivers/staging/gasket/apex.h
index 3bbceff..5537d96 100644
--- a/drivers/staging/gasket/apex.h
+++ b/drivers/staging/gasket/apex.h
@@ -20,6 +20,19 @@
u64 force_idle;
};
+/* Performance expectation ioctl. */
+enum apex_performance_expectation {
+ APEX_PERFORMANCE_LOW = 0,
+ APEX_PERFORMANCE_MED = 1,
+ APEX_PERFORMANCE_HIGH = 2,
+ APEX_PERFORMANCE_MAX = 3,
+};
+
+struct apex_performance_expectation_ioctl {
+ /* Expected performance from apex. */
+ uint32_t performance;
+};
+
/* Base number for all Apex-common IOCTLs */
#define APEX_IOCTL_BASE 0x7F
@@ -27,4 +40,6 @@
#define APEX_IOCTL_GATE_CLOCK \
_IOW(APEX_IOCTL_BASE, 0, struct apex_gate_clock_ioctl)
+#define APEX_IOCTL_PERFORMANCE_EXPECTATION _IOW(APEX_IOCTL_BASE, 1, struct apex_performance_expectation_ioctl)
+
#endif /* __APEX_H__ */
diff --git a/drivers/staging/gasket/apex_driver.c b/drivers/staging/gasket/apex_driver.c
index 0cef1d6..5cc871e 100644
--- a/drivers/staging/gasket/apex_driver.c
+++ b/drivers/staging/gasket/apex_driver.c
@@ -5,6 +5,7 @@
* Copyright (C) 2018 Google, Inc.
*/
+#include <linux/atomic.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -17,6 +18,7 @@
#include <linux/printk.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
+#include <linux/workqueue.h>
#include "apex.h"
@@ -27,7 +29,7 @@
/* Constants */
#define APEX_DEVICE_NAME "Apex"
-#define APEX_DRIVER_VERSION "1.0"
+#define APEX_DRIVER_VERSION "1.1"
/* CSRs are in BAR 2. */
#define APEX_BAR_INDEX 2
@@ -62,11 +64,32 @@
/* Wait 100 ms between checks. Total 12 sec wait maximum. */
#define APEX_RESET_DELAY 100
+/* Interval between temperature polls, 0 disables polling */
+#define DEFAULT_APEX_TEMP_POLL_INTERVAL 5000
+
+/* apex device private data */
+struct apex_dev {
+ struct gasket_dev *gasket_dev_ptr;
+ struct delayed_work check_temperature_work;
+ u32 adc_trip_points[3];
+ atomic_t temp_poll_interval;
+};
+
/* Enumeration of the supported sysfs entries. */
enum sysfs_attribute_type {
ATTR_KERNEL_HIB_PAGE_TABLE_SIZE,
ATTR_KERNEL_HIB_SIMPLE_PAGE_TABLE_SIZE,
ATTR_KERNEL_HIB_NUM_ACTIVE_PAGES,
+ ATTR_TEMP,
+ ATTR_TEMP_WARN1,
+ ATTR_TEMP_WARN1_EN,
+ ATTR_TEMP_WARN2,
+ ATTR_TEMP_WARN2_EN,
+ ATTR_TEMP_TRIP0,
+ ATTR_TEMP_TRIP1,
+ ATTR_TEMP_TRIP2,
+ ATTR_TEMP_POLL_INTERVAL,
+ ATTR_UNIQUE_ID,
};
/*
@@ -98,6 +121,14 @@
APEX_BAR2_REG_USER_HIB_DMA_PAUSED = 0x486E0,
APEX_BAR2_REG_IDLEGENERATOR_IDLEGEN_IDLEREGISTER = 0x4A000,
APEX_BAR2_REG_KERNEL_HIB_PAGE_TABLE = 0x50000,
+ APEX_BAR2_REG_OMC0_D0 = 0x01a0d0,
+ APEX_BAR2_REG_OMC0_D4 = 0x01a0d4,
+ APEX_BAR2_REG_OMC0_D8 = 0x01a0d8,
+ APEX_BAR2_REG_OMC0_DC = 0x01a0dc,
+ APEX_BAR2_REG_EFUSE_DC = 0x01a2dc,
+ APEX_BAR2_REG_EFUSE_E0 = 0x01a2e0,
+ APEX_BAR2_REG_EFUSE_E4 = 0x01a2e4,
+ APEX_BAR2_REG_EFUSE_E8 = 0x01a2e8,
/* Error registers - Used mostly for debug */
APEX_BAR2_REG_USER_HIB_ERROR_STATUS = 0x86f0,
@@ -138,9 +169,6 @@
{ 0x48000, 0x1000 },
};
-static const struct gasket_mappable_region cm_mappable_regions[1] = { { 0x0,
- APEX_CH_MEM_BYTES } };
-
/* Gasket device interrupts enums must be dense (i.e., no empty slots). */
enum apex_interrupt {
APEX_INTERRUPT_INSTR_QUEUE = 0,
@@ -228,7 +256,6 @@
},
};
-
/* Allows device to enter power save upon driver close(). */
static int allow_power_save = 1;
@@ -247,6 +274,38 @@
module_param(allow_hw_clock_gating, int, 0644);
module_param(bypass_top_level, int, 0644);
+/* Temperature points in milli C at which DFS is toggled */
+#define DEFAULT_TRIP_POINT0_TEMP 85000
+#define DEFAULT_TRIP_POINT1_TEMP 90000
+#define DEFAULT_TRIP_POINT2_TEMP 95000
+
+static int trip_point0_temp = DEFAULT_TRIP_POINT0_TEMP;
+static int trip_point1_temp = DEFAULT_TRIP_POINT1_TEMP;
+static int trip_point2_temp = DEFAULT_TRIP_POINT2_TEMP;
+
+module_param(trip_point0_temp, int, 0644);
+module_param(trip_point1_temp, int, 0644);
+module_param(trip_point2_temp, int, 0644);
+
+/* Hardware monitored temperature trip points in milli C
+ Apex chip drives INTR line when reaching hw_temp_warn1 temperature,
+ and SD_ALARM line when reaching hw_temp_warn2 if corresponding
+ hw_temp_warn*_en is set to true.
+ */
+static int hw_temp_warn1 = 100000;
+static int hw_temp_warn2 = 100000;
+static bool hw_temp_warn1_en = false;
+static bool hw_temp_warn2_en = true;
+
+module_param(hw_temp_warn1, int, 0644);
+module_param(hw_temp_warn2, int, 0644);
+module_param(hw_temp_warn1_en, bool, 0644);
+module_param(hw_temp_warn2_en, bool, 0644);
+
+/* Temperature poll interval in ms */
+static int temp_poll_interval = DEFAULT_APEX_TEMP_POLL_INTERVAL;
+module_param(temp_poll_interval, int, 0644);
+
/* Check the device status registers and return device status ALIVE or DEAD. */
static int apex_get_status(struct gasket_dev *gasket_dev)
{
@@ -491,6 +550,62 @@
return 0;
}
+/* apex_set_performance_expectation: Adjust clock rates for Apex. */
+static long apex_set_performance_expectation(
+ struct gasket_dev *gasket_dev,
+ struct apex_performance_expectation_ioctl __user *argp)
+{
+ struct apex_performance_expectation_ioctl ibuf;
+ uint32_t rg_gcb_clk_div = 0;
+ uint32_t rg_axi_clk_fixed = 0;
+ const int AXI_CLK_FIXED_SHIFT = 2;
+ const int MCU_CLK_FIXED_SHIFT = 3;
+
+ // 8051 clock is fixed for PCIe, as it's not used at all.
+ const uint32_t rg_8051_clk_fixed = 1;
+
+ if (bypass_top_level)
+ return 0;
+
+ if (copy_from_user(&ibuf, argp, sizeof(ibuf)))
+ return -EFAULT;
+
+ switch (ibuf.performance) {
+ case APEX_PERFORMANCE_LOW:
+ rg_gcb_clk_div = 3;
+ rg_axi_clk_fixed = 0;
+ break;
+
+ case APEX_PERFORMANCE_MED:
+ rg_gcb_clk_div = 2;
+ rg_axi_clk_fixed = 0;
+ break;
+
+ case APEX_PERFORMANCE_HIGH:
+ rg_gcb_clk_div = 1;
+ rg_axi_clk_fixed = 0;
+ break;
+
+ case APEX_PERFORMANCE_MAX:
+ rg_gcb_clk_div = 0;
+ rg_axi_clk_fixed = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Set clock rates for GCB, AXI, and 8051:
+ */
+ gasket_read_modify_write_32(
+ gasket_dev, APEX_BAR_INDEX, APEX_BAR2_REG_SCU_3,
+ (rg_gcb_clk_div | (rg_axi_clk_fixed << AXI_CLK_FIXED_SHIFT) | (rg_8051_clk_fixed << MCU_CLK_FIXED_SHIFT)),
+ /*mask_width=*/4, /*mask_shift=*/28);
+
+ return 0;
+}
+
/* Apex-specific ioctl handler. */
static long apex_ioctl(struct file *filp, uint cmd, void __user *argp)
{
@@ -502,17 +617,32 @@
switch (cmd) {
case APEX_IOCTL_GATE_CLOCK:
return apex_clock_gating(gasket_dev, argp);
+ case APEX_IOCTL_PERFORMANCE_EXPECTATION:
+ return apex_set_performance_expectation(gasket_dev, argp);
default:
return -ENOTTY; /* unknown command */
}
}
+/* Linear fit optimized for 25C-100C */
+static int adc_to_millic(int adc)
+{
+ return (662 - adc) * 250 + 550;
+}
+
+static int millic_to_adc(int millic)
+{
+ return (550 - millic) / 250 + 662;
+}
+
/* Display driver sysfs entries. */
static ssize_t sysfs_show(struct device *device, struct device_attribute *attr,
char *buf)
{
int ret;
+ unsigned value, value2, value3, value4;
struct gasket_dev *gasket_dev;
+ struct apex_dev *apex_dev;
struct gasket_sysfs_attribute *gasket_attr;
enum sysfs_attribute_type type;
@@ -522,6 +652,13 @@
return -ENODEV;
}
+ if (!gasket_dev->pci_dev ||
+ !(apex_dev = pci_get_drvdata(gasket_dev->pci_dev))) {
+ dev_err(device, "Can't find apex_dev data\n");
+ gasket_sysfs_put_device_data(device, gasket_dev);
+ return -ENODEV;
+ }
+
gasket_attr = gasket_sysfs_get_attr(device, attr);
if (!gasket_attr) {
dev_err(device, "No Apex device sysfs attr data found\n");
@@ -529,7 +666,7 @@
return -ENODEV;
}
- type = (enum sysfs_attribute_type)gasket_sysfs_get_attr(device, attr);
+ type = (enum sysfs_attribute_type)gasket_attr->data.attr_type;
switch (type) {
case ATTR_KERNEL_HIB_PAGE_TABLE_SIZE:
ret = scnprintf(buf, PAGE_SIZE, "%u\n",
@@ -546,6 +683,162 @@
gasket_page_table_num_active_pages(
gasket_dev->page_table[0]));
break;
+ case ATTR_TEMP:
+ value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_DC);
+ value = (value >> 16) & ((1 << 10) - 1);
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n", adc_to_millic(value));
+ break;
+ case ATTR_TEMP_WARN1:
+ value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D4);
+ value = (value >> 16) & ((1 << 10) - 1);
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n", adc_to_millic(value));
+ break;
+ case ATTR_TEMP_WARN2:
+ value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D8);
+ value = (value >> 16) & ((1 << 10) - 1);
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n", adc_to_millic(value));
+ break;
+ case ATTR_TEMP_WARN1_EN:
+ value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D4);
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n", value >> 31);
+ break;
+ case ATTR_TEMP_WARN2_EN:
+ value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D8);
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n", value >> 31);
+ break;
+ case ATTR_TEMP_TRIP0:
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n",
+ adc_to_millic(apex_dev->adc_trip_points[0]));
+ break;
+ case ATTR_TEMP_TRIP1:
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n",
+ adc_to_millic(apex_dev->adc_trip_points[1]));
+ break;
+ case ATTR_TEMP_TRIP2:
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n",
+ adc_to_millic(apex_dev->adc_trip_points[2]));
+ break;
+ case ATTR_TEMP_POLL_INTERVAL:
+ ret = scnprintf(buf, PAGE_SIZE, "%i\n",
+ atomic_read(&apex_dev->temp_poll_interval));
+ break;
+ case ATTR_UNIQUE_ID:
+ value = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_EFUSE_DC);
+ value2 = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_EFUSE_E0);
+ value3 = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_EFUSE_E4);
+ value4 = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_EFUSE_E8);
+ ret = snprintf(buf, PAGE_SIZE, "%.8x%.8x%.8x%.8x\n", value4,
+ value3, value2, value);
+ break;
+
+ default:
+ dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
+ attr->attr.name);
+ ret = 0;
+ break;
+ }
+
+ gasket_sysfs_put_attr(device, gasket_attr);
+ gasket_sysfs_put_device_data(device, gasket_dev);
+ return ret;
+}
+
+/* Set driver sysfs entries. */
+static ssize_t sysfs_store(struct device *device, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = count, value;
+ struct gasket_dev *gasket_dev;
+ struct apex_dev *apex_dev;
+ struct gasket_sysfs_attribute *gasket_attr;
+ enum sysfs_attribute_type type;
+
+ if (kstrtoint(buf, 10, &value))
+ return -EINVAL;
+
+ gasket_dev = gasket_sysfs_get_device_data(device);
+ if (!gasket_dev) {
+ dev_err(device, "No Apex device sysfs mapping found\n");
+ return -ENODEV;
+ }
+
+ if (!gasket_dev->pci_dev ||
+ !(apex_dev = pci_get_drvdata(gasket_dev->pci_dev))) {
+ dev_err(device, "Can't find apex_dev data\n");
+ gasket_sysfs_put_device_data(device, gasket_dev);
+ return -ENODEV;
+ }
+
+ gasket_attr = gasket_sysfs_get_attr(device, attr);
+ if (!gasket_attr) {
+ dev_err(device, "No Apex device sysfs attr data found\n");
+ gasket_sysfs_put_device_data(device, gasket_dev);
+ return -ENODEV;
+ }
+
+ type = (enum sysfs_attribute_type)gasket_attr->data.attr_type;
+ switch (type) {
+ case ATTR_TEMP_WARN1:
+ value = millic_to_adc(value);
+ gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D4, value, 10,
+ 16);
+ break;
+ case ATTR_TEMP_WARN2:
+ value = millic_to_adc(value);
+ gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D8, value, 10,
+ 16);
+ break;
+ case ATTR_TEMP_WARN1_EN:
+ value = value > 0 ? 1 : 0;
+ gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D4, value, 1,
+ 31);
+ break;
+ case ATTR_TEMP_WARN2_EN:
+ value = value > 0 ? 1 : 0;
+ gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D8, value, 1,
+ 31);
+ break;
+ case ATTR_TEMP_TRIP0:
+ value = millic_to_adc(value);
+ /* Note: that adc values should be in descending order */
+ if (value >= apex_dev->adc_trip_points[1]) {
+ apex_dev->adc_trip_points[0] = value;
+ } else ret = -EINVAL;
+ break;
+ case ATTR_TEMP_TRIP1:
+ value = millic_to_adc(value);
+ if (value <= apex_dev->adc_trip_points[0] &&
+ value >= apex_dev->adc_trip_points[2]) {
+ apex_dev->adc_trip_points[1] = value;
+ } else ret = -EINVAL;
+ break;
+ case ATTR_TEMP_TRIP2:
+ value = millic_to_adc(value);
+ if (value <= apex_dev->adc_trip_points[1]) {
+ apex_dev->adc_trip_points[2] = value;
+ } else ret = -EINVAL;
+ break;
+ case ATTR_TEMP_POLL_INTERVAL:
+ cancel_delayed_work_sync(&apex_dev->check_temperature_work);
+ atomic_set(&apex_dev->temp_poll_interval, value);
+ if (value > 0)
+ schedule_delayed_work(&apex_dev->check_temperature_work,
+ msecs_to_jiffies(value));
+
+ break;
default:
dev_dbg(gasket_dev->dev, "Unknown attribute: %s\n",
attr->attr.name);
@@ -565,9 +858,110 @@
ATTR_KERNEL_HIB_SIMPLE_PAGE_TABLE_SIZE),
GASKET_SYSFS_RO(node_0_num_mapped_pages, sysfs_show,
ATTR_KERNEL_HIB_NUM_ACTIVE_PAGES),
+ GASKET_SYSFS_RO(temp, sysfs_show, ATTR_TEMP),
+ GASKET_SYSFS_RW(hw_temp_warn1, sysfs_show, sysfs_store,
+ ATTR_TEMP_WARN1),
+ GASKET_SYSFS_RW(hw_temp_warn1_en, sysfs_show, sysfs_store,
+ ATTR_TEMP_WARN1_EN),
+ GASKET_SYSFS_RW(hw_temp_warn2, sysfs_show, sysfs_store,
+ ATTR_TEMP_WARN2),
+ GASKET_SYSFS_RW(hw_temp_warn2_en, sysfs_show, sysfs_store,
+ ATTR_TEMP_WARN2_EN),
+ GASKET_SYSFS_RW(trip_point0_temp, sysfs_show, sysfs_store,
+ ATTR_TEMP_TRIP0),
+ GASKET_SYSFS_RW(trip_point1_temp, sysfs_show, sysfs_store,
+ ATTR_TEMP_TRIP1),
+ GASKET_SYSFS_RW(trip_point2_temp, sysfs_show, sysfs_store,
+ ATTR_TEMP_TRIP2),
+ GASKET_SYSFS_RW(temp_poll_interval, sysfs_show, sysfs_store,
+ ATTR_TEMP_POLL_INTERVAL),
+ GASKET_SYSFS_RO(unique_id, sysfs_show, ATTR_UNIQUE_ID),
GASKET_END_OF_ATTR_ARRAY
};
+static void apply_module_params(struct apex_dev *apex_dev) {
+ kernel_param_lock(THIS_MODULE);
+
+ /* use defaults if trip point temperatures are not in ascending order */
+ if (trip_point0_temp > trip_point1_temp ||
+ trip_point1_temp > trip_point2_temp) {
+ dev_warn(apex_dev->gasket_dev_ptr->dev,
+ "Invalid module parameters for temperature trip points"
+ ", using defaults\n");
+ trip_point0_temp = DEFAULT_TRIP_POINT0_TEMP;
+ trip_point1_temp = DEFAULT_TRIP_POINT1_TEMP;
+ trip_point2_temp = DEFAULT_TRIP_POINT2_TEMP;
+ }
+
+ apex_dev->adc_trip_points[0] = millic_to_adc(trip_point0_temp);
+ apex_dev->adc_trip_points[1] = millic_to_adc(trip_point1_temp);
+ apex_dev->adc_trip_points[2] = millic_to_adc(trip_point2_temp);
+ atomic_set(&apex_dev->temp_poll_interval, temp_poll_interval);
+
+ gasket_read_modify_write_32(apex_dev->gasket_dev_ptr, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D4,
+ millic_to_adc(hw_temp_warn1), 10, 16);
+ gasket_read_modify_write_32(apex_dev->gasket_dev_ptr, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D8,
+ millic_to_adc(hw_temp_warn2), 10, 16);
+ if (hw_temp_warn1_en)
+ gasket_read_modify_write_32(apex_dev->gasket_dev_ptr,
+ APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D4, 1, 1, 31);
+
+ if (hw_temp_warn2_en)
+ gasket_read_modify_write_32(apex_dev->gasket_dev_ptr,
+ APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D8, 1, 1, 31);
+
+ kernel_param_unlock(THIS_MODULE);
+}
+
+static void check_temperature_work_handler(struct work_struct *work) {
+ int i, temp_poll_interval;
+ u32 adc_temp, clk_div, tmp;
+ const u32 mask = ((1 << 2) - 1) << 28;
+ struct apex_dev *apex_dev =
+ container_of(work, struct apex_dev,
+ check_temperature_work.work);
+ struct gasket_dev *gasket_dev = apex_dev->gasket_dev_ptr;
+
+ mutex_lock(&gasket_dev->mutex);
+
+ /* Read current temperature */
+ adc_temp = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_DC);
+ adc_temp = (adc_temp >> 16) & ((1 << 10) - 1);
+
+ /* Find closest trip point
+ Note: that adc values are in descending order */
+ for (i = ARRAY_SIZE(apex_dev->adc_trip_points) - 1; i >= 0; --i) {
+ if (adc_temp <= apex_dev->adc_trip_points[i])
+ break;
+ }
+ /* Compute divider value and shift into appropriate bit location */
+ clk_div = (i + 1) << 28;
+
+ /* Modify gcb clk divider if it's different from current one */
+ tmp = gasket_dev_read_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_SCU_3);
+ if (clk_div != (tmp & mask)) {
+ tmp = (tmp & ~mask) | clk_div;
+ gasket_dev_write_32(gasket_dev, tmp, APEX_BAR_INDEX,
+ APEX_BAR2_REG_SCU_3);
+ dev_warn(gasket_dev->dev,
+ "Apex performance %sthrottled due to temperature\n",
+ i == -1 ? "not " : "");
+ }
+
+ mutex_unlock(&gasket_dev->mutex);
+
+ temp_poll_interval = atomic_read(&apex_dev->temp_poll_interval);
+ if (temp_poll_interval > 0)
+ schedule_delayed_work(&apex_dev->check_temperature_work,
+ msecs_to_jiffies(temp_poll_interval));
+}
+
/* On device open, perform a core reinit reset. */
static int apex_device_open_cb(struct gasket_dev *gasket_dev)
{
@@ -583,17 +977,25 @@
pdev->class = (PCI_CLASS_SYSTEM_OTHER << 8) | pdev->class;
}
DECLARE_PCI_FIXUP_CLASS_HEADER(APEX_PCI_VENDOR_ID, APEX_PCI_DEVICE_ID,
- PCI_CLASS_NOT_DEFINED, 8, apex_pci_fixup_class);
+ PCI_ANY_ID, 8, apex_pci_fixup_class);
static int apex_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
{
- int ret;
+ int ret, temp_poll_interval;
ulong page_table_ready, msix_table_ready;
int retries = 0;
struct gasket_dev *gasket_dev;
+ struct apex_dev *apex_dev;
ret = pci_enable_device(pci_dev);
+#ifdef MODULE
+ if (ret) {
+ apex_pci_fixup_class(pci_dev);
+ pci_bus_assign_resources(pci_dev->bus);
+ ret = pci_enable_device(pci_dev);
+ }
+#endif
if (ret) {
dev_err(&pci_dev->dev, "error enabling PCI device\n");
return ret;
@@ -608,7 +1010,18 @@
return ret;
}
- pci_set_drvdata(pci_dev, gasket_dev);
+ apex_dev = kzalloc(sizeof(*apex_dev), GFP_KERNEL);
+ if (!apex_dev) {
+ dev_err(&pci_dev->dev, "no memory for device\n");
+ ret = -ENOMEM;
+ goto remove_device;
+ }
+
+ INIT_DELAYED_WORK(&apex_dev->check_temperature_work,
+ check_temperature_work_handler);
+ apex_dev->gasket_dev_ptr = gasket_dev;
+ apply_module_params(apex_dev);
+ pci_set_drvdata(pci_dev, apex_dev);
apex_reset(gasket_dev);
while (retries < APEX_RESET_RETRY) {
@@ -633,6 +1046,20 @@
goto remove_device;
}
+ // Enable thermal sensor clocks
+ gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D0, 0x1, 1, 7);
+
+ // Enable thermal sensor (ENAD ENVR ENBG)
+ gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_D8, 0x7, 3, 0);
+
+ // Enable OMC thermal sensor controller
+ // This bit should be asserted 100 us after ENAD ENVR ENBG
+ schedule_timeout(usecs_to_jiffies(100));
+ gasket_read_modify_write_32(gasket_dev, APEX_BAR_INDEX,
+ APEX_BAR2_REG_OMC0_DC, 0x1, 1, 0);
+
ret = gasket_sysfs_create_entries(gasket_dev->dev_info.device,
apex_sysfs_attrs);
if (ret)
@@ -648,19 +1075,36 @@
if (allow_power_save)
apex_enter_reset(gasket_dev);
+ /* Enable thermal polling */
+ temp_poll_interval = atomic_read(&apex_dev->temp_poll_interval);
+ if (temp_poll_interval > 0)
+ schedule_delayed_work(&apex_dev->check_temperature_work,
+ msecs_to_jiffies(temp_poll_interval));
return 0;
remove_device:
gasket_pci_remove_device(pci_dev);
pci_disable_device(pci_dev);
+ kfree(apex_dev);
return ret;
}
static void apex_pci_remove(struct pci_dev *pci_dev)
{
- struct gasket_dev *gasket_dev = pci_get_drvdata(pci_dev);
+ struct apex_dev *apex_dev = pci_get_drvdata(pci_dev);
+ struct gasket_dev *gasket_dev;
+
+ if (!apex_dev) {
+ dev_err(&pci_dev->dev, "NULL apex_dev\n");
+ goto remove_device;
+ }
+ gasket_dev = apex_dev->gasket_dev_ptr;
+
+ cancel_delayed_work_sync(&apex_dev->check_temperature_work);
+ kfree(apex_dev);
gasket_disable_device(gasket_dev);
+remove_device:
gasket_pci_remove_device(pci_dev);
pci_disable_device(pci_dev);
}
diff --git a/drivers/staging/gasket/gasket.h b/drivers/staging/gasket/gasket.h
index a0f065c..93e7af1 100644
--- a/drivers/staging/gasket/gasket.h
+++ b/drivers/staging/gasket/gasket.h
@@ -38,6 +38,31 @@
};
/*
+ * Structure for ioctl mapping buffers with flags when using the Gasket
+ * page_table module.
+ */
+struct gasket_page_table_ioctl_flags {
+ struct gasket_page_table_ioctl base;
+ /*
+ * Flags indicating status and attribute requests from the host.
+ * NOTE: STATUS bit does not need to be set in this request.
+ * Set RESERVED bits to 0 to ensure backwards compatibility.
+ *
+ * Bitfields:
+ * [0] - STATUS: indicates if this entry/slot is free
+ * 0 = PTE_FREE
+ * 1 = PTE_INUSE
+ * [2:1] - DMA_DIRECTION: dma_data_direction requested by host
+ * 00 = DMA_BIDIRECTIONAL
+ * 01 = DMA_TO_DEVICE
+ * 10 = DMA_FROM_DEVICE
+ * 11 = DMA_NONE
+ * [31:3] - RESERVED
+ */
+ u32 flags;
+};
+
+/*
* Common structure for ioctls mapping and unmapping buffers when using the
* Gasket page_table module.
* dma_address: phys addr start of coherent memory, allocated by kernel
@@ -119,4 +144,12 @@
#define GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR \
_IOWR(GASKET_IOCTL_BASE, 11, struct gasket_coherent_alloc_config_ioctl)
+/*
+ * Tells the kernel to map size bytes at host_address to device_address in
+ * page_table_index page table. Passes flags to indicate additional attribute
+ * requests for the mapped memory.
+ */
+#define GASKET_IOCTL_MAP_BUFFER_FLAGS \
+ _IOW(GASKET_IOCTL_BASE, 12, struct gasket_page_table_ioctl_flags)
+
#endif /* __GASKET_H__ */
diff --git a/drivers/staging/gasket/gasket_constants.h b/drivers/staging/gasket/gasket_constants.h
index 50d87c7..ff113bc 100644
--- a/drivers/staging/gasket/gasket_constants.h
+++ b/drivers/staging/gasket/gasket_constants.h
@@ -3,7 +3,7 @@
#ifndef __GASKET_CONSTANTS_H__
#define __GASKET_CONSTANTS_H__
-#define GASKET_FRAMEWORK_VERSION "1.1.2"
+#define GASKET_FRAMEWORK_VERSION "1.1.3"
/*
* The maximum number of simultaneous device types supported by the framework.
diff --git a/drivers/staging/gasket/gasket_core.c b/drivers/staging/gasket/gasket_core.c
index d12ab56..16112bb 100644
--- a/drivers/staging/gasket/gasket_core.c
+++ b/drivers/staging/gasket/gasket_core.c
@@ -24,6 +24,7 @@
#include <linux/init.h>
#include <linux/of.h>
#include <linux/pid_namespace.h>
+#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/sched.h>
@@ -102,6 +103,16 @@
ATTR_USER_MEM_RANGES
};
+/* On some arm64 systems pcie dma controller can only access lower 4GB of
+ * addresses. Unfortunately vendor BSP isn't providing any means of determining
+ * this limitation and there're no errors reported if access to higher addresses
+ * if being done. This parameter allows to workaround this issue by pretending
+ * that our device only supports 32 bit addresses. This in turn will cause
+ * dma driver to use shadow buffers located in low 32 bit address space.
+ */
+static int dma_bit_mask = 64;
+module_param(dma_bit_mask, int, 0644);
+
/* Perform a standard Gasket callback. */
static inline int
check_and_invoke_callback(struct gasket_dev *gasket_dev,
@@ -109,8 +120,6 @@
{
int ret = 0;
- dev_dbg(gasket_dev->dev, "check_and_invoke_callback %p\n",
- cb_function);
if (cb_function) {
mutex_lock(&gasket_dev->mutex);
ret = cb_function(gasket_dev);
@@ -126,11 +135,8 @@
{
int ret = 0;
- if (cb_function) {
- dev_dbg(gasket_dev->dev,
- "Invoking device-specific callback.\n");
+ if (cb_function)
ret = cb_function(gasket_dev);
- }
return ret;
}
@@ -189,26 +195,26 @@
* Returns 0 if successful, a negative error code otherwise.
*/
static int gasket_alloc_dev(struct gasket_internal_desc *internal_desc,
- struct device *parent, struct gasket_dev **pdev,
- const char *kobj_name)
+ struct device *parent, struct gasket_dev **pdev)
{
int dev_idx;
const struct gasket_driver_desc *driver_desc =
internal_desc->driver_desc;
struct gasket_dev *gasket_dev;
struct gasket_cdev_info *dev_info;
+ const char *parent_name = dev_name(parent);
- pr_debug("Allocating a Gasket device %s.\n", kobj_name);
+ pr_debug("Allocating a Gasket device, parent %s.\n", parent_name);
*pdev = NULL;
- dev_idx = gasket_find_dev_slot(internal_desc, kobj_name);
+ dev_idx = gasket_find_dev_slot(internal_desc, parent_name);
if (dev_idx < 0)
return dev_idx;
gasket_dev = *pdev = kzalloc(sizeof(*gasket_dev), GFP_KERNEL);
if (!gasket_dev) {
- pr_err("no memory for device %s\n", kobj_name);
+ pr_err("no memory for device, parent %s\n", parent_name);
return -ENOMEM;
}
internal_desc->devs[dev_idx] = gasket_dev;
@@ -217,8 +223,9 @@
gasket_dev->internal_desc = internal_desc;
gasket_dev->dev_idx = dev_idx;
- snprintf(gasket_dev->kobj_name, GASKET_NAME_MAX, "%s", kobj_name);
+ snprintf(gasket_dev->kobj_name, GASKET_NAME_MAX, "%s", parent_name);
gasket_dev->dev = get_device(parent);
+ gasket_dev->dma_dev = get_device(parent);
/* gasket_bar_data is uninitialized. */
gasket_dev->num_page_tables = driver_desc->num_page_tables;
/* max_page_table_size and *page table are uninit'ed */
@@ -231,10 +238,9 @@
dev_info->devt =
MKDEV(driver_desc->major, driver_desc->minor +
gasket_dev->dev_idx);
- dev_info->device = device_create(internal_desc->class, parent,
- dev_info->devt, gasket_dev, dev_info->name);
-
- dev_dbg(dev_info->device, "Gasket device allocated.\n");
+ dev_info->device =
+ device_create(internal_desc->class, parent, dev_info->devt,
+ gasket_dev, dev_info->name);
/* cdev has not yet been added; cdev_added is 0 */
dev_info->gasket_dev_ptr = gasket_dev;
@@ -252,6 +258,7 @@
internal_desc->devs[gasket_dev->dev_idx] = NULL;
mutex_unlock(&internal_desc->mutex);
put_device(gasket_dev->dev);
+ put_device(gasket_dev->dma_dev);
kfree(gasket_dev);
}
@@ -319,8 +326,9 @@
goto fail;
}
- dma_set_mask(&gasket_dev->pci_dev->dev, DMA_BIT_MASK(64));
- dma_set_coherent_mask(&gasket_dev->pci_dev->dev, DMA_BIT_MASK(64));
+ dma_set_mask(&gasket_dev->pci_dev->dev, DMA_BIT_MASK(dma_bit_mask));
+ dma_set_coherent_mask(&gasket_dev->pci_dev->dev,
+ DMA_BIT_MASK(dma_bit_mask));
return 0;
@@ -634,6 +642,7 @@
gasket_dev->internal_desc->driver_desc;
int i;
+ dev_dbg(gasket_dev->dev, "disabling device\n");
/* Only delete the device if it has been successfully added. */
if (gasket_dev->dev_info.cdev_added)
cdev_del(&gasket_dev->dev_info.cdev);
@@ -652,13 +661,13 @@
EXPORT_SYMBOL(gasket_disable_device);
/*
- * Registered descriptor lookup.
+ * Registered driver descriptor lookup for PCI devices.
*
* Precondition: Called with g_mutex held (to avoid a race on return).
* Returns NULL if no matching device was found.
*/
static struct gasket_internal_desc *
-lookup_internal_desc(struct pci_dev *pci_dev)
+lookup_pci_internal_desc(struct pci_dev *pci_dev)
{
int i;
@@ -674,6 +683,25 @@
}
/*
+ * Registered driver descriptor lookup for platform devices.
+ * Caller must hold g_mutex.
+ */
+static struct gasket_internal_desc *
+lookup_platform_internal_desc(struct platform_device *pdev)
+{
+ int i;
+
+ __must_hold(&g_mutex);
+ for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
+ if (g_descs[i].driver_desc &&
+ strcmp(g_descs[i].driver_desc->name, pdev->name) == 0)
+ return &g_descs[i];
+ }
+
+ return NULL;
+}
+
+/*
* Verifies that the user has permissions to perform the requested mapping and
* that the provided descriptor/range is of adequate size to hold the range to
* be mapped.
@@ -992,13 +1020,14 @@
}
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- ret = remap_pfn_range(vma, vma->vm_start,
- (gasket_dev->coherent_buffer.phys_base) >>
- PAGE_SHIFT, requested_length, vma->vm_page_prot);
+ vma->vm_pgoff = 0;
+ ret = dma_mmap_coherent(gasket_dev->dma_dev, vma,
+ gasket_dev->coherent_buffer.virt_base,
+ gasket_dev->coherent_buffer.phys_base,
+ requested_length);
if (ret) {
- dev_err(gasket_dev->dev, "Error remapping PFN range err=%d.\n",
- ret);
+ dev_err(gasket_dev->dev,
+ "Error mmapping coherent buffer err=%d.\n", ret);
trace_gasket_mmap_exit(ret);
return ret;
}
@@ -1348,6 +1377,7 @@
.open = gasket_open,
.release = gasket_release,
.unlocked_ioctl = gasket_ioctl,
+ .compat_ioctl = gasket_ioctl,
};
/* Perform final init and marks the device as active. */
@@ -1358,13 +1388,8 @@
const struct gasket_driver_desc *driver_desc =
gasket_dev->internal_desc->driver_desc;
- ret = gasket_interrupt_init(gasket_dev, driver_desc->name,
- driver_desc->interrupt_type,
- driver_desc->interrupts,
- driver_desc->num_interrupts,
- driver_desc->interrupt_pack_width,
- driver_desc->interrupt_bar_index,
- driver_desc->wire_interrupt_offsets);
+ dev_dbg(gasket_dev->dev, "enabling device\n");
+ ret = gasket_interrupt_init(gasket_dev);
if (ret) {
dev_err(gasket_dev->dev,
"Critical failure to allocate interrupts: %d\n", ret);
@@ -1420,6 +1445,56 @@
}
EXPORT_SYMBOL(gasket_enable_device);
+static int __gasket_add_device(struct device *parent_dev,
+ struct gasket_internal_desc *internal_desc,
+ struct gasket_dev **gasket_devp)
+{
+ int ret;
+ struct gasket_dev *gasket_dev;
+ const struct gasket_driver_desc *driver_desc =
+ internal_desc->driver_desc;
+
+ ret = gasket_alloc_dev(internal_desc, parent_dev, &gasket_dev);
+ if (ret)
+ return ret;
+ if (IS_ERR(gasket_dev->dev_info.device)) {
+ dev_err(parent_dev, "Cannot create %s device %s [ret = %ld]\n",
+ driver_desc->name, gasket_dev->dev_info.name,
+ PTR_ERR(gasket_dev->dev_info.device));
+ ret = -ENODEV;
+ goto free_gasket_dev;
+ }
+
+ ret = gasket_sysfs_create_mapping(gasket_dev->dev_info.device,
+ gasket_dev);
+ if (ret)
+ goto remove_device;
+
+ ret = gasket_sysfs_create_entries(gasket_dev->dev_info.device,
+ gasket_sysfs_generic_attrs);
+ if (ret)
+ goto remove_sysfs_mapping;
+
+ *gasket_devp = gasket_dev;
+ return 0;
+
+remove_sysfs_mapping:
+ gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
+remove_device:
+ device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
+free_gasket_dev:
+ gasket_free_dev(gasket_dev);
+ return ret;
+}
+
+static void __gasket_remove_device(struct gasket_internal_desc *internal_desc,
+ struct gasket_dev *gasket_dev)
+{
+ gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
+ device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
+ gasket_free_dev(gasket_dev);
+}
+
/*
* Add PCI gasket device.
*
@@ -1432,16 +1507,14 @@
struct gasket_dev **gasket_devp)
{
int ret;
- const char *kobj_name = dev_name(&pci_dev->dev);
struct gasket_internal_desc *internal_desc;
struct gasket_dev *gasket_dev;
- const struct gasket_driver_desc *driver_desc;
struct device *parent;
- pr_debug("add PCI device %s\n", kobj_name);
+ dev_dbg(&pci_dev->dev, "add PCI gasket device\n");
mutex_lock(&g_mutex);
- internal_desc = lookup_internal_desc(pci_dev);
+ internal_desc = lookup_pci_internal_desc(pci_dev);
mutex_unlock(&g_mutex);
if (!internal_desc) {
dev_err(&pci_dev->dev,
@@ -1449,29 +1522,15 @@
return -ENODEV;
}
- driver_desc = internal_desc->driver_desc;
-
parent = &pci_dev->dev;
- ret = gasket_alloc_dev(internal_desc, parent, &gasket_dev, kobj_name);
+ ret = __gasket_add_device(parent, internal_desc, &gasket_dev);
if (ret)
return ret;
- gasket_dev->pci_dev = pci_dev;
- if (IS_ERR_OR_NULL(gasket_dev->dev_info.device)) {
- pr_err("Cannot create %s device %s [ret = %ld]\n",
- driver_desc->name, gasket_dev->dev_info.name,
- PTR_ERR(gasket_dev->dev_info.device));
- ret = -ENODEV;
- goto fail1;
- }
+ gasket_dev->pci_dev = pci_dev;
ret = gasket_setup_pci(pci_dev, gasket_dev);
if (ret)
- goto fail2;
-
- ret = gasket_sysfs_create_mapping(gasket_dev->dev_info.device,
- gasket_dev);
- if (ret)
- goto fail3;
+ goto cleanup_pci;
/*
* Once we've created the mapping structures successfully, attempt to
@@ -1482,24 +1541,15 @@
if (ret) {
dev_err(gasket_dev->dev,
"Cannot create sysfs pci link: %d\n", ret);
- goto fail3;
+ goto cleanup_pci;
}
- ret = gasket_sysfs_create_entries(gasket_dev->dev_info.device,
- gasket_sysfs_generic_attrs);
- if (ret)
- goto fail4;
*gasket_devp = gasket_dev;
return 0;
-fail4:
-fail3:
- gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
-fail2:
+cleanup_pci:
gasket_cleanup_pci(gasket_dev);
- device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
-fail1:
- gasket_free_dev(gasket_dev);
+ __gasket_remove_device(internal_desc, gasket_dev);
return ret;
}
EXPORT_SYMBOL(gasket_pci_add_device);
@@ -1510,18 +1560,15 @@
int i;
struct gasket_internal_desc *internal_desc;
struct gasket_dev *gasket_dev = NULL;
- const struct gasket_driver_desc *driver_desc;
/* Find the device desc. */
mutex_lock(&g_mutex);
- internal_desc = lookup_internal_desc(pci_dev);
+ internal_desc = lookup_pci_internal_desc(pci_dev);
if (!internal_desc) {
mutex_unlock(&g_mutex);
return;
}
mutex_unlock(&g_mutex);
- driver_desc = internal_desc->driver_desc;
-
/* Now find the specific device */
mutex_lock(&internal_desc->mutex);
for (i = 0; i < GASKET_DEV_MAX; i++) {
@@ -1540,13 +1587,84 @@
internal_desc->driver_desc->name);
gasket_cleanup_pci(gasket_dev);
-
- gasket_sysfs_remove_mapping(gasket_dev->dev_info.device);
- device_destroy(internal_desc->class, gasket_dev->dev_info.devt);
- gasket_free_dev(gasket_dev);
+ __gasket_remove_device(internal_desc, gasket_dev);
}
EXPORT_SYMBOL(gasket_pci_remove_device);
+/* Add platform gasket device. Called by Gasket device probe function. */
+int gasket_platform_add_device(struct platform_device *pdev,
+ struct gasket_dev **gasket_devp)
+{
+ int ret;
+ struct gasket_internal_desc *internal_desc;
+ struct gasket_dev *gasket_dev;
+ struct device *parent;
+
+ dev_dbg(&pdev->dev, "add platform gasket device\n");
+
+ mutex_lock(&g_mutex);
+ internal_desc = lookup_platform_internal_desc(pdev);
+ mutex_unlock(&g_mutex);
+ if (!internal_desc) {
+ dev_err(&pdev->dev,
+ "%s called for unknown driver type\n", __func__);
+ return -ENODEV;
+ }
+
+ parent = &pdev->dev;
+ ret = __gasket_add_device(parent, internal_desc, &gasket_dev);
+ if (ret)
+ return ret;
+
+ gasket_dev->platform_dev = pdev;
+ *gasket_devp = gasket_dev;
+ return 0;
+}
+EXPORT_SYMBOL(gasket_platform_add_device);
+
+/* Remove a platform gasket device. */
+void gasket_platform_remove_device(struct platform_device *pdev)
+{
+ int i;
+ struct gasket_internal_desc *internal_desc;
+ struct gasket_dev *gasket_dev = NULL;
+
+ /* Find the device desc. */
+ mutex_lock(&g_mutex);
+ internal_desc = lookup_platform_internal_desc(pdev);
+ mutex_unlock(&g_mutex);
+ if (!internal_desc)
+ return;
+
+ /* Now find the specific device */
+ mutex_lock(&internal_desc->mutex);
+ for (i = 0; i < GASKET_DEV_MAX; i++) {
+ if (internal_desc->devs[i] &&
+ internal_desc->devs[i]->platform_dev == pdev) {
+ gasket_dev = internal_desc->devs[i];
+ break;
+ }
+ }
+ mutex_unlock(&internal_desc->mutex);
+
+ if (!gasket_dev)
+ return;
+
+ dev_dbg(gasket_dev->dev, "remove %s platform gasket device\n",
+ internal_desc->driver_desc->name);
+
+ __gasket_remove_device(internal_desc, gasket_dev);
+}
+EXPORT_SYMBOL(gasket_platform_remove_device);
+
+void gasket_set_dma_device(struct gasket_dev *gasket_dev,
+ struct device *dma_dev)
+{
+ put_device(gasket_dev->dma_dev);
+ gasket_dev->dma_dev = get_device(dma_dev);
+}
+EXPORT_SYMBOL(gasket_set_dma_device);
+
/**
* Lookup a name by number in a num_name table.
* @num: Number to lookup.
@@ -1791,7 +1909,6 @@
{
int i;
- pr_debug("%s\n", __func__);
mutex_lock(&g_mutex);
for (i = 0; i < GASKET_FRAMEWORK_DESC_MAX; i++) {
g_descs[i].driver_desc = NULL;
@@ -1804,13 +1921,8 @@
return 0;
}
-static void __exit gasket_exit(void)
-{
- pr_debug("%s\n", __func__);
-}
MODULE_DESCRIPTION("Google Gasket driver framework");
MODULE_VERSION(GASKET_FRAMEWORK_VERSION);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rob Springer <rspringer@google.com>");
module_init(gasket_init);
-module_exit(gasket_exit);
diff --git a/drivers/staging/gasket/gasket_core.h b/drivers/staging/gasket/gasket_core.h
index 275fd0b..7405b6a 100644
--- a/drivers/staging/gasket/gasket_core.h
+++ b/drivers/staging/gasket/gasket_core.h
@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -50,8 +51,7 @@
/* Type of the interrupt supported by the device. */
enum gasket_interrupt_type {
PCI_MSIX = 0,
- PCI_MSI = 1,
- PLATFORM_WIRE = 2,
+ DEVICE_MANAGED = 1, /* Managed externally in device driver */
};
/*
@@ -69,12 +69,6 @@
int packing;
};
-/* Offsets to the wire interrupt handling registers */
-struct gasket_wire_interrupt_offsets {
- u64 pending_bit_array;
- u64 mask_array;
-};
-
/*
* This enum is used to identify memory regions being part of the physical
* memory that belongs to a device.
@@ -266,9 +260,15 @@
/* Device info */
struct device *dev;
- /* PCI subsystem metadata. */
+ /* DMA device to use, may be same as above or a parent */
+ struct device *dma_dev;
+
+ /* PCI device pointer for PCI devices */
struct pci_dev *pci_dev;
+ /* Platform device pointer for platform devices */
+ struct platform_device *platform_dev;
+
/* This device's index into internal_desc->devs. */
int dev_idx;
@@ -384,9 +384,6 @@
*/
struct gasket_coherent_buffer_desc coherent_buffer_description;
- /* Offset of wire interrupt registers. */
- const struct gasket_wire_interrupt_offsets *wire_interrupt_offsets;
-
/* Interrupt type. (One of gasket_interrupt_type). */
int interrupt_type;
@@ -543,6 +540,17 @@
/* Remove a PCI gasket device. */
void gasket_pci_remove_device(struct pci_dev *pci_dev);
+/* Add a platform gasket device. */
+int gasket_platform_add_device(struct platform_device *pdev,
+ struct gasket_dev **gasket_devp);
+
+/* Remove a platform gasket device. */
+void gasket_platform_remove_device(struct platform_device *pdev);
+
+/* Set DMA device to use (if different from PCI/platform device) */
+void gasket_set_dma_device(struct gasket_dev *gasket_dev,
+ struct device *dma_dev);
+
/* Enable a Gasket device. */
int gasket_enable_device(struct gasket_dev *gasket_dev);
@@ -587,28 +595,28 @@
const struct gasket_num_name *table);
/* Handy inlines */
-static inline ulong gasket_dev_read_64(struct gasket_dev *gasket_dev, int bar,
+static inline u64 gasket_dev_read_64(struct gasket_dev *gasket_dev, int bar,
ulong location)
{
- return readq(&gasket_dev->bar_data[bar].virt_base[location]);
+ return readq_relaxed(&gasket_dev->bar_data[bar].virt_base[location]);
}
static inline void gasket_dev_write_64(struct gasket_dev *dev, u64 value,
int bar, ulong location)
{
- writeq(value, &dev->bar_data[bar].virt_base[location]);
+ writeq_relaxed(value, &dev->bar_data[bar].virt_base[location]);
}
static inline void gasket_dev_write_32(struct gasket_dev *dev, u32 value,
int bar, ulong location)
{
- writel(value, &dev->bar_data[bar].virt_base[location]);
+ writel_relaxed(value, &dev->bar_data[bar].virt_base[location]);
}
static inline u32 gasket_dev_read_32(struct gasket_dev *dev, int bar,
ulong location)
{
- return readl(&dev->bar_data[bar].virt_base[location]);
+ return readl_relaxed(&dev->bar_data[bar].virt_base[location]);
}
static inline void gasket_read_modify_write_64(struct gasket_dev *dev, int bar,
diff --git a/drivers/staging/gasket/gasket_interrupt.c b/drivers/staging/gasket/gasket_interrupt.c
index 1cfbc12..1e8b960 100644
--- a/drivers/staging/gasket/gasket_interrupt.c
+++ b/drivers/staging/gasket/gasket_interrupt.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/printk.h>
+#include <linux/rwlock.h>
#include <linux/version.h>
#ifdef GASKET_KERNEL_TRACE_SUPPORT
#define CREATE_TRACE_POINTS
@@ -45,9 +46,6 @@
/* The width of a single interrupt in a packed interrupt register. */
int pack_width;
- /* offset of wire interrupt registers */
- const struct gasket_wire_interrupt_offsets *wire_interrupt_offsets;
-
/*
* Design-wise, these elements should be bundled together, but
* pci_enable_msix's interface requires that they be managed
@@ -63,6 +61,9 @@
/* The eventfd "callback" data for each interrupt. */
struct eventfd_ctx **eventfd_ctxs;
+ /* Spinlock to protect read/write races to eventfd_ctxs. */
+ rwlock_t eventfd_ctx_lock;
+
/* The number of times each interrupt has been called. */
ulong *interrupt_counts;
@@ -80,8 +81,8 @@
{
int i;
int pack_shift;
- ulong mask;
- ulong value;
+ u64 mask;
+ u64 value;
struct gasket_interrupt_data *interrupt_data =
gasket_dev->interrupt_data;
@@ -92,18 +93,8 @@
dev_dbg(gasket_dev->dev, "Running interrupt setup\n");
- if (interrupt_data->type == PLATFORM_WIRE ||
- interrupt_data->type == PCI_MSI) {
- /* Nothing needs to be done for platform or PCI devices. */
- return;
- }
-
- if (interrupt_data->type != PCI_MSIX) {
- dev_dbg(gasket_dev->dev,
- "Cannot handle unsupported interrupt type %d\n",
- interrupt_data->type);
- return;
- }
+ if (interrupt_data->type == DEVICE_MANAGED)
+ return; /* device driver handles setup */
/* Setup the MSIX table. */
@@ -157,9 +148,24 @@
}
}
-static irqreturn_t gasket_msix_interrupt_handler(int irq, void *dev_id)
+void
+gasket_handle_interrupt(struct gasket_interrupt_data *interrupt_data,
+ int interrupt_index)
{
struct eventfd_ctx *ctx;
+
+ trace_gasket_interrupt_event(interrupt_data->name, interrupt_index);
+ read_lock(&interrupt_data->eventfd_ctx_lock);
+ ctx = interrupt_data->eventfd_ctxs[interrupt_index];
+ if (ctx)
+ eventfd_signal(ctx, 1);
+ read_unlock(&interrupt_data->eventfd_ctx_lock);
+
+ ++(interrupt_data->interrupt_counts[interrupt_index]);
+}
+
+static irqreturn_t gasket_msix_interrupt_handler(int irq, void *dev_id)
+{
struct gasket_interrupt_data *interrupt_data = dev_id;
int interrupt = -1;
int i;
@@ -175,14 +181,7 @@
pr_err("Received unknown irq %d\n", irq);
return IRQ_HANDLED;
}
- trace_gasket_interrupt_event(interrupt_data->name, interrupt);
-
- ctx = interrupt_data->eventfd_ctxs[interrupt];
- if (ctx)
- eventfd_signal(ctx, 1);
-
- ++(interrupt_data->interrupt_counts[interrupt]);
-
+ gasket_handle_interrupt(interrupt_data, interrupt);
return IRQ_HANDLED;
}
@@ -192,6 +191,12 @@
int ret = 1;
int i;
+ interrupt_data->msix_entries =
+ kcalloc(interrupt_data->num_interrupts,
+ sizeof(struct msix_entry), GFP_KERNEL);
+ if (!interrupt_data->msix_entries)
+ return -ENOMEM;
+
for (i = 0; i < interrupt_data->num_interrupts; i++) {
interrupt_data->msix_entries[i].entry = i;
interrupt_data->msix_entries[i].vector = 0;
@@ -319,58 +324,45 @@
GASKET_END_OF_ATTR_ARRAY,
};
-int gasket_interrupt_init(struct gasket_dev *gasket_dev, const char *name,
- int type,
- const struct gasket_interrupt_desc *interrupts,
- int num_interrupts, int pack_width, int bar_index,
- const struct gasket_wire_interrupt_offsets *wire_int_offsets)
+int gasket_interrupt_init(struct gasket_dev *gasket_dev)
{
int ret;
struct gasket_interrupt_data *interrupt_data;
+ const struct gasket_driver_desc *driver_desc =
+ gasket_get_driver_desc(gasket_dev);
interrupt_data = kzalloc(sizeof(struct gasket_interrupt_data),
GFP_KERNEL);
if (!interrupt_data)
return -ENOMEM;
gasket_dev->interrupt_data = interrupt_data;
- interrupt_data->name = name;
- interrupt_data->type = type;
+ interrupt_data->name = driver_desc->name;
+ interrupt_data->type = driver_desc->interrupt_type;
interrupt_data->pci_dev = gasket_dev->pci_dev;
- interrupt_data->num_interrupts = num_interrupts;
- interrupt_data->interrupts = interrupts;
- interrupt_data->interrupt_bar_index = bar_index;
- interrupt_data->pack_width = pack_width;
- interrupt_data->num_configured = 0;
- interrupt_data->wire_interrupt_offsets = wire_int_offsets;
+ interrupt_data->num_interrupts = driver_desc->num_interrupts;
+ interrupt_data->interrupts = driver_desc->interrupts;
+ interrupt_data->interrupt_bar_index = driver_desc->interrupt_bar_index;
+ interrupt_data->pack_width = driver_desc->interrupt_pack_width;
- /* Allocate all dynamic structures. */
- interrupt_data->msix_entries = kcalloc(num_interrupts,
- sizeof(struct msix_entry),
- GFP_KERNEL);
- if (!interrupt_data->msix_entries) {
- kfree(interrupt_data);
- return -ENOMEM;
- }
-
- interrupt_data->eventfd_ctxs = kcalloc(num_interrupts,
+ interrupt_data->eventfd_ctxs = kcalloc(driver_desc->num_interrupts,
sizeof(struct eventfd_ctx *),
GFP_KERNEL);
if (!interrupt_data->eventfd_ctxs) {
- kfree(interrupt_data->msix_entries);
kfree(interrupt_data);
return -ENOMEM;
}
- interrupt_data->interrupt_counts = kcalloc(num_interrupts,
+ interrupt_data->interrupt_counts = kcalloc(driver_desc->num_interrupts,
sizeof(ulong),
GFP_KERNEL);
if (!interrupt_data->interrupt_counts) {
kfree(interrupt_data->eventfd_ctxs);
- kfree(interrupt_data->msix_entries);
kfree(interrupt_data);
return -ENOMEM;
}
+ rwlock_init(&interrupt_data->eventfd_ctx_lock);
+
switch (interrupt_data->type) {
case PCI_MSIX:
ret = gasket_interrupt_msix_init(interrupt_data);
@@ -379,12 +371,12 @@
force_msix_interrupt_unmasking(gasket_dev);
break;
- case PCI_MSI:
- case PLATFORM_WIRE:
+ case DEVICE_MANAGED: /* Device driver manages IRQ init */
+ interrupt_data->num_configured = interrupt_data->num_interrupts;
+ ret = 0;
+ break;
+
default:
- dev_err(gasket_dev->dev,
- "Cannot handle unsupported interrupt type %d\n",
- interrupt_data->type);
ret = -EINVAL;
}
@@ -409,14 +401,17 @@
{
int i;
- for (i = 0; i < interrupt_data->num_configured; i++)
+ for (i = 0; i < interrupt_data->num_configured; i++) {
+ gasket_interrupt_clear_eventfd(interrupt_data, i);
free_irq(interrupt_data->msix_entries[i].vector,
interrupt_data);
+ }
interrupt_data->num_configured = 0;
if (interrupt_data->msix_configured)
pci_disable_msix(interrupt_data->pci_dev);
interrupt_data->msix_configured = 0;
+ kfree(interrupt_data->msix_entries);
}
int gasket_interrupt_reinit(struct gasket_dev *gasket_dev)
@@ -438,20 +433,20 @@
force_msix_interrupt_unmasking(gasket_dev);
break;
- case PCI_MSI:
- case PLATFORM_WIRE:
+ case DEVICE_MANAGED: /* Device driver manages IRQ reinit */
+ ret = 0;
+ break;
+
default:
- dev_dbg(gasket_dev->dev,
- "Cannot handle unsupported interrupt type %d\n",
- gasket_dev->interrupt_data->type);
ret = -EINVAL;
}
if (ret) {
- /* Failing to setup MSIx will cause the device
+ /* Failing to setup interrupts will cause the device
* to report GASKET_STATUS_LAMED, but is not fatal.
*/
- dev_warn(gasket_dev->dev, "Couldn't init msix: %d\n", ret);
+ dev_warn(gasket_dev->dev, "Couldn't reinit interrupts: %d\n",
+ ret);
return 0;
}
@@ -487,17 +482,15 @@
gasket_interrupt_msix_cleanup(interrupt_data);
break;
- case PCI_MSI:
- case PLATFORM_WIRE:
+ case DEVICE_MANAGED: /* Device driver manages IRQ cleanup */
+ break;
+
default:
- dev_dbg(gasket_dev->dev,
- "Cannot handle unsupported interrupt type %d\n",
- interrupt_data->type);
+ break;
}
kfree(interrupt_data->interrupt_counts);
kfree(interrupt_data->eventfd_ctxs);
- kfree(interrupt_data->msix_entries);
kfree(interrupt_data);
gasket_dev->interrupt_data = NULL;
}
@@ -509,11 +502,6 @@
return GASKET_STATUS_DEAD;
}
- if (!gasket_dev->interrupt_data->msix_configured) {
- dev_dbg(gasket_dev->dev, "Interrupt not initialized\n");
- return GASKET_STATUS_LAMED;
- }
-
if (gasket_dev->interrupt_data->num_configured !=
gasket_dev->interrupt_data->num_interrupts) {
dev_dbg(gasket_dev->dev,
@@ -527,24 +515,38 @@
int gasket_interrupt_set_eventfd(struct gasket_interrupt_data *interrupt_data,
int interrupt, int event_fd)
{
- struct eventfd_ctx *ctx = eventfd_ctx_fdget(event_fd);
-
- if (IS_ERR(ctx))
- return PTR_ERR(ctx);
+ struct eventfd_ctx *ctx;
+ ulong flags;
if (interrupt < 0 || interrupt >= interrupt_data->num_interrupts)
return -EINVAL;
+ ctx = eventfd_ctx_fdget(event_fd);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ /* Put the old eventfd ctx before setting, else we leak the ref. */
+ write_lock_irqsave(&interrupt_data->eventfd_ctx_lock, flags);
+ if (interrupt_data->eventfd_ctxs[interrupt] != NULL)
+ eventfd_ctx_put(interrupt_data->eventfd_ctxs[interrupt]);
interrupt_data->eventfd_ctxs[interrupt] = ctx;
+ write_unlock_irqrestore(&interrupt_data->eventfd_ctx_lock, flags);
return 0;
}
int gasket_interrupt_clear_eventfd(struct gasket_interrupt_data *interrupt_data,
int interrupt)
{
+ ulong flags;
+
if (interrupt < 0 || interrupt >= interrupt_data->num_interrupts)
return -EINVAL;
+ /* Put the old eventfd ctx before clearing, else we leak the ref. */
+ write_lock_irqsave(&interrupt_data->eventfd_ctx_lock, flags);
+ if (interrupt_data->eventfd_ctxs[interrupt] != NULL)
+ eventfd_ctx_put(interrupt_data->eventfd_ctxs[interrupt]);
interrupt_data->eventfd_ctxs[interrupt] = NULL;
+ write_unlock_irqrestore(&interrupt_data->eventfd_ctx_lock, flags);
return 0;
}
diff --git a/drivers/staging/gasket/gasket_interrupt.h b/drivers/staging/gasket/gasket_interrupt.h
index 835af439..b17b723 100644
--- a/drivers/staging/gasket/gasket_interrupt.h
+++ b/drivers/staging/gasket/gasket_interrupt.h
@@ -24,30 +24,8 @@
/*
* Initialize the interrupt module.
* @gasket_dev: The Gasket device structure for the device to be initted.
- * @type: Type of the interrupt. (See gasket_interrupt_type).
- * @name: The name to associate with these interrupts.
- * @interrupts: An array of all interrupt descriptions for this device.
- * @num_interrupts: The length of the @interrupts array.
- * @pack_width: The width, in bits, of a single field in a packed interrupt reg.
- * @bar_index: The bar containing all interrupt registers.
- *
- * Allocates and initializes data to track interrupt state for a device.
- * After this call, no interrupts will be configured/delivered; call
- * gasket_interrupt_set_vector[_packed] to associate each interrupt with an
- * __iomem location, then gasket_interrupt_set_eventfd to associate an eventfd
- * with an interrupt.
- *
- * If num_interrupts interrupts are not available, this call will return a
- * negative error code. In that case, gasket_interrupt_cleanup should still be
- * called. Returns 0 on success (which can include a device where interrupts
- * are not possible to set up, but is otherwise OK; that device will report
- * status LAMED.)
*/
-int gasket_interrupt_init(struct gasket_dev *gasket_dev, const char *name,
- int type,
- const struct gasket_interrupt_desc *interrupts,
- int num_interrupts, int pack_width, int bar_index,
- const struct gasket_wire_interrupt_offsets *wire_int_offsets);
+int gasket_interrupt_init(struct gasket_dev *gasket_dev);
/*
* Clean up a device's interrupt structure.
@@ -67,6 +45,11 @@
*/
int gasket_interrupt_reinit(struct gasket_dev *gasket_dev);
+/* Handle gasket interrupt processing, called from an external handler. */
+void
+gasket_handle_interrupt(struct gasket_interrupt_data *interrupt_data,
+ int interrupt_index);
+
/*
* Reset the counts stored in the interrupt subsystem.
* @gasket_dev: The Gasket information structure for this device.
diff --git a/drivers/staging/gasket/gasket_ioctl.c b/drivers/staging/gasket/gasket_ioctl.c
index 0ca48e6..d36861d 100644
--- a/drivers/staging/gasket/gasket_ioctl.c
+++ b/drivers/staging/gasket/gasket_ioctl.c
@@ -20,6 +20,7 @@
#define trace_gasket_ioctl_integer_data(x)
#define trace_gasket_ioctl_eventfd_data(x, ...)
#define trace_gasket_ioctl_page_table_data(x, ...)
+#define trace_gasket_ioctl_page_table_flags_data(x, ...)
#define trace_gasket_ioctl_config_coherent_allocator(x, ...)
#endif
@@ -130,29 +131,59 @@
}
/* Map a userspace buffer to a device virtual address. */
+static int gasket_map_buffers_common(struct gasket_dev *gasket_dev,
+ struct gasket_page_table_ioctl_flags
+ *pibuf)
+{
+ if (pibuf->base.page_table_index >= gasket_dev->num_page_tables)
+ return -EFAULT;
+
+ if (gasket_page_table_are_addrs_bad(gasket_dev->page_table[pibuf->base.page_table_index],
+ pibuf->base.host_address,
+ pibuf->base.device_address,
+ pibuf->base.size))
+ return -EINVAL;
+
+ return gasket_page_table_map(gasket_dev->page_table[pibuf->base.page_table_index],
+ pibuf->base.host_address,
+ pibuf->base.device_address,
+ pibuf->base.size / PAGE_SIZE,
+ pibuf->flags);
+}
+
static int gasket_map_buffers(struct gasket_dev *gasket_dev,
struct gasket_page_table_ioctl __user *argp)
{
- struct gasket_page_table_ioctl ibuf;
+ struct gasket_page_table_ioctl_flags ibuf;
- if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl)))
+ if (copy_from_user(&ibuf.base, argp, sizeof(struct gasket_page_table_ioctl)))
return -EFAULT;
- trace_gasket_ioctl_page_table_data(ibuf.page_table_index, ibuf.size,
- ibuf.host_address,
- ibuf.device_address);
+ ibuf.flags = 0;
- if (ibuf.page_table_index >= gasket_dev->num_page_tables)
+ trace_gasket_ioctl_page_table_data(ibuf.base.page_table_index,
+ ibuf.base.size,
+ ibuf.base.host_address,
+ ibuf.base.device_address);
+
+ return gasket_map_buffers_common(gasket_dev, &ibuf);
+}
+
+static int gasket_map_buffers_flags(struct gasket_dev *gasket_dev,
+ struct gasket_page_table_ioctl_flags __user *argp)
+{
+ struct gasket_page_table_ioctl_flags ibuf;
+
+ if (copy_from_user(&ibuf, argp, sizeof(struct gasket_page_table_ioctl_flags)))
return -EFAULT;
- if (gasket_page_table_are_addrs_bad(gasket_dev->page_table[ibuf.page_table_index],
- ibuf.host_address,
- ibuf.device_address, ibuf.size))
- return -EINVAL;
+ trace_gasket_ioctl_page_table_flags_data(ibuf.base.page_table_index,
+ ibuf.base.size,
+ ibuf.base.host_address,
+ ibuf.base.device_address,
+ ibuf.flags);
- return gasket_page_table_map(gasket_dev->page_table[ibuf.page_table_index],
- ibuf.host_address, ibuf.device_address,
- ibuf.size / PAGE_SIZE);
+ return gasket_map_buffers_common(gasket_dev, &ibuf);
}
/* Unmap a userspace buffer from a device virtual address. */
@@ -191,6 +222,7 @@
{
int ret;
struct gasket_coherent_alloc_config_ioctl ibuf;
+ dma_addr_t dma_address;
if (copy_from_user(&ibuf, argp,
sizeof(struct gasket_coherent_alloc_config_ioctl)))
@@ -206,16 +238,21 @@
return -ENOMEM;
if (ibuf.enable == 0) {
+ dma_address = ibuf.dma_address;
ret = gasket_free_coherent_memory(gasket_dev, ibuf.size,
- ibuf.dma_address,
+ dma_address,
ibuf.page_table_index);
} else {
ret = gasket_alloc_coherent_memory(gasket_dev, ibuf.size,
- &ibuf.dma_address,
+ &dma_address,
ibuf.page_table_index);
}
if (ret)
return ret;
+
+ if (ibuf.enable != 0)
+ ibuf.dma_address = dma_address;
+
if (copy_to_user(argp, &ibuf, sizeof(ibuf)))
return -EFAULT;
@@ -252,6 +289,7 @@
return alive && write;
case GASKET_IOCTL_MAP_BUFFER:
+ case GASKET_IOCTL_MAP_BUFFER_FLAGS:
case GASKET_IOCTL_UNMAP_BUFFER:
return alive && write;
@@ -336,6 +374,9 @@
case GASKET_IOCTL_MAP_BUFFER:
retval = gasket_map_buffers(gasket_dev, argp);
break;
+ case GASKET_IOCTL_MAP_BUFFER_FLAGS:
+ retval = gasket_map_buffers_flags(gasket_dev, argp);
+ break;
case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
retval = gasket_config_coherent_allocator(gasket_dev, argp);
break;
@@ -381,6 +422,7 @@
case GASKET_IOCTL_PAGE_TABLE_SIZE:
case GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE:
case GASKET_IOCTL_MAP_BUFFER:
+ case GASKET_IOCTL_MAP_BUFFER_FLAGS:
case GASKET_IOCTL_UNMAP_BUFFER:
case GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
case GASKET_IOCTL_CONFIG_COHERENT_ALLOCATOR:
diff --git a/drivers/staging/gasket/gasket_page_table.c b/drivers/staging/gasket/gasket_page_table.c
index d4c5f8a..b5438da 100644
--- a/drivers/staging/gasket/gasket_page_table.c
+++ b/drivers/staging/gasket/gasket_page_table.c
@@ -10,10 +10,18 @@
*
* This file assumes 4kB pages throughout; can be factored out when necessary.
*
- * Address format is as follows:
+ * There is a configurable number of page table entries, as well as a
+ * configurable bit index for the extended address flag. Both of these are
+ * specified in gasket_page_table_init through the page_table_config parameter.
+ *
+ * The following example assumes:
+ * page_table_config->total_entries = 8192
+ * page_table_config->extended_bit = 63
+ *
+ * Address format:
* Simple addresses - those whose containing pages are directly placed in the
* device's address translation registers - are laid out as:
- * [ 63 - 40: Unused | 39 - 28: 0 | 27 - 12: page index | 11 - 0: page offset ]
+ * [ 63 - 25: 0 | 24 - 12: page index | 11 - 0: page offset ]
* page index: The index of the containing page in the device's address
* translation registers.
* page offset: The index of the address into the containing page.
@@ -21,7 +29,7 @@
* Extended address - those whose containing pages are contained in a second-
* level page table whose address is present in the device's address translation
* registers - are laid out as:
- * [ 63 - 40: Unused | 39: flag | 38 - 37: 0 | 36 - 21: dev/level 0 index |
+ * [ 63: flag | 62 - 34: 0 | 33 - 21: dev/level 0 index |
* 20 - 12: host/level 1 index | 11 - 0: page offset ]
* flag: Marker indicating that this is an extended address. Always 1.
* dev index: The index of the first-level page in the device's extended
@@ -79,6 +87,19 @@
*/
#define GASKET_EXTENDED_LVL1_SHIFT 12
+/*
+ * Utilities for accessing flags bitfields.
+ */
+#define MASK(field) (((1u << field##_WIDTH) - 1) << field##_SHIFT)
+#define GET(field, flags) (((flags) & MASK(field)) >> field##_SHIFT)
+#define SET(field, flags, val) (((flags) & ~MASK(field)) | ((val) << field##_SHIFT))
+
+#define FLAGS_STATUS_SHIFT 0
+#define FLAGS_STATUS_WIDTH 1
+
+#define FLAGS_DMA_DIRECTION_SHIFT 1
+#define FLAGS_DMA_DIRECTION_WIDTH 2
+
/* Type declarations */
/* Valid states for a struct gasket_page_table_entry. */
enum pte_status {
@@ -100,14 +121,12 @@
* to the actual page mapped and described by this structure.
*/
struct gasket_page_table_entry {
- /* The status of this entry/slot: free or in use. */
- enum pte_status status;
-
- /* Address of the page in DMA space. */
- dma_addr_t dma_addr;
-
- /* Linux page descriptor for the page described by this structure. */
- struct page *page;
+ /*
+ * Internal structure matches gasket_page_table_ioctl_flags.flags.
+ * NOTE: All fields should have a default value of 0. This ensures that
+ * the kernel will be backwards compatible with old drivers.
+ */
+ u32 flags;
/*
* Index for alignment into host vaddrs.
@@ -119,6 +138,12 @@
*/
int offset;
+ /* Address of the page in DMA space. */
+ dma_addr_t dma_addr;
+
+ /* Linux page descriptor for the page described by this structure. */
+ struct page *page;
+
/*
* If this is an extended and first-level entry, sublevel points
* to the second-level entries underneath this entry.
@@ -143,7 +168,7 @@
u64 user_virt;
/* User virtual address that was mapped by the mmap kernel subsystem */
- u64 kernel_virt;
+ dma_addr_t kernel_virt;
/*
* Whether this page has been mapped into a user land process virtual
@@ -297,7 +322,7 @@
int i;
for (i = 0; i < num_entries; i++) {
- if (ptes[i].status != PTE_FREE)
+ if (GET(FLAGS_STATUS, ptes[i].flags) != PTE_FREE)
return false;
}
@@ -313,16 +338,14 @@
u64 __iomem *slot)
{
/* Release the page table from the driver */
- pte->status = PTE_FREE;
+ pte->flags = SET(FLAGS_STATUS, pte->flags, PTE_FREE);
/* Release the page table from the device */
writeq(0, slot);
- /* Force sync around the address release. */
- mb();
if (pte->dma_addr)
dma_unmap_page(pg_tbl->device, pte->dma_addr, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
+ DMA_TO_DEVICE);
vfree(pte->sublevel);
@@ -349,7 +372,7 @@
slot = pg_tbl->base_slot + pg_tbl->num_simple_entries;
pte < pg_tbl->entries + pg_tbl->config.total_entries;
pte++, slot++) {
- if (pte->status == PTE_INUSE) {
+ if (GET(FLAGS_STATUS, pte->flags) == PTE_INUSE) {
if (gasket_is_pte_range_free(pte->sublevel,
GASKET_PAGES_PER_SUBTABLE))
gasket_free_extended_subtable(pg_tbl, pte,
@@ -398,7 +421,7 @@
start = min(pg_tbl->num_simple_entries, num_simple_entries);
for (i = start; i < pg_tbl->config.total_entries; i++) {
- if (pg_tbl->entries[i].status != PTE_FREE) {
+ if (GET(FLAGS_STATUS, pg_tbl->entries[i].flags) != PTE_FREE) {
dev_err(pg_tbl->device, "entry %d is not free\n", i);
mutex_unlock(&pg_tbl->mutex);
return -EBUSY;
@@ -435,6 +458,19 @@
return min <= host_addr && host_addr < max;
}
+/* Safely return a page to the OS. */
+static bool gasket_release_page(struct page *page)
+{
+ if (!page)
+ return false;
+
+ if (!PageReserved(page))
+ SetPageDirty(page);
+ put_page(page);
+
+ return true;
+}
+
/*
* Get and map last level page table buffers.
*
@@ -446,7 +482,8 @@
static int gasket_perform_mapping(struct gasket_page_table *pg_tbl,
struct gasket_page_table_entry *ptes,
u64 __iomem *slots, ulong host_addr,
- uint num_pages, int is_simple_mapping)
+ uint num_pages, u32 flags,
+ int is_simple_mapping)
{
int ret;
ulong offset;
@@ -455,6 +492,12 @@
ulong page_addr;
int i;
+ if (GET(FLAGS_DMA_DIRECTION, flags) == DMA_NONE) {
+ dev_err(pg_tbl->device, "invalid DMA direction flags=0x%lx\n",
+ (unsigned long)flags);
+ return -EINVAL;
+ }
+
for (i = 0; i < num_pages; i++) {
page_addr = host_addr + i * PAGE_SIZE;
offset = page_addr & (PAGE_SIZE - 1);
@@ -484,44 +527,43 @@
ptes[i].offset = offset;
/* Map the page into DMA space. */
- ptes[i].dma_addr =
- dma_map_page(pg_tbl->device, page, 0, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
+ ptes[i].dma_addr = dma_map_page(pg_tbl->device, page, 0, PAGE_SIZE,
+ GET(FLAGS_DMA_DIRECTION, flags));
dev_dbg(pg_tbl->device,
"%s i %d pte %p pfn %p -> mapped %llx\n",
__func__, i, &ptes[i],
(void *)page_to_pfn(page),
(unsigned long long)ptes[i].dma_addr);
- if (ptes[i].dma_addr == -1) {
+ if (dma_mapping_error(pg_tbl->device,
+ ptes[i].dma_addr)) {
dev_dbg(pg_tbl->device,
"%s i %d -> fail to map page %llx "
- "[pfn %p ohys %p]\n",
+ "[pfn %p phys %p]\n",
__func__, i,
(unsigned long long)ptes[i].dma_addr,
(void *)page_to_pfn(page),
(void *)page_to_phys(page));
- return -1;
+
+ /* clean up */
+ if (gasket_release_page(ptes[i].page))
+ --pg_tbl->num_active_pages;
+
+ memset(&ptes[i], 0, sizeof(struct gasket_page_table_entry));
+ return -EINVAL;
}
- /* Wait until the page is mapped. */
- mb();
}
/* Make the DMA-space address available to the device. */
dma_addr = (ptes[i].dma_addr + offset) | GASKET_VALID_SLOT_FLAG;
- if (is_simple_mapping) {
+ if (is_simple_mapping)
writeq(dma_addr, &slots[i]);
- } else {
+ else
((u64 __force *)slots)[i] = dma_addr;
- /* Extended page table vectors are in DRAM,
- * and so need to be synced each time they are updated.
- */
- dma_map_single(pg_tbl->device,
- (void *)&((u64 __force *)slots)[i],
- sizeof(u64), DMA_TO_DEVICE);
- }
- ptes[i].status = PTE_INUSE;
+
+ /* Set PTE flags equal to flags param with STATUS=PTE_INUSE. */
+ ptes[i].flags = SET(FLAGS_STATUS, flags, PTE_INUSE);
}
return 0;
}
@@ -531,7 +573,7 @@
* Does not perform validity checking.
*/
static int gasket_simple_page_idx(struct gasket_page_table *pg_tbl,
- ulong dev_addr)
+ u64 dev_addr)
{
return (dev_addr >> GASKET_SIMPLE_PAGE_SHIFT) &
(pg_tbl->config.total_entries - 1);
@@ -542,10 +584,10 @@
* Does not perform validity checking.
*/
static ulong gasket_extended_lvl0_page_idx(struct gasket_page_table *pg_tbl,
- ulong dev_addr)
+ u64 dev_addr)
{
return (dev_addr >> GASKET_EXTENDED_LVL0_SHIFT) &
- ((1 << GASKET_EXTENDED_LVL0_WIDTH) - 1);
+ (pg_tbl->config.total_entries - 1);
}
/*
@@ -553,7 +595,7 @@
* Does not perform validity checking.
*/
static ulong gasket_extended_lvl1_page_idx(struct gasket_page_table *pg_tbl,
- ulong dev_addr)
+ u64 dev_addr)
{
return (dev_addr >> GASKET_EXTENDED_LVL1_SHIFT) &
(GASKET_PAGES_PER_SUBTABLE - 1);
@@ -564,7 +606,7 @@
* The page table mutex must be held by the caller.
*/
static int gasket_alloc_simple_entries(struct gasket_page_table *pg_tbl,
- ulong dev_addr, uint num_pages)
+ u64 dev_addr, uint num_pages)
{
if (!gasket_is_pte_range_free(pg_tbl->entries +
gasket_simple_page_idx(pg_tbl, dev_addr),
@@ -574,19 +616,6 @@
return 0;
}
-/* Safely return a page to the OS. */
-static bool gasket_release_page(struct page *page)
-{
- if (!page)
- return false;
-
- if (!PageReserved(page))
- SetPageDirty(page);
- put_page(page);
-
- return true;
-}
-
/*
* Unmap and release mapped pages.
* The page table mutex must be held by the caller.
@@ -603,23 +632,20 @@
*/
for (i = 0; i < num_pages; i++) {
/* release the address from the device, */
- if (is_simple_mapping || ptes[i].status == PTE_INUSE)
+ if (is_simple_mapping)
writeq(0, &slots[i]);
else
((u64 __force *)slots)[i] = 0;
- /* Force sync around the address release. */
- mb();
/* release the address from the driver, */
- if (ptes[i].status == PTE_INUSE) {
- if (ptes[i].dma_addr) {
- dma_unmap_page(pg_tbl->device, ptes[i].dma_addr,
- PAGE_SIZE, DMA_FROM_DEVICE);
+ if (GET(FLAGS_STATUS, ptes[i].flags) == PTE_INUSE) {
+ if (ptes[i].page && ptes[i].dma_addr) {
+ dma_unmap_page(pg_tbl->device, ptes[i].dma_addr, PAGE_SIZE,
+ GET(FLAGS_DMA_DIRECTION, ptes[i].flags));
}
if (gasket_release_page(ptes[i].page))
--pg_tbl->num_active_pages;
}
- ptes[i].status = PTE_FREE;
/* and clear the PTE. */
memset(&ptes[i], 0, sizeof(struct gasket_page_table_entry));
@@ -631,7 +657,7 @@
* The page table mutex must be held by the caller.
*/
static void gasket_unmap_simple_pages(struct gasket_page_table *pg_tbl,
- ulong dev_addr, uint num_pages)
+ u64 dev_addr, uint num_pages)
{
uint slot = gasket_simple_page_idx(pg_tbl, dev_addr);
@@ -644,7 +670,7 @@
* The page table mutex must be held by the caller.
*/
static void gasket_unmap_extended_pages(struct gasket_page_table *pg_tbl,
- ulong dev_addr, uint num_pages)
+ u64 dev_addr, uint num_pages)
{
uint slot_idx, remain, len;
struct gasket_page_table_entry *pte;
@@ -659,12 +685,19 @@
/* TODO: Add check to ensure pte remains valid? */
len = min(remain, GASKET_PAGES_PER_SUBTABLE - slot_idx);
- if (pte->status == PTE_INUSE) {
+ if (GET(FLAGS_STATUS, pte->flags) == PTE_INUSE) {
slot_base = (u64 __iomem *)(page_address(pte->page) +
pte->offset);
gasket_perform_unmapping(pg_tbl,
pte->sublevel + slot_idx,
slot_base + slot_idx, len, 0);
+ /*
+ * Extended page tables are in DRAM so they need to be
+ * synced each time they are updated.
+ */
+ dma_sync_single_for_device(pg_tbl->device,
+ pte->dma_addr + slot_idx * sizeof(u64),
+ len * sizeof(u64), DMA_TO_DEVICE);
}
remain -= len;
@@ -675,7 +708,7 @@
/* Evaluates to nonzero if the specified virtual address is simple. */
static inline bool gasket_addr_is_simple(struct gasket_page_table *pg_tbl,
- ulong addr)
+ u64 addr)
{
return !((addr) & (pg_tbl)->extended_flag);
}
@@ -684,38 +717,21 @@
* Convert (simple, page, offset) into a device address.
* Examples:
* Simple page 0, offset 32:
- * Input (0, 0, 32), Output 0x20
+ * Input (1, 0, 32), Output 0x20
* Simple page 1000, offset 511:
- * Input (0, 1000, 512), Output 0x3E81FF
+ * Input (1, 1000, 511), Output 0x3E81FF
* Extended page 0, offset 32:
* Input (0, 0, 32), Output 0x8000000020
* Extended page 1000, offset 511:
- * Input (1, 1000, 512), Output 0x8003E81FF
+ * Input (0, 1000, 511), Output 0x8003E81FF
*/
-static ulong gasket_components_to_dev_address(struct gasket_page_table *pg_tbl,
+static u64 gasket_components_to_dev_address(struct gasket_page_table *pg_tbl,
int is_simple, uint page_index,
uint offset)
{
- ulong lvl0_index, lvl1_index;
+ u64 dev_addr = (page_index << GASKET_SIMPLE_PAGE_SHIFT) | offset;
- if (is_simple) {
- /* Return simple addresses directly. */
- lvl0_index = page_index & (pg_tbl->config.total_entries - 1);
- return (lvl0_index << GASKET_SIMPLE_PAGE_SHIFT) | offset;
- }
-
- /*
- * This could be compressed into fewer statements, but
- * A) the compiler should optimize it
- * B) this is not slow
- * C) this is an uncommon operation
- * D) this is actually readable this way.
- */
- lvl0_index = page_index / GASKET_PAGES_PER_SUBTABLE;
- lvl1_index = page_index & (GASKET_PAGES_PER_SUBTABLE - 1);
- return (pg_tbl)->extended_flag |
- (lvl0_index << GASKET_EXTENDED_LVL0_SHIFT) |
- (lvl1_index << GASKET_EXTENDED_LVL1_SHIFT) | offset;
+ return is_simple ? dev_addr : (pg_tbl->extended_flag | dev_addr);
}
/*
@@ -726,7 +742,7 @@
* currently-partitioned simple pages.
*/
static bool gasket_is_simple_dev_addr_bad(struct gasket_page_table *pg_tbl,
- ulong dev_addr, uint num_pages)
+ u64 dev_addr, uint num_pages)
{
ulong page_offset = dev_addr & (PAGE_SIZE - 1);
ulong page_index =
@@ -734,7 +750,7 @@
if (gasket_components_to_dev_address(pg_tbl, 1, page_index,
page_offset) != dev_addr) {
- dev_err(pg_tbl->device, "address is invalid, 0x%lX\n",
+ dev_err(pg_tbl->device, "address is invalid, 0x%llX\n",
dev_addr);
return true;
}
@@ -764,19 +780,19 @@
* currently-partitioned extended pages.
*/
static bool gasket_is_extended_dev_addr_bad(struct gasket_page_table *pg_tbl,
- ulong dev_addr, uint num_pages)
+ u64 dev_addr, uint num_pages)
{
/* Starting byte index of dev_addr into the first mapped page */
ulong page_offset = dev_addr & (PAGE_SIZE - 1);
ulong page_global_idx, page_lvl0_idx;
ulong num_lvl0_pages;
- ulong addr;
+ u64 addr;
/* check if the device address is out of bound */
addr = dev_addr & ~((pg_tbl)->extended_flag);
if (addr >> (GASKET_EXTENDED_LVL0_WIDTH + GASKET_EXTENDED_LVL0_SHIFT)) {
- dev_err(pg_tbl->device, "device address out of bounds: 0x%lx\n",
- dev_addr);
+ dev_err(pg_tbl->device,
+ "device address out of bounds: 0x%llx\n", dev_addr);
return true;
}
@@ -793,7 +809,7 @@
if (gasket_components_to_dev_address(pg_tbl, 0, page_global_idx,
page_offset) != dev_addr) {
- dev_err(pg_tbl->device, "address is invalid: 0x%lx\n",
+ dev_err(pg_tbl->device, "address is invalid: 0x%llx\n",
dev_addr);
return true;
}
@@ -821,7 +837,7 @@
* The page table mutex must be held by the caller.
*/
static void gasket_page_table_unmap_nolock(struct gasket_page_table *pg_tbl,
- ulong dev_addr, uint num_pages)
+ u64 dev_addr, uint num_pages)
{
if (!num_pages)
return;
@@ -837,8 +853,8 @@
* If there is an error, no pages are mapped.
*/
static int gasket_map_simple_pages(struct gasket_page_table *pg_tbl,
- ulong host_addr, ulong dev_addr,
- uint num_pages)
+ ulong host_addr, u64 dev_addr,
+ uint num_pages, u32 flags)
{
int ret;
uint slot_idx = gasket_simple_page_idx(pg_tbl, dev_addr);
@@ -846,14 +862,15 @@
ret = gasket_alloc_simple_entries(pg_tbl, dev_addr, num_pages);
if (ret) {
dev_err(pg_tbl->device,
- "page table slots %u (@ 0x%lx) to %u are not available\n",
- slot_idx, dev_addr, slot_idx + num_pages - 1);
+ "page table slots %u (@ 0x%llx) to %u are not available\n",
+ slot_idx, (long long unsigned int)dev_addr,
+ slot_idx + num_pages - 1);
return ret;
}
ret = gasket_perform_mapping(pg_tbl, pg_tbl->entries + slot_idx,
pg_tbl->base_slot + slot_idx, host_addr,
- num_pages, 1);
+ num_pages, flags, 1);
if (ret) {
gasket_page_table_unmap_nolock(pg_tbl, dev_addr, num_pages);
@@ -896,15 +913,29 @@
/* Map the page into DMA space. */
pte->dma_addr = dma_map_page(pg_tbl->device, pte->page, 0, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
- /* Wait until the page is mapped. */
- mb();
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(pg_tbl->device, pte->dma_addr)) {
+ dev_dbg(pg_tbl->device,
+ "%s -> fail to map page %llx "
+ "[pfn %p phys %p]\n",
+ __func__,
+ (unsigned long long)pte->dma_addr,
+ (void *)page_to_pfn(pte->page),
+ (void *)page_to_phys(pte->page));
+
+ /* clean up */
+ free_page(page_addr);
+ vfree(pte->sublevel);
+ memset(pte, 0, sizeof(struct gasket_page_table_entry));
+
+ return -ENOMEM;
+ }
/* make the addresses available to the device */
dma_addr = (pte->dma_addr + pte->offset) | GASKET_VALID_SLOT_FLAG;
writeq(dma_addr, slot);
- pte->status = PTE_INUSE;
+ pte->flags = SET(FLAGS_STATUS, pte->flags, PTE_INUSE);
return 0;
}
@@ -924,7 +955,7 @@
* The page table mutex must be held by the caller.
*/
static int gasket_alloc_extended_entries(struct gasket_page_table *pg_tbl,
- ulong dev_addr, uint num_entries)
+ u64 dev_addr, uint num_entries)
{
int ret = 0;
uint remain, subtable_slot_idx, len;
@@ -942,7 +973,7 @@
len = min(remain,
GASKET_PAGES_PER_SUBTABLE - subtable_slot_idx);
- if (pte->status == PTE_FREE) {
+ if (GET(FLAGS_STATUS, pte->flags) == PTE_FREE) {
ret = gasket_alloc_extended_subtable(pg_tbl, pte, slot);
if (ret) {
dev_err(pg_tbl->device,
@@ -969,11 +1000,11 @@
* If there is an error, no pages are mapped.
*/
static int gasket_map_extended_pages(struct gasket_page_table *pg_tbl,
- ulong host_addr, ulong dev_addr,
- uint num_pages)
+ ulong host_addr, u64 dev_addr,
+ uint num_pages, u32 flags)
{
int ret;
- ulong dev_addr_end;
+ u64 dev_addr_end;
uint slot_idx, remain, len;
struct gasket_page_table_entry *pte;
u64 __iomem *slot_base;
@@ -982,11 +1013,11 @@
if (ret) {
dev_addr_end = dev_addr + (num_pages / PAGE_SIZE) - 1;
dev_err(pg_tbl->device,
- "page table slots (%lu,%lu) (@ 0x%lx) to (%lu,%lu) are "
- "not available\n",
+ "page table slots (%lu,%lu) (@ 0x%llx) to (%lu,%lu) "
+ "are not available\n",
gasket_extended_lvl0_page_idx(pg_tbl, dev_addr),
- dev_addr,
gasket_extended_lvl1_page_idx(pg_tbl, dev_addr),
+ (long long unsigned int)dev_addr,
gasket_extended_lvl0_page_idx(pg_tbl, dev_addr_end),
gasket_extended_lvl1_page_idx(pg_tbl, dev_addr_end));
return ret;
@@ -1004,13 +1035,21 @@
(u64 __iomem *)(page_address(pte->page) + pte->offset);
ret = gasket_perform_mapping(pg_tbl, pte->sublevel + slot_idx,
slot_base + slot_idx, host_addr,
- len, 0);
+ len, flags, 0);
if (ret) {
gasket_page_table_unmap_nolock(pg_tbl, dev_addr,
num_pages);
return ret;
}
+ /*
+ * Extended page tables are in DRAM so they need to be synced
+ * each time they are updated.
+ */
+ dma_sync_single_for_device(pg_tbl->device,
+ pte->dma_addr + slot_idx * sizeof(u64),
+ len * sizeof(u64), DMA_TO_DEVICE);
+
remain -= len;
slot_idx = 0;
pte++;
@@ -1029,7 +1068,7 @@
* The page table mutex is held for the entire operation.
*/
int gasket_page_table_map(struct gasket_page_table *pg_tbl, ulong host_addr,
- ulong dev_addr, uint num_pages)
+ u64 dev_addr, uint num_pages, u32 flags)
{
int ret;
@@ -1040,18 +1079,18 @@
if (gasket_addr_is_simple(pg_tbl, dev_addr)) {
ret = gasket_map_simple_pages(pg_tbl, host_addr, dev_addr,
- num_pages);
+ num_pages, flags);
} else {
ret = gasket_map_extended_pages(pg_tbl, host_addr, dev_addr,
- num_pages);
+ num_pages, flags);
}
mutex_unlock(&pg_tbl->mutex);
dev_dbg(pg_tbl->device,
- "%s done: ha %llx daddr %llx num %d, ret %d\n",
+ "%s done: ha %llx daddr %llx num %d, flags %x ret %d\n",
__func__, (unsigned long long)host_addr,
- (unsigned long long)dev_addr, num_pages, ret);
+ (unsigned long long)dev_addr, num_pages, flags, ret);
return ret;
}
EXPORT_SYMBOL(gasket_page_table_map);
@@ -1065,7 +1104,7 @@
*
* The page table mutex is held for the entire operation.
*/
-void gasket_page_table_unmap(struct gasket_page_table *pg_tbl, ulong dev_addr,
+void gasket_page_table_unmap(struct gasket_page_table *pg_tbl, u64 dev_addr,
uint num_pages)
{
if (!num_pages)
@@ -1110,7 +1149,7 @@
/* See gasket_page_table.h for description. */
int gasket_page_table_lookup_page(
- struct gasket_page_table *pg_tbl, ulong dev_addr, struct page **ppage,
+ struct gasket_page_table *pg_tbl, u64 dev_addr, struct page **ppage,
ulong *poffset)
{
uint page_num;
@@ -1123,7 +1162,7 @@
goto fail;
pte = pg_tbl->entries + page_num;
- if (pte->status != PTE_INUSE)
+ if (GET(FLAGS_STATUS, pte->flags) != PTE_INUSE)
goto fail;
} else {
/* Find the level 0 entry, */
@@ -1132,13 +1171,13 @@
goto fail;
pte = pg_tbl->entries + pg_tbl->num_simple_entries + page_num;
- if (pte->status != PTE_INUSE)
+ if (GET(FLAGS_STATUS, pte->flags) != PTE_INUSE)
goto fail;
/* and its contained level 1 entry. */
page_num = gasket_extended_lvl1_page_idx(pg_tbl, dev_addr);
pte = pte->sublevel + page_num;
- if (pte->status != PTE_INUSE)
+ if (GET(FLAGS_STATUS, pte->flags) != PTE_INUSE)
goto fail;
}
@@ -1151,12 +1190,12 @@
*ppage = NULL;
*poffset = 0;
mutex_unlock(&pg_tbl->mutex);
- return -1;
+ return -EINVAL;
}
/* See gasket_page_table.h for description. */
bool gasket_page_table_are_addrs_bad(
- struct gasket_page_table *pg_tbl, ulong host_addr, ulong dev_addr,
+ struct gasket_page_table *pg_tbl, ulong host_addr, u64 dev_addr,
ulong bytes)
{
if (host_addr & (PAGE_SIZE - 1)) {
@@ -1172,7 +1211,7 @@
/* See gasket_page_table.h for description. */
bool gasket_page_table_is_dev_addr_bad(
- struct gasket_page_table *pg_tbl, ulong dev_addr, ulong bytes)
+ struct gasket_page_table *pg_tbl, u64 dev_addr, ulong bytes)
{
uint num_pages = bytes / PAGE_SIZE;
@@ -1291,7 +1330,7 @@
return -EINVAL;
mem = dma_alloc_coherent(gasket_get_device(gasket_dev),
- num_pages * PAGE_SIZE, &handle, 0);
+ num_pages * PAGE_SIZE, &handle, GFP_KERNEL);
if (!mem)
goto nomem;
@@ -1303,7 +1342,6 @@
GFP_KERNEL);
if (!gasket_dev->page_table[index]->coherent_pages)
goto nomem;
- *dma_address = 0;
gasket_dev->coherent_buffer.length_bytes =
PAGE_SIZE * (num_pages);
@@ -1315,23 +1353,22 @@
gasket_dev->page_table[index]->coherent_pages[j].paddr =
handle + j * PAGE_SIZE;
gasket_dev->page_table[index]->coherent_pages[j].kernel_virt =
- (u64)mem + j * PAGE_SIZE;
+ (ulong)mem + j * PAGE_SIZE;
}
- if (*dma_address == 0)
- goto nomem;
return 0;
nomem:
if (mem) {
dma_free_coherent(gasket_get_device(gasket_dev),
num_pages * PAGE_SIZE, mem, handle);
+ gasket_dev->coherent_buffer.length_bytes = 0;
+ gasket_dev->coherent_buffer.virt_base = NULL;
+ gasket_dev->coherent_buffer.phys_base = 0;
}
- if (gasket_dev->page_table[index]->coherent_pages) {
- kfree(gasket_dev->page_table[index]->coherent_pages);
- gasket_dev->page_table[index]->coherent_pages = NULL;
- }
+ kfree(gasket_dev->page_table[index]->coherent_pages);
+ gasket_dev->page_table[index]->coherent_pages = NULL;
gasket_dev->page_table[index]->num_coherent_pages = 0;
return -ENOMEM;
}
@@ -1350,15 +1387,8 @@
if (driver_desc->coherent_buffer_description.base != dma_address)
return -EADDRNOTAVAIL;
- if (gasket_dev->coherent_buffer.length_bytes) {
- dma_free_coherent(gasket_get_device(gasket_dev),
- gasket_dev->coherent_buffer.length_bytes,
- gasket_dev->coherent_buffer.virt_base,
- gasket_dev->coherent_buffer.phys_base);
- gasket_dev->coherent_buffer.length_bytes = 0;
- gasket_dev->coherent_buffer.virt_base = NULL;
- gasket_dev->coherent_buffer.phys_base = 0;
- }
+ gasket_free_coherent_memory_all(gasket_dev, index);
+
return 0;
}
@@ -1378,4 +1408,8 @@
gasket_dev->coherent_buffer.virt_base = NULL;
gasket_dev->coherent_buffer.phys_base = 0;
}
+
+ kfree(gasket_dev->page_table[index]->coherent_pages);
+ gasket_dev->page_table[index]->coherent_pages = NULL;
+ gasket_dev->page_table[index]->num_coherent_pages = 0;
}
diff --git a/drivers/staging/gasket/gasket_page_table.h b/drivers/staging/gasket/gasket_page_table.h
index 7b01b73..609e1d9 100644
--- a/drivers/staging/gasket/gasket_page_table.h
+++ b/drivers/staging/gasket/gasket_page_table.h
@@ -85,6 +85,8 @@
* @host_addr: Starting host virtual memory address of the pages.
* @dev_addr: Starting device address of the pages.
* @num_pages: Number of [4kB] pages to map.
+ * @flags: Specifies attributes to apply to the pages.
+ * Internal structure matches gasket_page_table_ioctl_flags.flags.
*
* Description: Maps the "num_pages" pages of host memory pointed to by
* host_addr to the address "dev_addr" in device memory.
@@ -95,7 +97,7 @@
* If there is an error, no pages are mapped.
*/
int gasket_page_table_map(struct gasket_page_table *page_table, ulong host_addr,
- ulong dev_addr, uint num_pages);
+ u64 dev_addr, uint num_pages, u32 flags);
/*
* Un-map host pages from device memory.
@@ -106,7 +108,7 @@
* Description: The inverse of gasket_map_pages. Unmaps pages from the device.
*/
void gasket_page_table_unmap(struct gasket_page_table *page_table,
- ulong dev_addr, uint num_pages);
+ u64 dev_addr, uint num_pages);
/*
* Unmap ALL host pages from device memory.
@@ -146,7 +148,7 @@
* and offset are returned through the pointers, if successful.
*/
int gasket_page_table_lookup_page(struct gasket_page_table *page_table,
- ulong dev_addr, struct page **page,
+ u64 dev_addr, struct page **page,
ulong *poffset);
/*
@@ -163,7 +165,7 @@
* Returns true if the mapping is bad, false otherwise.
*/
bool gasket_page_table_are_addrs_bad(struct gasket_page_table *page_table,
- ulong host_addr, ulong dev_addr,
+ ulong host_addr, u64 dev_addr,
ulong bytes);
/*
@@ -179,7 +181,7 @@
* Returns true if the address is bad, false otherwise.
*/
bool gasket_page_table_is_dev_addr_bad(struct gasket_page_table *page_table,
- ulong dev_addr, ulong bytes);
+ u64 dev_addr, ulong bytes);
/*
* Gets maximum size for the given page table.
diff --git a/drivers/staging/gasket/gasket_sysfs.h b/drivers/staging/gasket/gasket_sysfs.h
index f32eaf8..e8f29a3 100644
--- a/drivers/staging/gasket/gasket_sysfs.h
+++ b/drivers/staging/gasket/gasket_sysfs.h
@@ -79,6 +79,13 @@
.data.attr_type = _attr_type \
}
+#define GASKET_SYSFS_RW(_name, _show_function, _store_function, _attr_type) \
+ { \
+ .attr = __ATTR(_name, S_IWUSR | S_IWGRP | S_IRUGO, \
+ _show_function, _store_function), \
+ .data.attr_type = _attr_type \
+ }
+
/* Initializes the Gasket sysfs subsystem.
*
* Description: Performs one-time initialization. Must be called before usage
@@ -152,8 +159,8 @@
* Returns the Gasket sysfs attribute associated with the kernel device
* attribute and device structure itself. Upon success, this call will take a
* reference to internal sysfs data that must be released with a call to
- * gasket_sysfs_get_device_data. While this reference is held, the underlying
- * device sysfs information/structure will remain valid/will not be deleted.
+ * gasket_sysfs_put_attr. While this reference is held, the underlying device
+ * sysfs information/structure will remain valid/will not be deleted.
*/
struct gasket_sysfs_attribute *
gasket_sysfs_get_attr(struct device *device, struct device_attribute *attr);