summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-09-04 01:41:38 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-04 01:41:38 +0200
commitff474e8ca8547d09cb82ebab56d4c96f9eea01ce (patch)
treec004122f04a405eddb7929cd0297ccdce016f1b8 /drivers
parentMerge branch 'pcmcia' of git://ftp.arm.linux.org.uk/~rmk/linux-arm (diff)
parentcxl: Set up and enable PSL Timebase (diff)
downloadlinux-ff474e8ca8547d09cb82ebab56d4c96f9eea01ce.tar.xz
linux-ff474e8ca8547d09cb82ebab56d4c96f9eea01ce.zip
Merge tag 'powerpc-4.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc updates from Michael Ellerman: - support "hybrid" iommu/direct DMA ops for coherent_mask < dma_mask from Benjamin Herrenschmidt - EEH fixes for SRIOV from Gavin - introduce rtas_get_sensor_fast() for IRQ handlers from Thomas Huth - use hardware RNG for arch_get_random_seed_* not arch_get_random_* from Paul Mackerras - seccomp filter support from Michael Ellerman - opal_cec_reboot2() handling for HMIs & machine checks from Mahesh Salgaonkar - add powerpc timebase as a trace clock source from Naveen N. Rao - misc cleanups in the xmon, signal & SLB code from Anshuman Khandual - add an inline function to update POWER8 HID0 from Gautham R. Shenoy - fix pte_pagesize_index() crash on 4K w/64K hash from Michael Ellerman - drop support for 64K local store on 4K kernels from Michael Ellerman - move dma_get_required_mask() from pnv_phb to pci_controller_ops from Andrew Donnellan - initialize distance lookup table from drconf path from Nikunj A Dadhania - enable RTC class support from Vaibhav Jain - disable automatically blocked PCI config from Gavin Shan - add LEDs driver for PowerNV platform from Vasant Hegde - fix endianness issues in the HVSI driver from Laurent Dufour - kexec endian fixes from Samuel Mendoza-Jonas - fix corrupted pdn list from Gavin Shan - fix fenced PHB caused by eeh_slot_error_detail() from Gavin Shan - Freescale updates from Scott: Highlights include 32-bit memcpy/memset optimizations, checksum optimizations, 85xx config fragments and updates, device tree updates, e6500 fixes for non-SMP, and misc cleanup and minor fixes. - a ton of cxl updates & fixes: - add explicit precision specifiers from Rasmus Villemoes - use more common format specifier from Rasmus Villemoes - destroy cxl_adapter_idr on module_exit from Johannes Thumshirn - destroy afu->contexts_idr on release of an afu from Johannes Thumshirn - compile with -Werror from Daniel Axtens - EEH support from Daniel Axtens - plug irq_bitmap getting leaked in cxl_context from Vaibhav Jain - add alternate MMIO error handling from Ian Munsie - allow release of contexts which have been OPENED but not STARTED from Andrew Donnellan - remove use of macro DEFINE_PCI_DEVICE_TABLE from Vaishali Thakkar - release irqs if memory allocation fails from Vaibhav Jain - remove racy attempt to force EEH invocation in reset from Daniel Axtens - fix + cleanup error paths in cxl_dev_context_init from Ian Munsie - fix force unmapping mmaps of contexts allocated through the kernel api from Ian Munsie - set up and enable PSL Timebase from Philippe Bergheaud * tag 'powerpc-4.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (140 commits) cxl: Set up and enable PSL Timebase cxl: Fix force unmapping mmaps of contexts allocated through the kernel api cxl: Fix + cleanup error paths in cxl_dev_context_init powerpc/eeh: Fix fenced PHB caused by eeh_slot_error_detail() powerpc/pseries: Cleanup on pci_dn_reconfig_notifier() powerpc/pseries: Fix corrupted pdn list powerpc/powernv: Enable LEDS support powerpc/iommu: Set default DMA offset in dma_dev_setup cxl: Remove racy attempt to force EEH invocation in reset cxl: Release irqs if memory allocation fails cxl: Remove use of macro DEFINE_PCI_DEVICE_TABLE powerpc/powernv: Fix mis-merge of OPAL support for LEDS driver powerpc/powernv: Reset HILE before kexec_sequence() powerpc/kexec: Reset secondary cpu endianness before kexec powerpc/hvsi: Fix endianness issues in the HVSI driver leds/powernv: Add driver for PowerNV platform powerpc/powernv: Create LED platform device powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states powerpc/powernv: Fix the log message when disabling VF cxl: Allow release of contexts which have been OPENED but not STARTED ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/leds/Kconfig11
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-powernv.c345
-rw-r--r--drivers/macintosh/therm_windtunnel.c2
-rw-r--r--drivers/macintosh/windfarm.h4
-rw-r--r--drivers/macintosh/windfarm_core.c47
-rw-r--r--drivers/memory/fsl_ifc.c43
-rw-r--r--drivers/misc/cxl/Kconfig7
-rw-r--r--drivers/misc/cxl/Makefile2
-rw-r--r--drivers/misc/cxl/api.c59
-rw-r--r--drivers/misc/cxl/context.c22
-rw-r--r--drivers/misc/cxl/cxl.h94
-rw-r--r--drivers/misc/cxl/debugfs.c2
-rw-r--r--drivers/misc/cxl/file.c27
-rw-r--r--drivers/misc/cxl/irq.c56
-rw-r--r--drivers/misc/cxl/main.c1
-rw-r--r--drivers/misc/cxl/native.c119
-rw-r--r--drivers/misc/cxl/pci.c611
-rw-r--r--drivers/misc/cxl/sysfs.c26
-rw-r--r--drivers/misc/cxl/trace.h10
-rw-r--r--drivers/misc/cxl/vphb.c34
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c258
-rw-r--r--drivers/tty/hvc/hvsi.c46
23 files changed, 1410 insertions, 417 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 23408bd68fdc..bea24bd4d519 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -565,6 +565,17 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
+config LEDS_POWERNV
+ tristate "LED support for PowerNV Platform"
+ depends on LEDS_CLASS
+ depends on PPC_POWERNV
+ depends on OF
+ help
+ This option enables support for the system LEDs present on
+ PowerNV platforms. Say 'y' to enable this support in kernel.
+ To compile this driver as a module, choose 'm' here: the module
+ will be called leds-powernv.
+
config LEDS_SYSCON
bool "LED support for LEDs on system controllers"
depends on LEDS_CLASS=y
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 8d6a24a2f513..6a943d16ecab 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
obj-$(CONFIG_LEDS_PM8941_WLED) += leds-pm8941-wled.o
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
+obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c
new file mode 100644
index 000000000000..2c5c5b12ab64
--- /dev/null
+++ b/drivers/leds/leds-powernv.c
@@ -0,0 +1,345 @@
+/*
+ * PowerNV LED Driver
+ *
+ * Copyright IBM Corp. 2015
+ *
+ * Author: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
+ * Author: Anshuman Khandual <khandual@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/opal.h>
+
+/* Map LED type to description. */
+struct led_type_map {
+ const int type;
+ const char *desc;
+};
+static const struct led_type_map led_type_map[] = {
+ {OPAL_SLOT_LED_TYPE_ID, "identify"},
+ {OPAL_SLOT_LED_TYPE_FAULT, "fault"},
+ {OPAL_SLOT_LED_TYPE_ATTN, "attention"},
+ {-1, NULL},
+};
+
+struct powernv_led_common {
+ /*
+ * By default unload path resets all the LEDs. But on PowerNV
+ * platform we want to retain LED state across reboot as these
+ * are controlled by firmware. Also service processor can modify
+ * the LEDs independent of OS. Hence avoid resetting LEDs in
+ * unload path.
+ */
+ bool led_disabled;
+
+ /* Max supported LED type */
+ __be64 max_led_type;
+
+ /* glabal lock */
+ struct mutex lock;
+};
+
+/* PowerNV LED data */
+struct powernv_led_data {
+ struct led_classdev cdev;
+ char *loc_code; /* LED location code */
+ int led_type; /* OPAL_SLOT_LED_TYPE_* */
+
+ struct powernv_led_common *common;
+};
+
+
+/* Returns OPAL_SLOT_LED_TYPE_* for given led type string */
+static int powernv_get_led_type(const char *led_type_desc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(led_type_map); i++)
+ if (!strcmp(led_type_map[i].desc, led_type_desc))
+ return led_type_map[i].type;
+
+ return -1;
+}
+
+/*
+ * This commits the state change of the requested LED through an OPAL call.
+ * This function is called from work queue task context when ever it gets
+ * scheduled. This function can sleep at opal_async_wait_response call.
+ */
+static void powernv_led_set(struct powernv_led_data *powernv_led,
+ enum led_brightness value)
+{
+ int rc, token;
+ u64 led_mask, led_value = 0;
+ __be64 max_type;
+ struct opal_msg msg;
+ struct device *dev = powernv_led->cdev.dev;
+ struct powernv_led_common *powernv_led_common = powernv_led->common;
+
+ /* Prepare for the OPAL call */
+ max_type = powernv_led_common->max_led_type;
+ led_mask = OPAL_SLOT_LED_STATE_ON << powernv_led->led_type;
+ if (value)
+ led_value = led_mask;
+
+ /* OPAL async call */
+ token = opal_async_get_token_interruptible();
+ if (token < 0) {
+ if (token != -ERESTARTSYS)
+ dev_err(dev, "%s: Couldn't get OPAL async token\n",
+ __func__);
+ return;
+ }
+
+ rc = opal_leds_set_ind(token, powernv_led->loc_code,
+ led_mask, led_value, &max_type);
+ if (rc != OPAL_ASYNC_COMPLETION) {
+ dev_err(dev, "%s: OPAL set LED call failed for %s [rc=%d]\n",
+ __func__, powernv_led->loc_code, rc);
+ goto out_token;
+ }
+
+ rc = opal_async_wait_response(token, &msg);
+ if (rc) {
+ dev_err(dev,
+ "%s: Failed to wait for the async response [rc=%d]\n",
+ __func__, rc);
+ goto out_token;
+ }
+
+ rc = be64_to_cpu(msg.params[1]);
+ if (rc != OPAL_SUCCESS)
+ dev_err(dev, "%s : OAPL async call returned failed [rc=%d]\n",
+ __func__, rc);
+
+out_token:
+ opal_async_release_token(token);
+}
+
+/*
+ * This function fetches the LED state for a given LED type for
+ * mentioned LED classdev structure.
+ */
+static enum led_brightness powernv_led_get(struct powernv_led_data *powernv_led)
+{
+ int rc;
+ __be64 mask, value, max_type;
+ u64 led_mask, led_value;
+ struct device *dev = powernv_led->cdev.dev;
+ struct powernv_led_common *powernv_led_common = powernv_led->common;
+
+ /* Fetch all LED status */
+ mask = cpu_to_be64(0);
+ value = cpu_to_be64(0);
+ max_type = powernv_led_common->max_led_type;
+
+ rc = opal_leds_get_ind(powernv_led->loc_code,
+ &mask, &value, &max_type);
+ if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) {
+ dev_err(dev, "%s: OPAL get led call failed [rc=%d]\n",
+ __func__, rc);
+ return LED_OFF;
+ }
+
+ led_mask = be64_to_cpu(mask);
+ led_value = be64_to_cpu(value);
+
+ /* LED status available */
+ if (!((led_mask >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)) {
+ dev_err(dev, "%s: LED status not available for %s\n",
+ __func__, powernv_led->cdev.name);
+ return LED_OFF;
+ }
+
+ /* LED status value */
+ if ((led_value >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)
+ return LED_FULL;
+
+ return LED_OFF;
+}
+
+/*
+ * LED classdev 'brightness_get' function. This schedules work
+ * to update LED state.
+ */
+static void powernv_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct powernv_led_data *powernv_led =
+ container_of(led_cdev, struct powernv_led_data, cdev);
+ struct powernv_led_common *powernv_led_common = powernv_led->common;
+
+ /* Do not modify LED in unload path */
+ if (powernv_led_common->led_disabled)
+ return;
+
+ mutex_lock(&powernv_led_common->lock);
+ powernv_led_set(powernv_led, value);
+ mutex_unlock(&powernv_led_common->lock);
+}
+
+/* LED classdev 'brightness_get' function */
+static enum led_brightness powernv_brightness_get(struct led_classdev *led_cdev)
+{
+ struct powernv_led_data *powernv_led =
+ container_of(led_cdev, struct powernv_led_data, cdev);
+
+ return powernv_led_get(powernv_led);
+}
+
+/*
+ * This function registers classdev structure for any given type of LED on
+ * a given child LED device node.
+ */
+static int powernv_led_create(struct device *dev,
+ struct powernv_led_data *powernv_led,
+ const char *led_type_desc)
+{
+ int rc;
+
+ /* Make sure LED type is supported */
+ powernv_led->led_type = powernv_get_led_type(led_type_desc);
+ if (powernv_led->led_type == -1) {
+ dev_warn(dev, "%s: No support for led type : %s\n",
+ __func__, led_type_desc);
+ return -EINVAL;
+ }
+
+ /* Create the name for classdev */
+ powernv_led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
+ powernv_led->loc_code,
+ led_type_desc);
+ if (!powernv_led->cdev.name) {
+ dev_err(dev,
+ "%s: Memory allocation failed for classdev name\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ powernv_led->cdev.brightness_set = powernv_brightness_set;
+ powernv_led->cdev.brightness_get = powernv_brightness_get;
+ powernv_led->cdev.brightness = LED_OFF;
+ powernv_led->cdev.max_brightness = LED_FULL;
+
+ /* Register the classdev */
+ rc = devm_led_classdev_register(dev, &powernv_led->cdev);
+ if (rc) {
+ dev_err(dev, "%s: Classdev registration failed for %s\n",
+ __func__, powernv_led->cdev.name);
+ }
+
+ return rc;
+}
+
+/* Go through LED device tree node and register LED classdev structure */
+static int powernv_led_classdev(struct platform_device *pdev,
+ struct device_node *led_node,
+ struct powernv_led_common *powernv_led_common)
+{
+ const char *cur = NULL;
+ int rc = -1;
+ struct property *p;
+ struct device_node *np;
+ struct powernv_led_data *powernv_led;
+ struct device *dev = &pdev->dev;
+
+ for_each_child_of_node(led_node, np) {
+ p = of_find_property(np, "led-types", NULL);
+ if (!p)
+ continue;
+
+ while ((cur = of_prop_next_string(p, cur)) != NULL) {
+ powernv_led = devm_kzalloc(dev, sizeof(*powernv_led),
+ GFP_KERNEL);
+ if (!powernv_led)
+ return -ENOMEM;
+
+ powernv_led->common = powernv_led_common;
+ powernv_led->loc_code = (char *)np->name;
+
+ rc = powernv_led_create(dev, powernv_led, cur);
+ if (rc)
+ return rc;
+ } /* while end */
+ }
+
+ return rc;
+}
+
+/* Platform driver probe */
+static int powernv_led_probe(struct platform_device *pdev)
+{
+ struct device_node *led_node;
+ struct powernv_led_common *powernv_led_common;
+ struct device *dev = &pdev->dev;
+
+ led_node = of_find_node_by_path("/ibm,opal/leds");
+ if (!led_node) {
+ dev_err(dev, "%s: LED parent device node not found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ powernv_led_common = devm_kzalloc(dev, sizeof(*powernv_led_common),
+ GFP_KERNEL);
+ if (!powernv_led_common)
+ return -ENOMEM;
+
+ mutex_init(&powernv_led_common->lock);
+ powernv_led_common->max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX);
+
+ platform_set_drvdata(pdev, powernv_led_common);
+
+ return powernv_led_classdev(pdev, led_node, powernv_led_common);
+}
+
+/* Platform driver remove */
+static int powernv_led_remove(struct platform_device *pdev)
+{
+ struct powernv_led_common *powernv_led_common;
+
+ /* Disable LED operation */
+ powernv_led_common = platform_get_drvdata(pdev);
+ powernv_led_common->led_disabled = true;
+
+ /* Destroy lock */
+ mutex_destroy(&powernv_led_common->lock);
+
+ dev_info(&pdev->dev, "PowerNV led module unregistered\n");
+ return 0;
+}
+
+/* Platform driver property match */
+static const struct of_device_id powernv_led_match[] = {
+ {
+ .compatible = "ibm,opal-v3-led",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, powernv_led_match);
+
+static struct platform_driver powernv_led_driver = {
+ .probe = powernv_led_probe,
+ .remove = powernv_led_remove,
+ .driver = {
+ .name = "powernv-led-driver",
+ .of_match_table = powernv_led_match,
+ },
+};
+
+module_platform_driver(powernv_led_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PowerNV LED driver");
+MODULE_AUTHOR("Vasant Hegde <hegdevasant@linux.vnet.ibm.com>");
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c
index 109dcaa15934..68dcbcb4fc5b 100644
--- a/drivers/macintosh/therm_windtunnel.c
+++ b/drivers/macintosh/therm_windtunnel.c
@@ -408,6 +408,7 @@ static const struct i2c_device_id therm_windtunnel_id[] = {
{ "therm_adm1030", adm1030 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, therm_windtunnel_id);
static int
do_probe(struct i2c_client *cl, const struct i2c_device_id *id)
@@ -459,6 +460,7 @@ static const struct of_device_id therm_of_match[] = {{
.compatible = "adm1030"
}, {}
};
+MODULE_DEVICE_TABLE(of, therm_of_match);
static struct platform_driver therm_of_driver = {
.driver = {
diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h
index 028cdac2d33d..901c42f71b5a 100644
--- a/drivers/macintosh/windfarm.h
+++ b/drivers/macintosh/windfarm.h
@@ -53,11 +53,9 @@ struct wf_control {
* the kref and wf_unregister_control will decrement it, thus the
* object creating/disposing a given control shouldn't assume it
* still exists after wf_unregister_control has been called.
- * wf_find_control will inc the refcount for you
*/
extern int wf_register_control(struct wf_control *ct);
extern void wf_unregister_control(struct wf_control *ct);
-extern struct wf_control * wf_find_control(const char *name);
extern int wf_get_control(struct wf_control *ct);
extern void wf_put_control(struct wf_control *ct);
@@ -117,7 +115,6 @@ struct wf_sensor {
/* Same lifetime rules as controls */
extern int wf_register_sensor(struct wf_sensor *sr);
extern void wf_unregister_sensor(struct wf_sensor *sr);
-extern struct wf_sensor * wf_find_sensor(const char *name);
extern int wf_get_sensor(struct wf_sensor *sr);
extern void wf_put_sensor(struct wf_sensor *sr);
@@ -144,7 +141,6 @@ extern int wf_unregister_client(struct notifier_block *nb);
/* Overtemp conditions. Those are refcounted */
extern void wf_set_overtemp(void);
extern void wf_clear_overtemp(void);
-extern int wf_is_overtemp(void);
#define WF_EVENT_NEW_CONTROL 0 /* param is wf_control * */
#define WF_EVENT_NEW_SENSOR 1 /* param is wf_sensor * */
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c
index 3ee198b65843..465d770ab0bb 100644
--- a/drivers/macintosh/windfarm_core.c
+++ b/drivers/macintosh/windfarm_core.c
@@ -72,7 +72,7 @@ static inline void wf_notify(int event, void *param)
blocking_notifier_call_chain(&wf_client_list, event, param);
}
-int wf_critical_overtemp(void)
+static int wf_critical_overtemp(void)
{
static char * critical_overtemp_path = "/sbin/critical_overtemp";
char *argv[] = { critical_overtemp_path, NULL };
@@ -84,7 +84,6 @@ int wf_critical_overtemp(void)
return call_usermodehelper(critical_overtemp_path,
argv, envp, UMH_WAIT_EXEC);
}
-EXPORT_SYMBOL_GPL(wf_critical_overtemp);
static int wf_thread_func(void *data)
{
@@ -255,24 +254,6 @@ void wf_unregister_control(struct wf_control *ct)
}
EXPORT_SYMBOL_GPL(wf_unregister_control);
-struct wf_control * wf_find_control(const char *name)
-{
- struct wf_control *ct;
-
- mutex_lock(&wf_lock);
- list_for_each_entry(ct, &wf_controls, link) {
- if (!strcmp(ct->name, name)) {
- if (wf_get_control(ct))
- ct = NULL;
- mutex_unlock(&wf_lock);
- return ct;
- }
- }
- mutex_unlock(&wf_lock);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(wf_find_control);
-
int wf_get_control(struct wf_control *ct)
{
if (!try_module_get(ct->ops->owner))
@@ -368,24 +349,6 @@ void wf_unregister_sensor(struct wf_sensor *sr)
}
EXPORT_SYMBOL_GPL(wf_unregister_sensor);
-struct wf_sensor * wf_find_sensor(const char *name)
-{
- struct wf_sensor *sr;
-
- mutex_lock(&wf_lock);
- list_for_each_entry(sr, &wf_sensors, link) {
- if (!strcmp(sr->name, name)) {
- if (wf_get_sensor(sr))
- sr = NULL;
- mutex_unlock(&wf_lock);
- return sr;
- }
- }
- mutex_unlock(&wf_lock);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(wf_find_sensor);
-
int wf_get_sensor(struct wf_sensor *sr)
{
if (!try_module_get(sr->ops->owner))
@@ -435,7 +398,7 @@ int wf_unregister_client(struct notifier_block *nb)
{
mutex_lock(&wf_lock);
blocking_notifier_chain_unregister(&wf_client_list, nb);
- wf_client_count++;
+ wf_client_count--;
if (wf_client_count == 0)
wf_stop_thread();
mutex_unlock(&wf_lock);
@@ -474,12 +437,6 @@ void wf_clear_overtemp(void)
}
EXPORT_SYMBOL_GPL(wf_clear_overtemp);
-int wf_is_overtemp(void)
-{
- return (wf_overtemp != 0);
-}
-EXPORT_SYMBOL_GPL(wf_is_overtemp);
-
static int __init windfarm_core_init(void)
{
DBG("wf: core loaded\n");
diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c
index 410c39749872..e87459f6d686 100644
--- a/drivers/memory/fsl_ifc.c
+++ b/drivers/memory/fsl_ifc.c
@@ -62,7 +62,7 @@ int fsl_ifc_find(phys_addr_t addr_base)
return -ENODEV;
for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
- u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
+ u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
if (cspr & CSPR_V && (cspr & CSPR_BA) ==
convert_ifc_address(addr_base))
return i;
@@ -79,16 +79,16 @@ static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
/*
* Clear all the common status and event registers
*/
- if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
+ if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
/* enable all error and events */
- out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN);
+ ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en);
/* enable all error and event interrupts */
- out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN);
- out_be32(&ifc->cm_erattr0, 0x0);
- out_be32(&ifc->cm_erattr1, 0x0);
+ ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en);
+ ifc_out32(0x0, &ifc->cm_erattr0);
+ ifc_out32(0x0, &ifc->cm_erattr1);
return 0;
}
@@ -127,9 +127,9 @@ static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
spin_lock_irqsave(&nand_irq_lock, flags);
- stat = in_be32(&ifc->ifc_nand.nand_evter_stat);
+ stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
if (stat) {
- out_be32(&ifc->ifc_nand.nand_evter_stat, stat);
+ ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat);
ctrl->nand_stat = stat;
wake_up(&ctrl->nand_wait);
}
@@ -161,16 +161,16 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
irqreturn_t ret = IRQ_NONE;
/* read for chip select error */
- cs_err = in_be32(&ifc->cm_evter_stat);
+ cs_err = ifc_in32(&ifc->cm_evter_stat);
if (cs_err) {
dev_err(ctrl->dev, "transaction sent to IFC is not mapped to"
"any memory bank 0x%08X\n", cs_err);
/* clear the chip select error */
- out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER);
+ ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
/* read error attribute registers print the error information */
- status = in_be32(&ifc->cm_erattr0);
- err_addr = in_be32(&ifc->cm_erattr1);
+ status = ifc_in32(&ifc->cm_erattr0);
+ err_addr = ifc_in32(&ifc->cm_erattr1);
if (status & IFC_CM_ERATTR0_ERTYP_READ)
dev_err(ctrl->dev, "Read transaction error"
@@ -231,6 +231,23 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
goto err;
}
+ version = ifc_in32(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
+ FSL_IFC_VERSION_MASK;
+ banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
+ dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
+ version >> 24, (version >> 16) & 0xf, banks);
+
+ fsl_ifc_ctrl_dev->version = version;
+ fsl_ifc_ctrl_dev->banks = banks;
+
+ if (of_property_read_bool(dev->dev.of_node, "little-endian")) {
+ fsl_ifc_ctrl_dev->little_endian = true;
+ dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n");
+ } else {
+ fsl_ifc_ctrl_dev->little_endian = false;
+ dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n");
+ }
+
version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
FSL_IFC_VERSION_MASK;
banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
diff --git a/drivers/misc/cxl/Kconfig b/drivers/misc/cxl/Kconfig
index b6db9ebd52c2..8756d06e2bb8 100644
--- a/drivers/misc/cxl/Kconfig
+++ b/drivers/misc/cxl/Kconfig
@@ -11,11 +11,16 @@ config CXL_KERNEL_API
bool
default n
+config CXL_EEH
+ bool
+ default n
+
config CXL
tristate "Support for IBM Coherent Accelerators (CXL)"
- depends on PPC_POWERNV && PCI_MSI
+ depends on PPC_POWERNV && PCI_MSI && EEH
select CXL_BASE
select CXL_KERNEL_API
+ select CXL_EEH
default m
help
Select this option to enable driver support for IBM Coherent
diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile
index 14e3f8219a11..6f484dfe78f9 100644
--- a/drivers/misc/cxl/Makefile
+++ b/drivers/misc/cxl/Makefile
@@ -1,3 +1,5 @@
+ccflags-y := -Werror
+
cxl-y += main.o file.o irq.o fault.o native.o
cxl-y += context.o sysfs.o debugfs.o pci.o trace.o
cxl-y += vphb.o api.o
diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c
index 729e0851167d..8af12c884b04 100644
--- a/drivers/misc/cxl/api.c
+++ b/drivers/misc/cxl/api.c
@@ -12,11 +12,13 @@
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <misc/cxl.h>
+#include <linux/fs.h>
#include "cxl.h"
struct cxl_context *cxl_dev_context_init(struct pci_dev *dev)
{
+ struct address_space *mapping;
struct cxl_afu *afu;
struct cxl_context *ctx;
int rc;
@@ -25,19 +27,42 @@ struct cxl_context *cxl_dev_context_init(struct pci_dev *dev)
get_device(&afu->dev);
ctx = cxl_context_alloc();
- if (IS_ERR(ctx))
- return ctx;
+ if (IS_ERR(ctx)) {
+ rc = PTR_ERR(ctx);
+ goto err_dev;
+ }
- /* Make it a slave context. We can promote it later? */
- rc = cxl_context_init(ctx, afu, false, NULL);
- if (rc) {
- kfree(ctx);
- put_device(&afu->dev);
- return ERR_PTR(-ENOMEM);
+ ctx->kernelapi = true;
+
+ /*
+ * Make our own address space since we won't have one from the
+ * filesystem like the user api has, and even if we do associate a file
+ * with this context we don't want to use the global anonymous inode's
+ * address space as that can invalidate unrelated users:
+ */
+ mapping = kmalloc(sizeof(struct address_space), GFP_KERNEL);
+ if (!mapping) {
+ rc = -ENOMEM;
+ goto err_ctx;
}
+ address_space_init_once(mapping);
+
+ /* Make it a slave context. We can promote it later? */
+ rc = cxl_context_init(ctx, afu, false, mapping);
+ if (rc)
+ goto err_mapping;
+
cxl_assign_psn_space(ctx);
return ctx;
+
+err_mapping:
+ kfree(mapping);
+err_ctx:
+ kfree(ctx);
+err_dev:
+ put_device(&afu->dev);
+ return ERR_PTR(rc);
}
EXPORT_SYMBOL_GPL(cxl_dev_context_init);
@@ -59,7 +84,7 @@ EXPORT_SYMBOL_GPL(cxl_get_phys_dev);
int cxl_release_context(struct cxl_context *ctx)
{
- if (ctx->status != CLOSED)
+ if (ctx->status >= STARTED)
return -EBUSY;
put_device(&ctx->afu->dev);
@@ -255,9 +280,16 @@ struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops,
file = anon_inode_getfile("cxl", fops, ctx, flags);
if (IS_ERR(file))
- put_unused_fd(fdtmp);
+ goto err_fd;
+
+ file->f_mapping = ctx->mapping;
+
*fd = fdtmp;
return file;
+
+err_fd:
+ put_unused_fd(fdtmp);
+ return NULL;
}
EXPORT_SYMBOL_GPL(cxl_get_fd);
@@ -327,3 +359,10 @@ int cxl_afu_reset(struct cxl_context *ctx)
return cxl_afu_check_and_enable(afu);
}
EXPORT_SYMBOL_GPL(cxl_afu_reset);
+
+void cxl_perst_reloads_same_image(struct cxl_afu *afu,
+ bool perst_reloads_same_image)
+{
+ afu->adapter->perst_same_image = perst_reloads_same_image;
+}
+EXPORT_SYMBOL_GPL(cxl_perst_reloads_same_image);
diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c
index 1287148629c0..e762f85ee233 100644
--- a/drivers/misc/cxl/context.c
+++ b/drivers/misc/cxl/context.c
@@ -126,6 +126,18 @@ static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
if (ctx->status != STARTED) {
mutex_unlock(&ctx->status_mutex);
pr_devel("%s: Context not started, failing problem state access\n", __func__);
+ if (ctx->mmio_err_ff) {
+ if (!ctx->ff_page) {
+ ctx->ff_page = alloc_page(GFP_USER);
+ if (!ctx->ff_page)
+ return VM_FAULT_OOM;
+ memset(page_address(ctx->ff_page), 0xff, PAGE_SIZE);
+ }
+ get_page(ctx->ff_page);
+ vmf->page = ctx->ff_page;
+ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
+ return 0;
+ }
return VM_FAULT_SIGBUS;
}
@@ -193,7 +205,11 @@ int __detach_context(struct cxl_context *ctx)
if (status != STARTED)
return -EBUSY;
- WARN_ON(cxl_detach_process(ctx));
+ /* Only warn if we detached while the link was OK.
+ * If detach fails when hw is down, we don't care.
+ */
+ WARN_ON(cxl_detach_process(ctx) &&
+ cxl_adapter_link_ok(ctx->afu->adapter));
flush_work(&ctx->fault_work); /* Only needed for dedicated process */
put_pid(ctx->pid);
cxl_ctx_put();
@@ -253,7 +269,11 @@ static void reclaim_ctx(struct rcu_head *rcu)
struct cxl_context *ctx = container_of(rcu, struct cxl_context, rcu);
free_page((u64)ctx->sstp);
+ if (ctx->ff_page)
+ __free_page(ctx->ff_page);
ctx->sstp = NULL;
+ if (ctx->kernelapi)
+ kfree(ctx->mapping);
kfree(ctx);
}
diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h
index 4fd66cabde1e..1c30ef77073d 100644
--- a/drivers/misc/cxl/cxl.h
+++ b/drivers/misc/cxl/cxl.h
@@ -34,7 +34,7 @@ extern uint cxl_verbose;
* Bump version each time a user API change is made, whether it is
* backwards compatible ot not.
*/
-#define CXL_API_VERSION 1
+#define CXL_API_VERSION 2
#define CXL_API_VERSION_COMPATIBLE 1
/*
@@ -83,8 +83,10 @@ static const cxl_p1_reg_t CXL_PSL_AFUSEL = {0x00B0};
/* 0x00C0:7EFF Implementation dependent area */
static const cxl_p1_reg_t CXL_PSL_FIR1 = {0x0100};
static const cxl_p1_reg_t CXL_PSL_FIR2 = {0x0108};
+static const cxl_p1_reg_t CXL_PSL_Timebase = {0x0110};
static const cxl_p1_reg_t CXL_PSL_VERSION = {0x0118};
static const cxl_p1_reg_t CXL_PSL_RESLCKTO = {0x0128};
+static const cxl_p1_reg_t CXL_PSL_TB_CTLSTAT = {0x0140};
static const cxl_p1_reg_t CXL_PSL_FIR_CNTL = {0x0148};
static const cxl_p1_reg_t CXL_PSL_DSNDCTL = {0x0150};
static const cxl_p1_reg_t CXL_PSL_SNWRALLOC = {0x0158};
@@ -152,6 +154,9 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0};
#define CXL_PSL_SPAP_Size_Shift 4
#define CXL_PSL_SPAP_V 0x0000000000000001ULL
+/****** CXL_PSL_Control ****************************************************/
+#define CXL_PSL_Control_tb 0x0000000000000001ULL
+
/****** CXL_PSL_DLCNTL *****************************************************/
#define CXL_PSL_DLCNTL_D (0x1ull << (63-28))
#define CXL_PSL_DLCNTL_C (0x1ull << (63-29))
@@ -418,6 +423,9 @@ struct cxl_context {
/* Used to unmap any mmaps when force detaching */
struct address_space *mapping;
struct mutex mapping_lock;
+ struct page *ff_page;
+ bool mmio_err_ff;
+ bool kernelapi;
spinlock_t sste_lock; /* Protects segment table entries */
struct cxl_sste *sstp;
@@ -493,6 +501,7 @@ struct cxl {
bool user_image_loaded;
bool perst_loads_image;
bool perst_select_user;
+ bool perst_same_image;
};
int cxl_alloc_one_irq(struct cxl *adapter);
@@ -531,16 +540,33 @@ struct cxl_process_element {
__be32 software_state;
} __packed;
+static inline bool cxl_adapter_link_ok(struct cxl *cxl)
+{
+ struct pci_dev *pdev;
+
+ pdev = to_pci_dev(cxl->dev.parent);
+ return !pci_channel_offline(pdev);
+}
+
static inline void __iomem *_cxl_p1_addr(struct cxl *cxl, cxl_p1_reg_t reg)
{
WARN_ON(!cpu_has_feature(CPU_FTR_HVMODE));
return cxl->p1_mmio + cxl_reg_off(reg);
}
-#define cxl_p1_write(cxl, reg, val) \
- out_be64(_cxl_p1_addr(cxl, reg), val)
-#define cxl_p1_read(cxl, reg) \
- in_be64(_cxl_p1_addr(cxl, reg))
+static inline void cxl_p1_write(struct cxl *cxl, cxl_p1_reg_t reg, u64 val)
+{
+ if (likely(cxl_adapter_link_ok(cxl)))
+ out_be64(_cxl_p1_addr(cxl, reg), val);
+}
+
+static inline u64 cxl_p1_read(struct cxl *cxl, cxl_p1_reg_t reg)
+{
+ if (likely(cxl_adapter_link_ok(cxl)))
+ return in_be64(_cxl_p1_addr(cxl, reg));
+ else
+ return ~0ULL;
+}
static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg)
{
@@ -548,26 +574,56 @@ static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg
return afu->p1n_mmio + cxl_reg_off(reg);
}
-#define cxl_p1n_write(afu, reg, val) \
- out_be64(_cxl_p1n_addr(afu, reg), val)
-#define cxl_p1n_read(afu, reg) \
- in_be64(_cxl_p1n_addr(afu, reg))
+static inline void cxl_p1n_write(struct cxl_afu *afu, cxl_p1n_reg_t reg, u64 val)
+{
+ if (likely(cxl_adapter_link_ok(afu->adapter)))
+ out_be64(_cxl_p1n_addr(afu, reg), val);
+}
+
+static inline u64 cxl_p1n_read(struct cxl_afu *afu, cxl_p1n_reg_t reg)
+{
+ if (likely(cxl_adapter_link_ok(afu->adapter)))
+ return in_be64(_cxl_p1n_addr(afu, reg));
+ else
+ return ~0ULL;
+}
static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg)
{
return afu->p2n_mmio + cxl_reg_off(reg);
}
-#define cxl_p2n_write(afu, reg, val) \
- out_be64(_cxl_p2n_addr(afu, reg), val)
-#define cxl_p2n_read(afu, reg) \
- in_be64(_cxl_p2n_addr(afu, reg))
+static inline void cxl_p2n_write(struct cxl_afu *afu, cxl_p2n_reg_t reg, u64 val)
+{
+ if (likely(cxl_adapter_link_ok(afu->adapter)))
+ out_be64(_cxl_p2n_addr(afu, reg), val);
+}
+
+static inline u64 cxl_p2n_read(struct cxl_afu *afu, cxl_p2n_reg_t reg)
+{
+ if (likely(cxl_adapter_link_ok(afu->adapter)))
+ return in_be64(_cxl_p2n_addr(afu, reg));
+ else
+ return ~0ULL;
+}
+static inline u64 cxl_afu_cr_read64(struct cxl_afu *afu, int cr, u64 off)
+{
+ if (likely(cxl_adapter_link_ok(afu->adapter)))
+ return in_le64((afu)->afu_desc_mmio + (afu)->crs_offset +
+ ((cr) * (afu)->crs_len) + (off));
+ else
+ return ~0ULL;
+}
-#define cxl_afu_cr_read64(afu, cr, off) \
- in_le64((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off))
-#define cxl_afu_cr_read32(afu, cr, off) \
- in_le32((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off))
+static inline u32 cxl_afu_cr_read32(struct cxl_afu *afu, int cr, u64 off)
+{
+ if (likely(cxl_adapter_link_ok(afu->adapter)))
+ return in_le32((afu)->afu_desc_mmio + (afu)->crs_offset +
+ ((cr) * (afu)->crs_len) + (off));
+ else
+ return 0xffffffff;
+}
u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off);
u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off);
@@ -585,6 +641,9 @@ void unregister_cxl_calls(struct cxl_calls *calls);
int cxl_alloc_adapter_nr(struct cxl *adapter);
void cxl_remove_adapter_nr(struct cxl *adapter);
+int cxl_alloc_spa(struct cxl_afu *afu);
+void cxl_release_spa(struct cxl_afu *afu);
+
int cxl_file_init(void);
void cxl_file_exit(void);
int cxl_register_adapter(struct cxl *adapter);
@@ -675,6 +734,7 @@ int cxl_psl_purge(struct cxl_afu *afu);
void cxl_stop_trace(struct cxl *cxl);
int cxl_pci_vphb_add(struct cxl_afu *afu);
+void cxl_pci_vphb_reconfigure(struct cxl_afu *afu);
void cxl_pci_vphb_remove(struct cxl_afu *afu);
extern struct pci_driver cxl_pci_driver;
diff --git a/drivers/misc/cxl/debugfs.c b/drivers/misc/cxl/debugfs.c
index 825c412580bc..18df6f44af2a 100644
--- a/drivers/misc/cxl/debugfs.c
+++ b/drivers/misc/cxl/debugfs.c
@@ -48,7 +48,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_io_x64, debugfs_io_u64_get, debugfs_io_u64_set, "0x
static struct dentry *debugfs_create_io_x64(const char *name, umode_t mode,
struct dentry *parent, u64 __iomem *value)
{
- return debugfs_create_file(name, mode, parent, (void *)value, &fops_io_x64);
+ return debugfs_create_file(name, mode, parent, (void __force *)value, &fops_io_x64);
}
int cxl_debugfs_adapter_add(struct cxl *adapter)
diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c
index e3f4b69527a9..a30bf285b5bd 100644
--- a/drivers/misc/cxl/file.c
+++ b/drivers/misc/cxl/file.c
@@ -73,6 +73,11 @@ static int __afu_open(struct inode *inode, struct file *file, bool master)
if (!afu->current_mode)
goto err_put_afu;
+ if (!cxl_adapter_link_ok(adapter)) {
+ rc = -EIO;
+ goto err_put_afu;
+ }
+
if (!(ctx = cxl_context_alloc())) {
rc = -ENOMEM;
goto err_put_afu;
@@ -179,6 +184,8 @@ static long afu_ioctl_start_work(struct cxl_context *ctx,
if (work.flags & CXL_START_WORK_AMR)
amr = work.amr & mfspr(SPRN_UAMOR);
+ ctx->mmio_err_ff = !!(work.flags & CXL_START_WORK_ERR_FF);
+
/*
* We grab the PID here and not in the file open to allow for the case
* where a process (master, some daemon, etc) has opened the chardev on
@@ -238,6 +245,9 @@ long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (ctx->status == CLOSED)
return -EIO;
+ if (!cxl_adapter_link_ok(ctx->afu->adapter))
+ return -EIO;
+
pr_devel("afu_ioctl\n");
switch (cmd) {
case CXL_IOCTL_START_WORK:
@@ -251,7 +261,7 @@ long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL;
}
-long afu_compat_ioctl(struct file *file, unsigned int cmd,
+static long afu_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return afu_ioctl(file, cmd, arg);
@@ -265,6 +275,9 @@ int afu_mmap(struct file *file, struct vm_area_struct *vm)
if (ctx->status != STARTED)
return -EIO;
+ if (!cxl_adapter_link_ok(ctx->afu->adapter))
+ return -EIO;
+
return cxl_context_iomap(ctx, vm);
}
@@ -309,6 +322,9 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count,
int rc;
DEFINE_WAIT(wait);
+ if (!cxl_adapter_link_ok(ctx->afu->adapter))
+ return -EIO;
+
if (count < CXL_READ_MIN_SIZE)
return -EINVAL;
@@ -319,6 +335,11 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count,
if (ctx_event_pending(ctx))
break;
+ if (!cxl_adapter_link_ok(ctx->afu->adapter)) {
+ rc = -EIO;
+ goto out;
+ }
+
if (file->f_flags & O_NONBLOCK) {
rc = -EAGAIN;
goto out;
@@ -396,7 +417,7 @@ const struct file_operations afu_fops = {
.mmap = afu_mmap,
};
-const struct file_operations afu_master_fops = {
+static const struct file_operations afu_master_fops = {
.owner = THIS_MODULE,
.open = afu_master_open,
.poll = afu_poll,
@@ -519,7 +540,7 @@ int __init cxl_file_init(void)
* If these change we really need to update API. Either change some
* flags or update API version number CXL_API_VERSION.
*/
- BUILD_BUG_ON(CXL_API_VERSION != 1);
+ BUILD_BUG_ON(CXL_API_VERSION != 2);
BUILD_BUG_ON(sizeof(struct cxl_ioctl_start_work) != 64);
BUILD_BUG_ON(sizeof(struct cxl_event_header) != 8);
BUILD_BUG_ON(sizeof(struct cxl_event_afu_interrupt) != 8);
diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c
index 680cd263436d..583b42afeda2 100644
--- a/drivers/misc/cxl/irq.c
+++ b/drivers/misc/cxl/irq.c
@@ -30,12 +30,12 @@ static irqreturn_t handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr, u6
serr = cxl_p1n_read(ctx->afu, CXL_PSL_SERR_An);
afu_debug = cxl_p1n_read(ctx->afu, CXL_AFU_DEBUG_An);
- dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat);
- dev_crit(&ctx->afu->dev, "PSL_FIR1: 0x%.16llx\n", fir1);
- dev_crit(&ctx->afu->dev, "PSL_FIR2: 0x%.16llx\n", fir2);
- dev_crit(&ctx->afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr);
- dev_crit(&ctx->afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice);
- dev_crit(&ctx->afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug);
+ dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%016llx\n", errstat);
+ dev_crit(&ctx->afu->dev, "PSL_FIR1: 0x%016llx\n", fir1);
+ dev_crit(&ctx->afu->dev, "PSL_FIR2: 0x%016llx\n", fir2);
+ dev_crit(&ctx->afu->dev, "PSL_SERR_An: 0x%016llx\n", serr);
+ dev_crit(&ctx->afu->dev, "PSL_FIR_SLICE_An: 0x%016llx\n", fir_slice);
+ dev_crit(&ctx->afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%016llx\n", afu_debug);
dev_crit(&ctx->afu->dev, "STOPPING CXL TRACE\n");
cxl_stop_trace(ctx->afu->adapter);
@@ -54,10 +54,10 @@ irqreturn_t cxl_slice_irq_err(int irq, void *data)
fir_slice = cxl_p1n_read(afu, CXL_PSL_FIR_SLICE_An);
errstat = cxl_p2n_read(afu, CXL_PSL_ErrStat_An);
afu_debug = cxl_p1n_read(afu, CXL_AFU_DEBUG_An);
- dev_crit(&afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr);
- dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice);
- dev_crit(&afu->dev, "CXL_PSL_ErrStat_An: 0x%.16llx\n", errstat);
- dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug);
+ dev_crit(&afu->dev, "PSL_SERR_An: 0x%016llx\n", serr);
+ dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%016llx\n", fir_slice);
+ dev_crit(&afu->dev, "CXL_PSL_ErrStat_An: 0x%016llx\n", errstat);
+ dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%016llx\n", afu_debug);
cxl_p1n_write(afu, CXL_PSL_SERR_An, serr);
@@ -72,7 +72,7 @@ static irqreturn_t cxl_irq_err(int irq, void *data)
WARN(1, "CXL ERROR interrupt %i\n", irq);
err_ivte = cxl_p1_read(adapter, CXL_PSL_ErrIVTE);
- dev_crit(&adapter->dev, "PSL_ErrIVTE: 0x%.16llx\n", err_ivte);
+ dev_crit(&adapter->dev, "PSL_ErrIVTE: 0x%016llx\n", err_ivte);
dev_crit(&adapter->dev, "STOPPING CXL TRACE\n");
cxl_stop_trace(adapter);
@@ -80,7 +80,7 @@ static irqreturn_t cxl_irq_err(int irq, void *data)
fir1 = cxl_p1_read(adapter, CXL_PSL_FIR1);
fir2 = cxl_p1_read(adapter, CXL_PSL_FIR2);
- dev_crit(&adapter->dev, "PSL_FIR1: 0x%.16llx\nPSL_FIR2: 0x%.16llx\n", fir1, fir2);
+ dev_crit(&adapter->dev, "PSL_FIR1: 0x%016llx\nPSL_FIR2: 0x%016llx\n", fir1, fir2);
return IRQ_HANDLED;
}
@@ -147,7 +147,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info)
if (dsisr & CXL_PSL_DSISR_An_PE)
return handle_psl_slice_error(ctx, dsisr, irq_info->errstat);
if (dsisr & CXL_PSL_DSISR_An_AE) {
- pr_devel("CXL interrupt: AFU Error %.llx\n", irq_info->afu_err);
+ pr_devel("CXL interrupt: AFU Error 0x%016llx\n", irq_info->afu_err);
if (ctx->pending_afu_err) {
/*
@@ -158,7 +158,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info)
* probably best that we log them somewhere:
*/
dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error "
- "undelivered to pe %i: %.llx\n",
+ "undelivered to pe %i: 0x%016llx\n",
ctx->pe, irq_info->afu_err);
} else {
spin_lock(&ctx->lock);
@@ -211,8 +211,8 @@ static irqreturn_t cxl_irq_multiplexed(int irq, void *data)
}
rcu_read_unlock();
- WARN(1, "Unable to demultiplex CXL PSL IRQ for PE %i DSISR %.16llx DAR"
- " %.16llx\n(Possible AFU HW issue - was a term/remove acked"
+ WARN(1, "Unable to demultiplex CXL PSL IRQ for PE %i DSISR %016llx DAR"
+ " %016llx\n(Possible AFU HW issue - was a term/remove acked"
" with outstanding transactions?)\n", ph, irq_info.dsisr,
irq_info.dar);
return fail_psl_irq(afu, &irq_info);
@@ -341,6 +341,9 @@ int cxl_register_psl_err_irq(struct cxl *adapter)
void cxl_release_psl_err_irq(struct cxl *adapter)
{
+ if (adapter->err_virq != irq_find_mapping(NULL, adapter->err_hwirq))
+ return;
+
cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000);
cxl_unmap_irq(adapter->err_virq, adapter);
cxl_release_one_irq(adapter, adapter->err_hwirq);
@@ -374,6 +377,9 @@ int cxl_register_serr_irq(struct cxl_afu *afu)
void cxl_release_serr_irq(struct cxl_afu *afu)
{
+ if (afu->serr_virq != irq_find_mapping(NULL, afu->serr_hwirq))
+ return;
+
cxl_p1n_write(afu, CXL_PSL_SERR_An, 0x0000000000000000);
cxl_unmap_irq(afu->serr_virq, afu);
cxl_release_one_irq(afu->adapter, afu->serr_hwirq);
@@ -400,12 +406,15 @@ int cxl_register_psl_irq(struct cxl_afu *afu)
void cxl_release_psl_irq(struct cxl_afu *afu)
{
+ if (afu->psl_virq != irq_find_mapping(NULL, afu->psl_hwirq))
+ return;
+
cxl_unmap_irq(afu->psl_virq, afu);
cxl_release_one_irq(afu->adapter, afu->psl_hwirq);
kfree(afu->psl_irq_name);
}
-void afu_irq_name_free(struct cxl_context *ctx)
+static void afu_irq_name_free(struct cxl_context *ctx)
{
struct cxl_irq_name *irq_name, *tmp;
@@ -421,6 +430,9 @@ int afu_allocate_irqs(struct cxl_context *ctx, u32 count)
int rc, r, i, j = 1;
struct cxl_irq_name *irq_name;
+ /* Initialize the list head to hold irq names */
+ INIT_LIST_HEAD(&ctx->irq_names);
+
if ((rc = cxl_alloc_irq_ranges(&ctx->irqs, ctx->afu->adapter, count)))
return rc;
@@ -432,13 +444,12 @@ int afu_allocate_irqs(struct cxl_context *ctx, u32 count)
ctx->irq_bitmap = kcalloc(BITS_TO_LONGS(count),
sizeof(*ctx->irq_bitmap), GFP_KERNEL);
if (!ctx->irq_bitmap)
- return -ENOMEM;
+ goto out;
/*
* Allocate names first. If any fail, bail out before allocating
* actual hardware IRQs.
*/
- INIT_LIST_HEAD(&ctx->irq_names);
for (r = 1; r < CXL_IRQ_RANGES; r++) {
for (i = 0; i < ctx->irqs.range[r]; i++) {
irq_name = kmalloc(sizeof(struct cxl_irq_name),
@@ -460,11 +471,12 @@ int afu_allocate_irqs(struct cxl_context *ctx, u32 count)
return 0;
out:
+ cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter);
afu_irq_name_free(ctx);
return -ENOMEM;
}
-void afu_register_hwirqs(struct cxl_context *ctx)
+static void afu_register_hwirqs(struct cxl_context *ctx)
{
irq_hw_number_t hwirq;
struct cxl_irq_name *irq_name;
@@ -511,4 +523,8 @@ void afu_release_irqs(struct cxl_context *ctx, void *cookie)
afu_irq_name_free(ctx);
cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter);
+
+ kfree(ctx->irq_bitmap);
+ ctx->irq_bitmap = NULL;
+ ctx->irq_count = 0;
}
diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c
index 4a164ab8b35a..9fde75ed4fac 100644
--- a/drivers/misc/cxl/main.c
+++ b/drivers/misc/cxl/main.c
@@ -222,6 +222,7 @@ static void exit_cxl(void)
cxl_debugfs_exit();
cxl_file_exit();
unregister_cxl_calls(&cxl_calls);
+ idr_destroy(&cxl_adapter_idr);
}
module_init(init_cxl);
diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c
index 10567f245818..b37f2e8004f5 100644
--- a/drivers/misc/cxl/native.c
+++ b/drivers/misc/cxl/native.c
@@ -41,7 +41,14 @@ static int afu_control(struct cxl_afu *afu, u64 command,
rc = -EBUSY;
goto out;
}
- pr_devel_ratelimited("AFU control... (0x%.16llx)\n",
+
+ if (!cxl_adapter_link_ok(afu->adapter)) {
+ afu->enabled = enabled;
+ rc = -EIO;
+ goto out;
+ }
+
+ pr_devel_ratelimited("AFU control... (0x%016llx)\n",
AFU_Cntl | command);
cpu_relax();
AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
@@ -85,6 +92,10 @@ int __cxl_afu_reset(struct cxl_afu *afu)
int cxl_afu_check_and_enable(struct cxl_afu *afu)
{
+ if (!cxl_adapter_link_ok(afu->adapter)) {
+ WARN(1, "Refusing to enable afu while link down!\n");
+ return -EIO;
+ }
if (afu->enabled)
return 0;
return afu_enable(afu);
@@ -103,6 +114,12 @@ int cxl_psl_purge(struct cxl_afu *afu)
pr_devel("PSL purge request\n");
+ if (!cxl_adapter_link_ok(afu->adapter)) {
+ dev_warn(&afu->dev, "PSL Purge called with link down, ignoring\n");
+ rc = -EIO;
+ goto out;
+ }
+
if ((AFU_Cntl & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) {
WARN(1, "psl_purge request while AFU not disabled!\n");
cxl_afu_disable(afu);
@@ -119,14 +136,19 @@ int cxl_psl_purge(struct cxl_afu *afu)
rc = -EBUSY;
goto out;
}
+ if (!cxl_adapter_link_ok(afu->adapter)) {
+ rc = -EIO;
+ goto out;
+ }
+
dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
- pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%.16llx PSL_DSISR: 0x%.16llx\n", PSL_CNTL, dsisr);
+ pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%016llx PSL_DSISR: 0x%016llx\n", PSL_CNTL, dsisr);
if (dsisr & CXL_PSL_DSISR_TRANS) {
dar = cxl_p2n_read(afu, CXL_PSL_DAR_An);
- dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%.16llx, DAR: 0x%.16llx\n", dsisr, dar);
+ dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%016llx, DAR: 0x%016llx\n", dsisr, dar);
cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE);
} else if (dsisr) {
- dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%.16llx\n", dsisr);
+ dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%016llx\n", dsisr);
cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A);
} else {
cpu_relax();
@@ -161,10 +183,8 @@ static int spa_max_procs(int spa_size)
return ((spa_size / 8) - 96) / 17;
}
-static int alloc_spa(struct cxl_afu *afu)
+int cxl_alloc_spa(struct cxl_afu *afu)
{
- u64 spap;
-
/* Work out how many pages to allocate */
afu->spa_order = 0;
do {
@@ -183,6 +203,13 @@ static int alloc_spa(struct cxl_afu *afu)
pr_devel("spa pages: %i afu->spa_max_procs: %i afu->num_procs: %i\n",
1<<afu->spa_order, afu->spa_max_procs, afu->num_procs);
+ return 0;
+}
+
+static void attach_spa(struct cxl_afu *afu)
+{
+ u64 spap;
+
afu->sw_command_status = (__be64 *)((char *)afu->spa +
((afu->spa_max_procs + 3) * 128));
@@ -191,14 +218,19 @@ static int alloc_spa(struct cxl_afu *afu)
spap |= CXL_PSL_SPAP_V;
pr_devel("cxl: SPA allocated at 0x%p. Max processes: %i, sw_command_status: 0x%p CXL_PSL_SPAP_An=0x%016llx\n", afu->spa, afu->spa_max_procs, afu->sw_command_status, spap);
cxl_p1n_write(afu, CXL_PSL_SPAP_An, spap);
-
- return 0;
}
-static void release_spa(struct cxl_afu *afu)
+static inline void detach_spa(struct cxl_afu *afu)
{
cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0);
- free_pages((unsigned long) afu->spa, afu->spa_order);
+}
+
+void cxl_release_spa(struct cxl_afu *afu)
+{
+ if (afu->spa) {
+ free_pages((unsigned long) afu->spa, afu->spa_order);
+ afu->spa = NULL;
+ }
}
int cxl_tlb_slb_invalidate(struct cxl *adapter)
@@ -215,6 +247,8 @@ int cxl_tlb_slb_invalidate(struct cxl *adapter)
dev_warn(&adapter->dev, "WARNING: CXL adapter wide TLBIA timed out!\n");
return -EBUSY;
}
+ if (!cxl_adapter_link_ok(adapter))
+ return -EIO;
cpu_relax();
}
@@ -224,6 +258,8 @@ int cxl_tlb_slb_invalidate(struct cxl *adapter)
dev_warn(&adapter->dev, "WARNING: CXL adapter wide SLBIA timed out!\n");
return -EBUSY;
}
+ if (!cxl_adapter_link_ok(adapter))
+ return -EIO;
cpu_relax();
}
return 0;
@@ -240,6 +276,11 @@ int cxl_afu_slbia(struct cxl_afu *afu)
dev_warn(&afu->dev, "WARNING: CXL AFU SLBIA timed out!\n");
return -EBUSY;
}
+ /* If the adapter has gone down, we can assume that we
+ * will PERST it and that will invalidate everything.
+ */
+ if (!cxl_adapter_link_ok(afu->adapter))
+ return -EIO;
cpu_relax();
}
return 0;
@@ -279,6 +320,8 @@ static void slb_invalid(struct cxl_context *ctx)
cxl_p1_write(adapter, CXL_PSL_SLBIA, CXL_TLB_SLB_IQ_LPIDPID);
while (1) {
+ if (!cxl_adapter_link_ok(adapter))
+ break;
slbia = cxl_p1_read(adapter, CXL_PSL_SLBIA);
if (!(slbia & CXL_TLB_SLB_P))
break;
@@ -308,6 +351,11 @@ static int do_process_element_cmd(struct cxl_context *ctx,
rc = -EBUSY;
goto out;
}
+ if (!cxl_adapter_link_ok(ctx->afu->adapter)) {
+ dev_warn(&ctx->afu->dev, "WARNING: Device link down, aborting Process Element Command!\n");
+ rc = -EIO;
+ goto out;
+ }
state = be64_to_cpup(ctx->afu->sw_command_status);
if (state == ~0ULL) {
pr_err("cxl: Error adding process element to AFU\n");
@@ -355,8 +403,13 @@ static int terminate_process_element(struct cxl_context *ctx)
mutex_lock(&ctx->afu->spa_mutex);
pr_devel("%s Terminate pe: %i started\n", __func__, ctx->pe);
- rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_TERMINATE,
- CXL_PE_SOFTWARE_STATE_V | CXL_PE_SOFTWARE_STATE_T);
+ /* We could be asked to terminate when the hw is down. That
+ * should always succeed: it's not running if the hw has gone
+ * away and is being reset.
+ */
+ if (cxl_adapter_link_ok(ctx->afu->adapter))
+ rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_TERMINATE,
+ CXL_PE_SOFTWARE_STATE_V | CXL_PE_SOFTWARE_STATE_T);
ctx->elem->software_state = 0; /* Remove Valid bit */
pr_devel("%s Terminate pe: %i finished\n", __func__, ctx->pe);
mutex_unlock(&ctx->afu->spa_mutex);
@@ -369,7 +422,14 @@ static int remove_process_element(struct cxl_context *ctx)
mutex_lock(&ctx->afu->spa_mutex);
pr_devel("%s Remove pe: %i started\n", __func__, ctx->pe);
- if (!(rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_REMOVE, 0)))
+
+ /* We could be asked to remove when the hw is down. Again, if
+ * the hw is down, the PE is gone, so we succeed.
+ */
+ if (cxl_adapter_link_ok(ctx->afu->adapter))
+ rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_REMOVE, 0);
+
+ if (!rc)
ctx->pe_inserted = false;
slb_invalid(ctx);
pr_devel("%s Remove pe: %i finished\n", __func__, ctx->pe);
@@ -397,8 +457,11 @@ static int activate_afu_directed(struct cxl_afu *afu)
dev_info(&afu->dev, "Activating AFU directed mode\n");
- if (alloc_spa(afu))
- return -ENOMEM;
+ if (afu->spa == NULL) {
+ if (cxl_alloc_spa(afu))
+ return -ENOMEM;
+ }
+ attach_spa(afu);
cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_AFU);
cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL);
@@ -492,9 +555,7 @@ static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr)
if ((result = cxl_afu_check_and_enable(ctx->afu)))
return result;
- add_process_element(ctx);
-
- return 0;
+ return add_process_element(ctx);
}
static int deactivate_afu_directed(struct cxl_afu *afu)
@@ -511,8 +572,6 @@ static int deactivate_afu_directed(struct cxl_afu *afu)
cxl_afu_disable(afu);
cxl_psl_purge(afu);
- release_spa(afu);
-
return 0;
}
@@ -614,6 +673,11 @@ int cxl_afu_activate_mode(struct cxl_afu *afu, int mode)
if (!(mode & afu->modes_supported))
return -EINVAL;
+ if (!cxl_adapter_link_ok(afu->adapter)) {
+ WARN(1, "Device link is down, refusing to activate!\n");
+ return -EIO;
+ }
+
if (mode == CXL_MODE_DIRECTED)
return activate_afu_directed(afu);
if (mode == CXL_MODE_DEDICATED)
@@ -624,6 +688,11 @@ int cxl_afu_activate_mode(struct cxl_afu *afu, int mode)
int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr)
{
+ if (!cxl_adapter_link_ok(ctx->afu->adapter)) {
+ WARN(1, "Device link is down, refusing to attach process!\n");
+ return -EIO;
+ }
+
ctx->kernel = kernel;
if (ctx->afu->current_mode == CXL_MODE_DIRECTED)
return attach_afu_directed(ctx, wed, amr);
@@ -668,6 +737,12 @@ int cxl_get_irq(struct cxl_afu *afu, struct cxl_irq_info *info)
{
u64 pidtid;
+ /* If the adapter has gone away, we can't get any meaningful
+ * information.
+ */
+ if (!cxl_adapter_link_ok(afu->adapter))
+ return -EIO;
+
info->dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
info->dar = cxl_p2n_read(afu, CXL_PSL_DAR_An);
info->dsr = cxl_p2n_read(afu, CXL_PSL_DSR_An);
@@ -684,7 +759,7 @@ static void recover_psl_err(struct cxl_afu *afu, u64 errstat)
{
u64 dsisr;
- pr_devel("RECOVERING FROM PSL ERROR... (0x%.16llx)\n", errstat);
+ pr_devel("RECOVERING FROM PSL ERROR... (0x%016llx)\n", errstat);
/* Clear PSL_DSISR[PE] */
dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index 32ad09705949..02c85160bfe9 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
@@ -24,6 +24,7 @@
#include <asm/io.h>
#include "cxl.h"
+#include <misc/cxl.h>
#define CXL_PCI_VSEC_ID 0x1280
@@ -133,7 +134,7 @@ u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off)
return (val >> ((off & 0x3) * 8)) & 0xff;
}
-static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = {
+static const struct pci_device_id cxl_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), },
{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), },
{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x04cf), },
@@ -369,6 +370,55 @@ static int init_implementation_adapter_regs(struct cxl *adapter, struct pci_dev
return 0;
}
+#define TBSYNC_CNT(n) (((u64)n & 0x7) << (63-6))
+#define _2048_250MHZ_CYCLES 1
+
+static int cxl_setup_psl_timebase(struct cxl *adapter, struct pci_dev *dev)
+{
+ u64 psl_tb;
+ int delta;
+ unsigned int retry = 0;
+ struct device_node *np;
+
+ if (!(np = pnv_pci_get_phb_node(dev)))
+ return -ENODEV;
+
+ /* Do not fail when CAPP timebase sync is not supported by OPAL */
+ of_node_get(np);
+ if (! of_get_property(np, "ibm,capp-timebase-sync", NULL)) {
+ of_node_put(np);
+ pr_err("PSL: Timebase sync: OPAL support missing\n");
+ return 0;
+ }
+ of_node_put(np);
+
+ /*
+ * Setup PSL Timebase Control and Status register
+ * with the recommended Timebase Sync Count value
+ */
+ cxl_p1_write(adapter, CXL_PSL_TB_CTLSTAT,
+ TBSYNC_CNT(2 * _2048_250MHZ_CYCLES));
+
+ /* Enable PSL Timebase */
+ cxl_p1_write(adapter, CXL_PSL_Control, 0x0000000000000000);
+ cxl_p1_write(adapter, CXL_PSL_Control, CXL_PSL_Control_tb);
+
+ /* Wait until CORE TB and PSL TB difference <= 16usecs */
+ do {
+ msleep(1);
+ if (retry++ > 5) {
+ pr_err("PSL: Timebase sync: giving up!\n");
+ return -EIO;
+ }
+ psl_tb = cxl_p1_read(adapter, CXL_PSL_Timebase);
+ delta = mftb() - psl_tb;
+ if (delta < 0)
+ delta = -delta;
+ } while (cputime_to_usecs(delta) > 16);
+
+ return 0;
+}
+
static int init_implementation_afu_regs(struct cxl_afu *afu)
{
/* read/write masks for this slice */
@@ -539,10 +589,18 @@ err:
static void cxl_unmap_slice_regs(struct cxl_afu *afu)
{
- if (afu->p2n_mmio)
+ if (afu->p2n_mmio) {
iounmap(afu->p2n_mmio);
- if (afu->p1n_mmio)
+ afu->p2n_mmio = NULL;
+ }
+ if (afu->p1n_mmio) {
iounmap(afu->p1n_mmio);
+ afu->p1n_mmio = NULL;
+ }
+ if (afu->afu_desc_mmio) {
+ iounmap(afu->afu_desc_mmio);
+ afu->afu_desc_mmio = NULL;
+ }
}
static void cxl_release_afu(struct device *dev)
@@ -551,6 +609,9 @@ static void cxl_release_afu(struct device *dev)
pr_devel("cxl_release_afu\n");
+ idr_destroy(&afu->contexts_idr);
+ cxl_release_spa(afu);
+
kfree(afu);
}
@@ -656,7 +717,7 @@ static int sanitise_afu_regs(struct cxl_afu *afu)
*/
reg = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
if ((reg & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) {
- dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#.16llx\n", reg);
+ dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#016llx\n", reg);
if (__cxl_afu_reset(afu))
return -EIO;
if (cxl_afu_disable(afu))
@@ -677,7 +738,7 @@ static int sanitise_afu_regs(struct cxl_afu *afu)
cxl_p2n_write(afu, CXL_SSTP0_An, 0x0000000000000000);
reg = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
if (reg) {
- dev_warn(&afu->dev, "AFU had pending DSISR: %#.16llx\n", reg);
+ dev_warn(&afu->dev, "AFU had pending DSISR: %#016llx\n", reg);
if (reg & CXL_PSL_DSISR_TRANS)
cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE);
else
@@ -686,12 +747,12 @@ static int sanitise_afu_regs(struct cxl_afu *afu)
reg = cxl_p1n_read(afu, CXL_PSL_SERR_An);
if (reg) {
if (reg & ~0xffff)
- dev_warn(&afu->dev, "AFU had pending SERR: %#.16llx\n", reg);
+ dev_warn(&afu->dev, "AFU had pending SERR: %#016llx\n", reg);
cxl_p1n_write(afu, CXL_PSL_SERR_An, reg & ~0xffff);
}
reg = cxl_p2n_read(afu, CXL_PSL_ErrStat_An);
if (reg) {
- dev_warn(&afu->dev, "AFU had pending error status: %#.16llx\n", reg);
+ dev_warn(&afu->dev, "AFU had pending error status: %#016llx\n", reg);
cxl_p2n_write(afu, CXL_PSL_ErrStat_An, reg);
}
@@ -742,45 +803,70 @@ ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
return count;
}
-static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
+static int cxl_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pci_dev *dev)
{
- struct cxl_afu *afu;
- bool free = true;
int rc;
- if (!(afu = cxl_alloc_afu(adapter, slice)))
- return -ENOMEM;
-
- if ((rc = dev_set_name(&afu->dev, "afu%i.%i", adapter->adapter_num, slice)))
- goto err1;
-
if ((rc = cxl_map_slice_regs(afu, adapter, dev)))
- goto err1;
+ return rc;
if ((rc = sanitise_afu_regs(afu)))
- goto err2;
+ goto err1;
/* We need to reset the AFU before we can read the AFU descriptor */
if ((rc = __cxl_afu_reset(afu)))
- goto err2;
+ goto err1;
if (cxl_verbose)
dump_afu_descriptor(afu);
if ((rc = cxl_read_afu_descriptor(afu)))
- goto err2;
+ goto err1;
if ((rc = cxl_afu_descriptor_looks_ok(afu)))
- goto err2;
+ goto err1;
if ((rc = init_implementation_afu_regs(afu)))
- goto err2;
+ goto err1;
if ((rc = cxl_register_serr_irq(afu)))
- goto err2;
+ goto err1;
if ((rc = cxl_register_psl_irq(afu)))
- goto err3;
+ goto err2;
+
+ return 0;
+
+err2:
+ cxl_release_serr_irq(afu);
+err1:
+ cxl_unmap_slice_regs(afu);
+ return rc;
+}
+
+static void cxl_deconfigure_afu(struct cxl_afu *afu)
+{
+ cxl_release_psl_irq(afu);
+ cxl_release_serr_irq(afu);
+ cxl_unmap_slice_regs(afu);
+}
+
+static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
+{
+ struct cxl_afu *afu;
+ int rc;
+
+ afu = cxl_alloc_afu(adapter, slice);
+ if (!afu)
+ return -ENOMEM;
+
+ rc = dev_set_name(&afu->dev, "afu%i.%i", adapter->adapter_num, slice);
+ if (rc)
+ goto err_free;
+
+ rc = cxl_configure_afu(afu, adapter, dev);
+ if (rc)
+ goto err_free;
/* Don't care if this fails */
cxl_debugfs_afu_add(afu);
@@ -795,10 +881,6 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
if ((rc = cxl_sysfs_afu_add(afu)))
goto err_put1;
-
- if ((rc = cxl_afu_select_best_mode(afu)))
- goto err_put2;
-
adapter->afu[afu->slice] = afu;
if ((rc = cxl_pci_vphb_add(afu)))
@@ -806,21 +888,16 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
return 0;
-err_put2:
- cxl_sysfs_afu_remove(afu);
err_put1:
- device_unregister(&afu->dev);
- free = false;
+ cxl_deconfigure_afu(afu);
cxl_debugfs_afu_remove(afu);
- cxl_release_psl_irq(afu);
-err3:
- cxl_release_serr_irq(afu);
-err2:
- cxl_unmap_slice_regs(afu);
-err1:
- if (free)
- kfree(afu);
+ device_unregister(&afu->dev);
+ return rc;
+
+err_free:
+ kfree(afu);
return rc;
+
}
static void cxl_remove_afu(struct cxl_afu *afu)
@@ -840,10 +917,7 @@ static void cxl_remove_afu(struct cxl_afu *afu)
cxl_context_detach_all(afu);
cxl_afu_deactivate_mode(afu);
- cxl_release_psl_irq(afu);
- cxl_release_serr_irq(afu);
- cxl_unmap_slice_regs(afu);
-
+ cxl_deconfigure_afu(afu);
device_unregister(&afu->dev);
}
@@ -851,16 +925,15 @@ int cxl_reset(struct cxl *adapter)
{
struct pci_dev *dev = to_pci_dev(adapter->dev.parent);
int rc;
- int i;
- u32 val;
-
- dev_info(&dev->dev, "CXL reset\n");
- for (i = 0; i < adapter->slices; i++) {
- cxl_pci_vphb_remove(adapter->afu[i]);
- cxl_remove_afu(adapter->afu[i]);
+ if (adapter->perst_same_image) {
+ dev_warn(&dev->dev,
+ "cxl: refusing to reset/reflash when perst_reloads_same_image is set.\n");
+ return -EINVAL;
}
+ dev_info(&dev->dev, "CXL reset\n");
+
/* pcie_warm_reset requests a fundamental pci reset which includes a
* PERST assert/deassert. PERST triggers a loading of the image
* if "user" or "factory" is selected in sysfs */
@@ -869,20 +942,6 @@ int cxl_reset(struct cxl *adapter)
return rc;
}
- /* the PERST done above fences the PHB. So, reset depends on EEH
- * to unbind the driver, tell Sapphire to reinit the PHB, and rebind
- * the driver. Do an mmio read explictly to ensure EEH notices the
- * fenced PHB. Retry for a few seconds before giving up. */
- i = 0;
- while (((val = mmio_read32be(adapter->p1_mmio)) != 0xffffffff) &&
- (i < 5)) {
- msleep(500);
- i++;
- }
-
- if (val != 0xffffffff)
- dev_err(&dev->dev, "cxl: PERST failed to trigger EEH\n");
-
return rc;
}
@@ -893,7 +952,7 @@ static int cxl_map_adapter_regs(struct cxl *adapter, struct pci_dev *dev)
if (pci_request_region(dev, 0, "priv 1 regs"))
goto err2;
- pr_devel("cxl_map_adapter_regs: p1: %#.16llx %#llx, p2: %#.16llx %#llx",
+ pr_devel("cxl_map_adapter_regs: p1: %#016llx %#llx, p2: %#016llx %#llx",
p1_base(dev), p1_size(dev), p2_base(dev), p2_size(dev));
if (!(adapter->p1_mmio = ioremap(p1_base(dev), p1_size(dev))))
@@ -917,10 +976,16 @@ err1:
static void cxl_unmap_adapter_regs(struct cxl *adapter)
{
- if (adapter->p1_mmio)
+ if (adapter->p1_mmio) {
iounmap(adapter->p1_mmio);
- if (adapter->p2_mmio)
+ adapter->p1_mmio = NULL;
+ pci_release_region(to_pci_dev(adapter->dev.parent), 2);
+ }
+ if (adapter->p2_mmio) {
iounmap(adapter->p2_mmio);
+ adapter->p2_mmio = NULL;
+ pci_release_region(to_pci_dev(adapter->dev.parent), 0);
+ }
}
static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev)
@@ -949,7 +1014,6 @@ static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev)
CXL_READ_VSEC_BASE_IMAGE(dev, vsec, &adapter->base_image);
CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state);
adapter->user_image_loaded = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED);
- adapter->perst_loads_image = true;
adapter->perst_select_user = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED);
CXL_READ_VSEC_NAFUS(dev, vsec, &adapter->slices);
@@ -1009,81 +1073,138 @@ static void cxl_release_adapter(struct device *dev)
pr_devel("cxl_release_adapter\n");
+ cxl_remove_adapter_nr(adapter);
+
kfree(adapter);
}
-static struct cxl *cxl_alloc_adapter(struct pci_dev *dev)
+static struct cxl *cxl_alloc_adapter(void)
{
struct cxl *adapter;
if (!(adapter = kzalloc(sizeof(struct cxl), GFP_KERNEL)))
return NULL;
- adapter->dev.parent = &dev->dev;
- adapter->dev.release = cxl_release_adapter;
- pci_set_drvdata(dev, adapter);
spin_lock_init(&adapter->afu_list_lock);
+ if (cxl_alloc_adapter_nr(adapter))
+ goto err1;
+
+ if (dev_set_name(&adapter->dev, "card%i", adapter->adapter_num))
+ goto err2;
+
return adapter;
+
+err2:
+ cxl_remove_adapter_nr(adapter);
+err1:
+ kfree(adapter);
+ return NULL;
}
+#define CXL_PSL_ErrIVTE_tberror (0x1ull << (63-31))
+
static int sanitise_adapter_regs(struct cxl *adapter)
{
- cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000);
+ /* Clear PSL tberror bit by writing 1 to it */
+ cxl_p1_write(adapter, CXL_PSL_ErrIVTE, CXL_PSL_ErrIVTE_tberror);
return cxl_tlb_slb_invalidate(adapter);
}
-static struct cxl *cxl_init_adapter(struct pci_dev *dev)
+/* This should contain *only* operations that can safely be done in
+ * both creation and recovery.
+ */
+static int cxl_configure_adapter(struct cxl *adapter, struct pci_dev *dev)
{
- struct cxl *adapter;
- bool free = true;
int rc;
+ adapter->dev.parent = &dev->dev;
+ adapter->dev.release = cxl_release_adapter;
+ pci_set_drvdata(dev, adapter);
- if (!(adapter = cxl_alloc_adapter(dev)))
- return ERR_PTR(-ENOMEM);
+ rc = pci_enable_device(dev);
+ if (rc) {
+ dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc);
+ return rc;
+ }
if ((rc = cxl_read_vsec(adapter, dev)))
- goto err1;
+ return rc;
if ((rc = cxl_vsec_looks_ok(adapter, dev)))
- goto err1;
+ return rc;
if ((rc = setup_cxl_bars(dev)))
- goto err1;
+ return rc;
if ((rc = switch_card_to_cxl(dev)))
- goto err1;
-
- if ((rc = cxl_alloc_adapter_nr(adapter)))
- goto err1;
-
- if ((rc = dev_set_name(&adapter->dev, "card%i", adapter->adapter_num)))
- goto err2;
+ return rc;
if ((rc = cxl_update_image_control(adapter)))
- goto err2;
+ return rc;
if ((rc = cxl_map_adapter_regs(adapter, dev)))
- goto err2;
+ return rc;
if ((rc = sanitise_adapter_regs(adapter)))
- goto err2;
+ goto err;
if ((rc = init_implementation_adapter_regs(adapter, dev)))
- goto err3;
+ goto err;
if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_CAPI)))
- goto err3;
+ goto err;
/* If recovery happened, the last step is to turn on snooping.
* In the non-recovery case this has no effect */
- if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON))) {
- goto err3;
- }
+ if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON)))
+ goto err;
+
+ if ((rc = cxl_setup_psl_timebase(adapter, dev)))
+ goto err;
if ((rc = cxl_register_psl_err_irq(adapter)))
- goto err3;
+ goto err;
+
+ return 0;
+
+err:
+ cxl_unmap_adapter_regs(adapter);
+ return rc;
+
+}
+
+static void cxl_deconfigure_adapter(struct cxl *adapter)
+{
+ struct pci_dev *pdev = to_pci_dev(adapter->dev.parent);
+
+ cxl_release_psl_err_irq(adapter);
+ cxl_unmap_adapter_regs(adapter);
+
+ pci_disable_device(pdev);
+}
+
+static struct cxl *cxl_init_adapter(struct pci_dev *dev)
+{
+ struct cxl *adapter;
+ int rc;
+
+ adapter = cxl_alloc_adapter();
+ if (!adapter)
+ return ERR_PTR(-ENOMEM);
+
+ /* Set defaults for parameters which need to persist over
+ * configure/reconfigure
+ */
+ adapter->perst_loads_image = true;
+ adapter->perst_same_image = false;
+
+ rc = cxl_configure_adapter(adapter, dev);
+ if (rc) {
+ pci_disable_device(dev);
+ cxl_release_adapter(&adapter->dev);
+ return ERR_PTR(rc);
+ }
/* Don't care if this one fails: */
cxl_debugfs_adapter_add(adapter);
@@ -1101,37 +1222,25 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev)
return adapter;
err_put1:
- device_unregister(&adapter->dev);
- free = false;
+ /* This should mirror cxl_remove_adapter, except without the
+ * sysfs parts
+ */
cxl_debugfs_adapter_remove(adapter);
- cxl_release_psl_err_irq(adapter);
-err3:
- cxl_unmap_adapter_regs(adapter);
-err2:
- cxl_remove_adapter_nr(adapter);
-err1:
- if (free)
- kfree(adapter);
+ cxl_deconfigure_adapter(adapter);
+ device_unregister(&adapter->dev);
return ERR_PTR(rc);
}
static void cxl_remove_adapter(struct cxl *adapter)
{
- struct pci_dev *pdev = to_pci_dev(adapter->dev.parent);
-
- pr_devel("cxl_release_adapter\n");
+ pr_devel("cxl_remove_adapter\n");
cxl_sysfs_adapter_remove(adapter);
cxl_debugfs_adapter_remove(adapter);
- cxl_release_psl_err_irq(adapter);
- cxl_unmap_adapter_regs(adapter);
- cxl_remove_adapter_nr(adapter);
- device_unregister(&adapter->dev);
+ cxl_deconfigure_adapter(adapter);
- pci_release_region(pdev, 0);
- pci_release_region(pdev, 2);
- pci_disable_device(pdev);
+ device_unregister(&adapter->dev);
}
static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id)
@@ -1145,21 +1254,21 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (cxl_verbose)
dump_cxl_config_space(dev);
- if ((rc = pci_enable_device(dev))) {
- dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc);
- return rc;
- }
-
adapter = cxl_init_adapter(dev);
if (IS_ERR(adapter)) {
dev_err(&dev->dev, "cxl_init_adapter failed: %li\n", PTR_ERR(adapter));
- pci_disable_device(dev);
return PTR_ERR(adapter);
}
for (slice = 0; slice < adapter->slices; slice++) {
- if ((rc = cxl_init_afu(adapter, slice, dev)))
+ if ((rc = cxl_init_afu(adapter, slice, dev))) {
dev_err(&dev->dev, "AFU %i failed to initialise: %i\n", slice, rc);
+ continue;
+ }
+
+ rc = cxl_afu_select_best_mode(adapter->afu[slice]);
+ if (rc)
+ dev_err(&dev->dev, "AFU %i failed to start: %i\n", slice, rc);
}
return 0;
@@ -1183,10 +1292,262 @@ static void cxl_remove(struct pci_dev *dev)
cxl_remove_adapter(adapter);
}
+static pci_ers_result_t cxl_vphb_error_detected(struct cxl_afu *afu,
+ pci_channel_state_t state)
+{
+ struct pci_dev *afu_dev;
+ pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET;
+ pci_ers_result_t afu_result = PCI_ERS_RESULT_NEED_RESET;
+
+ /* There should only be one entry, but go through the list
+ * anyway
+ */
+ list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
+ if (!afu_dev->driver)
+ continue;
+
+ afu_dev->error_state = state;
+
+ if (afu_dev->driver->err_handler)
+ afu_result = afu_dev->driver->err_handler->error_detected(afu_dev,
+ state);
+ /* Disconnect trumps all, NONE trumps NEED_RESET */
+ if (afu_result == PCI_ERS_RESULT_DISCONNECT)
+ result = PCI_ERS_RESULT_DISCONNECT;
+ else if ((afu_result == PCI_ERS_RESULT_NONE) &&
+ (result == PCI_ERS_RESULT_NEED_RESET))
+ result = PCI_ERS_RESULT_NONE;
+ }
+ return result;
+}
+
+static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ struct cxl *adapter = pci_get_drvdata(pdev);
+ struct cxl_afu *afu;
+ pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET;
+ int i;
+
+ /* At this point, we could still have an interrupt pending.
+ * Let's try to get them out of the way before they do
+ * anything we don't like.
+ */
+ schedule();
+
+ /* If we're permanently dead, give up. */
+ if (state == pci_channel_io_perm_failure) {
+ /* Tell the AFU drivers; but we don't care what they
+ * say, we're going away.
+ */
+ for (i = 0; i < adapter->slices; i++) {
+ afu = adapter->afu[i];
+ cxl_vphb_error_detected(afu, state);
+ }
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ /* Are we reflashing?
+ *
+ * If we reflash, we could come back as something entirely
+ * different, including a non-CAPI card. As such, by default
+ * we don't participate in the process. We'll be unbound and
+ * the slot re-probed. (TODO: check EEH doesn't blindly rebind
+ * us!)
+ *
+ * However, this isn't the entire story: for reliablity
+ * reasons, we usually want to reflash the FPGA on PERST in
+ * order to get back to a more reliable known-good state.
+ *
+ * This causes us a bit of a problem: if we reflash we can't
+ * trust that we'll come back the same - we could have a new
+ * image and been PERSTed in order to load that
+ * image. However, most of the time we actually *will* come
+ * back the same - for example a regular EEH event.
+ *
+ * Therefore, we allow the user to assert that the image is
+ * indeed the same and that we should continue on into EEH
+ * anyway.
+ */
+ if (adapter->perst_loads_image && !adapter->perst_same_image) {
+ /* TODO take the PHB out of CXL mode */
+ dev_info(&pdev->dev, "reflashing, so opting out of EEH!\n");
+ return PCI_ERS_RESULT_NONE;
+ }
+
+ /*
+ * At this point, we want to try to recover. We'll always
+ * need a complete slot reset: we don't trust any other reset.
+ *
+ * Now, we go through each AFU:
+ * - We send the driver, if bound, an error_detected callback.
+ * We expect it to clean up, but it can also tell us to give
+ * up and permanently detach the card. To simplify things, if
+ * any bound AFU driver doesn't support EEH, we give up on EEH.
+ *
+ * - We detach all contexts associated with the AFU. This
+ * does not free them, but puts them into a CLOSED state
+ * which causes any the associated files to return useful
+ * errors to userland. It also unmaps, but does not free,
+ * any IRQs.
+ *
+ * - We clean up our side: releasing and unmapping resources we hold
+ * so we can wire them up again when the hardware comes back up.
+ *
+ * Driver authors should note:
+ *
+ * - Any contexts you create in your kernel driver (except
+ * those associated with anonymous file descriptors) are
+ * your responsibility to free and recreate. Likewise with
+ * any attached resources.
+ *
+ * - We will take responsibility for re-initialising the
+ * device context (the one set up for you in
+ * cxl_pci_enable_device_hook and accessed through
+ * cxl_get_context). If you've attached IRQs or other
+ * resources to it, they remains yours to free.
+ *
+ * You can call the same functions to release resources as you
+ * normally would: we make sure that these functions continue
+ * to work when the hardware is down.
+ *
+ * Two examples:
+ *
+ * 1) If you normally free all your resources at the end of
+ * each request, or if you use anonymous FDs, your
+ * error_detected callback can simply set a flag to tell
+ * your driver not to start any new calls. You can then
+ * clear the flag in the resume callback.
+ *
+ * 2) If you normally allocate your resources on startup:
+ * * Set a flag in error_detected as above.
+ * * Let CXL detach your contexts.
+ * * In slot_reset, free the old resources and allocate new ones.
+ * * In resume, clear the flag to allow things to start.
+ */
+ for (i = 0; i < adapter->slices; i++) {
+ afu = adapter->afu[i];
+
+ result = cxl_vphb_error_detected(afu, state);
+
+ /* Only continue if everyone agrees on NEED_RESET */
+ if (result != PCI_ERS_RESULT_NEED_RESET)
+ return result;
+
+ cxl_context_detach_all(afu);
+ cxl_afu_deactivate_mode(afu);
+ cxl_deconfigure_afu(afu);
+ }
+ cxl_deconfigure_adapter(adapter);
+
+ return result;
+}
+
+static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev)
+{
+ struct cxl *adapter = pci_get_drvdata(pdev);
+ struct cxl_afu *afu;
+ struct cxl_context *ctx;
+ struct pci_dev *afu_dev;
+ pci_ers_result_t afu_result = PCI_ERS_RESULT_RECOVERED;
+ pci_ers_result_t result = PCI_ERS_RESULT_RECOVERED;
+ int i;
+
+ if (cxl_configure_adapter(adapter, pdev))
+ goto err;
+
+ for (i = 0; i < adapter->slices; i++) {
+ afu = adapter->afu[i];
+
+ if (cxl_configure_afu(afu, adapter, pdev))
+ goto err;
+
+ if (cxl_afu_select_best_mode(afu))
+ goto err;
+
+ cxl_pci_vphb_reconfigure(afu);
+
+ list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
+ /* Reset the device context.
+ * TODO: make this less disruptive
+ */
+ ctx = cxl_get_context(afu_dev);
+
+ if (ctx && cxl_release_context(ctx))
+ goto err;
+
+ ctx = cxl_dev_context_init(afu_dev);
+ if (!ctx)
+ goto err;
+
+ afu_dev->dev.archdata.cxl_ctx = ctx;
+
+ if (cxl_afu_check_and_enable(afu))
+ goto err;
+
+ afu_dev->error_state = pci_channel_io_normal;
+
+ /* If there's a driver attached, allow it to
+ * chime in on recovery. Drivers should check
+ * if everything has come back OK, but
+ * shouldn't start new work until we call
+ * their resume function.
+ */
+ if (!afu_dev->driver)
+ continue;
+
+ if (afu_dev->driver->err_handler &&
+ afu_dev->driver->err_handler->slot_reset)
+ afu_result = afu_dev->driver->err_handler->slot_reset(afu_dev);
+
+ if (afu_result == PCI_ERS_RESULT_DISCONNECT)
+ result = PCI_ERS_RESULT_DISCONNECT;
+ }
+ }
+ return result;
+
+err:
+ /* All the bits that happen in both error_detected and cxl_remove
+ * should be idempotent, so we don't need to worry about leaving a mix
+ * of unconfigured and reconfigured resources.
+ */
+ dev_err(&pdev->dev, "EEH recovery failed. Asking to be disconnected.\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+}
+
+static void cxl_pci_resume(struct pci_dev *pdev)
+{
+ struct cxl *adapter = pci_get_drvdata(pdev);
+ struct cxl_afu *afu;
+ struct pci_dev *afu_dev;
+ int i;
+
+ /* Everything is back now. Drivers should restart work now.
+ * This is not the place to be checking if everything came back up
+ * properly, because there's no return value: do that in slot_reset.
+ */
+ for (i = 0; i < adapter->slices; i++) {
+ afu = adapter->afu[i];
+
+ list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
+ if (afu_dev->driver && afu_dev->driver->err_handler &&
+ afu_dev->driver->err_handler->resume)
+ afu_dev->driver->err_handler->resume(afu_dev);
+ }
+ }
+}
+
+static const struct pci_error_handlers cxl_err_handler = {
+ .error_detected = cxl_pci_error_detected,
+ .slot_reset = cxl_pci_slot_reset,
+ .resume = cxl_pci_resume,
+};
+
struct pci_driver cxl_pci_driver = {
.name = "cxl-pci",
.id_table = cxl_pci_tbl,
.probe = cxl_probe,
.remove = cxl_remove,
.shutdown = cxl_remove,
+ .err_handler = &cxl_err_handler,
};
diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c
index 87cd747bb511..25868c2ec03e 100644
--- a/drivers/misc/cxl/sysfs.c
+++ b/drivers/misc/cxl/sysfs.c
@@ -112,12 +112,38 @@ static ssize_t load_image_on_perst_store(struct device *device,
return count;
}
+static ssize_t perst_reloads_same_image_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cxl *adapter = to_cxl_adapter(device);
+
+ return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->perst_same_image);
+}
+
+static ssize_t perst_reloads_same_image_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cxl *adapter = to_cxl_adapter(device);
+ int rc;
+ int val;
+
+ rc = sscanf(buf, "%i", &val);
+ if ((rc != 1) || !(val == 1 || val == 0))
+ return -EINVAL;
+
+ adapter->perst_same_image = (val == 1 ? true : false);
+ return count;
+}
+
static struct device_attribute adapter_attrs[] = {
__ATTR_RO(caia_version),
__ATTR_RO(psl_revision),
__ATTR_RO(base_image),
__ATTR_RO(image_loaded),
__ATTR_RW(load_image_on_perst),
+ __ATTR_RW(perst_reloads_same_image),
__ATTR(reset, S_IWUSR, NULL, reset_adapter_store),
};
diff --git a/drivers/misc/cxl/trace.h b/drivers/misc/cxl/trace.h
index ae434d87887e..6e1e2adfba8e 100644
--- a/drivers/misc/cxl/trace.h
+++ b/drivers/misc/cxl/trace.h
@@ -105,7 +105,7 @@ TRACE_EVENT(cxl_attach,
__entry->num_interrupts = num_interrupts;
),
- TP_printk("afu%i.%i pid=%i pe=%i wed=0x%.16llx irqs=%i amr=0x%llx",
+ TP_printk("afu%i.%i pid=%i pe=%i wed=0x%016llx irqs=%i amr=0x%llx",
__entry->card,
__entry->afu,
__entry->pid,
@@ -177,7 +177,7 @@ TRACE_EVENT(cxl_psl_irq,
__entry->dar = dar;
),
- TP_printk("afu%i.%i pe=%i irq=%i dsisr=%s dar=0x%.16llx",
+ TP_printk("afu%i.%i pe=%i irq=%i dsisr=%s dar=0x%016llx",
__entry->card,
__entry->afu,
__entry->pe,
@@ -233,7 +233,7 @@ TRACE_EVENT(cxl_ste_miss,
__entry->dar = dar;
),
- TP_printk("afu%i.%i pe=%i dar=0x%.16llx",
+ TP_printk("afu%i.%i pe=%i dar=0x%016llx",
__entry->card,
__entry->afu,
__entry->pe,
@@ -264,7 +264,7 @@ TRACE_EVENT(cxl_ste_write,
__entry->v = v;
),
- TP_printk("afu%i.%i pe=%i SSTE[%i] E=0x%.16llx V=0x%.16llx",
+ TP_printk("afu%i.%i pe=%i SSTE[%i] E=0x%016llx V=0x%016llx",
__entry->card,
__entry->afu,
__entry->pe,
@@ -295,7 +295,7 @@ TRACE_EVENT(cxl_pte_miss,
__entry->dar = dar;
),
- TP_printk("afu%i.%i pe=%i dsisr=%s dar=0x%.16llx",
+ TP_printk("afu%i.%i pe=%i dsisr=%s dar=0x%016llx",
__entry->card,
__entry->afu,
__entry->pe,
diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c
index 2eba002b580b..6dd16a6d153f 100644
--- a/drivers/misc/cxl/vphb.c
+++ b/drivers/misc/cxl/vphb.c
@@ -138,6 +138,26 @@ static int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn,
return 0;
}
+
+static inline bool cxl_config_link_ok(struct pci_bus *bus)
+{
+ struct pci_controller *phb;
+ struct cxl_afu *afu;
+
+ /* Config space IO is based on phb->cfg_addr, which is based on
+ * afu_desc_mmio. This isn't safe to read/write when the link
+ * goes down, as EEH tears down MMIO space.
+ *
+ * Check if the link is OK before proceeding.
+ */
+
+ phb = pci_bus_to_host(bus);
+ if (phb == NULL)
+ return false;
+ afu = (struct cxl_afu *)phb->private_data;
+ return cxl_adapter_link_ok(afu->adapter);
+}
+
static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val)
{
@@ -150,6 +170,9 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
if (rc)
return rc;
+ if (!cxl_config_link_ok(bus))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
/* Can only read 32 bits */
*val = (in_le32(ioaddr) >> shift) & mask;
return PCIBIOS_SUCCESSFUL;
@@ -167,6 +190,9 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
if (rc)
return rc;
+ if (!cxl_config_link_ok(bus))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
/* Can only write 32 bits so do read-modify-write */
mask <<= shift;
val <<= shift;
@@ -240,6 +266,14 @@ int cxl_pci_vphb_add(struct cxl_afu *afu)
return 0;
}
+void cxl_pci_vphb_reconfigure(struct cxl_afu *afu)
+{
+ /* When we are reconfigured, the AFU's MMIO space is unmapped
+ * and remapped. We need to reflect this in the PHB's view of
+ * the world.
+ */
+ afu->phb->cfg_addr = afu->afu_desc_mmio + afu->crs_offset;
+}
void cxl_pci_vphb_remove(struct cxl_afu *afu)
{
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 51394e59901b..a4e27e891153 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -238,8 +238,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
ifc_nand_ctrl->page = page_addr;
/* Program ROW0/COL0 */
- iowrite32be(page_addr, &ifc->ifc_nand.row0);
- iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0);
+ ifc_out32(page_addr, &ifc->ifc_nand.row0);
+ ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0);
buf_num = page_addr & priv->bufnum_mask;
@@ -301,19 +301,19 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
int i;
/* set the chip select for NAND Transaction */
- iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT,
- &ifc->ifc_nand.nand_csel);
+ ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT,
+ &ifc->ifc_nand.nand_csel);
dev_vdbg(priv->dev,
"%s: fir0=%08x fcr0=%08x\n",
__func__,
- ioread32be(&ifc->ifc_nand.nand_fir0),
- ioread32be(&ifc->ifc_nand.nand_fcr0));
+ ifc_in32(&ifc->ifc_nand.nand_fir0),
+ ifc_in32(&ifc->ifc_nand.nand_fcr0));
ctrl->nand_stat = 0;
/* start read/write seq */
- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
/* wait for command complete flag or timeout */
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
@@ -336,7 +336,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
int sector_end = sector + chip->ecc.steps - 1;
for (i = sector / 4; i <= sector_end / 4; i++)
- eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]);
+ eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]);
for (i = sector; i <= sector_end; i++) {
errors = check_read_ecc(mtd, ctrl, eccstat, i);
@@ -376,33 +376,33 @@ static void fsl_ifc_do_read(struct nand_chip *chip,
/* Program FIR/IFC_NAND_FCR0 for Small/Large page */
if (mtd->writesize > 512) {
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1);
-
- iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
- (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT),
- &ifc->ifc_nand.nand_fcr0);
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
+
+ ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT),
+ &ifc->ifc_nand.nand_fcr0);
} else {
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
- (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(0x0, &ifc->ifc_nand.nand_fir1);
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fir1);
if (oob)
- iowrite32be(NAND_CMD_READOOB <<
- IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(NAND_CMD_READOOB <<
+ IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
else
- iowrite32be(NAND_CMD_READ0 <<
- IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(NAND_CMD_READ0 <<
+ IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
}
}
@@ -422,7 +422,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
switch (command) {
/* READ0 read the entire buffer to use hardware ECC. */
case NAND_CMD_READ0:
- iowrite32be(0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, 0, page_addr, 0);
ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
@@ -437,7 +437,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* READOOB reads only the OOB because no ECC is performed. */
case NAND_CMD_READOOB:
- iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, column, page_addr, 1);
ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
@@ -453,19 +453,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
if (command == NAND_CMD_PARAM)
timing = IFC_FIR_OP_RBCD;
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
- (timing << IFC_NAND_FIR0_OP2_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- iowrite32be(column, &ifc->ifc_nand.row3);
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (timing << IFC_NAND_FIR0_OP2_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(column, &ifc->ifc_nand.row3);
/*
* although currently it's 8 bytes for READID, we always read
* the maximum 256 bytes(for PARAM)
*/
- iowrite32be(256, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
ifc_nand_ctrl->read_bytes = 256;
set_addr(mtd, 0, 0, 0);
@@ -480,16 +480,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* ERASE2 uses the block and page address from ERASE1 */
case NAND_CMD_ERASE2:
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT),
- &ifc->ifc_nand.nand_fir0);
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
- iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
- (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT),
- &ifc->ifc_nand.nand_fcr0);
+ ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) |
+ (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT),
+ &ifc->ifc_nand.nand_fcr0);
- iowrite32be(0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
ifc_nand_ctrl->read_bytes = 0;
fsl_ifc_run_command(mtd);
return;
@@ -506,19 +506,18 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) |
(NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT);
- iowrite32be(
- (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
- (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) |
- (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(
- (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) |
- (IFC_FIR_OP_RDSTAT <<
- IFC_NAND_FIR1_OP6_SHIFT) |
- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT),
- &ifc->ifc_nand.nand_fir1);
+ ifc_out32(
+ (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |
+ (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) |
+ (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(
+ (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) |
+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT),
+ &ifc->ifc_nand.nand_fir1);
} else {
nand_fcr0 = ((NAND_CMD_PAGEPROG <<
IFC_NAND_FCR0_CMD1_SHIFT) |
@@ -527,20 +526,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
(NAND_CMD_STATUS <<
IFC_NAND_FCR0_CMD3_SHIFT));
- iowrite32be(
+ ifc_out32(
(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) |
(IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) |
(IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) |
(IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT),
&ifc->ifc_nand.nand_fir0);
- iowrite32be(
- (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) |
- (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) |
- (IFC_FIR_OP_RDSTAT <<
- IFC_NAND_FIR1_OP7_SHIFT) |
- (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT),
- &ifc->ifc_nand.nand_fir1);
+ ifc_out32(
+ (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) |
+ (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) |
+ (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT),
+ &ifc->ifc_nand.nand_fir1);
if (column >= mtd->writesize)
nand_fcr0 |=
@@ -555,7 +553,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
column -= mtd->writesize;
ifc_nand_ctrl->oob = 1;
}
- iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0);
set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob);
return;
}
@@ -563,24 +561,26 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
case NAND_CMD_PAGEPROG: {
if (ifc_nand_ctrl->oob) {
- iowrite32be(ifc_nand_ctrl->index -
- ifc_nand_ctrl->column,
- &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(ifc_nand_ctrl->index -
+ ifc_nand_ctrl->column,
+ &ifc->ifc_nand.nand_fbcr);
} else {
- iowrite32be(0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0, &ifc->ifc_nand.nand_fbcr);
}
fsl_ifc_run_command(mtd);
return;
}
- case NAND_CMD_STATUS:
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- iowrite32be(1, &ifc->ifc_nand.nand_fbcr);
+ case NAND_CMD_STATUS: {
+ void __iomem *addr;
+
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, 0, 0, 0);
ifc_nand_ctrl->read_bytes = 1;
@@ -590,17 +590,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
* The chip always seems to report that it is
* write-protected, even when it is not.
*/
+ addr = ifc_nand_ctrl->addr;
if (chip->options & NAND_BUSWIDTH_16)
- setbits16(ifc_nand_ctrl->addr, NAND_STATUS_WP);
+ ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr);
else
- setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP);
+ ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr);
return;
+ }
case NAND_CMD_RESET:
- iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT,
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT,
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
fsl_ifc_run_command(mtd);
return;
@@ -658,7 +660,7 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
*/
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
offset = ifc_nand_ctrl->index++;
- return in_8(ifc_nand_ctrl->addr + offset);
+ return ifc_in8(ifc_nand_ctrl->addr + offset);
}
dev_err(priv->dev, "%s: beyond end of buffer\n", __func__);
@@ -680,7 +682,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
* next byte.
*/
if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) {
- data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index);
+ data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index);
ifc_nand_ctrl->index += 2;
return (uint8_t) data;
}
@@ -726,18 +728,18 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
u32 nand_fsr;
/* Use READ_STATUS command, but wait for the device to be ready */
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- iowrite32be(1, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(1, &ifc->ifc_nand.nand_fbcr);
set_addr(mtd, 0, 0, 0);
ifc_nand_ctrl->read_bytes = 1;
fsl_ifc_run_command(mtd);
- nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr);
+ nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr);
/*
* The chip always seems to report that it is
@@ -829,34 +831,34 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
uint32_t cs = priv->bank;
/* Save CSOR and CSOR_ext */
- csor = ioread32be(&ifc->csor_cs[cs].csor);
- csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext);
+ csor = ifc_in32(&ifc->csor_cs[cs].csor);
+ csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext);
/* chage PageSize 8K and SpareSize 1K*/
csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000;
- iowrite32be(csor_8k, &ifc->csor_cs[cs].csor);
- iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext);
+ ifc_out32(csor_8k, &ifc->csor_cs[cs].csor);
+ ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext);
/* READID */
- iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
- (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
- (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
- &ifc->ifc_nand.nand_fir0);
- iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
- &ifc->ifc_nand.nand_fcr0);
- iowrite32be(0x0, &ifc->ifc_nand.row3);
+ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
+ (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
+ (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT),
+ &ifc->ifc_nand.nand_fir0);
+ ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT,
+ &ifc->ifc_nand.nand_fcr0);
+ ifc_out32(0x0, &ifc->ifc_nand.row3);
- iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr);
+ ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr);
/* Program ROW0/COL0 */
- iowrite32be(0x0, &ifc->ifc_nand.row0);
- iowrite32be(0x0, &ifc->ifc_nand.col0);
+ ifc_out32(0x0, &ifc->ifc_nand.row0);
+ ifc_out32(0x0, &ifc->ifc_nand.col0);
/* set the chip select for NAND Transaction */
- iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel);
+ ifc_out32(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel);
/* start read seq */
- iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
+ ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt);
/* wait for command complete flag or timeout */
wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat,
@@ -866,8 +868,8 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv)
printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n");
/* Restore CSOR and CSOR_ext */
- iowrite32be(csor, &ifc->csor_cs[cs].csor);
- iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext);
+ ifc_out32(csor, &ifc->csor_cs[cs].csor);
+ ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext);
}
static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
@@ -884,7 +886,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
/* fill in nand_chip structure */
/* set up function call table */
- if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16)
+ if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16)
chip->read_byte = fsl_ifc_read_byte16;
else
chip->read_byte = fsl_ifc_read_byte;
@@ -898,13 +900,13 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
- iowrite32be(0x0, &ifc->ifc_nand.ncfgr);
+ ifc_out32(0x0, &ifc->ifc_nand.ncfgr);
/* set up nand options */
chip->bbt_options = NAND_BBT_USE_FLASH;
chip->options = NAND_NO_SUBPAGE_WRITE;
- if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) {
+ if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) {
chip->read_byte = fsl_ifc_read_byte16;
chip->options |= NAND_BUSWIDTH_16;
} else {
@@ -917,7 +919,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
chip->ecc.read_page = fsl_ifc_read_page;
chip->ecc.write_page = fsl_ifc_write_page;
- csor = ioread32be(&ifc->csor_cs[priv->bank].csor);
+ csor = ifc_in32(&ifc->csor_cs[priv->bank].csor);
/* Hardware generates ECC per 512 Bytes */
chip->ecc.size = 512;
@@ -1006,7 +1008,7 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank,
phys_addr_t addr)
{
- u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr);
+ u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr);
if (!(cspr & CSPR_V))
return 0;
@@ -1092,16 +1094,16 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
dev_set_drvdata(priv->dev, priv);
- iowrite32be(IFC_NAND_EVTER_EN_OPC_EN |
- IFC_NAND_EVTER_EN_FTOER_EN |
- IFC_NAND_EVTER_EN_WPER_EN,
- &ifc->ifc_nand.nand_evter_en);
+ ifc_out32(IFC_NAND_EVTER_EN_OPC_EN |
+ IFC_NAND_EVTER_EN_FTOER_EN |
+ IFC_NAND_EVTER_EN_WPER_EN,
+ &ifc->ifc_nand.nand_evter_en);
/* enable NAND Machine Interrupts */
- iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN |
- IFC_NAND_EVTER_INTR_FTOERIR_EN |
- IFC_NAND_EVTER_INTR_WPERIR_EN,
- &ifc->ifc_nand.nand_evter_intr_en);
+ ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN |
+ IFC_NAND_EVTER_INTR_FTOERIR_EN |
+ IFC_NAND_EVTER_INTR_WPERIR_EN,
+ &ifc->ifc_nand.nand_evter_intr_en);
priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
if (!priv->mtd.name) {
ret = -ENOMEM;
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index 41901997c0d6..a75146f600cb 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -240,9 +240,9 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet,
{
struct hvsi_control *header = (struct hvsi_control *)packet;
- switch (header->verb) {
+ switch (be16_to_cpu(header->verb)) {
case VSV_MODEM_CTL_UPDATE:
- if ((header->word & HVSI_TSCD) == 0) {
+ if ((be32_to_cpu(header->word) & HVSI_TSCD) == 0) {
/* CD went away; no more connection */
pr_debug("hvsi%i: CD dropped\n", hp->index);
hp->mctrl &= TIOCM_CD;
@@ -267,6 +267,7 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet,
static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet)
{
struct hvsi_query_response *resp = (struct hvsi_query_response *)packet;
+ uint32_t mctrl_word;
switch (hp->state) {
case HVSI_WAIT_FOR_VER_RESPONSE:
@@ -274,9 +275,10 @@ static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet)
break;
case HVSI_WAIT_FOR_MCTRL_RESPONSE:
hp->mctrl = 0;
- if (resp->u.mctrl_word & HVSI_TSDTR)
+ mctrl_word = be32_to_cpu(resp->u.mctrl_word);
+ if (mctrl_word & HVSI_TSDTR)
hp->mctrl |= TIOCM_DTR;
- if (resp->u.mctrl_word & HVSI_TSCD)
+ if (mctrl_word & HVSI_TSCD)
hp->mctrl |= TIOCM_CD;
__set_state(hp, HVSI_OPEN);
break;
@@ -295,10 +297,10 @@ static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno)
packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
packet.hdr.len = sizeof(struct hvsi_query_response);
- packet.hdr.seqno = atomic_inc_return(&hp->seqno);
- packet.verb = VSV_SEND_VERSION_NUMBER;
+ packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
+ packet.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER);
packet.u.version = HVSI_VERSION;
- packet.query_seqno = query_seqno+1;
+ packet.query_seqno = cpu_to_be16(query_seqno+1);
pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
@@ -319,7 +321,7 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet)
switch (hp->state) {
case HVSI_WAIT_FOR_VER_QUERY:
- hvsi_version_respond(hp, query->hdr.seqno);
+ hvsi_version_respond(hp, be16_to_cpu(query->hdr.seqno));
__set_state(hp, HVSI_OPEN);
break;
default:
@@ -555,8 +557,8 @@ static int hvsi_query(struct hvsi_struct *hp, uint16_t verb)
packet.hdr.type = VS_QUERY_PACKET_HEADER;
packet.hdr.len = sizeof(struct hvsi_query);
- packet.hdr.seqno = atomic_inc_return(&hp->seqno);
- packet.verb = verb;
+ packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
+ packet.verb = cpu_to_be16(verb);
pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
@@ -596,14 +598,14 @@ static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl)
struct hvsi_control packet __ALIGNED__;
int wrote;
- packet.hdr.type = VS_CONTROL_PACKET_HEADER,
- packet.hdr.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.type = VS_CONTROL_PACKET_HEADER;
+ packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
packet.hdr.len = sizeof(struct hvsi_control);
- packet.verb = VSV_SET_MODEM_CTL;
- packet.mask = HVSI_TSDTR;
+ packet.verb = cpu_to_be16(VSV_SET_MODEM_CTL);
+ packet.mask = cpu_to_be32(HVSI_TSDTR);
if (mctrl & TIOCM_DTR)
- packet.word = HVSI_TSDTR;
+ packet.word = cpu_to_be32(HVSI_TSDTR);
pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
@@ -680,7 +682,7 @@ static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count)
BUG_ON(count > HVSI_MAX_OUTGOING_DATA);
packet.hdr.type = VS_DATA_PACKET_HEADER;
- packet.hdr.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
packet.hdr.len = count + sizeof(struct hvsi_header);
memcpy(&packet.data, buf, count);
@@ -697,9 +699,9 @@ static void hvsi_close_protocol(struct hvsi_struct *hp)
struct hvsi_control packet __ALIGNED__;
packet.hdr.type = VS_CONTROL_PACKET_HEADER;
- packet.hdr.seqno = atomic_inc_return(&hp->seqno);
+ packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno));
packet.hdr.len = 6;
- packet.verb = VSV_CLOSE_PROTOCOL;
+ packet.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL);
pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len);
dbg_dump_hex((uint8_t*)&packet, packet.hdr.len);
@@ -1180,7 +1182,7 @@ static int __init hvsi_console_init(void)
/* search device tree for vty nodes */
for_each_compatible_node(vty, "serial", "hvterm-protocol") {
struct hvsi_struct *hp;
- const uint32_t *vtermno, *irq;
+ const __be32 *vtermno, *irq;
vtermno = of_get_property(vty, "reg", NULL);
irq = of_get_property(vty, "interrupts", NULL);
@@ -1202,11 +1204,11 @@ static int __init hvsi_console_init(void)
hp->index = hvsi_count;
hp->inbuf_end = hp->inbuf;
hp->state = HVSI_CLOSED;
- hp->vtermno = *vtermno;
- hp->virq = irq_create_mapping(NULL, irq[0]);
+ hp->vtermno = be32_to_cpup(vtermno);
+ hp->virq = irq_create_mapping(NULL, be32_to_cpup(irq));
if (hp->virq == 0) {
printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n",
- __func__, irq[0]);
+ __func__, be32_to_cpup(irq));
tty_port_destroy(&hp->port);
continue;
}