diff options
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/83xx/suspend.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/85xx/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/85xx/corenet_generic.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/85xx/smp.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/85xx/t1042rdb_diu.c | 152 | ||||
-rw-r--r-- | arch/powerpc/platforms/Kconfig.cputype | 7 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/cpufreq_spudemand.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/context.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/fault.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/file.c | 65 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sched.c | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/spufs.h | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/powermac/smp.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/Kconfig | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/smp.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/dlpar.c | 38 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-cpu.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-memory.c | 272 |
19 files changed, 465 insertions, 95 deletions
diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c index 08f92f6ed228..978b85bb3233 100644 --- a/arch/powerpc/platforms/83xx/suspend.c +++ b/arch/powerpc/platforms/83xx/suspend.c @@ -15,6 +15,7 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/wait.h> +#include <linux/sched/signal.h> #include <linux/kthread.h> #include <linux/freezer.h> #include <linux/suspend.h> diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 7bc86dae9517..fe19dad568e2 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_P1022_RDK) += p1022_rdk.o obj-$(CONFIG_P1023_RDB) += p1023_rdb.o obj-$(CONFIG_TWR_P102x) += twr_p102x.o obj-$(CONFIG_CORENET_GENERIC) += corenet_generic.o +obj-$(CONFIG_FB_FSL_DIU) += t1042rdb_diu.o obj-$(CONFIG_STX_GP3) += stx_gp3.o obj-$(CONFIG_TQM85xx) += tqm85xx.o obj-$(CONFIG_SBC8548) += sbc8548.o diff --git a/arch/powerpc/platforms/85xx/corenet_generic.c b/arch/powerpc/platforms/85xx/corenet_generic.c index 6c0ba75fb256..ac191a7a1337 100644 --- a/arch/powerpc/platforms/85xx/corenet_generic.c +++ b/arch/powerpc/platforms/85xx/corenet_generic.c @@ -157,6 +157,7 @@ static const char * const boards[] __initconst = { "fsl,T1040RDB", "fsl,T1042RDB", "fsl,T1042RDB_PI", + "keymile,kmcent2", "keymile,kmcoge4", "varisys,CYRUS", NULL diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index a83a6d26090d..078097a0b09d 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -12,6 +12,7 @@ #include <linux/stddef.h> #include <linux/kernel.h> +#include <linux/sched/hotplug.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/of.h> diff --git a/arch/powerpc/platforms/85xx/t1042rdb_diu.c b/arch/powerpc/platforms/85xx/t1042rdb_diu.c new file mode 100644 index 000000000000..58fa3d319f1c --- /dev/null +++ b/arch/powerpc/platforms/85xx/t1042rdb_diu.c @@ -0,0 +1,152 @@ +/* + * T1042 platform DIU operation + * + * Copyright 2014 Freescale Semiconductor Inc. + * + * 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/io.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <sysdev/fsl_soc.h> + +/*DIU Pixel ClockCR offset in scfg*/ +#define CCSR_SCFG_PIXCLKCR 0x28 + +/* DIU Pixel Clock bits of the PIXCLKCR */ +#define PIXCLKCR_PXCKEN 0x80000000 +#define PIXCLKCR_PXCKINV 0x40000000 +#define PIXCLKCR_PXCKDLY 0x0000FF00 +#define PIXCLKCR_PXCLK_MASK 0x00FF0000 + +/* Some CPLD register definitions */ +#define CPLD_DIUCSR 0x16 +#define CPLD_DIUCSR_DVIEN 0x80 +#define CPLD_DIUCSR_BACKLIGHT 0x0f + +struct device_node *cpld_node; + +/** + * t1042rdb_set_monitor_port: switch the output to a different monitor port + */ +static void t1042rdb_set_monitor_port(enum fsl_diu_monitor_port port) +{ + static void __iomem *cpld_base; + + cpld_base = of_iomap(cpld_node, 0); + if (!cpld_base) { + pr_err("%s: Could not map cpld registers\n", __func__); + goto exit; + } + + switch (port) { + case FSL_DIU_PORT_DVI: + /* Enable the DVI(HDMI) port, disable the DFP and + * the backlight + */ + clrbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_DVIEN); + break; + case FSL_DIU_PORT_LVDS: + /* + * LVDS also needs backlight enabled, otherwise the display + * will be blank. + */ + /* Enable the DFP port, disable the DVI*/ + setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 8); + setbits8(cpld_base + CPLD_DIUCSR, 0x01 << 4); + setbits8(cpld_base + CPLD_DIUCSR, CPLD_DIUCSR_BACKLIGHT); + break; + default: + pr_err("%s: Unsupported monitor port %i\n", __func__, port); + } + + iounmap(cpld_base); +exit: + of_node_put(cpld_node); +} + +/** + * t1042rdb_set_pixel_clock: program the DIU's clock + * @pixclock: pixel clock in ps (pico seconds) + */ +static void t1042rdb_set_pixel_clock(unsigned int pixclock) +{ + struct device_node *scfg_np; + void __iomem *scfg; + unsigned long freq; + u64 temp; + u32 pxclk; + + scfg_np = of_find_compatible_node(NULL, NULL, "fsl,t1040-scfg"); + if (!scfg_np) { + pr_err("%s: Missing scfg node. Can not display video.\n", + __func__); + return; + } + + scfg = of_iomap(scfg_np, 0); + of_node_put(scfg_np); + if (!scfg) { + pr_err("%s: Could not map device. Can not display video.\n", + __func__); + return; + } + + /* Convert pixclock into frequency */ + temp = 1000000000000ULL; + do_div(temp, pixclock); + freq = temp; + + /* + * 'pxclk' is the ratio of the platform clock to the pixel clock. + * This number is programmed into the PIXCLKCR register, and the valid + * range of values is 2-255. + */ + pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq); + pxclk = clamp_t(u32, pxclk, 2, 255); + + /* Disable the pixel clock, and set it to non-inverted and no delay */ + clrbits32(scfg + CCSR_SCFG_PIXCLKCR, + PIXCLKCR_PXCKEN | PIXCLKCR_PXCKDLY | PIXCLKCR_PXCLK_MASK); + + /* Enable the clock and set the pxclk */ + setbits32(scfg + CCSR_SCFG_PIXCLKCR, PIXCLKCR_PXCKEN | (pxclk << 16)); + + iounmap(scfg); +} + +/** + * t1042rdb_valid_monitor_port: set the monitor port for sysfs + */ +static enum fsl_diu_monitor_port +t1042rdb_valid_monitor_port(enum fsl_diu_monitor_port port) +{ + switch (port) { + case FSL_DIU_PORT_DVI: + case FSL_DIU_PORT_LVDS: + return port; + default: + return FSL_DIU_PORT_DVI; /* Dual-link LVDS is not supported */ + } +} + +static int __init t1042rdb_diu_init(void) +{ + cpld_node = of_find_compatible_node(NULL, NULL, "fsl,t1042rdb-cpld"); + if (!cpld_node) + return 0; + + diu_ops.set_monitor_port = t1042rdb_set_monitor_port; + diu_ops.set_pixel_clock = t1042rdb_set_pixel_clock; + diu_ops.valid_monitor_port = t1042rdb_valid_monitor_port; + + return 0; +} + +early_initcall(t1042rdb_diu_init); diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 6e89e5a8d4fb..99b0ae8acb78 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -172,6 +172,13 @@ config PPC_FPU bool default y if PPC64 +config PPC_8xx_PERF_EVENT + bool "PPC 8xx perf events" + depends on PPC_8xx && PERF_EVENTS + help + This is Performance Events support for PPC 8xx. The 8xx doesn't + have a PMU but some events are emulated using 8xx features. + config FSL_EMB_PERFMON bool "Freescale Embedded Perfmon" depends on E500 || PPC_83xx diff --git a/arch/powerpc/platforms/cell/cpufreq_spudemand.c b/arch/powerpc/platforms/cell/cpufreq_spudemand.c index 88301e53f085..882944c36ef5 100644 --- a/arch/powerpc/platforms/cell/cpufreq_spudemand.c +++ b/arch/powerpc/platforms/cell/cpufreq_spudemand.c @@ -22,6 +22,7 @@ #include <linux/cpufreq.h> #include <linux/sched.h> +#include <linux/sched/loadavg.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/workqueue.h> diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 3b4152faeb1f..b500b17254a0 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -25,6 +25,8 @@ #include <linux/slab.h> #include <linux/atomic.h> #include <linux/sched.h> +#include <linux/sched/mm.h> + #include <asm/spu.h> #include <asm/spu_csa.h> #include "spufs.h" diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index e29e4d5afa2d..870c0a82d560 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -19,7 +19,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/sched.h> +#include <linux/sched/signal.h> #include <linux/mm.h> #include <asm/spu.h> diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index e5ec1368f0cd..ae2f740a82f1 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -683,23 +683,13 @@ size_t spu_ibox_read(struct spu_context *ctx, u32 *data) return ctx->ops->ibox_read(ctx, data); } -static int spufs_ibox_fasync(int fd, struct file *file, int on) -{ - struct spu_context *ctx = file->private_data; - - return fasync_helper(fd, file, on, &ctx->ibox_fasync); -} - /* interrupt-level ibox callback function. */ void spufs_ibox_callback(struct spu *spu) { struct spu_context *ctx = spu->ctx; - if (!ctx) - return; - - wake_up_all(&ctx->ibox_wq); - kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); + if (ctx) + wake_up_all(&ctx->ibox_wq); } /* @@ -794,7 +784,6 @@ static const struct file_operations spufs_ibox_fops = { .open = spufs_pipe_open, .read = spufs_ibox_read, .poll = spufs_ibox_poll, - .fasync = spufs_ibox_fasync, .llseek = no_llseek, }; @@ -832,26 +821,13 @@ size_t spu_wbox_write(struct spu_context *ctx, u32 data) return ctx->ops->wbox_write(ctx, data); } -static int spufs_wbox_fasync(int fd, struct file *file, int on) -{ - struct spu_context *ctx = file->private_data; - int ret; - - ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); - - return ret; -} - /* interrupt-level wbox callback function. */ void spufs_wbox_callback(struct spu *spu) { struct spu_context *ctx = spu->ctx; - if (!ctx) - return; - - wake_up_all(&ctx->wbox_wq); - kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); + if (ctx) + wake_up_all(&ctx->wbox_wq); } /* @@ -944,7 +920,6 @@ static const struct file_operations spufs_wbox_fops = { .open = spufs_pipe_open, .write = spufs_wbox_write, .poll = spufs_wbox_poll, - .fasync = spufs_wbox_fasync, .llseek = no_llseek, }; @@ -1520,28 +1495,8 @@ void spufs_mfc_callback(struct spu *spu) { struct spu_context *ctx = spu->ctx; - if (!ctx) - return; - - wake_up_all(&ctx->mfc_wq); - - pr_debug("%s %s\n", __func__, spu->name); - if (ctx->mfc_fasync) { - u32 free_elements, tagstatus; - unsigned int mask; - - /* no need for spu_acquire in interrupt context */ - free_elements = ctx->ops->get_mfc_free_elements(ctx); - tagstatus = ctx->ops->read_mfc_tagstatus(ctx); - - mask = 0; - if (free_elements & 0xffff) - mask |= POLLOUT; - if (tagstatus & ctx->tagwait) - mask |= POLLIN; - - kill_fasync(&ctx->mfc_fasync, SIGIO, mask); - } + if (ctx) + wake_up_all(&ctx->mfc_wq); } static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) @@ -1803,13 +1758,6 @@ static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int data return err; } -static int spufs_mfc_fasync(int fd, struct file *file, int on) -{ - struct spu_context *ctx = file->private_data; - - return fasync_helper(fd, file, on, &ctx->mfc_fasync); -} - static const struct file_operations spufs_mfc_fops = { .open = spufs_mfc_open, .release = spufs_mfc_release, @@ -1818,7 +1766,6 @@ static const struct file_operations spufs_mfc_fops = { .poll = spufs_mfc_poll, .flush = spufs_mfc_flush, .fsync = spufs_mfc_fsync, - .fasync = spufs_mfc_fasync, .mmap = spufs_mfc_mmap, .llseek = no_llseek, }; diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 460f5f31d5cb..1fbb5da17dd2 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -23,7 +23,8 @@ #undef DEBUG #include <linux/errno.h> -#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/sched/loadavg.h> #include <linux/sched/rt.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -140,7 +141,7 @@ void __spu_update_sched_info(struct spu_context *ctx) * runqueue. The context will be rescheduled on the proper node * if it is timesliced or preempted. */ - cpumask_copy(&ctx->cpus_allowed, tsk_cpus_allowed(current)); + cpumask_copy(&ctx->cpus_allowed, ¤t->cpus_allowed); /* Save the current cpu id for spu interrupt routing. */ ctx->last_ran = raw_smp_processor_id(); diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index bcfd6f063efa..5e59f80e95db 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -27,6 +27,7 @@ #include <linux/spinlock.h> #include <linux/fs.h> #include <linux/cpumask.h> +#include <linux/sched/signal.h> #include <asm/spu.h> #include <asm/spu_csa.h> @@ -102,9 +103,6 @@ struct spu_context { wait_queue_head_t stop_wq; wait_queue_head_t mfc_wq; wait_queue_head_t run_wq; - struct fasync_struct *ibox_fasync; - struct fasync_struct *wbox_fasync; - struct fasync_struct *mfc_fasync; u32 tagwait; struct spu_context_ops *ops; struct work_struct reap_work; diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index c9eb7d6540ea..746ca7321b03 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -23,6 +23,7 @@ */ #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/sched/hotplug.h> #include <linux/smp.h> #include <linux/interrupt.h> #include <linux/kernel_stat.h> diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig index 604190cab522..3a07e4dcf97c 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -5,7 +5,8 @@ config PPC_POWERNV select PPC_XICS select PPC_ICP_NATIVE select PPC_P7_NAP - select PPC_PCI_CHOICE if EMBEDDED + select PCI + select PCI_MSI select EPAPR_BOOT select PPC_INDIRECT_PIO select PPC_UDBG_16550 diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index e0f83c204ccc..6901a06da2f9 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -1468,14 +1468,12 @@ void pnv_pci_sriov_disable(struct pci_dev *pdev) struct pnv_phb *phb; struct pnv_ioda_pe *pe; struct pci_dn *pdn; - struct pci_sriov *iov; u16 num_vfs, i; bus = pdev->bus; hose = pci_bus_to_host(bus); phb = hose->private_data; pdn = pci_get_pdn(pdev); - iov = pdev->sriov; num_vfs = pdn->num_vfs; /* Release VF PEs */ diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index e39e6c428af1..8b67e1eefb5c 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/sched.h> +#include <linux/sched/hotplug.h> #include <linux/smp.h> #include <linux/interrupt.h> #include <linux/delay.h> diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index d3a81e746fc4..193e052fa0dd 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -354,11 +354,17 @@ static int handle_dlpar_errorlog(struct pseries_hp_errorlog *hp_elog) switch (hp_elog->id_type) { case PSERIES_HP_ELOG_ID_DRC_COUNT: hp_elog->_drc_u.drc_count = - be32_to_cpu(hp_elog->_drc_u.drc_count); + be32_to_cpu(hp_elog->_drc_u.drc_count); break; case PSERIES_HP_ELOG_ID_DRC_INDEX: hp_elog->_drc_u.drc_index = - be32_to_cpu(hp_elog->_drc_u.drc_index); + be32_to_cpu(hp_elog->_drc_u.drc_index); + break; + case PSERIES_HP_ELOG_ID_DRC_IC: + hp_elog->_drc_u.ic.count = + be32_to_cpu(hp_elog->_drc_u.ic.count); + hp_elog->_drc_u.ic.index = + be32_to_cpu(hp_elog->_drc_u.ic.index); } switch (hp_elog->resource) { @@ -467,7 +473,33 @@ static int dlpar_parse_id_type(char **cmd, struct pseries_hp_errorlog *hp_elog) if (!arg) return -EINVAL; - if (sysfs_streq(arg, "index")) { + if (sysfs_streq(arg, "indexed-count")) { + hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_IC; + arg = strsep(cmd, " "); + if (!arg) { + pr_err("No DRC count specified.\n"); + return -EINVAL; + } + + if (kstrtou32(arg, 0, &count)) { + pr_err("Invalid DRC count specified.\n"); + return -EINVAL; + } + + arg = strsep(cmd, " "); + if (!arg) { + pr_err("No DRC Index specified.\n"); + return -EINVAL; + } + + if (kstrtou32(arg, 0, &index)) { + pr_err("Invalid DRC Index specified.\n"); + return -EINVAL; + } + + hp_elog->_drc_u.ic.count = cpu_to_be32(count); + hp_elog->_drc_u.ic.index = cpu_to_be32(index); + } else if (sysfs_streq(arg, "index")) { hp_elog->id_type = PSERIES_HP_ELOG_ID_DRC_INDEX; arg = strsep(cmd, " "); if (!arg) { diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index a1b63e00b2f7..7bc0e91f8715 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -24,6 +24,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/sched.h> /* for idle_task_exit */ +#include <linux/sched/hotplug.h> #include <linux/cpu.h> #include <linux/of.h> #include <linux/slab.h> diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 3381c20edbc0..e104c71ea44a 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -320,6 +320,19 @@ static int dlpar_remove_device_tree_lmb(struct of_drconf_cell *lmb) return dlpar_update_device_tree_lmb(lmb); } +static struct memory_block *lmb_to_memblock(struct of_drconf_cell *lmb) +{ + unsigned long section_nr; + struct mem_section *mem_sect; + struct memory_block *mem_block; + + section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr)); + mem_sect = __nr_to_section(section_nr); + + mem_block = find_memory_block(mem_sect); + return mem_block; +} + #ifdef CONFIG_MEMORY_HOTREMOVE static int pseries_remove_memblock(unsigned long base, unsigned int memblock_size) { @@ -407,19 +420,6 @@ static bool lmb_is_removable(struct of_drconf_cell *lmb) static int dlpar_add_lmb(struct of_drconf_cell *); -static struct memory_block *lmb_to_memblock(struct of_drconf_cell *lmb) -{ - unsigned long section_nr; - struct mem_section *mem_sect; - struct memory_block *mem_block; - - section_nr = pfn_to_section_nr(PFN_DOWN(lmb->base_addr)); - mem_sect = __nr_to_section(section_nr); - - mem_block = find_memory_block(mem_sect); - return mem_block; -} - static int dlpar_remove_lmb(struct of_drconf_cell *lmb) { struct memory_block *mem_block; @@ -601,6 +601,94 @@ static int dlpar_memory_readd_by_index(u32 drc_index, struct property *prop) return rc; } + +static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index, + struct property *prop) +{ + struct of_drconf_cell *lmbs; + u32 num_lmbs, *p; + int i, rc, start_lmb_found; + int lmbs_available = 0, start_index = 0, end_index; + + pr_info("Attempting to hot-remove %u LMB(s) at %x\n", + lmbs_to_remove, drc_index); + + if (lmbs_to_remove == 0) + return -EINVAL; + + p = prop->value; + num_lmbs = *p++; + lmbs = (struct of_drconf_cell *)p; + start_lmb_found = 0; + + /* Navigate to drc_index */ + while (start_index < num_lmbs) { + if (lmbs[start_index].drc_index == drc_index) { + start_lmb_found = 1; + break; + } + + start_index++; + } + + if (!start_lmb_found) + return -EINVAL; + + end_index = start_index + lmbs_to_remove; + + /* Validate that there are enough LMBs to satisfy the request */ + for (i = start_index; i < end_index; i++) { + if (lmbs[i].flags & DRCONF_MEM_RESERVED) + break; + + lmbs_available++; + } + + if (lmbs_available < lmbs_to_remove) + return -EINVAL; + + for (i = start_index; i < end_index; i++) { + if (!(lmbs[i].flags & DRCONF_MEM_ASSIGNED)) + continue; + + rc = dlpar_remove_lmb(&lmbs[i]); + if (rc) + break; + + lmbs[i].reserved = 1; + } + + if (rc) { + pr_err("Memory indexed-count-remove failed, adding any removed LMBs\n"); + + for (i = start_index; i < end_index; i++) { + if (!lmbs[i].reserved) + continue; + + rc = dlpar_add_lmb(&lmbs[i]); + if (rc) + pr_err("Failed to add LMB, drc index %x\n", + be32_to_cpu(lmbs[i].drc_index)); + + lmbs[i].reserved = 0; + } + rc = -EINVAL; + } else { + for (i = start_index; i < end_index; i++) { + if (!lmbs[i].reserved) + continue; + + dlpar_release_drc(lmbs[i].drc_index); + pr_info("Memory at %llx (drc index %x) was hot-removed\n", + lmbs[i].base_addr, lmbs[i].drc_index); + + lmbs[i].reserved = 0; + } + } + + return rc; +} + #else static inline int pseries_remove_memblock(unsigned long base, unsigned int memblock_size) @@ -628,9 +716,32 @@ static int dlpar_memory_remove_by_index(u32 drc_index, struct property *prop) { return -EOPNOTSUPP; } +static int dlpar_memory_readd_by_index(u32 drc_index, struct property *prop) +{ + return -EOPNOTSUPP; +} +static int dlpar_memory_remove_by_ic(u32 lmbs_to_remove, u32 drc_index, + struct property *prop) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_MEMORY_HOTREMOVE */ +static int dlpar_online_lmb(struct of_drconf_cell *lmb) +{ + struct memory_block *mem_block; + int rc; + + mem_block = lmb_to_memblock(lmb); + if (!mem_block) + return -EINVAL; + + rc = device_online(&mem_block->dev); + put_device(&mem_block->dev); + return rc; +} + static int dlpar_add_lmb(struct of_drconf_cell *lmb) { unsigned long block_sz; @@ -654,10 +765,18 @@ static int dlpar_add_lmb(struct of_drconf_cell *lmb) /* Add the memory */ rc = add_memory(nid, lmb->base_addr, block_sz); - if (rc) + if (rc) { dlpar_remove_device_tree_lmb(lmb); - else + return rc; + } + + rc = dlpar_online_lmb(lmb); + if (rc) { + remove_memory(nid, lmb->base_addr, block_sz); + dlpar_remove_device_tree_lmb(lmb); + } else { lmb->flags |= DRCONF_MEM_ASSIGNED; + } return rc; } @@ -776,6 +895,97 @@ static int dlpar_memory_add_by_index(u32 drc_index, struct property *prop) return rc; } +static int dlpar_memory_add_by_ic(u32 lmbs_to_add, u32 drc_index, + struct property *prop) +{ + struct of_drconf_cell *lmbs; + u32 num_lmbs, *p; + int i, rc, start_lmb_found; + int lmbs_available = 0, start_index = 0, end_index; + + pr_info("Attempting to hot-add %u LMB(s) at index %x\n", + lmbs_to_add, drc_index); + + if (lmbs_to_add == 0) + return -EINVAL; + + p = prop->value; + num_lmbs = *p++; + lmbs = (struct of_drconf_cell *)p; + start_lmb_found = 0; + + /* Navigate to drc_index */ + while (start_index < num_lmbs) { + if (lmbs[start_index].drc_index == drc_index) { + start_lmb_found = 1; + break; + } + + start_index++; + } + + if (!start_lmb_found) + return -EINVAL; + + end_index = start_index + lmbs_to_add; + + /* Validate that the LMBs in this range are not reserved */ + for (i = start_index; i < end_index; i++) { + if (lmbs[i].flags & DRCONF_MEM_RESERVED) + break; + + lmbs_available++; + } + + if (lmbs_available < lmbs_to_add) + return -EINVAL; + + for (i = start_index; i < end_index; i++) { + if (lmbs[i].flags & DRCONF_MEM_ASSIGNED) + continue; + + rc = dlpar_acquire_drc(lmbs[i].drc_index); + if (rc) + break; + + rc = dlpar_add_lmb(&lmbs[i]); + if (rc) { + dlpar_release_drc(lmbs[i].drc_index); + break; + } + + lmbs[i].reserved = 1; + } + + if (rc) { + pr_err("Memory indexed-count-add failed, removing any added LMBs\n"); + + for (i = start_index; i < end_index; i++) { + if (!lmbs[i].reserved) + continue; + + rc = dlpar_remove_lmb(&lmbs[i]); + if (rc) + pr_err("Failed to remove LMB, drc index %x\n", + be32_to_cpu(lmbs[i].drc_index)); + else + dlpar_release_drc(lmbs[i].drc_index); + } + rc = -EINVAL; + } else { + for (i = start_index; i < end_index; i++) { + if (!lmbs[i].reserved) + continue; + + pr_info("Memory at %llx (drc index %x) was hot-added\n", + lmbs[i].base_addr, lmbs[i].drc_index); + lmbs[i].reserved = 0; + } + } + + return rc; +} + int dlpar_memory(struct pseries_hp_errorlog *hp_elog) { struct device_node *dn; @@ -783,9 +993,6 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog) u32 count, drc_index; int rc; - count = hp_elog->_drc_u.drc_count; - drc_index = hp_elog->_drc_u.drc_index; - lock_device_hotplug(); dn = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); @@ -802,22 +1009,39 @@ int dlpar_memory(struct pseries_hp_errorlog *hp_elog) switch (hp_elog->action) { case PSERIES_HP_ELOG_ACTION_ADD: - if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) + if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { + count = hp_elog->_drc_u.drc_count; rc = dlpar_memory_add_by_count(count, prop); - else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) + } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { + drc_index = hp_elog->_drc_u.drc_index; rc = dlpar_memory_add_by_index(drc_index, prop); - else + } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) { + count = hp_elog->_drc_u.ic.count; + drc_index = hp_elog->_drc_u.ic.index; + rc = dlpar_memory_add_by_ic(count, drc_index, prop); + } else { rc = -EINVAL; + } + break; case PSERIES_HP_ELOG_ACTION_REMOVE: - if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) + if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_COUNT) { + count = hp_elog->_drc_u.drc_count; rc = dlpar_memory_remove_by_count(count, prop); - else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) + } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_INDEX) { + drc_index = hp_elog->_drc_u.drc_index; rc = dlpar_memory_remove_by_index(drc_index, prop); - else + } else if (hp_elog->id_type == PSERIES_HP_ELOG_ID_DRC_IC) { + count = hp_elog->_drc_u.ic.count; + drc_index = hp_elog->_drc_u.ic.index; + rc = dlpar_memory_remove_by_ic(count, drc_index, prop); + } else { rc = -EINVAL; + } + break; case PSERIES_HP_ELOG_ACTION_READD: + drc_index = hp_elog->_drc_u.drc_index; rc = dlpar_memory_readd_by_index(drc_index, prop); break; default: |