diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-04 01:41:38 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-04 01:41:38 +0200 |
commit | ff474e8ca8547d09cb82ebab56d4c96f9eea01ce (patch) | |
tree | c004122f04a405eddb7929cd0297ccdce016f1b8 /drivers | |
parent | Merge branch 'pcmcia' of git://ftp.arm.linux.org.uk/~rmk/linux-arm (diff) | |
parent | cxl: Set up and enable PSL Timebase (diff) | |
download | linux-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/Kconfig | 11 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-powernv.c | 345 | ||||
-rw-r--r-- | drivers/macintosh/therm_windtunnel.c | 2 | ||||
-rw-r--r-- | drivers/macintosh/windfarm.h | 4 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_core.c | 47 | ||||
-rw-r--r-- | drivers/memory/fsl_ifc.c | 43 | ||||
-rw-r--r-- | drivers/misc/cxl/Kconfig | 7 | ||||
-rw-r--r-- | drivers/misc/cxl/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/cxl/api.c | 59 | ||||
-rw-r--r-- | drivers/misc/cxl/context.c | 22 | ||||
-rw-r--r-- | drivers/misc/cxl/cxl.h | 94 | ||||
-rw-r--r-- | drivers/misc/cxl/debugfs.c | 2 | ||||
-rw-r--r-- | drivers/misc/cxl/file.c | 27 | ||||
-rw-r--r-- | drivers/misc/cxl/irq.c | 56 | ||||
-rw-r--r-- | drivers/misc/cxl/main.c | 1 | ||||
-rw-r--r-- | drivers/misc/cxl/native.c | 119 | ||||
-rw-r--r-- | drivers/misc/cxl/pci.c | 611 | ||||
-rw-r--r-- | drivers/misc/cxl/sysfs.c | 26 | ||||
-rw-r--r-- | drivers/misc/cxl/trace.h | 10 | ||||
-rw-r--r-- | drivers/misc/cxl/vphb.c | 34 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_ifc_nand.c | 258 | ||||
-rw-r--r-- | drivers/tty/hvc/hvsi.c | 46 |
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; } |