Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 /*
0004  * Handles hot and cold plug of persistent memory regions on pseries.
0005  */
0006 
0007 #define pr_fmt(fmt)     "pseries-pmem: " fmt
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/interrupt.h>
0011 #include <linux/delay.h>
0012 #include <linux/sched.h>    /* for idle_task_exit */
0013 #include <linux/sched/hotplug.h>
0014 #include <linux/cpu.h>
0015 #include <linux/of.h>
0016 #include <linux/of_platform.h>
0017 #include <linux/slab.h>
0018 #include <asm/rtas.h>
0019 #include <asm/firmware.h>
0020 #include <asm/machdep.h>
0021 #include <asm/vdso_datapage.h>
0022 #include <asm/plpar_wrappers.h>
0023 #include <asm/topology.h>
0024 
0025 #include "pseries.h"
0026 
0027 static struct device_node *pmem_node;
0028 
0029 static ssize_t pmem_drc_add_node(u32 drc_index)
0030 {
0031     struct device_node *dn;
0032     int rc;
0033 
0034     pr_debug("Attempting to add pmem node, drc index: %x\n", drc_index);
0035 
0036     rc = dlpar_acquire_drc(drc_index);
0037     if (rc) {
0038         pr_err("Failed to acquire DRC, rc: %d, drc index: %x\n",
0039             rc, drc_index);
0040         return -EINVAL;
0041     }
0042 
0043     dn = dlpar_configure_connector(cpu_to_be32(drc_index), pmem_node);
0044     if (!dn) {
0045         pr_err("configure-connector failed for drc %x\n", drc_index);
0046         dlpar_release_drc(drc_index);
0047         return -EINVAL;
0048     }
0049 
0050     /* NB: The of reconfig notifier creates platform device from the node */
0051     rc = dlpar_attach_node(dn, pmem_node);
0052     if (rc) {
0053         pr_err("Failed to attach node %pOF, rc: %d, drc index: %x\n",
0054             dn, rc, drc_index);
0055 
0056         if (dlpar_release_drc(drc_index))
0057             dlpar_free_cc_nodes(dn);
0058 
0059         return rc;
0060     }
0061 
0062     pr_info("Successfully added %pOF, drc index: %x\n", dn, drc_index);
0063 
0064     return 0;
0065 }
0066 
0067 static ssize_t pmem_drc_remove_node(u32 drc_index)
0068 {
0069     struct device_node *dn;
0070     uint32_t index;
0071     int rc;
0072 
0073     for_each_child_of_node(pmem_node, dn) {
0074         if (of_property_read_u32(dn, "ibm,my-drc-index", &index))
0075             continue;
0076         if (index == drc_index)
0077             break;
0078     }
0079 
0080     if (!dn) {
0081         pr_err("Attempting to remove unused DRC index %x\n", drc_index);
0082         return -ENODEV;
0083     }
0084 
0085     pr_debug("Attempting to remove %pOF, drc index: %x\n", dn, drc_index);
0086 
0087     /* * NB: tears down the ibm,pmemory device as a side-effect */
0088     rc = dlpar_detach_node(dn);
0089     if (rc)
0090         return rc;
0091 
0092     rc = dlpar_release_drc(drc_index);
0093     if (rc) {
0094         pr_err("Failed to release drc (%x) for CPU %pOFn, rc: %d\n",
0095             drc_index, dn, rc);
0096         dlpar_attach_node(dn, pmem_node);
0097         return rc;
0098     }
0099 
0100     pr_info("Successfully removed PMEM with drc index: %x\n", drc_index);
0101 
0102     return 0;
0103 }
0104 
0105 int dlpar_hp_pmem(struct pseries_hp_errorlog *hp_elog)
0106 {
0107     u32 drc_index;
0108     int rc;
0109 
0110     /* slim chance, but we might get a hotplug event while booting */
0111     if (!pmem_node)
0112         pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory");
0113     if (!pmem_node) {
0114         pr_err("Hotplug event for a pmem device, but none exists\n");
0115         return -ENODEV;
0116     }
0117 
0118     if (hp_elog->id_type != PSERIES_HP_ELOG_ID_DRC_INDEX) {
0119         pr_err("Unsupported hotplug event type %d\n",
0120                 hp_elog->id_type);
0121         return -EINVAL;
0122     }
0123 
0124     drc_index = hp_elog->_drc_u.drc_index;
0125 
0126     lock_device_hotplug();
0127 
0128     if (hp_elog->action == PSERIES_HP_ELOG_ACTION_ADD) {
0129         rc = pmem_drc_add_node(drc_index);
0130     } else if (hp_elog->action == PSERIES_HP_ELOG_ACTION_REMOVE) {
0131         rc = pmem_drc_remove_node(drc_index);
0132     } else {
0133         pr_err("Unsupported hotplug action (%d)\n", hp_elog->action);
0134         rc = -EINVAL;
0135     }
0136 
0137     unlock_device_hotplug();
0138     return rc;
0139 }
0140 
0141 static const struct of_device_id drc_pmem_match[] = {
0142     { .type = "ibm,persistent-memory", },
0143     {}
0144 };
0145 
0146 static int pseries_pmem_init(void)
0147 {
0148     /*
0149      * Only supported on POWER8 and above.
0150      */
0151     if (!cpu_has_feature(CPU_FTR_ARCH_207S))
0152         return 0;
0153 
0154     pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory");
0155     if (!pmem_node)
0156         return 0;
0157 
0158     /*
0159      * The generic OF bus probe/populate handles creating platform devices
0160      * from the child (ibm,pmemory) nodes. The generic code registers an of
0161      * reconfig notifier to handle the hot-add/remove cases too.
0162      */
0163     of_platform_bus_probe(pmem_node, drc_pmem_match, NULL);
0164 
0165     return 0;
0166 }
0167 machine_arch_initcall(pseries, pseries_pmem_init);