diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 08:37:40 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-30 08:37:40 -0700 |
commit | ec31b2124158f60c515ed84bd5e40db1a883c7b6 (patch) | |
tree | 05de4bebce63cf1eaf25d24c9de4b59e5aa7e100 /arch/powerpc/platforms | |
parent | ca72cddfcb6afd75a808da4f027325fa63a1b856 (diff) | |
parent | eabd90944b3a00766e84da3d117ea0f3e0a3b1a3 (diff) | |
download | linux-ec31b2124158f60c515ed84bd5e40db1a883c7b6.tar.bz2 |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc:
[POWERPC] Fix crashkernel= handling when no crashkernel= specified
[POWERPC] Make emergency stack safe for current_thread_info() use
[POWERPC] spufs: add .gitignore for spu_save_dump.h & spu_restore_dump.h
[POWERPC] spufs: trace spu_acquire_saved events
[POWERPC] spufs: fix marker name for find_victim
[POWERPC] spufs: add marker for destroy_spu_context
[POWERPC] spufs: add sputrace marker parameter names
[POWERPC] spufs: add context switch notification log
[POWERPC] mpc5200: defconfigs for CM5200, Lite5200B, Motion-PRO and TQM5200
[POWERPC] mpc5200: Switch mpc5200 dts files to dts-v1 format
[POWERPC] mpc5200: Fix FEC error handling on FIFO errors
[POWERPC] mpc5200: add Phytec pcm030 board support
[POWERPC] mpc5200: add gpiolib support for mpc5200
[POWERPC] mpc5200: add interrupt type function
[POWERPC] mpc5200: Fix unterminated of_device_id table
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/52xx/Kconfig | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/mpc5200_simple.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_gpio.c | 465 | ||||
-rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_pic.c | 38 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/.gitignore | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/context.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/file.c | 166 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/run.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sched.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/spufs.h | 33 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sputrace.c | 36 |
12 files changed, 738 insertions, 21 deletions
diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig index cf945d55c276..acd2fc8cf492 100644 --- a/arch/powerpc/platforms/52xx/Kconfig +++ b/arch/powerpc/platforms/52xx/Kconfig @@ -44,3 +44,9 @@ config PPC_MPC5200_BUGFIX It is safe to say 'Y' here +config PPC_MPC5200_GPIO + bool "MPC5200 GPIO support" + depends on PPC_MPC52xx + select HAVE_GPIO_LIB + help + Enable gpiolib support for mpc5200 based boards diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile index fe1b81bb5224..daf0e1568d6d 100644 --- a/arch/powerpc/platforms/52xx/Makefile +++ b/arch/powerpc/platforms/52xx/Makefile @@ -14,3 +14,5 @@ obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o ifeq ($(CONFIG_PPC_LITE5200),y) obj-$(CONFIG_PM) += lite5200_sleep.o lite5200_pm.o endif + +obj-$(CONFIG_PPC_MPC5200_GPIO) += mpc52xx_gpio.o
\ No newline at end of file diff --git a/arch/powerpc/platforms/52xx/mpc5200_simple.c b/arch/powerpc/platforms/52xx/mpc5200_simple.c index c48b82bc2aad..a3bda0b9f1ff 100644 --- a/arch/powerpc/platforms/52xx/mpc5200_simple.c +++ b/arch/powerpc/platforms/52xx/mpc5200_simple.c @@ -51,6 +51,7 @@ static void __init mpc5200_simple_setup_arch(void) /* list of the supported boards */ static char *board[] __initdata = { "promess,motionpro", + "phytec,pcm030", "schindler,cm5200", "tqc,tqm5200", NULL diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpio.c b/arch/powerpc/platforms/52xx/mpc52xx_gpio.c new file mode 100644 index 000000000000..48da5dfe4856 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpio.c @@ -0,0 +1,465 @@ +/* + * MPC52xx gpio driver + * + * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/of.h> +#include <linux/kernel.h> +#include <linux/of_gpio.h> +#include <linux/io.h> +#include <linux/of_platform.h> + +#include <asm/gpio.h> +#include <asm/mpc52xx.h> +#include <sysdev/fsl_soc.h> + +static DEFINE_SPINLOCK(gpio_lock); + +struct mpc52xx_gpiochip { + struct of_mm_gpio_chip mmchip; + unsigned int shadow_dvo; + unsigned int shadow_gpioe; + unsigned int shadow_ddr; +}; + +/* + * GPIO LIB API implementation for wakeup GPIOs. + * + * There's a maximum of 8 wakeup GPIOs. Which of these are available + * for use depends on your board setup. + * + * 0 -> GPIO_WKUP_7 + * 1 -> GPIO_WKUP_6 + * 2 -> PSC6_1 + * 3 -> PSC6_0 + * 4 -> ETH_17 + * 5 -> PSC3_9 + * 6 -> PSC2_4 + * 7 -> PSC1_4 + * + */ +static int mpc52xx_wkup_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs; + unsigned int ret; + + ret = (in_8(®s->wkup_ival) >> (7 - gpio)) & 1; + + pr_debug("%s: gpio: %d ret: %d\n", __func__, gpio, ret); + + return ret; +} + +static inline void +__mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpiochip *chip = container_of(mm_gc, + struct mpc52xx_gpiochip, mmchip); + struct mpc52xx_gpio_wkup __iomem *regs = mm_gc->regs; + + if (val) + chip->shadow_dvo |= 1 << (7 - gpio); + else + chip->shadow_dvo &= ~(1 << (7 - gpio)); + + out_8(®s->wkup_dvo, chip->shadow_dvo); +} + +static void +mpc52xx_wkup_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + __mpc52xx_wkup_gpio_set(gc, gpio, val); + + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); +} + +static int mpc52xx_wkup_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpiochip *chip = container_of(mm_gc, + struct mpc52xx_gpiochip, mmchip); + struct mpc52xx_gpio_wkup *regs = mm_gc->regs; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + /* set the direction */ + chip->shadow_ddr &= ~(1 << (7 - gpio)); + out_8(®s->wkup_ddr, chip->shadow_ddr); + + /* and enable the pin */ + chip->shadow_gpioe |= 1 << (7 - gpio); + out_8(®s->wkup_gpioe, chip->shadow_gpioe); + + spin_unlock_irqrestore(&gpio_lock, flags); + + return 0; +} + +static int +mpc52xx_wkup_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpio_wkup *regs = mm_gc->regs; + struct mpc52xx_gpiochip *chip = container_of(mm_gc, + struct mpc52xx_gpiochip, mmchip); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + __mpc52xx_wkup_gpio_set(gc, gpio, val); + + /* Then set direction */ + chip->shadow_ddr |= 1 << (7 - gpio); + out_8(®s->wkup_ddr, chip->shadow_ddr); + + /* Finally enable the pin */ + chip->shadow_gpioe |= 1 << (7 - gpio); + out_8(®s->wkup_gpioe, chip->shadow_gpioe); + + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; +} + +static int __devinit mpc52xx_wkup_gpiochip_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct mpc52xx_gpiochip *chip; + struct mpc52xx_gpio_wkup *regs; + struct of_gpio_chip *ofchip; + int ret; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + ofchip = &chip->mmchip.of_gc; + + ofchip->gpio_cells = 2; + ofchip->gc.ngpio = 8; + ofchip->gc.direction_input = mpc52xx_wkup_gpio_dir_in; + ofchip->gc.direction_output = mpc52xx_wkup_gpio_dir_out; + ofchip->gc.get = mpc52xx_wkup_gpio_get; + ofchip->gc.set = mpc52xx_wkup_gpio_set; + + ret = of_mm_gpiochip_add(ofdev->node, &chip->mmchip); + if (ret) + return ret; + + regs = chip->mmchip.regs; + chip->shadow_gpioe = in_8(®s->wkup_gpioe); + chip->shadow_ddr = in_8(®s->wkup_ddr); + chip->shadow_dvo = in_8(®s->wkup_dvo); + + return 0; +} + +static int mpc52xx_gpiochip_remove(struct of_device *ofdev) +{ + return -EBUSY; +} + +static const struct of_device_id mpc52xx_wkup_gpiochip_match[] = { + { + .compatible = "fsl,mpc5200-gpio-wkup", + }, + {} +}; + +static struct of_platform_driver mpc52xx_wkup_gpiochip_driver = { + .name = "gpio_wkup", + .match_table = mpc52xx_wkup_gpiochip_match, + .probe = mpc52xx_wkup_gpiochip_probe, + .remove = mpc52xx_gpiochip_remove, +}; + +/* + * GPIO LIB API implementation for simple GPIOs + * + * There's a maximum of 32 simple GPIOs. Which of these are available + * for use depends on your board setup. + * The numbering reflects the bit numbering in the port registers: + * + * 0..1 > reserved + * 2..3 > IRDA + * 4..7 > ETHR + * 8..11 > reserved + * 12..15 > USB + * 16..17 > reserved + * 18..23 > PSC3 + * 24..27 > PSC2 + * 28..31 > PSC1 + */ +static int mpc52xx_simple_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpio __iomem *regs = mm_gc->regs; + unsigned int ret; + + ret = (in_be32(®s->simple_ival) >> (31 - gpio)) & 1; + + return ret; +} + +static inline void +__mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpiochip *chip = container_of(mm_gc, + struct mpc52xx_gpiochip, mmchip); + struct mpc52xx_gpio __iomem *regs = mm_gc->regs; + + if (val) + chip->shadow_dvo |= 1 << (31 - gpio); + else + chip->shadow_dvo &= ~(1 << (31 - gpio)); + out_be32(®s->simple_dvo, chip->shadow_dvo); +} + +static void +mpc52xx_simple_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + __mpc52xx_simple_gpio_set(gc, gpio, val); + + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); +} + +static int mpc52xx_simple_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpiochip *chip = container_of(mm_gc, + struct mpc52xx_gpiochip, mmchip); + struct mpc52xx_gpio *regs = mm_gc->regs; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + /* set the direction */ + chip->shadow_ddr &= ~(1 << (31 - gpio)); + out_be32(®s->simple_ddr, chip->shadow_ddr); + + /* and enable the pin */ + chip->shadow_gpioe |= 1 << (31 - gpio); + out_be32(®s->simple_gpioe, chip->shadow_gpioe); + + spin_unlock_irqrestore(&gpio_lock, flags); + + return 0; +} + +static int +mpc52xx_simple_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpiochip *chip = container_of(mm_gc, + struct mpc52xx_gpiochip, mmchip); + struct mpc52xx_gpio *regs = mm_gc->regs; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + + /* First set initial value */ + __mpc52xx_simple_gpio_set(gc, gpio, val); + + /* Then set direction */ + chip->shadow_ddr |= 1 << (31 - gpio); + out_be32(®s->simple_ddr, chip->shadow_ddr); + + /* Finally enable the pin */ + chip->shadow_gpioe |= 1 << (31 - gpio); + out_be32(®s->simple_gpioe, chip->shadow_gpioe); + + spin_unlock_irqrestore(&gpio_lock, flags); + + pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; +} + +static int __devinit mpc52xx_simple_gpiochip_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct mpc52xx_gpiochip *chip; + struct of_gpio_chip *ofchip; + struct mpc52xx_gpio *regs; + int ret; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + ofchip = &chip->mmchip.of_gc; + + ofchip->gpio_cells = 2; + ofchip->gc.ngpio = 32; + ofchip->gc.direction_input = mpc52xx_simple_gpio_dir_in; + ofchip->gc.direction_output = mpc52xx_simple_gpio_dir_out; + ofchip->gc.get = mpc52xx_simple_gpio_get; + ofchip->gc.set = mpc52xx_simple_gpio_set; + + ret = of_mm_gpiochip_add(ofdev->node, &chip->mmchip); + if (ret) + return ret; + + regs = chip->mmchip.regs; + chip->shadow_gpioe = in_be32(®s->simple_gpioe); + chip->shadow_ddr = in_be32(®s->simple_ddr); + chip->shadow_dvo = in_be32(®s->simple_dvo); + + return 0; +} + +static const struct of_device_id mpc52xx_simple_gpiochip_match[] = { + { + .compatible = "fsl,mpc5200-gpio", + }, + {} +}; + +static struct of_platform_driver mpc52xx_simple_gpiochip_driver = { + .name = "gpio", + .match_table = mpc52xx_simple_gpiochip_match, + .probe = mpc52xx_simple_gpiochip_probe, + .remove = mpc52xx_gpiochip_remove, +}; + +/* + * GPIO LIB API implementation for gpt GPIOs. + * + * Each gpt only has a single GPIO. + */ +static int mpc52xx_gpt_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpt __iomem *regs = mm_gc->regs; + unsigned int ret; + + return (in_be32(®s->status) & (1 << (31 - 23))) ? 1 : 0; + + return ret; +} + +static void +mpc52xx_gpt_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpt __iomem *regs = mm_gc->regs; + + if (val) + out_be32(®s->mode, 0x34); + else + out_be32(®s->mode, 0x24); + + pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); +} + +static int mpc52xx_gpt_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct mpc52xx_gpt *regs = mm_gc->regs; + + out_be32(®s->mode, 0x04); + + return 0; +} + +static int +mpc52xx_gpt_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + mpc52xx_gpt_gpio_set(gc, gpio, val); + pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); + + return 0; +} + +static int __devinit mpc52xx_gpt_gpiochip_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct of_mm_gpio_chip *mmchip; + struct of_gpio_chip *chip; + + mmchip = kzalloc(sizeof(*mmchip), GFP_KERNEL); + if (!mmchip) + return -ENOMEM; + + chip = &mmchip->of_gc; + + chip->gpio_cells = 2; + chip->gc.ngpio = 1; + chip->gc.direction_input = mpc52xx_gpt_gpio_dir_in; + chip->gc.direction_output = mpc52xx_gpt_gpio_dir_out; + chip->gc.get = mpc52xx_gpt_gpio_get; + chip->gc.set = mpc52xx_gpt_gpio_set; + + return of_mm_gpiochip_add(ofdev->node, mmchip); +} + +static const struct of_device_id mpc52xx_gpt_gpiochip_match[] = { + { + .compatible = "fsl,mpc5200-gpt-gpio", + }, + {} +}; + +static struct of_platform_driver mpc52xx_gpt_gpiochip_driver = { + .name = "gpio_gpt", + .match_table = mpc52xx_gpt_gpiochip_match, + .probe = mpc52xx_gpt_gpiochip_probe, + .remove = mpc52xx_gpiochip_remove, +}; + +static int __init mpc52xx_gpio_init(void) +{ + if (of_register_platform_driver(&mpc52xx_wkup_gpiochip_driver)) + printk(KERN_ERR "Unable to register wakeup GPIO driver\n"); + + if (of_register_platform_driver(&mpc52xx_simple_gpiochip_driver)) + printk(KERN_ERR "Unable to register simple GPIO driver\n"); + + if (of_register_platform_driver(&mpc52xx_gpt_gpiochip_driver)) + printk(KERN_ERR "Unable to register gpt GPIO driver\n"); + + return 0; +} + + +/* Make sure we get initialised before anyone else tries to use us */ +subsys_initcall(mpc52xx_gpio_init); + +/* No exit call at the moment as we cannot unregister of gpio chips */ + +MODULE_DESCRIPTION("Freescale MPC52xx gpio driver"); +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de"); +MODULE_LICENSE("GPL v2"); + diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c index d0dead8b9a95..8479394e9ab4 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pic.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c @@ -18,6 +18,7 @@ #undef DEBUG +#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/of.h> #include <asm/io.h> @@ -109,11 +110,48 @@ static void mpc52xx_extirq_ack(unsigned int virq) io_be_setbit(&intr->ctrl, 27-l2irq); } +static int mpc52xx_extirq_set_type(unsigned int virq, unsigned int flow_type) +{ + u32 ctrl_reg, type; + int irq; + int l2irq; + + irq = irq_map[virq].hwirq; + l2irq = (irq & MPC52xx_IRQ_L2_MASK) >> MPC52xx_IRQ_L2_OFFSET; + + pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__, irq, l2irq, flow_type); + + switch (flow_type) { + case IRQF_TRIGGER_HIGH: + type = 0; + break; + case IRQF_TRIGGER_RISING: + type = 1; + break; + case IRQF_TRIGGER_FALLING: + type = 2; + break; + case IRQF_TRIGGER_LOW: + type = 3; + break; + default: + type = 0; + } + + ctrl_reg = in_be32(&intr->ctrl); + ctrl_reg &= ~(0x3 << (22 - (l2irq * 2))); + ctrl_reg |= (type << (22 - (l2irq * 2))); + out_be32(&intr->ctrl, ctrl_reg); + + return 0; +} + static struct irq_chip mpc52xx_extirq_irqchip = { .typename = " MPC52xx IRQ[0-3] ", .mask = mpc52xx_extirq_mask, .unmask = mpc52xx_extirq_unmask, .ack = mpc52xx_extirq_ack, + .set_type = mpc52xx_extirq_set_type, }; /* diff --git a/arch/powerpc/platforms/cell/spufs/.gitignore b/arch/powerpc/platforms/cell/spufs/.gitignore new file mode 100644 index 000000000000..a09ee8d84d6c --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/.gitignore @@ -0,0 +1,2 @@ +spu_save_dump.h +spu_restore_dump.h diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 0ad83aeb70b1..177735f79317 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -78,6 +78,7 @@ void destroy_spu_context(struct kref *kref) { struct spu_context *ctx; ctx = container_of(kref, struct spu_context, kref); + spu_context_nospu_trace(destroy_spu_context__enter, ctx); mutex_lock(&ctx->state_mutex); spu_deactivate(ctx); mutex_unlock(&ctx->state_mutex); @@ -88,6 +89,7 @@ void destroy_spu_context(struct kref *kref) kref_put(ctx->prof_priv_kref, ctx->prof_priv_release); BUG_ON(!list_empty(&ctx->rq)); atomic_dec(&nr_spu_contexts); + kfree(ctx->switch_log); kfree(ctx); } @@ -150,6 +152,8 @@ int spu_acquire_saved(struct spu_context *ctx) { int ret; + spu_context_nospu_trace(spu_acquire_saved__enter, ctx); + ret = spu_acquire(ctx); if (ret) return ret; diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 08f44d1971ac..80911a373400 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -2386,6 +2386,171 @@ static const struct file_operations spufs_stat_fops = { .release = single_release, }; +static inline int spufs_switch_log_used(struct spu_context *ctx) +{ + return (ctx->switch_log->head - ctx->switch_log->tail) % + SWITCH_LOG_BUFSIZE; +} + +static inline int spufs_switch_log_avail(struct spu_context *ctx) +{ + return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); +} + +static int spufs_switch_log_open(struct inode *inode, struct file *file) +{ + struct spu_context *ctx = SPUFS_I(inode)->i_ctx; + + /* + * We (ab-)use the mapping_lock here because it serves the similar + * purpose for synchronizing open/close elsewhere. Maybe it should + * be renamed eventually. + */ + mutex_lock(&ctx->mapping_lock); + if (ctx->switch_log) { + spin_lock(&ctx->switch_log->lock); + ctx->switch_log->head = 0; + ctx->switch_log->tail = 0; + spin_unlock(&ctx->switch_log->lock); + } else { + /* + * We allocate the switch log data structures on first open. + * They will never be free because we assume a context will + * be traced until it goes away. + */ + ctx->switch_log = kzalloc(sizeof(struct switch_log) + + SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry), + GFP_KERNEL); + if (!ctx->switch_log) + goto out; + spin_lock_init(&ctx->switch_log->lock); + init_waitqueue_head(&ctx->switch_log->wait); + } + mutex_unlock(&ctx->mapping_lock); + + return 0; + out: + mutex_unlock(&ctx->mapping_lock); + return -ENOMEM; +} + +static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) +{ + struct switch_log_entry *p; + + p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE; + + return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n", + (unsigned int) p->tstamp.tv_sec, + (unsigned int) p->tstamp.tv_nsec, + p->spu_id, + (unsigned int) p->type, + (unsigned int) p->val, + (unsigned long long) p->timebase); +} + +static ssize_t spufs_switch_log_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spu_context *ctx = SPUFS_I(inode)->i_ctx; + int error = 0, cnt = 0; + + if (!buf || len < 0) + return -EINVAL; + + while (cnt < len) { + char tbuf[128]; + int width; + + if (file->f_flags & O_NONBLOCK) { + if (spufs_switch_log_used(ctx) <= 0) + return cnt ? cnt : -EAGAIN; + } else { + /* Wait for data in buffer */ + error = wait_event_interruptible(ctx->switch_log->wait, + spufs_switch_log_used(ctx) > 0); + if (error) + break; + } + + spin_lock(&ctx->switch_log->lock); + if (ctx->switch_log->head == ctx->switch_log->tail) { + /* multiple readers race? */ + spin_unlock(&ctx->switch_log->lock); + continue; + } + + width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); + if (width < len) { + ctx->switch_log->tail = + (ctx->switch_log->tail + 1) % + SWITCH_LOG_BUFSIZE; + } + + spin_unlock(&ctx->switch_log->lock); + + /* + * If the record is greater than space available return + * partial buffer (so far) + */ + if (width >= len) + break; + + error = copy_to_user(buf + cnt, tbuf, width); + if (error) + break; + cnt += width; + } + + return cnt == 0 ? error : cnt; +} + +static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spu_context *ctx = SPUFS_I(inode)->i_ctx; + unsigned int mask = 0; + + poll_wait(file, &ctx->switch_log->wait, wait); + + if (spufs_switch_log_used(ctx) > 0) + mask |= POLLIN; + + return mask; +} + +static const struct file_operations spufs_switch_log_fops = { + .owner = THIS_MODULE, + .open = spufs_switch_log_open, + .read = spufs_switch_log_read, + .poll = spufs_switch_log_poll, +}; + +void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, + u32 type, u32 val) +{ + if (!ctx->switch_log) + return; + + spin_lock(&ctx->switch_log->lock); + if (spufs_switch_log_avail(ctx) > 1) { + struct switch_log_entry *p; + + p = ctx->switch_log->log + ctx->switch_log->head; + ktime_get_ts(&p->tstamp); + p->timebase = get_tb(); + p->spu_id = spu ? spu->number : -1; + p->type = type; + p->val = val; + + ctx->switch_log->head = + (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; + } + spin_unlock(&ctx->switch_log->lock); + + wake_up(&ctx->switch_log->wait); +} struct tree_descr spufs_dir_contents[] = { { "capabilities", &spufs_caps_fops, 0444, }, @@ -2422,6 +2587,7 @@ struct tree_descr spufs_dir_contents[] = { { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, { "tid", &spufs_tid_fops, 0444, }, { "stat", &spufs_stat_fops, 0444, }, + { "switch_log", &spufs_switch_log_fops, 0444 }, {}, }; diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 96bf7c2b86fc..a9c35b7b719f 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -405,6 +405,8 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) ret = spu_run_fini(ctx, npc, &status); spu_yield(ctx); + spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, status); + if ((status & SPU_STATUS_STOPPED_BY_STOP) && (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) ctx->stats.libassist++; diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 45dcd2693502..7298e7db2c83 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -240,6 +240,7 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) spu->mfc_callback = spufs_mfc_callback; mb(); spu_unmap_mappings(ctx); + spu_switch_log_notify(spu, ctx, SWITCH_LOG_START, 0); spu_restore(&ctx->csa, spu); spu->timestamp = jiffies; spu_cpu_affinity_set(spu, raw_smp_processor_id()); @@ -419,6 +420,7 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); spu_save(&ctx->csa, spu); + spu_switch_log_notify(spu, ctx, SWITCH_LOG_STOP, 0); spu->timestamp = jiffies; ctx->state = SPU_STATE_SAVED; spu->ibox_callback = NULL; @@ -591,7 +593,7 @@ static struct spu *find_victim(struct spu_context *ctx) struct spu *spu; int node, n; - spu_context_nospu_trace(spu_find_vitim__enter, ctx); + spu_context_nospu_trace(spu_find_victim__enter, ctx); /* * Look for a possible preemption candidate on the local node first. diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index cdc515182f82..7312745b7540 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -47,6 +47,30 @@ enum { SPU_SCHED_SPU_RUN, /* context is within spu_run */ }; +enum { + SWITCH_LOG_BUFSIZE = 4096, +}; + +enum { + SWITCH_LOG_START, + SWITCH_LOG_STOP, + SWITCH_LOG_EXIT, +}; + +struct switch_log { + spinlock_t lock; + wait_queue_head_t wait; + unsigned long head; + unsigned long tail; + struct switch_log_entry { + struct timespec tstamp; + s32 spu_id; + u32 type; + u32 val; + u64 timebase; + } log[]; +}; + struct spu_context { struct spu *spu; /* pointer to a physical SPU */ struct spu_state csa; /* SPU context save area. */ @@ -116,6 +140,9 @@ struct spu_context { unsigned long long libassist; } stats; + /* context switch log */ + struct switch_log *switch_log; + struct list_head aff_list; int aff_head; int aff_offset; @@ -256,6 +283,8 @@ int spu_activate(struct spu_context *ctx, unsigned long flags); void spu_deactivate(struct spu_context *ctx); void spu_yield(struct spu_context *ctx); void spu_switch_notify(struct spu *spu, struct spu_context *ctx); +void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, + u32 type, u32 val); void spu_set_timeslice(struct spu_context *ctx); void spu_update_sched_info(struct spu_context *ctx); void __spu_update_sched_info(struct spu_context *ctx); @@ -330,8 +359,8 @@ extern void spuctx_switch_state(struct spu_context *ctx, enum spu_utilization_state new_state); #define spu_context_trace(name, ctx, spu) \ - trace_mark(name, "%p %p", ctx, spu); + trace_mark(name, "ctx %p spu %p", ctx, spu); #define spu_context_nospu_trace(name, ctx) \ - trace_mark(name, "%p", ctx); + trace_mark(name, "ctx %p", ctx); #endif diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c index aea5286f1245..53202422ba72 100644 --- a/arch/powerpc/platforms/cell/spufs/sputrace.c +++ b/arch/powerpc/platforms/cell/spufs/sputrace.c @@ -171,24 +171,24 @@ static void spu_context_nospu_event(void *probe_private, void *call_data, } struct spu_probe spu_probes[] = { - { "spu_bind_context__enter", "%p %p", spu_context_event }, - { "spu_unbind_context__enter", "%p %p", spu_context_event }, - { "spu_get_idle__enter", "%p", spu_context_nospu_event }, - { "spu_get_idle__found", "%p %p", spu_context_event }, - { "spu_get_idle__not_found", "%p", spu_context_nospu_event }, - { "spu_find_victim__enter", "%p", spu_context_nospu_event }, - { "spusched_tick__preempt", "%p %p", spu_context_event }, - { "spusched_tick__newslice", "%p", spu_context_nospu_event }, - { "spu_yield__enter", "%p", spu_context_nospu_event }, - { "spu_deactivate__enter", "%p", spu_context_nospu_event }, - { "__spu_deactivate__unload", "%p %p", spu_context_event }, - { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event }, - { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event }, - { "spufs_ps_nopfn__wake", "%p %p", spu_context_event }, - { "spufs_ps_nopfn__insert", "%p %p", spu_context_event }, - { "spu_acquire_saved__enter", "%p", spu_context_nospu_event }, - { "destroy_spu_context__enter", "%p", spu_context_nospu_event }, - { "spufs_stop_callback__enter", "%p %p", spu_context_event }, + { "spu_bind_context__enter", "ctx %p spu %p", spu_context_event }, + { "spu_unbind_context__enter", "ctx %p spu %p", spu_context_event }, + { "spu_get_idle__enter", "ctx %p", spu_context_nospu_event }, + { "spu_get_idle__found", "ctx %p spu %p", spu_context_event }, + { "spu_get_idle__not_found", "ctx %p", spu_context_nospu_event }, + { "spu_find_victim__enter", "ctx %p", spu_context_nospu_event }, + { "spusched_tick__preempt", "ctx %p spu %p", spu_context_event }, + { "spusched_tick__newslice", "ctx %p", spu_context_nospu_event }, + { "spu_yield__enter", "ctx %p", spu_context_nospu_event }, + { "spu_deactivate__enter", "ctx %p", spu_context_nospu_event }, + { "__spu_deactivate__unload", "ctx %p spu %p", spu_context_event }, + { "spufs_ps_nopfn__enter", "ctx %p", spu_context_nospu_event }, + { "spufs_ps_nopfn__sleep", "ctx %p", spu_context_nospu_event }, + { "spufs_ps_nopfn__wake", "ctx %p spu %p", spu_context_event }, + { "spufs_ps_nopfn__insert", "ctx %p spu %p", spu_context_event }, + { "spu_acquire_saved__enter", "ctx %p", spu_context_nospu_event }, + { "destroy_spu_context__enter", "ctx %p", spu_context_nospu_event }, + { "spufs_stop_callback__enter", "ctx %p spu %p", spu_context_event }, }; static int __init sputrace_init(void) |