From 3a7a7176e840f448aae929f7761ea80cf892c665 Mon Sep 17 00:00:00 2001 From: Kyle Moffett Date: Thu, 22 Dec 2011 10:19:09 +0000 Subject: powerpc/mpic: Fix use of "flags" variable in mpic_alloc() The mpic_alloc() function takes a "flags" parameter and assigns it into the mpic->flags variable fairly early on, but several later pieces of code detect various device-tree properties and save them into the "mpic->flags" variable (EG: "big-endian" => MPIC_BIG_ENDIAN). Unfortunately, a number of codepaths (including several which test the flag MPIC_BIG_ENDIAN!) test "flags" instead of "mpic->flags", and get wrong answers as a result. Consolidate the device-tree flag tests early in mpic_alloc() and change all of the checks after "mpic->flags" is init'ed to use "mpic->flags". [BenH: Fixed up use of mpic->node before it's initialized] Signed-off-by: Kyle Moffett Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 4e9ccb1..9deec44 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1182,6 +1182,14 @@ struct mpic * __init mpic_alloc(struct device_node *node, } } + /* Read extra device-tree properties into the flags variable */ + if (of_get_property(node, "big-endian", NULL)) + flags |= MPIC_BIG_ENDIAN; + if (of_get_property(node, "pic-no-reset", NULL)) + flags |= MPIC_NO_RESET; + if (of_device_is_compatible(node, "fsl,mpic")) + flags |= MPIC_FSL; + mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL); if (mpic == NULL) goto err_of_node_put; @@ -1189,15 +1197,16 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->name = name; mpic->node = node; mpic->paddr = phys_addr; + mpic->flags = flags; mpic->hc_irq = mpic_irq_chip; mpic->hc_irq.name = name; - if (!(flags & MPIC_SECONDARY)) + if (!(mpic->flags & MPIC_SECONDARY)) mpic->hc_irq.irq_set_affinity = mpic_set_affinity; #ifdef CONFIG_MPIC_U3_HT_IRQS mpic->hc_ht_irq = mpic_irq_ht_chip; mpic->hc_ht_irq.name = name; - if (!(flags & MPIC_SECONDARY)) + if (!(mpic->flags & MPIC_SECONDARY)) mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity; #endif /* CONFIG_MPIC_U3_HT_IRQS */ @@ -1209,12 +1218,11 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->hc_tm = mpic_tm_chip; mpic->hc_tm.name = name; - mpic->flags = flags; mpic->isu_size = isu_size; mpic->irq_count = irq_count; mpic->num_sources = 0; /* so far */ - if (flags & MPIC_LARGE_VECTORS) + if (mpic->flags & MPIC_LARGE_VECTORS) intvec_top = 2047; else intvec_top = 255; @@ -1233,12 +1241,6 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->ipi_vecs[3] = intvec_top - 1; mpic->spurious_vec = intvec_top; - /* Check for "big-endian" in device-tree */ - if (of_get_property(mpic->node, "big-endian", NULL) != NULL) - mpic->flags |= MPIC_BIG_ENDIAN; - if (of_device_is_compatible(mpic->node, "fsl,mpic")) - mpic->flags |= MPIC_FSL; - /* Look for protected sources */ psrc = of_get_property(mpic->node, "protected-sources", &psize); if (psrc) { @@ -1254,11 +1256,11 @@ struct mpic * __init mpic_alloc(struct device_node *node, } #ifdef CONFIG_MPIC_WEIRD - mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)]; + mpic->hw_set = mpic_infos[MPIC_GET_REGSET(mpic->flags)]; #endif /* default register type */ - if (flags & MPIC_BIG_ENDIAN) + if (mpic->flags & MPIC_BIG_ENDIAN) mpic->reg_type = mpic_access_mmio_be; else mpic->reg_type = mpic_access_mmio_le; @@ -1268,10 +1270,10 @@ struct mpic * __init mpic_alloc(struct device_node *node, * only if the kernel includes DCR support. */ #ifdef CONFIG_PPC_DCR - if (flags & MPIC_USES_DCR) + if (mpic->flags & MPIC_USES_DCR) mpic->reg_type = mpic_access_dcr; #else - BUG_ON(flags & MPIC_USES_DCR); + BUG_ON(mpic->flags & MPIC_USES_DCR); #endif /* Map the global registers */ @@ -1283,10 +1285,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, /* When using a device-node, reset requests are only honored if the MPIC * is allowed to reset. */ - if (of_get_property(mpic->node, "pic-no-reset", NULL)) - mpic->flags |= MPIC_NO_RESET; - - if ((flags & MPIC_WANTS_RESET) && !(mpic->flags & MPIC_NO_RESET)) { + if ((mpic->flags & MPIC_WANTS_RESET) && !(mpic->flags & MPIC_NO_RESET)) { printk(KERN_DEBUG "mpic: Resetting\n"); mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) @@ -1297,12 +1296,12 @@ struct mpic * __init mpic_alloc(struct device_node *node, } /* CoreInt */ - if (flags & MPIC_ENABLE_COREINT) + if (mpic->flags & MPIC_ENABLE_COREINT) mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_COREINT); - if (flags & MPIC_ENABLE_MCK) + if (mpic->flags & MPIC_ENABLE_MCK) mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_MCK); @@ -1313,7 +1312,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, */ greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); if (isu_size == 0) { - if (flags & MPIC_BROKEN_FRR_NIRQS) + if (mpic->flags & MPIC_BROKEN_FRR_NIRQS) mpic->num_sources = mpic->irq_count; else mpic->num_sources = @@ -1347,8 +1346,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->irqhost = irq_alloc_host(mpic->node, IRQ_HOST_MAP_LINEAR, isu_size ? isu_size : mpic->num_sources, - &mpic_host_ops, - flags & MPIC_LARGE_VECTORS ? 2048 : 256); + &mpic_host_ops, intvec_top + 1); /* * FIXME: The code leaks the MPIC object and mappings here; this @@ -1383,7 +1381,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->next = mpics; mpics = mpic; - if (!(flags & MPIC_SECONDARY)) { + if (!(mpic->flags & MPIC_SECONDARY)) { mpic_primary = mpic; irq_set_default_host(mpic->irqhost); } -- cgit v0.10.2 From 98cca250aecaf3f1b2fec003e1c0ce0bfaa4be36 Mon Sep 17 00:00:00 2001 From: Kyle Moffett Date: Thu, 22 Dec 2011 10:19:10 +0000 Subject: fsl/mpic: Document and use the "big-endian" device-tree flag The MPIC code checks for a "big-endian" property and sets the flag MPIC_BIG_ENDIAN if one is present, although prior to the "mpic->flags" fixup that would never have worked anways. Unfortunately, even now that it works properly, the Freescale mpic device-node (the "PowerQUICC-III"-compatible one) does not specify it, so all of the board ports need to manually pass it to mpic_alloc(). Document the flag and add it to the pq3 device tree. Existing code will still need to pass the MPIC_BIG_ENDIAN flag because their dtb may not have this property, but new platforms shouldn't need to do so. Signed-off-by: Kyle Moffett Signed-off-by: Benjamin Herrenschmidt diff --git a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt index 2cf38bd..ebafba2 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt @@ -56,7 +56,14 @@ PROPERTIES to the client. The presence of this property also mandates that any initialization related to interrupt sources shall be limited to sources explicitly referenced in the device tree. - + + - big-endian + Usage: optional + Value type: + If present the MPIC will be assumed to be big-endian. Some + device-trees omit this property on MPIC nodes even when the MPIC is + in fact big-endian, so certain boards override this property. + INTERRUPT SPECIFIER DEFINITION Interrupt specifiers consists of 4 cells encoded as diff --git a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi index 5c80460..47f2b67 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi @@ -39,6 +39,7 @@ mpic: pic@40000 { reg = <0x40000 0x40000>; compatible = "fsl,mpic"; device_type = "open-pic"; + big-endian; }; timer@41100 { -- cgit v0.10.2 From 9ca163c8602681ad098910f48f89b97f0cb87c4f Mon Sep 17 00:00:00 2001 From: Kyle Moffett Date: Thu, 22 Dec 2011 10:19:11 +0000 Subject: fsl/mpic: Create and document the "single-cpu-affinity" device-tree flag The Freescale MPIC (and perhaps others in the future) is incapable of routing non-IPI interrupts to more than once CPU at a time. Currently all of the Freescale boards msut pass the MPIC_SINGLE_DEST_CPU flag to mpic_alloc(), but that information should really be present in the device-tree. Older board code can't rely on the device-tree having the property set, but newer platforms won't need it manually specified in the code. [BenH: Remove unrelated changes, folded in a different patch] Signed-off-by: Kyle Moffett Signed-off-by: Benjamin Herrenschmidt diff --git a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt index ebafba2..b393ccf 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt @@ -64,6 +64,12 @@ PROPERTIES device-trees omit this property on MPIC nodes even when the MPIC is in fact big-endian, so certain boards override this property. + - single-cpu-affinity + Usage: optional + Value type: + If present the MPIC will be assumed to only be able to route + non-IPI interrupts to a single CPU at a time (EG: Freescale MPIC). + INTERRUPT SPECIFIER DEFINITION Interrupt specifiers consists of 4 cells encoded as diff --git a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi index 47f2b67..658bd81 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi @@ -40,6 +40,7 @@ mpic: pic@40000 { compatible = "fsl,mpic"; device_type = "open-pic"; big-endian; + single-cpu-affinity; }; timer@41100 { diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 9deec44..c297a52 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1187,6 +1187,8 @@ struct mpic * __init mpic_alloc(struct device_node *node, flags |= MPIC_BIG_ENDIAN; if (of_get_property(node, "pic-no-reset", NULL)) flags |= MPIC_NO_RESET; + if (of_get_property(node, "single-cpu-affinity", NULL)) + flags |= MPIC_SINGLE_DEST_CPU; if (of_device_is_compatible(node, "fsl,mpic")) flags |= MPIC_FSL; -- cgit v0.10.2 From 5019609fce965dbdc66a7d947385fe92ca522231 Mon Sep 17 00:00:00 2001 From: Kyle Moffett Date: Thu, 22 Dec 2011 10:19:12 +0000 Subject: powerpc/mpic: Remove MPIC_BROKEN_FRR_NIRQS and duplicate irq_count The mpic->irq_count variable is only used as a software error-checking limit to determine whether or not an IRQ number is valid. In board code which does not manually specify an IRQ count to mpic_alloc(), i.e. 0, it is automatically detected from the number of ISUs and the ISU size. In practice, all hardware ends up with irq_count == num_sources, so all of the runtime checks on mpic->irq_count should just check the value of mpic->num_sources instead. When platform hardware does not correctly report the number of IRQs, which only happens on the MPC85xx/MPC86xx, the MPIC_BROKEN_FRR_NIRQS flag is used to override the detected value of num_sources with the manual irq_count parameter. Since there's no need to manually specify the number of IRQs except in this case, the extra flag can be eliminated and the test changed to "irq_count != 0". Signed-off-by: Kyle Moffett Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index 67b4d98..2ebac31 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -273,7 +273,6 @@ struct mpic unsigned int isu_size; unsigned int isu_shift; unsigned int isu_mask; - unsigned int irq_count; /* Number of sources */ unsigned int num_sources; /* default senses array */ @@ -363,8 +362,6 @@ struct mpic #define MPIC_ENABLE_MCK 0x00000200 /* Disable bias among target selection, spread interrupts evenly */ #define MPIC_NO_BIAS 0x00000400 -/* Ignore NIRQS as reported by FRR */ -#define MPIC_BROKEN_FRR_NIRQS 0x00000800 /* Destination only supports a single CPU at a time */ #define MPIC_SINGLE_DEST_CPU 0x00001000 /* Enable CoreInt delivery of interrupts */ diff --git a/arch/powerpc/platforms/85xx/corenet_ds.c b/arch/powerpc/platforms/85xx/corenet_ds.c index 07e3e6c..768479b 100644 --- a/arch/powerpc/platforms/85xx/corenet_ds.c +++ b/arch/powerpc/platforms/85xx/corenet_ds.c @@ -36,8 +36,7 @@ void __init corenet_ds_pic_init(void) { struct mpic *mpic; - unsigned int flags = MPIC_BIG_ENDIAN | - MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU; + unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU; if (ppc_md.get_irq == mpic_get_coreint_irq) flags |= MPIC_ENABLE_COREINT; diff --git a/arch/powerpc/platforms/85xx/mpc8536_ds.c b/arch/powerpc/platforms/85xx/mpc8536_ds.c index cf26682..d5373e0 100644 --- a/arch/powerpc/platforms/85xx/mpc8536_ds.c +++ b/arch/powerpc/platforms/85xx/mpc8536_ds.c @@ -38,7 +38,7 @@ void __init mpc8536_ds_pic_init(void) { struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS, + MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c index eefbb91..528b9a0 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c @@ -72,13 +72,13 @@ void __init mpc85xx_ds_pic_init(void) if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS-CAMP")) { mpic = mpic_alloc(NULL, 0, - MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); } else { mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); } diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 1d15a0c..a0cb770 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -436,7 +436,7 @@ static void __init mpc85xx_mds_pic_init(void) { struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | - MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, + MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index ccf520e..a23f6f3 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -49,13 +49,13 @@ void __init mpc85xx_rdb_pic_init(void) if (of_flat_dt_is_compatible(root, "fsl,MPC85XXRDB-CAMP")) { mpic = mpic_alloc(NULL, 0, - MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); } else { mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); } diff --git a/arch/powerpc/platforms/85xx/p1010rdb.c b/arch/powerpc/platforms/85xx/p1010rdb.c index 538bc3f..8650773 100644 --- a/arch/powerpc/platforms/85xx/p1010rdb.c +++ b/arch/powerpc/platforms/85xx/p1010rdb.c @@ -34,7 +34,7 @@ void __init p1010_rdb_pic_init(void) { struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | - MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, + MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index b0984ad..28266ab 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -244,7 +244,7 @@ void __init p1022_ds_pic_init(void) { struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/85xx/p1023_rds.c b/arch/powerpc/platforms/85xx/p1023_rds.c index d951e70..b1cd6a2 100644 --- a/arch/powerpc/platforms/85xx/p1023_rds.c +++ b/arch/powerpc/platforms/85xx/p1023_rds.c @@ -95,7 +95,7 @@ static void __init mpc85xx_rds_pic_init(void) { struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | - MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, + MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/85xx/xes_mpc85xx.c b/arch/powerpc/platforms/85xx/xes_mpc85xx.c index 3a69f8b..1ab4e02 100644 --- a/arch/powerpc/platforms/85xx/xes_mpc85xx.c +++ b/arch/powerpc/platforms/85xx/xes_mpc85xx.c @@ -45,7 +45,7 @@ void __init xes_mpc85xx_pic_init(void) { struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS, + MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/86xx/pic.c b/arch/powerpc/platforms/86xx/pic.c index 52bbfa0..27959d6 100644 --- a/arch/powerpc/platforms/86xx/pic.c +++ b/arch/powerpc/platforms/86xx/pic.c @@ -39,7 +39,7 @@ void __init mpc86xx_init_irq(void) struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | - MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, + MPIC_SINGLE_DEST_CPU, 0, 256, " MPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c index 9cfcf20..d7727de 100644 --- a/arch/powerpc/platforms/embedded6xx/holly.c +++ b/arch/powerpc/platforms/embedded6xx/holly.c @@ -157,8 +157,7 @@ static void __init holly_init_IRQ(void) mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_WANTS_RESET | MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108, - 24, - NR_IRQS-4, /* num_sources used */ + 24, 0, "Tsi108_PIC"); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c index bcfad92..9479f4e 100644 --- a/arch/powerpc/platforms/embedded6xx/linkstation.c +++ b/arch/powerpc/platforms/embedded6xx/linkstation.c @@ -83,7 +83,7 @@ static void __init linkstation_init_IRQ(void) struct mpic *mpic; mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET, - 4, 32, " EPIC "); + 4, 0, " EPIC "); BUG_ON(mpic == NULL); /* PCI IRQs */ diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index f3350d7..90acb54 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -111,8 +111,7 @@ static void __init mpc7448_hpc2_init_IRQ(void) mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_WANTS_RESET | MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108, - 24, - NR_IRQS-4, /* num_sources used */ + 24, 0, "Tsi108_PIC"); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/embedded6xx/storcenter.c b/arch/powerpc/platforms/embedded6xx/storcenter.c index afa6388..451dce4 100644 --- a/arch/powerpc/platforms/embedded6xx/storcenter.c +++ b/arch/powerpc/platforms/embedded6xx/storcenter.c @@ -85,7 +85,7 @@ static void __init storcenter_init_IRQ(void) struct mpic *mpic; mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET, - 16, 32, " OpenPIC "); + 16, 0, " OpenPIC "); BUG_ON(mpic == NULL); /* diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index f79f127..6d0a5df 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -190,9 +190,7 @@ static void __init pseries_mpic_init_IRQ(void) BUG_ON(openpic_addr == 0); /* Setup the openpic driver */ - mpic = mpic_alloc(pSeries_mpic_node, openpic_addr, 0, - 16, 250, /* isu size, irq count */ - " MPIC "); + mpic = mpic_alloc(pSeries_mpic_node, openpic_addr, 0, 16, 0, " MPIC "); BUG_ON(mpic == NULL); /* Add ISUs */ diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index c297a52..cbffeb7 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -873,7 +873,7 @@ int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n", mpic, d->irq, src, flow_type); - if (src >= mpic->irq_count) + if (src >= mpic->num_sources) return -EINVAL; if (flow_type == IRQ_TYPE_NONE) @@ -909,7 +909,7 @@ void mpic_set_vector(unsigned int virq, unsigned int vector) DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n", mpic, virq, src, vector); - if (src >= mpic->irq_count) + if (src >= mpic->num_sources) return; vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); @@ -926,7 +926,7 @@ void mpic_set_destination(unsigned int virq, unsigned int cpuid) DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n", mpic, virq, src, cpuid); - if (src >= mpic->irq_count) + if (src >= mpic->num_sources) return; mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); @@ -1006,7 +1006,7 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, return 0; } - if (hw >= mpic->irq_count) + if (hw >= mpic->num_sources) return -EINVAL; mpic_msi_reserve_hwirq(mpic, hw); @@ -1221,7 +1221,6 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->hc_tm.name = name; mpic->isu_size = isu_size; - mpic->irq_count = irq_count; mpic->num_sources = 0; /* so far */ if (mpic->flags & MPIC_LARGE_VECTORS) @@ -1314,8 +1313,8 @@ struct mpic * __init mpic_alloc(struct device_node *node, */ greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); if (isu_size == 0) { - if (mpic->flags & MPIC_BROKEN_FRR_NIRQS) - mpic->num_sources = mpic->irq_count; + if (irq_count) + mpic->num_sources = irq_count; else mpic->num_sources = ((greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) @@ -1450,10 +1449,6 @@ void __init mpic_init(struct mpic *mpic) (mpic->ipi_vecs[0] + i)); } - /* Initialize interrupt sources */ - if (mpic->irq_count == 0) - mpic->irq_count = mpic->num_sources; - /* Do the HT PIC fixups on U3 broken mpic */ DBG("MPIC flags: %x\n", mpic->flags); if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) { diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c index 0f67cd7..cfe3947 100644 --- a/arch/powerpc/sysdev/mpic_msi.c +++ b/arch/powerpc/sysdev/mpic_msi.c @@ -54,7 +54,7 @@ static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) for (i = 100; i < 105; i++) msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i); - for (i = 124; i < mpic->irq_count; i++) + for (i = 124; i < mpic->num_sources; i++) msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i); @@ -83,7 +83,7 @@ int mpic_msi_init_allocator(struct mpic *mpic) { int rc; - rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->irq_count, + rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->num_sources, mpic->irqhost->of_node); if (rc) return rc; -- cgit v0.10.2 From c1b8d45db4dbc64cc6015f97922f767fdf782f64 Mon Sep 17 00:00:00 2001 From: Kyle Moffett Date: Thu, 22 Dec 2011 10:19:13 +0000 Subject: powerpc/mpic: Add "last-interrupt-source" property to override hardware The FreeScale PowerQUICC-III-compatible (mpc85xx/mpc86xx) MPICs do not correctly report the number of hardware interrupt sources, so software needs to override the detected value with "256". To avoid needing to write custom board-specific code to detect that scenario, allow it to be easily overridden in the device-tree. Signed-off-by: Kyle Moffett Signed-off-by: Benjamin Herrenschmidt diff --git a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt index b393ccf..dc57446 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/mpic.txt @@ -70,6 +70,13 @@ PROPERTIES If present the MPIC will be assumed to only be able to route non-IPI interrupts to a single CPU at a time (EG: Freescale MPIC). + - last-interrupt-source + Usage: optional + Value type: + Some MPICs do not correctly report the number of hardware sources + in the global feature registers. If specified, this field will + override the value read from MPIC_GREG_FEATURE_LAST_SRC. + INTERRUPT SPECIFIER DEFINITION Interrupt specifiers consists of 4 cells encoded as diff --git a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi index 658bd81..fdedf7b 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-mpic.dtsi @@ -41,6 +41,7 @@ mpic: pic@40000 { device_type = "open-pic"; big-endian; single-cpu-affinity; + last-interrupt-source = <255>; }; timer@41100 { diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index cbffeb7..90171d4 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1149,6 +1149,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, u32 greg_feature; const char *vers; const u32 *psrc; + u32 last_irq; /* Default MPIC search parameters */ static const struct of_device_id __initconst mpic_device_id[] = { @@ -1220,7 +1221,6 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->hc_tm = mpic_tm_chip; mpic->hc_tm.name = name; - mpic->isu_size = isu_size; mpic->num_sources = 0; /* so far */ if (mpic->flags & MPIC_LARGE_VECTORS) @@ -1308,20 +1308,6 @@ struct mpic * __init mpic_alloc(struct device_node *node, | MPIC_GREG_GCONF_MCK); /* - * Read feature register. For non-ISU MPICs, num sources as well. On - * ISU MPICs, sources are counted as ISUs are added - */ - greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); - if (isu_size == 0) { - if (irq_count) - mpic->num_sources = irq_count; - else - mpic->num_sources = - ((greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) - >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1; - } - - /* * The MPIC driver will crash if there are more cores than we * can initialize, so we may as well catch that problem here. */ @@ -1336,18 +1322,38 @@ struct mpic * __init mpic_alloc(struct device_node *node, 0x1000); } + /* + * Read feature register. For non-ISU MPICs, num sources as well. On + * ISU MPICs, sources are counted as ISUs are added + */ + greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); + + /* + * By default, the last source number comes from the MPIC, but the + * device-tree and board support code can override it on buggy hw. + */ + last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) + >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; + of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq); + if (irq_count) + last_irq = irq_count - 1; + /* Initialize main ISU if none provided */ - if (mpic->isu_size == 0) { - mpic->isu_size = mpic->num_sources; + if (!isu_size) { + isu_size = last_irq + 1; + mpic->num_sources = isu_size; mpic_map(mpic, mpic->paddr, &mpic->isus[0], - MPIC_INFO(IRQ_BASE), MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); + MPIC_INFO(IRQ_BASE), + MPIC_INFO(IRQ_STRIDE) * isu_size); } + + mpic->isu_size = isu_size; mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); mpic->isu_mask = (1 << mpic->isu_shift) - 1; mpic->irqhost = irq_alloc_host(mpic->node, IRQ_HOST_MAP_LINEAR, - isu_size ? isu_size : mpic->num_sources, - &mpic_host_ops, intvec_top + 1); + mpic->isu_size, &mpic_host_ops, + intvec_top + 1); /* * FIXME: The code leaks the MPIC object and mappings here; this -- cgit v0.10.2 From e55d7f737d3daf4aaf41945c1829138c608662e9 Mon Sep 17 00:00:00 2001 From: Kyle Moffett Date: Thu, 22 Dec 2011 10:19:14 +0000 Subject: powerpc/mpic: Remove duplicate MPIC_WANTS_RESET flag There are two separate flags controlling whether or not the MPIC is reset during initialization, which is completely unnecessary, and only one of them can be specified in the device tree. Also, most platforms in-tree right now do actually want to reset the MPIC during initialization anyways, which means lots of duplicate code passing the MPIC_WANTS_RESET flag. Fix all of the callers which currently do not pass the MPIC_WANTS_RESET flag to pass the MPIC_NO_RESET flag, then remove the MPIC_WANTS_RESET flag and make the code reset the MPIC by default. Signed-off-by: Kyle Moffett Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index 2ebac31..d7e3fec 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -348,8 +348,6 @@ struct mpic #define MPIC_U3_HT_IRQS 0x00000004 /* Broken IPI registers (autodetected) */ #define MPIC_BROKEN_IPI 0x00000008 -/* MPIC wants a reset */ -#define MPIC_WANTS_RESET 0x00000010 /* Spurious vector requires EOI */ #define MPIC_SPV_EOI 0x00000020 /* No passthrough disable */ @@ -366,9 +364,7 @@ struct mpic #define MPIC_SINGLE_DEST_CPU 0x00001000 /* Enable CoreInt delivery of interrupts */ #define MPIC_ENABLE_COREINT 0x00002000 -/* Disable resetting of the MPIC. - * NOTE: This flag trumps MPIC_WANTS_RESET. - */ +/* Do not reset the MPIC during initialization */ #define MPIC_NO_RESET 0x00004000 /* Freescale MPIC (compatible includes "fsl,mpic") */ #define MPIC_FSL 0x00008000 diff --git a/arch/powerpc/platforms/44x/currituck.c b/arch/powerpc/platforms/44x/currituck.c index 3f6229b..583e67f 100644 --- a/arch/powerpc/platforms/44x/currituck.c +++ b/arch/powerpc/platforms/44x/currituck.c @@ -83,7 +83,7 @@ static void __init ppc47x_init_irq(void) * device-tree, just pass 0 to all arguments */ struct mpic *mpic = - mpic_alloc(np, 0, 0, 0, 0, " MPIC "); + mpic_alloc(np, 0, MPIC_NO_RESET, 0, 0, " MPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); ppc_md.get_irq = mpic_get_irq; diff --git a/arch/powerpc/platforms/44x/iss4xx.c b/arch/powerpc/platforms/44x/iss4xx.c index 5b8cdbb..a28a862 100644 --- a/arch/powerpc/platforms/44x/iss4xx.c +++ b/arch/powerpc/platforms/44x/iss4xx.c @@ -71,8 +71,7 @@ static void __init iss4xx_init_irq(void) /* The MPIC driver will get everything it needs from the * device-tree, just pass 0 to all arguments */ - struct mpic *mpic = mpic_alloc(np, 0, 0, 0, 0, - " MPIC "); + struct mpic *mpic = mpic_alloc(np, 0, MPIC_NO_RESET, 0, 0, " MPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); ppc_md.get_irq = mpic_get_irq; diff --git a/arch/powerpc/platforms/85xx/corenet_ds.c b/arch/powerpc/platforms/85xx/corenet_ds.c index 768479b..df69e99 100644 --- a/arch/powerpc/platforms/85xx/corenet_ds.c +++ b/arch/powerpc/platforms/85xx/corenet_ds.c @@ -36,7 +36,8 @@ void __init corenet_ds_pic_init(void) { struct mpic *mpic; - unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU; + unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU | + MPIC_NO_RESET; if (ppc_md.get_irq == mpic_get_coreint_irq) flags |= MPIC_ENABLE_COREINT; diff --git a/arch/powerpc/platforms/85xx/ksi8560.c b/arch/powerpc/platforms/85xx/ksi8560.c index 20f75d7..60120e5 100644 --- a/arch/powerpc/platforms/85xx/ksi8560.c +++ b/arch/powerpc/platforms/85xx/ksi8560.c @@ -57,8 +57,7 @@ static void machine_restart(char *cmd) static void __init ksi8560_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/mpc8536_ds.c b/arch/powerpc/platforms/85xx/mpc8536_ds.c index d5373e0..f588726 100644 --- a/arch/powerpc/platforms/85xx/mpc8536_ds.c +++ b/arch/powerpc/platforms/85xx/mpc8536_ds.c @@ -36,9 +36,7 @@ void __init mpc8536_ds_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c index 3bebb51..d19f675 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -50,8 +50,7 @@ static int mpc85xx_exclude_device(struct pci_controller *hose, static void __init mpc85xx_ads_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 40f03da..02d97e3 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -188,8 +188,7 @@ static struct irqaction mpc85xxcds_8259_irqaction = { static void __init mpc85xx_cds_pic_init(void) { struct mpic *mpic; - mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c index 528b9a0..6e23e3e 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c @@ -72,12 +72,12 @@ void __init mpc85xx_ds_pic_init(void) if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS-CAMP")) { mpic = mpic_alloc(NULL, 0, + MPIC_NO_RESET | MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); } else { mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index a0cb770..57aceb5 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -434,8 +434,7 @@ machine_arch_initcall(p1021_mds, swiotlb_setup_bus_notifier); static void __init mpc85xx_mds_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index a23f6f3..407c739 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -48,13 +48,12 @@ void __init mpc85xx_rdb_pic_init(void) unsigned long root = of_get_flat_dt_root(); if (of_flat_dt_is_compatible(root, "fsl,MPC85XXRDB-CAMP")) { - mpic = mpic_alloc(NULL, 0, + mpic = mpic_alloc(NULL, 0, MPIC_NO_RESET | MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); } else { mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); diff --git a/arch/powerpc/platforms/85xx/p1010rdb.c b/arch/powerpc/platforms/85xx/p1010rdb.c index 8650773..d8bd656 100644 --- a/arch/powerpc/platforms/85xx/p1010rdb.c +++ b/arch/powerpc/platforms/85xx/p1010rdb.c @@ -32,8 +32,7 @@ void __init p1010_rdb_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 28266ab..63ba790 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -242,9 +242,7 @@ p1022ds_valid_monitor_port(enum fsl_diu_monitor_port port) void __init p1022_ds_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN | + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/85xx/p1023_rds.c b/arch/powerpc/platforms/85xx/p1023_rds.c index b1cd6a2..6b07398 100644 --- a/arch/powerpc/platforms/85xx/p1023_rds.c +++ b/arch/powerpc/platforms/85xx/p1023_rds.c @@ -93,8 +93,7 @@ machine_device_initcall(p1023_rds, mpc85xx_common_publish_devices); static void __init mpc85xx_rds_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " OpenPIC "); diff --git a/arch/powerpc/platforms/85xx/sbc8548.c b/arch/powerpc/platforms/85xx/sbc8548.c index 184a507..1677b8a 100644 --- a/arch/powerpc/platforms/85xx/sbc8548.c +++ b/arch/powerpc/platforms/85xx/sbc8548.c @@ -54,8 +54,7 @@ static int sbc_rev; static void __init sbc8548_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/sbc8560.c b/arch/powerpc/platforms/85xx/sbc8560.c index 940752e..3c3bbcc 100644 --- a/arch/powerpc/platforms/85xx/sbc8560.c +++ b/arch/powerpc/platforms/85xx/sbc8560.c @@ -41,8 +41,7 @@ static void __init sbc8560_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/socrates.c b/arch/powerpc/platforms/85xx/socrates.c index 18f6359..b719192 100644 --- a/arch/powerpc/platforms/85xx/socrates.c +++ b/arch/powerpc/platforms/85xx/socrates.c @@ -48,8 +48,7 @@ static void __init socrates_pic_init(void) { struct device_node *np; - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/stx_gp3.c b/arch/powerpc/platforms/85xx/stx_gp3.c index e9e5234..27ca3a7 100644 --- a/arch/powerpc/platforms/85xx/stx_gp3.c +++ b/arch/powerpc/platforms/85xx/stx_gp3.c @@ -48,8 +48,7 @@ static void __init stx_gp3_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/tqm85xx.c b/arch/powerpc/platforms/85xx/tqm85xx.c index bf7c89f..d7504ce 100644 --- a/arch/powerpc/platforms/85xx/tqm85xx.c +++ b/arch/powerpc/platforms/85xx/tqm85xx.c @@ -47,7 +47,7 @@ static void __init tqm85xx_pic_init(void) { struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/85xx/xes_mpc85xx.c b/arch/powerpc/platforms/85xx/xes_mpc85xx.c index 1ab4e02..503c215 100644 --- a/arch/powerpc/platforms/85xx/xes_mpc85xx.c +++ b/arch/powerpc/platforms/85xx/xes_mpc85xx.c @@ -43,9 +43,7 @@ void __init xes_mpc85xx_pic_init(void) { - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | - MPIC_BIG_ENDIAN, + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); diff --git a/arch/powerpc/platforms/86xx/pic.c b/arch/powerpc/platforms/86xx/pic.c index 27959d6..22cc357 100644 --- a/arch/powerpc/platforms/86xx/pic.c +++ b/arch/powerpc/platforms/86xx/pic.c @@ -37,8 +37,7 @@ void __init mpc86xx_init_irq(void) int cascade_irq; #endif - struct mpic *mpic = mpic_alloc(NULL, 0, - MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, 0, 256, " MPIC "); BUG_ON(mpic == NULL); diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index 62002a7..fa3e294 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -197,7 +197,8 @@ static void __init mpic_init_IRQ(void) /* The MPIC driver will get everything it needs from the * device-tree, just pass 0 to all arguments */ - mpic = mpic_alloc(dn, 0, MPIC_SECONDARY, 0, 0, " MPIC "); + mpic = mpic_alloc(dn, 0, MPIC_SECONDARY | MPIC_NO_RESET, + 0, 0, " MPIC "); if (mpic == NULL) continue; mpic_init(mpic); diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index f1f17bb..c665d7d 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -435,7 +435,8 @@ static void __init chrp_find_openpic(void) if (len > 1) isu_size = iranges[3]; - chrp_mpic = mpic_alloc(np, opaddr, 0, isu_size, 0, " MPIC "); + chrp_mpic = mpic_alloc(np, opaddr, MPIC_NO_RESET, + isu_size, 0, " MPIC "); if (chrp_mpic == NULL) { printk(KERN_ERR "Failed to allocate MPIC structure\n"); goto bail; diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c index d7727de..ab51b21 100644 --- a/arch/powerpc/platforms/embedded6xx/holly.c +++ b/arch/powerpc/platforms/embedded6xx/holly.c @@ -154,8 +154,7 @@ static void __init holly_init_IRQ(void) struct device_node *cascade_node = NULL; #endif - mpic = mpic_alloc(NULL, 0, - MPIC_BIG_ENDIAN | MPIC_WANTS_RESET | + mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108, 24, 0, "Tsi108_PIC"); diff --git a/arch/powerpc/platforms/embedded6xx/linkstation.c b/arch/powerpc/platforms/embedded6xx/linkstation.c index 9479f4e..455e7c08 100644 --- a/arch/powerpc/platforms/embedded6xx/linkstation.c +++ b/arch/powerpc/platforms/embedded6xx/linkstation.c @@ -82,8 +82,7 @@ static void __init linkstation_init_IRQ(void) { struct mpic *mpic; - mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET, - 4, 0, " EPIC "); + mpic = mpic_alloc(NULL, 0, 0, 4, 0, " EPIC "); BUG_ON(mpic == NULL); /* PCI IRQs */ diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index 90acb54..74ccce3 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c @@ -108,8 +108,7 @@ static void __init mpc7448_hpc2_init_IRQ(void) struct device_node *cascade_node = NULL; #endif - mpic = mpic_alloc(NULL, 0, - MPIC_BIG_ENDIAN | MPIC_WANTS_RESET | + mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SPV_EOI | MPIC_NO_PTHROU_DIS | MPIC_REGSET_TSI108, 24, 0, "Tsi108_PIC"); diff --git a/arch/powerpc/platforms/embedded6xx/storcenter.c b/arch/powerpc/platforms/embedded6xx/storcenter.c index 451dce4..e0ed3c7 100644 --- a/arch/powerpc/platforms/embedded6xx/storcenter.c +++ b/arch/powerpc/platforms/embedded6xx/storcenter.c @@ -84,8 +84,7 @@ static void __init storcenter_init_IRQ(void) { struct mpic *mpic; - mpic = mpic_alloc(NULL, 0, MPIC_WANTS_RESET, - 16, 0, " OpenPIC "); + mpic = mpic_alloc(NULL, 0, 0, 16, 0, " OpenPIC "); BUG_ON(mpic == NULL); /* diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 0bcbfe7..3b7545a 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -262,7 +262,7 @@ static void __init maple_init_IRQ(void) flags |= MPIC_BIG_ENDIAN; /* XXX Maple specific bits */ - flags |= MPIC_U3_HT_IRQS | MPIC_WANTS_RESET; + flags |= MPIC_U3_HT_IRQS; /* All U3/U4 are big-endian, older SLOF firmware doesn't encode this */ flags |= MPIC_BIG_ENDIAN; diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index 98b7a7c..e777ad4 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c @@ -224,7 +224,7 @@ static __init void pas_init_IRQ(void) openpic_addr = of_read_number(opprop, naddr); printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); - mpic_flags = MPIC_LARGE_VECTORS | MPIC_NO_BIAS; + mpic_flags = MPIC_LARGE_VECTORS | MPIC_NO_BIAS | MPIC_NO_RESET; nmiprop = of_get_property(mpic_node, "nmi-source", NULL); if (nmiprop) diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 7761aab..0293d7a 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -469,7 +469,6 @@ static struct mpic * __init pmac_setup_one_mpic(struct device_node *np, pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0); - flags |= MPIC_WANTS_RESET; if (of_get_property(np, "big-endian", NULL)) flags |= MPIC_BIG_ENDIAN; diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 6d0a5df..d928412 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -190,7 +190,8 @@ static void __init pseries_mpic_init_IRQ(void) BUG_ON(openpic_addr == 0); /* Setup the openpic driver */ - mpic = mpic_alloc(pSeries_mpic_node, openpic_addr, 0, 16, 0, " MPIC "); + mpic = mpic_alloc(pSeries_mpic_node, openpic_addr, + MPIC_NO_RESET, 16, 0, " MPIC "); BUG_ON(mpic == NULL); /* Add ISUs */ diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 90171d4..b9b989d 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1286,7 +1286,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, /* When using a device-node, reset requests are only honored if the MPIC * is allowed to reset. */ - if ((mpic->flags & MPIC_WANTS_RESET) && !(mpic->flags & MPIC_NO_RESET)) { + if (!(mpic->flags & MPIC_NO_RESET)) { printk(KERN_DEBUG "mpic: Resetting\n"); mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) -- cgit v0.10.2 From 8e0aa6d436f303a37df7ec68758883ade077d123 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:14:14 +0000 Subject: fadump: Add documentation for firmware-assisted dump. Documentation for firmware-assisted dump. This document is based on the original documentation written for phyp assisted dump by Linas Vepstas and Manish Ahuja, with few changes to reflect the current implementation. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/Documentation/powerpc/firmware-assisted-dump.txt b/Documentation/powerpc/firmware-assisted-dump.txt new file mode 100644 index 0000000..3007bc9 --- /dev/null +++ b/Documentation/powerpc/firmware-assisted-dump.txt @@ -0,0 +1,270 @@ + + Firmware-Assisted Dump + ------------------------ + July 2011 + +The goal of firmware-assisted dump is to enable the dump of +a crashed system, and to do so from a fully-reset system, and +to minimize the total elapsed time until the system is back +in production use. + +- Firmware assisted dump (fadump) infrastructure is intended to replace + the existing phyp assisted dump. +- Fadump uses the same firmware interfaces and memory reservation model + as phyp assisted dump. +- Unlike phyp dump, fadump exports the memory dump through /proc/vmcore + in the ELF format in the same way as kdump. This helps us reuse the + kdump infrastructure for dump capture and filtering. +- Unlike phyp dump, userspace tool does not need to refer any sysfs + interface while reading /proc/vmcore. +- Unlike phyp dump, fadump allows user to release all the memory reserved + for dump, with a single operation of echo 1 > /sys/kernel/fadump_release_mem. +- Once enabled through kernel boot parameter, fadump can be + started/stopped through /sys/kernel/fadump_registered interface (see + sysfs files section below) and can be easily integrated with kdump + service start/stop init scripts. + +Comparing with kdump or other strategies, firmware-assisted +dump offers several strong, practical advantages: + +-- Unlike kdump, the system has been reset, and loaded + with a fresh copy of the kernel. In particular, + PCI and I/O devices have been reinitialized and are + in a clean, consistent state. +-- Once the dump is copied out, the memory that held the dump + is immediately available to the running kernel. And therefore, + unlike kdump, fadump doesn't need a 2nd reboot to get back + the system to the production configuration. + +The above can only be accomplished by coordination with, +and assistance from the Power firmware. The procedure is +as follows: + +-- The first kernel registers the sections of memory with the + Power firmware for dump preservation during OS initialization. + These registered sections of memory are reserved by the first + kernel during early boot. + +-- When a system crashes, the Power firmware will save + the low memory (boot memory of size larger of 5% of system RAM + or 256MB) of RAM to the previous registered region. It will + also save system registers, and hardware PTE's. + + NOTE: The term 'boot memory' means size of the low memory chunk + that is required for a kernel to boot successfully when + booted with restricted memory. By default, the boot memory + size will be the larger of 5% of system RAM or 256MB. + Alternatively, user can also specify boot memory size + through boot parameter 'fadump_reserve_mem=' which will + override the default calculated size. Use this option + if default boot memory size is not sufficient for second + kernel to boot successfully. + +-- After the low memory (boot memory) area has been saved, the + firmware will reset PCI and other hardware state. It will + *not* clear the RAM. It will then launch the bootloader, as + normal. + +-- The freshly booted kernel will notice that there is a new + node (ibm,dump-kernel) in the device tree, indicating that + there is crash data available from a previous boot. During + the early boot OS will reserve rest of the memory above + boot memory size effectively booting with restricted memory + size. This will make sure that the second kernel will not + touch any of the dump memory area. + +-- User-space tools will read /proc/vmcore to obtain the contents + of memory, which holds the previous crashed kernel dump in ELF + format. The userspace tools may copy this info to disk, or + network, nas, san, iscsi, etc. as desired. + +-- Once the userspace tool is done saving dump, it will echo + '1' to /sys/kernel/fadump_release_mem to release the reserved + memory back to general use, except the memory required for + next firmware-assisted dump registration. + + e.g. + # echo 1 > /sys/kernel/fadump_release_mem + +Please note that the firmware-assisted dump feature +is only available on Power6 and above systems with recent +firmware versions. + +Implementation details: +---------------------- + +During boot, a check is made to see if firmware supports +this feature on that particular machine. If it does, then +we check to see if an active dump is waiting for us. If yes +then everything but boot memory size of RAM is reserved during +early boot (See Fig. 2). This area is released once we finish +collecting the dump from user land scripts (e.g. kdump scripts) +that are run. If there is dump data, then the +/sys/kernel/fadump_release_mem file is created, and the reserved +memory is held. + +If there is no waiting dump data, then only the memory required +to hold CPU state, HPTE region, boot memory dump and elfcore +header, is reserved at the top of memory (see Fig. 1). This area +is *not* released: this region will be kept permanently reserved, +so that it can act as a receptacle for a copy of the boot memory +content in addition to CPU state and HPTE region, in the case a +crash does occur. + + o Memory Reservation during first kernel + + Low memory Top of memory + 0 boot memory size | + | | |<--Reserved dump area -->| + V V | Permanent Reservation V + +-----------+----------/ /----------+---+----+-----------+----+ + | | |CPU|HPTE| DUMP |ELF | + +-----------+----------/ /----------+---+----+-----------+----+ + | ^ + | | + \ / + ------------------------------------------- + Boot memory content gets transferred to + reserved area by firmware at the time of + crash + Fig. 1 + + o Memory Reservation during second kernel after crash + + Low memory Top of memory + 0 boot memory size | + | |<------------- Reserved dump area ----------- -->| + V V V + +-----------+----------/ /----------+---+----+-----------+----+ + | | |CPU|HPTE| DUMP |ELF | + +-----------+----------/ /----------+---+----+-----------+----+ + | | + V V + Used by second /proc/vmcore + kernel to boot + Fig. 2 + +Currently the dump will be copied from /proc/vmcore to a +a new file upon user intervention. The dump data available through +/proc/vmcore will be in ELF format. Hence the existing kdump +infrastructure (kdump scripts) to save the dump works fine with +minor modifications. + +The tools to examine the dump will be same as the ones +used for kdump. + +How to enable firmware-assisted dump (fadump): +------------------------------------- + +1. Set config option CONFIG_FA_DUMP=y and build kernel. +2. Boot into linux kernel with 'fadump=on' kernel cmdline option. +3. Optionally, user can also set 'fadump_reserve_mem=' kernel cmdline + to specify size of the memory to reserve for boot memory dump + preservation. + +NOTE: If firmware-assisted dump fails to reserve memory then it will + fallback to existing kdump mechanism if 'crashkernel=' option + is set at kernel cmdline. + +Sysfs/debugfs files: +------------ + +Firmware-assisted dump feature uses sysfs file system to hold +the control files and debugfs file to display memory reserved region. + +Here is the list of files under kernel sysfs: + + /sys/kernel/fadump_enabled + + This is used to display the fadump status. + 0 = fadump is disabled + 1 = fadump is enabled + + This interface can be used by kdump init scripts to identify if + fadump is enabled in the kernel and act accordingly. + + /sys/kernel/fadump_registered + + This is used to display the fadump registration status as well + as to control (start/stop) the fadump registration. + 0 = fadump is not registered. + 1 = fadump is registered and ready to handle system crash. + + To register fadump echo 1 > /sys/kernel/fadump_registered and + echo 0 > /sys/kernel/fadump_registered for un-register and stop the + fadump. Once the fadump is un-registered, the system crash will not + be handled and vmcore will not be captured. This interface can be + easily integrated with kdump service start/stop. + + /sys/kernel/fadump_release_mem + + This file is available only when fadump is active during + second kernel. This is used to release the reserved memory + region that are held for saving crash dump. To release the + reserved memory echo 1 to it: + + echo 1 > /sys/kernel/fadump_release_mem + + After echo 1, the content of the /sys/kernel/debug/powerpc/fadump_region + file will change to reflect the new memory reservations. + + The existing userspace tools (kdump infrastructure) can be easily + enhanced to use this interface to release the memory reserved for + dump and continue without 2nd reboot. + +Here is the list of files under powerpc debugfs: +(Assuming debugfs is mounted on /sys/kernel/debug directory.) + + /sys/kernel/debug/powerpc/fadump_region + + This file shows the reserved memory regions if fadump is + enabled otherwise this file is empty. The output format + is: + : [-] bytes, Dumped: + + e.g. + Contents when fadump is registered during first kernel + + # cat /sys/kernel/debug/powerpc/fadump_region + CPU : [0x0000006ffb0000-0x0000006fff001f] 0x40020 bytes, Dumped: 0x0 + HPTE: [0x0000006fff0020-0x0000006fff101f] 0x1000 bytes, Dumped: 0x0 + DUMP: [0x0000006fff1020-0x0000007fff101f] 0x10000000 bytes, Dumped: 0x0 + + Contents when fadump is active during second kernel + + # cat /sys/kernel/debug/powerpc/fadump_region + CPU : [0x0000006ffb0000-0x0000006fff001f] 0x40020 bytes, Dumped: 0x40020 + HPTE: [0x0000006fff0020-0x0000006fff101f] 0x1000 bytes, Dumped: 0x1000 + DUMP: [0x0000006fff1020-0x0000007fff101f] 0x10000000 bytes, Dumped: 0x10000000 + : [0x00000010000000-0x0000006ffaffff] 0x5ffb0000 bytes, Dumped: 0x5ffb0000 + +NOTE: Please refer to Documentation/filesystems/debugfs.txt on + how to mount the debugfs filesystem. + + +TODO: +----- + o Need to come up with the better approach to find out more + accurate boot memory size that is required for a kernel to + boot successfully when booted with restricted memory. + o The fadump implementation introduces a fadump crash info structure + in the scratch area before the ELF core header. The idea of introducing + this structure is to pass some important crash info data to the second + kernel which will help second kernel to populate ELF core header with + correct data before it gets exported through /proc/vmcore. The current + design implementation does not address a possibility of introducing + additional fields (in future) to this structure without affecting + compatibility. Need to come up with the better approach to address this. + The possible approaches are: + 1. Introduce version field for version tracking, bump up the version + whenever a new field is added to the structure in future. The version + field can be used to find out what fields are valid for the current + version of the structure. + 2. Reserve the area of predefined size (say PAGE_SIZE) for this + structure and have unused area as reserved (initialized to zero) + for future field additions. + The advantage of approach 1 over 2 is we don't need to reserve extra space. +--- +Author: Mahesh Salgaonkar +This document is based on the original documentation written for phyp +assisted dump by Linas Vepstas and Manish Ahuja. -- cgit v0.10.2 From eb39c8803d0e3d98fe74825f99287f63d55e6460 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:14:22 +0000 Subject: fadump: Reserve the memory for firmware assisted dump. Reserve the memory during early boot to preserve CPU state data, HPTE region and RMA (real mode area) region data in case of kernel crash. At the time of crash, powerpc firmware will store CPU state data, HPTE region data and move RMA region data to the reserved memory area. If the firmware-assisted dump fails to reserve the memory, then fallback to existing kexec-based kdump. Most of the code implementation to reserve memory has been adapted from phyp assisted dump implementation written by Linas Vepstas and Manish Ahuja This patch also introduces a config option CONFIG_FA_DUMP for firmware assisted dump feature on Powerpc (ppc64) architecture. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 1919634..afa4dab 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -386,6 +386,19 @@ config PHYP_DUMP If unsure, say "N" +config FA_DUMP + bool "Firmware-assisted dump" + depends on PPC64 && PPC_RTAS && CRASH_DUMP + help + A robust mechanism to get reliable kernel crash dump with + assistance from firmware. This approach does not use kexec, + instead firmware assists in booting the kdump kernel + while preserving memory contents. Firmware-assisted dump + is meant to be a kdump replacement offering robustness and + speed not possible without system firmware assistance. + + If unsure, say "N" + config IRQ_ALL_CPUS bool "Distribute interrupts on all CPUs by default" depends on SMP && !MV64360 diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h new file mode 100644 index 0000000..7be25d3 --- /dev/null +++ b/arch/powerpc/include/asm/fadump.h @@ -0,0 +1,71 @@ +/* + * Firmware Assisted dump header file. + * + * 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. + * + * 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. + * + * Copyright 2011 IBM Corporation + * Author: Mahesh Salgaonkar + */ + +#ifndef __PPC64_FA_DUMP_H__ +#define __PPC64_FA_DUMP_H__ + +#ifdef CONFIG_FA_DUMP + +/* + * The RMA region will be saved for later dumping when kernel crashes. + * RMA is Real Mode Area, the first block of logical memory address owned + * by logical partition, containing the storage that may be accessed with + * translate off. + */ +#define RMA_START 0x0 +#define RMA_END (ppc64_rma_size) + +/* + * On some Power systems where RMO is 128MB, it still requires minimum of + * 256MB for kernel to boot successfully. When kdump infrastructure is + * configured to save vmcore over network, we run into OOM issue while + * loading modules related to network setup. Hence we need aditional 64M + * of memory to avoid OOM issue. + */ +#define MIN_BOOT_MEM (((RMA_END < (0x1UL << 28)) ? (0x1UL << 28) : RMA_END) \ + + (0x1UL << 26)) + +/* Firmware provided dump sections */ +#define FADUMP_CPU_STATE_DATA 0x0001 +#define FADUMP_HPTE_REGION 0x0002 +#define FADUMP_REAL_MODE_REGION 0x0011 + +struct fw_dump { + unsigned long cpu_state_data_size; + unsigned long hpte_region_size; + unsigned long boot_memory_size; + unsigned long reserve_dump_area_start; + unsigned long reserve_dump_area_size; + /* cmd line option during boot */ + unsigned long reserve_bootvar; + + int ibm_configure_kernel_dump; + + unsigned long fadump_enabled:1; + unsigned long fadump_supported:1; + unsigned long dump_active:1; +}; + +extern int early_init_dt_scan_fw_dump(unsigned long node, + const char *uname, int depth, void *data); +extern int fadump_reserve_mem(void); +#endif +#endif diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index ee728e4..391bf7e 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_IBMVIO) += vio.o obj-$(CONFIG_IBMEBUS) += ibmebus.o obj-$(CONFIG_GENERIC_TBSYNC) += smp-tbsync.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o +obj-$(CONFIG_FA_DUMP) += fadump.o ifeq ($(CONFIG_PPC32),y) obj-$(CONFIG_E500) += idle_e500.o endif diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c new file mode 100644 index 0000000..deb276a --- /dev/null +++ b/arch/powerpc/kernel/fadump.c @@ -0,0 +1,246 @@ +/* + * Firmware Assisted dump: A robust mechanism to get reliable kernel crash + * dump with assistance from firmware. This approach does not use kexec, + * instead firmware assists in booting the kdump kernel while preserving + * memory contents. The most of the code implementation has been adapted + * from phyp assisted dump implementation written by Linas Vepstas and + * Manish Ahuja + * + * 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. + * + * 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. + * + * Copyright 2011 IBM Corporation + * Author: Mahesh Salgaonkar + */ + +#undef DEBUG +#define pr_fmt(fmt) "fadump: " fmt + +#include +#include + +#include +#include +#include +#include + +static struct fw_dump fw_dump; + +/* Scan the Firmware Assisted dump configuration details. */ +int __init early_init_dt_scan_fw_dump(unsigned long node, + const char *uname, int depth, void *data) +{ + __be32 *sections; + int i, num_sections; + unsigned long size; + const int *token; + + if (depth != 1 || strcmp(uname, "rtas") != 0) + return 0; + + /* + * Check if Firmware Assisted dump is supported. if yes, check + * if dump has been initiated on last reboot. + */ + token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL); + if (!token) + return 0; + + fw_dump.fadump_supported = 1; + fw_dump.ibm_configure_kernel_dump = *token; + + /* + * The 'ibm,kernel-dump' rtas node is present only if there is + * dump data waiting for us. + */ + if (of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL)) + fw_dump.dump_active = 1; + + /* Get the sizes required to store dump data for the firmware provided + * dump sections. + * For each dump section type supported, a 32bit cell which defines + * the ID of a supported section followed by two 32 bit cells which + * gives teh size of the section in bytes. + */ + sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", + &size); + + if (!sections) + return 0; + + num_sections = size / (3 * sizeof(u32)); + + for (i = 0; i < num_sections; i++, sections += 3) { + u32 type = (u32)of_read_number(sections, 1); + + switch (type) { + case FADUMP_CPU_STATE_DATA: + fw_dump.cpu_state_data_size = + of_read_ulong(§ions[1], 2); + break; + case FADUMP_HPTE_REGION: + fw_dump.hpte_region_size = + of_read_ulong(§ions[1], 2); + break; + } + } + return 1; +} + +/** + * fadump_calculate_reserve_size(): reserve variable boot area 5% of System RAM + * + * Function to find the largest memory size we need to reserve during early + * boot process. This will be the size of the memory that is required for a + * kernel to boot successfully. + * + * This function has been taken from phyp-assisted dump feature implementation. + * + * returns larger of 256MB or 5% rounded down to multiples of 256MB. + * + * TODO: Come up with better approach to find out more accurate memory size + * that is required for a kernel to boot successfully. + * + */ +static inline unsigned long fadump_calculate_reserve_size(void) +{ + unsigned long size; + + /* + * Check if the size is specified through fadump_reserve_mem= cmdline + * option. If yes, then use that. + */ + if (fw_dump.reserve_bootvar) + return fw_dump.reserve_bootvar; + + /* divide by 20 to get 5% of value */ + size = memblock_end_of_DRAM() / 20; + + /* round it down in multiples of 256 */ + size = size & ~0x0FFFFFFFUL; + + /* Truncate to memory_limit. We don't want to over reserve the memory.*/ + if (memory_limit && size > memory_limit) + size = memory_limit; + + return (size > MIN_BOOT_MEM ? size : MIN_BOOT_MEM); +} + +/* + * Calculate the total memory size required to be reserved for + * firmware-assisted dump registration. + */ +static unsigned long get_fadump_area_size(void) +{ + unsigned long size = 0; + + size += fw_dump.cpu_state_data_size; + size += fw_dump.hpte_region_size; + size += fw_dump.boot_memory_size; + + size = PAGE_ALIGN(size); + return size; +} + +int __init fadump_reserve_mem(void) +{ + unsigned long base, size, memory_boundary; + + if (!fw_dump.fadump_enabled) + return 0; + + if (!fw_dump.fadump_supported) { + printk(KERN_INFO "Firmware-assisted dump is not supported on" + " this hardware\n"); + fw_dump.fadump_enabled = 0; + return 0; + } + /* Initialize boot memory size */ + fw_dump.boot_memory_size = fadump_calculate_reserve_size(); + + /* + * Calculate the memory boundary. + * If memory_limit is less than actual memory boundary then reserve + * the memory for fadump beyond the memory_limit and adjust the + * memory_limit accordingly, so that the running kernel can run with + * specified memory_limit. + */ + if (memory_limit && memory_limit < memblock_end_of_DRAM()) { + size = get_fadump_area_size(); + if ((memory_limit + size) < memblock_end_of_DRAM()) + memory_limit += size; + else + memory_limit = memblock_end_of_DRAM(); + printk(KERN_INFO "Adjusted memory_limit for firmware-assisted" + " dump, now %#016llx\n", + (unsigned long long)memory_limit); + } + if (memory_limit) + memory_boundary = memory_limit; + else + memory_boundary = memblock_end_of_DRAM(); + + if (fw_dump.dump_active) { + printk(KERN_INFO "Firmware-assisted dump is active.\n"); + /* + * If last boot has crashed then reserve all the memory + * above boot_memory_size so that we don't touch it until + * dump is written to disk by userspace tool. This memory + * will be released for general use once the dump is saved. + */ + base = fw_dump.boot_memory_size; + size = memory_boundary - base; + memblock_reserve(base, size); + printk(KERN_INFO "Reserved %ldMB of memory at %ldMB " + "for saving crash dump\n", + (unsigned long)(size >> 20), + (unsigned long)(base >> 20)); + } else { + /* Reserve the memory at the top of memory. */ + size = get_fadump_area_size(); + base = memory_boundary - size; + memblock_reserve(base, size); + printk(KERN_INFO "Reserved %ldMB of memory at %ldMB " + "for firmware-assisted dump\n", + (unsigned long)(size >> 20), + (unsigned long)(base >> 20)); + } + fw_dump.reserve_dump_area_start = base; + fw_dump.reserve_dump_area_size = size; + return 1; +} + +/* Look for fadump= cmdline option. */ +static int __init early_fadump_param(char *p) +{ + if (!p) + return 1; + + if (strncmp(p, "on", 2) == 0) + fw_dump.fadump_enabled = 1; + else if (strncmp(p, "off", 3) == 0) + fw_dump.fadump_enabled = 0; + + return 0; +} +early_param("fadump", early_fadump_param); + +/* Look for fadump_reserve_mem= cmdline option */ +static int __init early_fadump_reserve_mem(char *p) +{ + if (p) + fw_dump.reserve_bootvar = memparse(p, &p); + return 0; +} +early_param("fadump_reserve_mem", early_fadump_reserve_mem); diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index abe405d..70222b3 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -55,6 +55,7 @@ #include #include #include +#include #include @@ -719,6 +720,11 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL); #endif +#ifdef CONFIG_FA_DUMP + /* scan tree to see if dump is active during last boot */ + of_scan_flat_dt(early_init_dt_scan_fw_dump, NULL); +#endif + /* Pre-initialize the cmd_line with the content of boot_commmand_line, * which will be empty except when the content of the variable has * been overriden by a bootloading mechanism. This happens typically @@ -750,7 +756,14 @@ void __init early_init_devtree(void *params) if (PHYSICAL_START > MEMORY_START) memblock_reserve(MEMORY_START, 0x8000); reserve_kdump_trampoline(); - reserve_crashkernel(); +#ifdef CONFIG_FA_DUMP + /* + * If we fail to reserve memory for firmware-assisted dump then + * fallback to kexec based kdump. + */ + if (fadump_reserve_mem() == 0) +#endif + reserve_crashkernel(); early_reserve_mem(); phyp_dump_reserve_mem(); -- cgit v0.10.2 From 3ccc00a7e04ff7718c9aebb4b0c982571c798759 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Mon, 20 Feb 2012 02:15:03 +0000 Subject: fadump: Register for firmware assisted dump. On 2012-02-20 11:02:51 Mon, Paul Mackerras wrote: > On Thu, Feb 16, 2012 at 04:44:30PM +0530, Mahesh J Salgaonkar wrote: > > If I have read the code correctly, we are going to get this printk on > non-pSeries machines or on older pSeries machines, even if the user > has not put the fadump=on option on the kernel command line. The > printk will be annoying since there is no actual error condition. It > seems to me that the condition for the printk should include > fw_dump.fadump_enabled. In other words you should probably add > > if (!fw_dump.fadump_enabled) > return 0; > > at the beginning of the function. Hi Paul, Thanks for pointing it out. Please find the updated patch below. The existing patches above this (4/10 through 10/10) cleanly applies on this update. Thanks, -Mahesh. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h index 7be25d3..bbaf278 100644 --- a/arch/powerpc/include/asm/fadump.h +++ b/arch/powerpc/include/asm/fadump.h @@ -48,6 +48,58 @@ #define FADUMP_HPTE_REGION 0x0002 #define FADUMP_REAL_MODE_REGION 0x0011 +/* Dump request flag */ +#define FADUMP_REQUEST_FLAG 0x00000001 + +/* FAD commands */ +#define FADUMP_REGISTER 1 +#define FADUMP_UNREGISTER 2 +#define FADUMP_INVALIDATE 3 + +/* Kernel Dump section info */ +struct fadump_section { + u32 request_flag; + u16 source_data_type; + u16 error_flags; + u64 source_address; + u64 source_len; + u64 bytes_dumped; + u64 destination_address; +}; + +/* ibm,configure-kernel-dump header. */ +struct fadump_section_header { + u32 dump_format_version; + u16 dump_num_sections; + u16 dump_status_flag; + u32 offset_first_dump_section; + + /* Fields for disk dump option. */ + u32 dd_block_size; + u64 dd_block_offset; + u64 dd_num_blocks; + u32 dd_offset_disk_path; + + /* Maximum time allowed to prevent an automatic dump-reboot. */ + u32 max_time_auto; +}; + +/* + * Firmware Assisted dump memory structure. This structure is required for + * registering future kernel dump with power firmware through rtas call. + * + * No disk dump option. Hence disk dump path string section is not included. + */ +struct fadump_mem_struct { + struct fadump_section_header header; + + /* Kernel dump sections */ + struct fadump_section cpu_state_data; + struct fadump_section hpte_region; + struct fadump_section rmr_region; +}; + +/* Firmware-assisted dump configuration details. */ struct fw_dump { unsigned long cpu_state_data_size; unsigned long hpte_region_size; @@ -62,10 +114,15 @@ struct fw_dump { unsigned long fadump_enabled:1; unsigned long fadump_supported:1; unsigned long dump_active:1; + unsigned long dump_registered:1; }; extern int early_init_dt_scan_fw_dump(unsigned long node, const char *uname, int depth, void *data); extern int fadump_reserve_mem(void); +extern int setup_fadump(void); +extern int is_fadump_active(void); +#else /* CONFIG_FA_DUMP */ +static inline int is_fadump_active(void) { return 0; } #endif #endif diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index deb276a..eb8f782 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -29,6 +29,9 @@ #include #include +#include +#include +#include #include #include @@ -36,6 +39,10 @@ #include static struct fw_dump fw_dump; +static struct fadump_mem_struct fdm; +static const struct fadump_mem_struct *fdm_active; + +static DEFINE_MUTEX(fadump_mutex); /* Scan the Firmware Assisted dump configuration details. */ int __init early_init_dt_scan_fw_dump(unsigned long node, @@ -64,7 +71,8 @@ int __init early_init_dt_scan_fw_dump(unsigned long node, * The 'ibm,kernel-dump' rtas node is present only if there is * dump data waiting for us. */ - if (of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL)) + fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL); + if (fdm_active) fw_dump.dump_active = 1; /* Get the sizes required to store dump data for the firmware provided @@ -98,6 +106,85 @@ int __init early_init_dt_scan_fw_dump(unsigned long node, return 1; } +int is_fadump_active(void) +{ + return fw_dump.dump_active; +} + +/* Print firmware assisted dump configurations for debugging purpose. */ +static void fadump_show_config(void) +{ + pr_debug("Support for firmware-assisted dump (fadump): %s\n", + (fw_dump.fadump_supported ? "present" : "no support")); + + if (!fw_dump.fadump_supported) + return; + + pr_debug("Fadump enabled : %s\n", + (fw_dump.fadump_enabled ? "yes" : "no")); + pr_debug("Dump Active : %s\n", + (fw_dump.dump_active ? "yes" : "no")); + pr_debug("Dump section sizes:\n"); + pr_debug(" CPU state data size: %lx\n", fw_dump.cpu_state_data_size); + pr_debug(" HPTE region size : %lx\n", fw_dump.hpte_region_size); + pr_debug("Boot memory size : %lx\n", fw_dump.boot_memory_size); +} + +static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm, + unsigned long addr) +{ + if (!fdm) + return 0; + + memset(fdm, 0, sizeof(struct fadump_mem_struct)); + addr = addr & PAGE_MASK; + + fdm->header.dump_format_version = 0x00000001; + fdm->header.dump_num_sections = 3; + fdm->header.dump_status_flag = 0; + fdm->header.offset_first_dump_section = + (u32)offsetof(struct fadump_mem_struct, cpu_state_data); + + /* + * Fields for disk dump option. + * We are not using disk dump option, hence set these fields to 0. + */ + fdm->header.dd_block_size = 0; + fdm->header.dd_block_offset = 0; + fdm->header.dd_num_blocks = 0; + fdm->header.dd_offset_disk_path = 0; + + /* set 0 to disable an automatic dump-reboot. */ + fdm->header.max_time_auto = 0; + + /* Kernel dump sections */ + /* cpu state data section. */ + fdm->cpu_state_data.request_flag = FADUMP_REQUEST_FLAG; + fdm->cpu_state_data.source_data_type = FADUMP_CPU_STATE_DATA; + fdm->cpu_state_data.source_address = 0; + fdm->cpu_state_data.source_len = fw_dump.cpu_state_data_size; + fdm->cpu_state_data.destination_address = addr; + addr += fw_dump.cpu_state_data_size; + + /* hpte region section */ + fdm->hpte_region.request_flag = FADUMP_REQUEST_FLAG; + fdm->hpte_region.source_data_type = FADUMP_HPTE_REGION; + fdm->hpte_region.source_address = 0; + fdm->hpte_region.source_len = fw_dump.hpte_region_size; + fdm->hpte_region.destination_address = addr; + addr += fw_dump.hpte_region_size; + + /* RMA region section */ + fdm->rmr_region.request_flag = FADUMP_REQUEST_FLAG; + fdm->rmr_region.source_data_type = FADUMP_REAL_MODE_REGION; + fdm->rmr_region.source_address = RMA_START; + fdm->rmr_region.source_len = fw_dump.boot_memory_size; + fdm->rmr_region.destination_address = addr; + addr += fw_dump.boot_memory_size; + + return addr; +} + /** * fadump_calculate_reserve_size(): reserve variable boot area 5% of System RAM * @@ -166,8 +253,15 @@ int __init fadump_reserve_mem(void) fw_dump.fadump_enabled = 0; return 0; } - /* Initialize boot memory size */ - fw_dump.boot_memory_size = fadump_calculate_reserve_size(); + /* + * Initialize boot memory size + * If dump is active then we have already calculated the size during + * first kernel. + */ + if (fdm_active) + fw_dump.boot_memory_size = fdm_active->rmr_region.source_len; + else + fw_dump.boot_memory_size = fadump_calculate_reserve_size(); /* * Calculate the memory boundary. @@ -244,3 +338,258 @@ static int __init early_fadump_reserve_mem(char *p) return 0; } early_param("fadump_reserve_mem", early_fadump_reserve_mem); + +static void register_fw_dump(struct fadump_mem_struct *fdm) +{ + int rc; + unsigned int wait_time; + + pr_debug("Registering for firmware-assisted kernel dump...\n"); + + /* TODO: Add upper time limit for the delay */ + do { + rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL, + FADUMP_REGISTER, fdm, + sizeof(struct fadump_mem_struct)); + + wait_time = rtas_busy_delay_time(rc); + if (wait_time) + mdelay(wait_time); + + } while (wait_time); + + switch (rc) { + case -1: + printk(KERN_ERR "Failed to register firmware-assisted kernel" + " dump. Hardware Error(%d).\n", rc); + break; + case -3: + printk(KERN_ERR "Failed to register firmware-assisted kernel" + " dump. Parameter Error(%d).\n", rc); + break; + case -9: + printk(KERN_ERR "firmware-assisted kernel dump is already " + " registered."); + fw_dump.dump_registered = 1; + break; + case 0: + printk(KERN_INFO "firmware-assisted kernel dump registration" + " is successful\n"); + fw_dump.dump_registered = 1; + break; + } +} + +static void register_fadump(void) +{ + /* + * If no memory is reserved then we can not register for firmware- + * assisted dump. + */ + if (!fw_dump.reserve_dump_area_size) + return; + + /* register the future kernel dump with firmware. */ + register_fw_dump(&fdm); +} + +static int fadump_unregister_dump(struct fadump_mem_struct *fdm) +{ + int rc = 0; + unsigned int wait_time; + + pr_debug("Un-register firmware-assisted dump\n"); + + /* TODO: Add upper time limit for the delay */ + do { + rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL, + FADUMP_UNREGISTER, fdm, + sizeof(struct fadump_mem_struct)); + + wait_time = rtas_busy_delay_time(rc); + if (wait_time) + mdelay(wait_time); + } while (wait_time); + + if (rc) { + printk(KERN_ERR "Failed to un-register firmware-assisted dump." + " unexpected error(%d).\n", rc); + return rc; + } + fw_dump.dump_registered = 0; + return 0; +} + +static ssize_t fadump_enabled_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", fw_dump.fadump_enabled); +} + +static ssize_t fadump_register_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", fw_dump.dump_registered); +} + +static ssize_t fadump_register_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + + if (!fw_dump.fadump_enabled || fdm_active) + return -EPERM; + + mutex_lock(&fadump_mutex); + + switch (buf[0]) { + case '0': + if (fw_dump.dump_registered == 0) { + ret = -EINVAL; + goto unlock_out; + } + /* Un-register Firmware-assisted dump */ + fadump_unregister_dump(&fdm); + break; + case '1': + if (fw_dump.dump_registered == 1) { + ret = -EINVAL; + goto unlock_out; + } + /* Register Firmware-assisted dump */ + register_fadump(); + break; + default: + ret = -EINVAL; + break; + } + +unlock_out: + mutex_unlock(&fadump_mutex); + return ret < 0 ? ret : count; +} + +static int fadump_region_show(struct seq_file *m, void *private) +{ + const struct fadump_mem_struct *fdm_ptr; + + if (!fw_dump.fadump_enabled) + return 0; + + if (fdm_active) + fdm_ptr = fdm_active; + else + fdm_ptr = &fdm; + + seq_printf(m, + "CPU : [%#016llx-%#016llx] %#llx bytes, " + "Dumped: %#llx\n", + fdm_ptr->cpu_state_data.destination_address, + fdm_ptr->cpu_state_data.destination_address + + fdm_ptr->cpu_state_data.source_len - 1, + fdm_ptr->cpu_state_data.source_len, + fdm_ptr->cpu_state_data.bytes_dumped); + seq_printf(m, + "HPTE: [%#016llx-%#016llx] %#llx bytes, " + "Dumped: %#llx\n", + fdm_ptr->hpte_region.destination_address, + fdm_ptr->hpte_region.destination_address + + fdm_ptr->hpte_region.source_len - 1, + fdm_ptr->hpte_region.source_len, + fdm_ptr->hpte_region.bytes_dumped); + seq_printf(m, + "DUMP: [%#016llx-%#016llx] %#llx bytes, " + "Dumped: %#llx\n", + fdm_ptr->rmr_region.destination_address, + fdm_ptr->rmr_region.destination_address + + fdm_ptr->rmr_region.source_len - 1, + fdm_ptr->rmr_region.source_len, + fdm_ptr->rmr_region.bytes_dumped); + + if (!fdm_active || + (fw_dump.reserve_dump_area_start == + fdm_ptr->cpu_state_data.destination_address)) + return 0; + + /* Dump is active. Show reserved memory region. */ + seq_printf(m, + " : [%#016llx-%#016llx] %#llx bytes, " + "Dumped: %#llx\n", + (unsigned long long)fw_dump.reserve_dump_area_start, + fdm_ptr->cpu_state_data.destination_address - 1, + fdm_ptr->cpu_state_data.destination_address - + fw_dump.reserve_dump_area_start, + fdm_ptr->cpu_state_data.destination_address - + fw_dump.reserve_dump_area_start); + return 0; +} + +static struct kobj_attribute fadump_attr = __ATTR(fadump_enabled, + 0444, fadump_enabled_show, + NULL); +static struct kobj_attribute fadump_register_attr = __ATTR(fadump_registered, + 0644, fadump_register_show, + fadump_register_store); + +static int fadump_region_open(struct inode *inode, struct file *file) +{ + return single_open(file, fadump_region_show, inode->i_private); +} + +static const struct file_operations fadump_region_fops = { + .open = fadump_region_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void fadump_init_files(void) +{ + struct dentry *debugfs_file; + int rc = 0; + + rc = sysfs_create_file(kernel_kobj, &fadump_attr.attr); + if (rc) + printk(KERN_ERR "fadump: unable to create sysfs file" + " fadump_enabled (%d)\n", rc); + + rc = sysfs_create_file(kernel_kobj, &fadump_register_attr.attr); + if (rc) + printk(KERN_ERR "fadump: unable to create sysfs file" + " fadump_registered (%d)\n", rc); + + debugfs_file = debugfs_create_file("fadump_region", 0444, + powerpc_debugfs_root, NULL, + &fadump_region_fops); + if (!debugfs_file) + printk(KERN_ERR "fadump: unable to create debugfs file" + " fadump_region\n"); + return; +} + +/* + * Prepare for firmware-assisted dump. + */ +int __init setup_fadump(void) +{ + if (!fw_dump.fadump_enabled) + return 0; + + if (!fw_dump.fadump_supported) { + printk(KERN_ERR "Firmware-assisted dump is not supported on" + " this hardware\n"); + return 0; + } + + fadump_show_config(); + /* Initialize the kernel dump memory structure for FAD registration. */ + if (fw_dump.reserve_dump_area_size) + init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start); + fadump_init_files(); + + return 1; +} +subsys_initcall(setup_fadump); diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index 0cfcf98..359f078 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -39,6 +39,7 @@ #include #include #include +#include #define DBG(...) @@ -445,7 +446,12 @@ void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist, static void iommu_table_clear(struct iommu_table *tbl) { - if (!is_kdump_kernel()) { + /* + * In case of firmware assisted dump system goes through clean + * reboot process at the time of system crash. Hence it's safe to + * clear the TCE entries if firmware assisted dump is active. + */ + if (!is_kdump_kernel() || is_fadump_active()) { /* Clear the table in case firmware left allocations in it */ ppc_md.tce_free(tbl, tbl->it_offset, tbl->it_size); return; diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 2d28218..b534bba 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -625,6 +626,16 @@ static void __init htab_initialize(void) /* Using a hypervisor which owns the htab */ htab_address = NULL; _SDR1 = 0; +#ifdef CONFIG_FA_DUMP + /* + * If firmware assisted dump is active firmware preserves + * the contents of htab along with entire partition memory. + * Clear the htab if firmware assisted dump is active so + * that we dont end up using old mappings. + */ + if (is_fadump_active() && ppc_md.hpte_clear_all) + ppc_md.hpte_clear_all(); +#endif } else { /* Find storage for the HPT. Must be contiguous in * the absolute address space. On cell we want it to be -- cgit v0.10.2 From 2df173d9e85d9e2c6a8933c63f0c034accff7e0f Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:14:37 +0000 Subject: fadump: Initialize elfcore header and add PT_LOAD program headers. Build the crash memory range list by traversing through system memory during the first kernel before we register for firmware-assisted dump. After the successful dump registration, initialize the elfcore header and populate PT_LOAD program headers with crash memory ranges. The elfcore header is saved in the scratch area within the reserved memory. The scratch area starts at the end of the memory reserved for saving RMR region contents. The scratch area contains fadump crash info structure that contains magic number for fadump validation and physical address where the eflcore header can be found. This structure will also be used to pass some important crash info data to the second kernel which will help second kernel to populate ELF core header with correct data before it gets exported through /proc/vmcore. Since the firmware preserves the entire partition memory at the time of crash the contents of the scratch area will be preserved till second kernel boot. Since the memory dump exported through /proc/vmcore is in ELF format similar to kdump, it will help us to reuse the kdump infrastructure for dump capture and filtering. Unlike phyp dump, userspace tool does not need to refer any sysfs interface while reading /proc/vmcore. NOTE: The current design implementation does not address a possibility of introducing additional fields (in future) to this structure without affecting compatibility. It's on TODO list to come up with better approach to address this. Reserved dump area start => +-------------------------------------+ | CPU state dump data | +-------------------------------------+ | HPTE region data | +-------------------------------------+ | RMR region data | Scratch area start => +-------------------------------------+ | fadump crash info structure { | | magic nummber | +------|---- elfcorehdr_addr | | | } | +----> +-------------------------------------+ | ELF core header | Reserved dump area end => +-------------------------------------+ Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h index bbaf278..9e172a5 100644 --- a/arch/powerpc/include/asm/fadump.h +++ b/arch/powerpc/include/asm/fadump.h @@ -43,6 +43,12 @@ #define MIN_BOOT_MEM (((RMA_END < (0x1UL << 28)) ? (0x1UL << 28) : RMA_END) \ + (0x1UL << 26)) +#define memblock_num_regions(memblock_type) (memblock.memblock_type.cnt) + +#ifndef ELF_CORE_EFLAGS +#define ELF_CORE_EFLAGS 0 +#endif + /* Firmware provided dump sections */ #define FADUMP_CPU_STATE_DATA 0x0001 #define FADUMP_HPTE_REGION 0x0002 @@ -56,6 +62,9 @@ #define FADUMP_UNREGISTER 2 #define FADUMP_INVALIDATE 3 +/* Dump status flag */ +#define FADUMP_ERROR_FLAG 0x2000 + /* Kernel Dump section info */ struct fadump_section { u32 request_flag; @@ -109,6 +118,7 @@ struct fw_dump { /* cmd line option during boot */ unsigned long reserve_bootvar; + unsigned long fadumphdr_addr; int ibm_configure_kernel_dump; unsigned long fadump_enabled:1; @@ -117,6 +127,39 @@ struct fw_dump { unsigned long dump_registered:1; }; +/* + * Copy the ascii values for first 8 characters from a string into u64 + * variable at their respective indexes. + * e.g. + * The string "FADMPINF" will be converted into 0x4641444d50494e46 + */ +static inline u64 str_to_u64(const char *str) +{ + u64 val = 0; + int i; + + for (i = 0; i < sizeof(val); i++) + val = (*str) ? (val << 8) | *str++ : val << 8; + return val; +} +#define STR_TO_HEX(x) str_to_u64(x) + +#define FADUMP_CRASH_INFO_MAGIC STR_TO_HEX("FADMPINF") + +/* fadump crash info structure */ +struct fadump_crash_info_header { + u64 magic_number; + u64 elfcorehdr_addr; +}; + +/* Crash memory ranges */ +#define INIT_CRASHMEM_RANGES (INIT_MEMBLOCK_REGIONS + 2) + +struct fad_crash_memory_ranges { + unsigned long long base; + unsigned long long size; +}; + extern int early_init_dt_scan_fw_dump(unsigned long node, const char *uname, int depth, void *data); extern int fadump_reserve_mem(void); diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index eb8f782..63857e1 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,8 @@ static struct fadump_mem_struct fdm; static const struct fadump_mem_struct *fdm_active; static DEFINE_MUTEX(fadump_mutex); +struct fad_crash_memory_ranges crash_memory_ranges[INIT_CRASHMEM_RANGES]; +int crash_mem_ranges; /* Scan the Firmware Assisted dump configuration details. */ int __init early_init_dt_scan_fw_dump(unsigned long node, @@ -235,6 +238,10 @@ static unsigned long get_fadump_area_size(void) size += fw_dump.cpu_state_data_size; size += fw_dump.hpte_region_size; size += fw_dump.boot_memory_size; + size += sizeof(struct fadump_crash_info_header); + size += sizeof(struct elfhdr); /* ELF core header.*/ + /* Program headers for crash memory regions. */ + size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2); size = PAGE_ALIGN(size); return size; @@ -300,6 +307,12 @@ int __init fadump_reserve_mem(void) "for saving crash dump\n", (unsigned long)(size >> 20), (unsigned long)(base >> 20)); + + fw_dump.fadumphdr_addr = + fdm_active->rmr_region.destination_address + + fdm_active->rmr_region.source_len; + pr_debug("fadumphdr_addr = %p\n", + (void *) fw_dump.fadumphdr_addr); } else { /* Reserve the memory at the top of memory. */ size = get_fadump_area_size(); @@ -380,8 +393,210 @@ static void register_fw_dump(struct fadump_mem_struct *fdm) } } +/* + * Validate and process the dump data stored by firmware before exporting + * it through '/proc/vmcore'. + */ +static int __init process_fadump(const struct fadump_mem_struct *fdm_active) +{ + struct fadump_crash_info_header *fdh; + + if (!fdm_active || !fw_dump.fadumphdr_addr) + return -EINVAL; + + /* Check if the dump data is valid. */ + if ((fdm_active->header.dump_status_flag == FADUMP_ERROR_FLAG) || + (fdm_active->rmr_region.error_flags != 0)) { + printk(KERN_ERR "Dump taken by platform is not valid\n"); + return -EINVAL; + } + if (fdm_active->rmr_region.bytes_dumped != + fdm_active->rmr_region.source_len) { + printk(KERN_ERR "Dump taken by platform is incomplete\n"); + return -EINVAL; + } + + /* Validate the fadump crash info header */ + fdh = __va(fw_dump.fadumphdr_addr); + if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) { + printk(KERN_ERR "Crash info header is not valid.\n"); + return -EINVAL; + } + + /* + * We are done validating dump info and elfcore header is now ready + * to be exported. set elfcorehdr_addr so that vmcore module will + * export the elfcore header through '/proc/vmcore'. + */ + elfcorehdr_addr = fdh->elfcorehdr_addr; + + return 0; +} + +static inline void fadump_add_crash_memory(unsigned long long base, + unsigned long long end) +{ + if (base == end) + return; + + pr_debug("crash_memory_range[%d] [%#016llx-%#016llx], %#llx bytes\n", + crash_mem_ranges, base, end - 1, (end - base)); + crash_memory_ranges[crash_mem_ranges].base = base; + crash_memory_ranges[crash_mem_ranges].size = end - base; + crash_mem_ranges++; +} + +static void fadump_exclude_reserved_area(unsigned long long start, + unsigned long long end) +{ + unsigned long long ra_start, ra_end; + + ra_start = fw_dump.reserve_dump_area_start; + ra_end = ra_start + fw_dump.reserve_dump_area_size; + + if ((ra_start < end) && (ra_end > start)) { + if ((start < ra_start) && (end > ra_end)) { + fadump_add_crash_memory(start, ra_start); + fadump_add_crash_memory(ra_end, end); + } else if (start < ra_start) { + fadump_add_crash_memory(start, ra_start); + } else if (ra_end < end) { + fadump_add_crash_memory(ra_end, end); + } + } else + fadump_add_crash_memory(start, end); +} + +static int fadump_init_elfcore_header(char *bufp) +{ + struct elfhdr *elf; + + elf = (struct elfhdr *) bufp; + bufp += sizeof(struct elfhdr); + memcpy(elf->e_ident, ELFMAG, SELFMAG); + elf->e_ident[EI_CLASS] = ELF_CLASS; + elf->e_ident[EI_DATA] = ELF_DATA; + elf->e_ident[EI_VERSION] = EV_CURRENT; + elf->e_ident[EI_OSABI] = ELF_OSABI; + memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); + elf->e_type = ET_CORE; + elf->e_machine = ELF_ARCH; + elf->e_version = EV_CURRENT; + elf->e_entry = 0; + elf->e_phoff = sizeof(struct elfhdr); + elf->e_shoff = 0; + elf->e_flags = ELF_CORE_EFLAGS; + elf->e_ehsize = sizeof(struct elfhdr); + elf->e_phentsize = sizeof(struct elf_phdr); + elf->e_phnum = 0; + elf->e_shentsize = 0; + elf->e_shnum = 0; + elf->e_shstrndx = 0; + + return 0; +} + +/* + * Traverse through memblock structure and setup crash memory ranges. These + * ranges will be used create PT_LOAD program headers in elfcore header. + */ +static void fadump_setup_crash_memory_ranges(void) +{ + struct memblock_region *reg; + unsigned long long start, end; + + pr_debug("Setup crash memory ranges.\n"); + crash_mem_ranges = 0; + /* + * add the first memory chunk (RMA_START through boot_memory_size) as + * a separate memory chunk. The reason is, at the time crash firmware + * will move the content of this memory chunk to different location + * specified during fadump registration. We need to create a separate + * program header for this chunk with the correct offset. + */ + fadump_add_crash_memory(RMA_START, fw_dump.boot_memory_size); + + for_each_memblock(memory, reg) { + start = (unsigned long long)reg->base; + end = start + (unsigned long long)reg->size; + if (start == RMA_START && end >= fw_dump.boot_memory_size) + start = fw_dump.boot_memory_size; + + /* add this range excluding the reserved dump area. */ + fadump_exclude_reserved_area(start, end); + } +} + +static int fadump_create_elfcore_headers(char *bufp) +{ + struct elfhdr *elf; + struct elf_phdr *phdr; + int i; + + fadump_init_elfcore_header(bufp); + elf = (struct elfhdr *)bufp; + bufp += sizeof(struct elfhdr); + + /* setup PT_LOAD sections. */ + + for (i = 0; i < crash_mem_ranges; i++) { + unsigned long long mbase, msize; + mbase = crash_memory_ranges[i].base; + msize = crash_memory_ranges[i].size; + + if (!msize) + continue; + + phdr = (struct elf_phdr *)bufp; + bufp += sizeof(struct elf_phdr); + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = mbase; + + if (mbase == RMA_START) { + /* + * The entire RMA region will be moved by firmware + * to the specified destination_address. Hence set + * the correct offset. + */ + phdr->p_offset = fdm.rmr_region.destination_address; + } + + phdr->p_paddr = mbase; + phdr->p_vaddr = (unsigned long)__va(mbase); + phdr->p_filesz = msize; + phdr->p_memsz = msize; + phdr->p_align = 0; + + /* Increment number of program headers. */ + (elf->e_phnum)++; + } + return 0; +} + +static unsigned long init_fadump_header(unsigned long addr) +{ + struct fadump_crash_info_header *fdh; + + if (!addr) + return 0; + + fw_dump.fadumphdr_addr = addr; + fdh = __va(addr); + addr += sizeof(struct fadump_crash_info_header); + + memset(fdh, 0, sizeof(struct fadump_crash_info_header)); + fdh->magic_number = FADUMP_CRASH_INFO_MAGIC; + fdh->elfcorehdr_addr = addr; + + return addr; +} + static void register_fadump(void) { + unsigned long addr; + void *vaddr; + /* * If no memory is reserved then we can not register for firmware- * assisted dump. @@ -389,6 +604,16 @@ static void register_fadump(void) if (!fw_dump.reserve_dump_area_size) return; + fadump_setup_crash_memory_ranges(); + + addr = fdm.rmr_region.destination_address + fdm.rmr_region.source_len; + /* Initialize fadump crash info header. */ + addr = init_fadump_header(addr); + vaddr = __va(addr); + + pr_debug("Creating ELF core headers at %#016lx\n", addr); + fadump_create_elfcore_headers(vaddr); + /* register the future kernel dump with firmware. */ register_fw_dump(&fdm); } @@ -585,8 +810,14 @@ int __init setup_fadump(void) } fadump_show_config(); + /* + * If dump data is available then see if it is valid and prepare for + * saving it to the disk. + */ + if (fw_dump.dump_active) + process_fadump(fdm_active); /* Initialize the kernel dump memory structure for FAD registration. */ - if (fw_dump.reserve_dump_area_size) + else if (fw_dump.reserve_dump_area_size) init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start); fadump_init_files(); -- cgit v0.10.2 From ebaeb5ae24379b5b635dc1d1fa6df904bc95b4d9 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:14:45 +0000 Subject: fadump: Convert firmware-assisted cpu state dump data into elf notes. When registered for firmware assisted dump on powerpc, firmware preserves the registers for the active CPUs during a system crash. This patch reads the cpu register data stored in Firmware-assisted dump format (except for crashing cpu) and converts it into elf notes and updates the PT_NOTE program header accordingly. The exact register state for crashing cpu is saved to fadump crash info structure in scratch area during crash_fadump() and read during second kernel boot. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h index 9e172a5..6768195 100644 --- a/arch/powerpc/include/asm/fadump.h +++ b/arch/powerpc/include/asm/fadump.h @@ -65,6 +65,18 @@ /* Dump status flag */ #define FADUMP_ERROR_FLAG 0x2000 +#define FADUMP_CPU_ID_MASK ((1UL << 32) - 1) + +#define CPU_UNKNOWN (~((u32)0)) + +/* Utility macros */ +#define SKIP_TO_NEXT_CPU(reg_entry) \ +({ \ + while (reg_entry->reg_id != REG_ID("CPUEND")) \ + reg_entry++; \ + reg_entry++; \ +}) + /* Kernel Dump section info */ struct fadump_section { u32 request_flag; @@ -119,6 +131,9 @@ struct fw_dump { unsigned long reserve_bootvar; unsigned long fadumphdr_addr; + unsigned long cpu_notes_buf; + unsigned long cpu_notes_buf_size; + int ibm_configure_kernel_dump; unsigned long fadump_enabled:1; @@ -143,13 +158,40 @@ static inline u64 str_to_u64(const char *str) return val; } #define STR_TO_HEX(x) str_to_u64(x) +#define REG_ID(x) str_to_u64(x) #define FADUMP_CRASH_INFO_MAGIC STR_TO_HEX("FADMPINF") +#define REGSAVE_AREA_MAGIC STR_TO_HEX("REGSAVE") + +/* The firmware-assisted dump format. + * + * The register save area is an area in the partition's memory used to preserve + * the register contents (CPU state data) for the active CPUs during a firmware + * assisted dump. The dump format contains register save area header followed + * by register entries. Each list of registers for a CPU starts with + * "CPUSTRT" and ends with "CPUEND". + */ + +/* Register save area header. */ +struct fadump_reg_save_area_header { + u64 magic_number; + u32 version; + u32 num_cpu_offset; +}; + +/* Register entry. */ +struct fadump_reg_entry { + u64 reg_id; + u64 reg_value; +}; /* fadump crash info structure */ struct fadump_crash_info_header { u64 magic_number; u64 elfcorehdr_addr; + u32 crashing_cpu; + struct pt_regs regs; + struct cpumask cpu_online_mask; }; /* Crash memory ranges */ @@ -165,7 +207,9 @@ extern int early_init_dt_scan_fw_dump(unsigned long node, extern int fadump_reserve_mem(void); extern int setup_fadump(void); extern int is_fadump_active(void); +extern void crash_fadump(struct pt_regs *, const char *); #else /* CONFIG_FA_DUMP */ static inline int is_fadump_active(void) { return 0; } +static inline void crash_fadump(struct pt_regs *regs, const char *str) { } #endif #endif diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index 63857e1..da68bda 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -240,6 +240,7 @@ static unsigned long get_fadump_area_size(void) size += fw_dump.boot_memory_size; size += sizeof(struct fadump_crash_info_header); size += sizeof(struct elfhdr); /* ELF core header.*/ + size += sizeof(struct elf_phdr); /* place holder for cpu notes */ /* Program headers for crash memory regions. */ size += sizeof(struct elf_phdr) * (memblock_num_regions(memory) + 2); @@ -393,6 +394,285 @@ static void register_fw_dump(struct fadump_mem_struct *fdm) } } +void crash_fadump(struct pt_regs *regs, const char *str) +{ + struct fadump_crash_info_header *fdh = NULL; + + if (!fw_dump.dump_registered || !fw_dump.fadumphdr_addr) + return; + + fdh = __va(fw_dump.fadumphdr_addr); + crashing_cpu = smp_processor_id(); + fdh->crashing_cpu = crashing_cpu; + crash_save_vmcoreinfo(); + + if (regs) + fdh->regs = *regs; + else + ppc_save_regs(&fdh->regs); + + fdh->cpu_online_mask = *cpu_online_mask; + + /* Call ibm,os-term rtas call to trigger firmware assisted dump */ + rtas_os_term((char *)str); +} + +#define GPR_MASK 0xffffff0000000000 +static inline int fadump_gpr_index(u64 id) +{ + int i = -1; + char str[3]; + + if ((id & GPR_MASK) == REG_ID("GPR")) { + /* get the digits at the end */ + id &= ~GPR_MASK; + id >>= 24; + str[2] = '\0'; + str[1] = id & 0xff; + str[0] = (id >> 8) & 0xff; + sscanf(str, "%d", &i); + if (i > 31) + i = -1; + } + return i; +} + +static inline void fadump_set_regval(struct pt_regs *regs, u64 reg_id, + u64 reg_val) +{ + int i; + + i = fadump_gpr_index(reg_id); + if (i >= 0) + regs->gpr[i] = (unsigned long)reg_val; + else if (reg_id == REG_ID("NIA")) + regs->nip = (unsigned long)reg_val; + else if (reg_id == REG_ID("MSR")) + regs->msr = (unsigned long)reg_val; + else if (reg_id == REG_ID("CTR")) + regs->ctr = (unsigned long)reg_val; + else if (reg_id == REG_ID("LR")) + regs->link = (unsigned long)reg_val; + else if (reg_id == REG_ID("XER")) + regs->xer = (unsigned long)reg_val; + else if (reg_id == REG_ID("CR")) + regs->ccr = (unsigned long)reg_val; + else if (reg_id == REG_ID("DAR")) + regs->dar = (unsigned long)reg_val; + else if (reg_id == REG_ID("DSISR")) + regs->dsisr = (unsigned long)reg_val; +} + +static struct fadump_reg_entry* +fadump_read_registers(struct fadump_reg_entry *reg_entry, struct pt_regs *regs) +{ + memset(regs, 0, sizeof(struct pt_regs)); + + while (reg_entry->reg_id != REG_ID("CPUEND")) { + fadump_set_regval(regs, reg_entry->reg_id, + reg_entry->reg_value); + reg_entry++; + } + reg_entry++; + return reg_entry; +} + +static u32 *fadump_append_elf_note(u32 *buf, char *name, unsigned type, + void *data, size_t data_len) +{ + struct elf_note note; + + note.n_namesz = strlen(name) + 1; + note.n_descsz = data_len; + note.n_type = type; + memcpy(buf, ¬e, sizeof(note)); + buf += (sizeof(note) + 3)/4; + memcpy(buf, name, note.n_namesz); + buf += (note.n_namesz + 3)/4; + memcpy(buf, data, note.n_descsz); + buf += (note.n_descsz + 3)/4; + + return buf; +} + +static void fadump_final_note(u32 *buf) +{ + struct elf_note note; + + note.n_namesz = 0; + note.n_descsz = 0; + note.n_type = 0; + memcpy(buf, ¬e, sizeof(note)); +} + +static u32 *fadump_regs_to_elf_notes(u32 *buf, struct pt_regs *regs) +{ + struct elf_prstatus prstatus; + + memset(&prstatus, 0, sizeof(prstatus)); + /* + * FIXME: How do i get PID? Do I really need it? + * prstatus.pr_pid = ???? + */ + elf_core_copy_kernel_regs(&prstatus.pr_reg, regs); + buf = fadump_append_elf_note(buf, KEXEC_CORE_NOTE_NAME, NT_PRSTATUS, + &prstatus, sizeof(prstatus)); + return buf; +} + +static void fadump_update_elfcore_header(char *bufp) +{ + struct elfhdr *elf; + struct elf_phdr *phdr; + + elf = (struct elfhdr *)bufp; + bufp += sizeof(struct elfhdr); + + /* First note is a place holder for cpu notes info. */ + phdr = (struct elf_phdr *)bufp; + + if (phdr->p_type == PT_NOTE) { + phdr->p_paddr = fw_dump.cpu_notes_buf; + phdr->p_offset = phdr->p_paddr; + phdr->p_filesz = fw_dump.cpu_notes_buf_size; + phdr->p_memsz = fw_dump.cpu_notes_buf_size; + } + return; +} + +static void *fadump_cpu_notes_buf_alloc(unsigned long size) +{ + void *vaddr; + struct page *page; + unsigned long order, count, i; + + order = get_order(size); + vaddr = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, order); + if (!vaddr) + return NULL; + + count = 1 << order; + page = virt_to_page(vaddr); + for (i = 0; i < count; i++) + SetPageReserved(page + i); + return vaddr; +} + +static void fadump_cpu_notes_buf_free(unsigned long vaddr, unsigned long size) +{ + struct page *page; + unsigned long order, count, i; + + order = get_order(size); + count = 1 << order; + page = virt_to_page(vaddr); + for (i = 0; i < count; i++) + ClearPageReserved(page + i); + __free_pages(page, order); +} + +/* + * Read CPU state dump data and convert it into ELF notes. + * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be + * used to access the data to allow for additional fields to be added without + * affecting compatibility. Each list of registers for a CPU starts with + * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes, + * 8 Byte ASCII identifier and 8 Byte register value. The register entry + * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part + * of register value. For more details refer to PAPR document. + * + * Only for the crashing cpu we ignore the CPU dump data and get exact + * state from fadump crash info structure populated by first kernel at the + * time of crash. + */ +static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm) +{ + struct fadump_reg_save_area_header *reg_header; + struct fadump_reg_entry *reg_entry; + struct fadump_crash_info_header *fdh = NULL; + void *vaddr; + unsigned long addr; + u32 num_cpus, *note_buf; + struct pt_regs regs; + int i, rc = 0, cpu = 0; + + if (!fdm->cpu_state_data.bytes_dumped) + return -EINVAL; + + addr = fdm->cpu_state_data.destination_address; + vaddr = __va(addr); + + reg_header = vaddr; + if (reg_header->magic_number != REGSAVE_AREA_MAGIC) { + printk(KERN_ERR "Unable to read register save area.\n"); + return -ENOENT; + } + pr_debug("--------CPU State Data------------\n"); + pr_debug("Magic Number: %llx\n", reg_header->magic_number); + pr_debug("NumCpuOffset: %x\n", reg_header->num_cpu_offset); + + vaddr += reg_header->num_cpu_offset; + num_cpus = *((u32 *)(vaddr)); + pr_debug("NumCpus : %u\n", num_cpus); + vaddr += sizeof(u32); + reg_entry = (struct fadump_reg_entry *)vaddr; + + /* Allocate buffer to hold cpu crash notes. */ + fw_dump.cpu_notes_buf_size = num_cpus * sizeof(note_buf_t); + fw_dump.cpu_notes_buf_size = PAGE_ALIGN(fw_dump.cpu_notes_buf_size); + note_buf = fadump_cpu_notes_buf_alloc(fw_dump.cpu_notes_buf_size); + if (!note_buf) { + printk(KERN_ERR "Failed to allocate 0x%lx bytes for " + "cpu notes buffer\n", fw_dump.cpu_notes_buf_size); + return -ENOMEM; + } + fw_dump.cpu_notes_buf = __pa(note_buf); + + pr_debug("Allocated buffer for cpu notes of size %ld at %p\n", + (num_cpus * sizeof(note_buf_t)), note_buf); + + if (fw_dump.fadumphdr_addr) + fdh = __va(fw_dump.fadumphdr_addr); + + for (i = 0; i < num_cpus; i++) { + if (reg_entry->reg_id != REG_ID("CPUSTRT")) { + printk(KERN_ERR "Unable to read CPU state data\n"); + rc = -ENOENT; + goto error_out; + } + /* Lower 4 bytes of reg_value contains logical cpu id */ + cpu = reg_entry->reg_value & FADUMP_CPU_ID_MASK; + if (!cpumask_test_cpu(cpu, &fdh->cpu_online_mask)) { + SKIP_TO_NEXT_CPU(reg_entry); + continue; + } + pr_debug("Reading register data for cpu %d...\n", cpu); + if (fdh && fdh->crashing_cpu == cpu) { + regs = fdh->regs; + note_buf = fadump_regs_to_elf_notes(note_buf, ®s); + SKIP_TO_NEXT_CPU(reg_entry); + } else { + reg_entry++; + reg_entry = fadump_read_registers(reg_entry, ®s); + note_buf = fadump_regs_to_elf_notes(note_buf, ®s); + } + } + fadump_final_note(note_buf); + + pr_debug("Updating elfcore header (%llx) with cpu notes\n", + fdh->elfcorehdr_addr); + fadump_update_elfcore_header((char *)__va(fdh->elfcorehdr_addr)); + return 0; + +error_out: + fadump_cpu_notes_buf_free((unsigned long)__va(fw_dump.cpu_notes_buf), + fw_dump.cpu_notes_buf_size); + fw_dump.cpu_notes_buf = 0; + fw_dump.cpu_notes_buf_size = 0; + return rc; + +} + /* * Validate and process the dump data stored by firmware before exporting * it through '/proc/vmcore'. @@ -400,18 +680,21 @@ static void register_fw_dump(struct fadump_mem_struct *fdm) static int __init process_fadump(const struct fadump_mem_struct *fdm_active) { struct fadump_crash_info_header *fdh; + int rc = 0; if (!fdm_active || !fw_dump.fadumphdr_addr) return -EINVAL; /* Check if the dump data is valid. */ if ((fdm_active->header.dump_status_flag == FADUMP_ERROR_FLAG) || + (fdm_active->cpu_state_data.error_flags != 0) || (fdm_active->rmr_region.error_flags != 0)) { printk(KERN_ERR "Dump taken by platform is not valid\n"); return -EINVAL; } - if (fdm_active->rmr_region.bytes_dumped != - fdm_active->rmr_region.source_len) { + if ((fdm_active->rmr_region.bytes_dumped != + fdm_active->rmr_region.source_len) || + !fdm_active->cpu_state_data.bytes_dumped) { printk(KERN_ERR "Dump taken by platform is incomplete\n"); return -EINVAL; } @@ -423,6 +706,10 @@ static int __init process_fadump(const struct fadump_mem_struct *fdm_active) return -EINVAL; } + rc = fadump_build_cpu_notes(fdm_active); + if (rc) + return rc; + /* * We are done validating dump info and elfcore header is now ready * to be exported. set elfcorehdr_addr so that vmcore module will @@ -537,6 +824,27 @@ static int fadump_create_elfcore_headers(char *bufp) elf = (struct elfhdr *)bufp; bufp += sizeof(struct elfhdr); + /* + * setup ELF PT_NOTE, place holder for cpu notes info. The notes info + * will be populated during second kernel boot after crash. Hence + * this PT_NOTE will always be the first elf note. + * + * NOTE: Any new ELF note addition should be placed after this note. + */ + phdr = (struct elf_phdr *)bufp; + bufp += sizeof(struct elf_phdr); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_vaddr = 0; + phdr->p_align = 0; + + phdr->p_offset = 0; + phdr->p_paddr = 0; + phdr->p_filesz = 0; + phdr->p_memsz = 0; + + (elf->e_phnum)++; + /* setup PT_LOAD sections. */ for (i = 0; i < crash_mem_ranges; i++) { @@ -588,6 +896,8 @@ static unsigned long init_fadump_header(unsigned long addr) memset(fdh, 0, sizeof(struct fadump_crash_info_header)); fdh->magic_number = FADUMP_CRASH_INFO_MAGIC; fdh->elfcorehdr_addr = addr; + /* We will set the crashing cpu id in crash_fadump() during crash. */ + fdh->crashing_cpu = CPU_UNKNOWN; return addr; } diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 77bb77d..4e62a56 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -61,6 +61,7 @@ #include #include #include +#include #include "setup.h" @@ -639,6 +640,11 @@ EXPORT_SYMBOL(check_legacy_ioport); static int ppc_panic_event(struct notifier_block *this, unsigned long event, void *ptr) { + /* + * If firmware-assisted dump has been registered then trigger + * firmware-assisted dump and let firmware handle everything else. + */ + crash_fadump(NULL, ptr); ppc_md.panic(ptr); /* May not return */ return NOTIFY_DONE; } diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index c091527..5d40e59 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -57,6 +57,7 @@ #include #include #include +#include #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) int (*__debugger)(struct pt_regs *regs) __read_mostly; @@ -145,6 +146,8 @@ static void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, arch_spin_unlock(&die_lock); raw_local_irq_restore(flags); + crash_fadump(regs, "die oops"); + /* * A system reset (0x100) is a request to dump, so we always send * it through the crashdump code. -- cgit v0.10.2 From d34c5f26cf7de52a72ee064698817a5a39b91767 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:14:53 +0000 Subject: fadump: Add PT_NOTE program header for vmcoreinfo Introduce a PT_NOTE program header that points to physical address of vmcoreinfo_note buffer declared in kernel/kexec.c. The vmcoreinfo note buffer is populated during crash_fadump() at the time of system crash. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index da68bda..a83bc90 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -814,6 +814,19 @@ static void fadump_setup_crash_memory_ranges(void) } } +/* + * If the given physical address falls within the boot memory region then + * return the relocated address that points to the dump region reserved + * for saving initial boot memory contents. + */ +static inline unsigned long fadump_relocate(unsigned long paddr) +{ + if (paddr > RMA_START && paddr < fw_dump.boot_memory_size) + return fdm.rmr_region.destination_address + paddr; + else + return paddr; +} + static int fadump_create_elfcore_headers(char *bufp) { struct elfhdr *elf; @@ -845,6 +858,22 @@ static int fadump_create_elfcore_headers(char *bufp) (elf->e_phnum)++; + /* setup ELF PT_NOTE for vmcoreinfo */ + phdr = (struct elf_phdr *)bufp; + bufp += sizeof(struct elf_phdr); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_vaddr = 0; + phdr->p_align = 0; + + phdr->p_paddr = fadump_relocate(paddr_vmcoreinfo_note()); + phdr->p_offset = phdr->p_paddr; + phdr->p_memsz = vmcoreinfo_max_size; + phdr->p_filesz = vmcoreinfo_max_size; + + /* Increment number of program headers. */ + (elf->e_phnum)++; + /* setup PT_LOAD sections. */ for (i = 0; i < crash_mem_ranges; i++) { -- cgit v0.10.2 From 162573937679ff36c9acd54268c047199dab564e Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:15:00 +0000 Subject: fadump: Introduce cleanup routine to invalidate /proc/vmcore. With the firmware-assisted dump support we don't require a reboot when we are in second kernel after crash. The second kernel after crash is a normal kernel boot and has knowledge about entire system RAM with the page tables initialized for entire system RAM. Hence once the dump is saved to disk, we can just release the reserved memory area for general use and continue with second kernel as production kernel. Hence when we release the reserved memory that contains dump data, the '/proc/vmcore' will not be valid anymore. Hence this patch introduces a cleanup routine that invalidates and removes the /proc/vmcore file. This routine will be invoked before we release the reserved dump memory area. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index b0f450a..0d5071d 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -700,3 +700,26 @@ static int __init vmcore_init(void) return 0; } module_init(vmcore_init) + +/* Cleanup function for vmcore module. */ +void vmcore_cleanup(void) +{ + struct list_head *pos, *next; + + if (proc_vmcore) { + remove_proc_entry(proc_vmcore->name, proc_vmcore->parent); + proc_vmcore = NULL; + } + + /* clear the vmcore list. */ + list_for_each_safe(pos, next, &vmcore_list) { + struct vmcore *m; + + m = list_entry(pos, struct vmcore, list); + list_del(&m->list); + kfree(m); + } + kfree(elfcorebuf); + elfcorebuf = NULL; +} +EXPORT_SYMBOL_GPL(vmcore_cleanup); -- cgit v0.10.2 From b500afff11f64227ca69fd2d05986d08d9573935 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:15:08 +0000 Subject: fadump: Invalidate registration and release reserved memory for general use. This patch introduces an sysfs interface '/sys/kernel/fadump_release_mem' to invalidate the last fadump registration, invalidate '/proc/vmcore', release the reserved memory for general use and re-register for future kernel dump. Once the dump is copied to the disk, unlike phyp dump, the userspace tool can release all the memory reserved for dump with one single operation of echo 1 to '/sys/kernel/fadump_release_mem'. Release the reserved memory region excluding the size of the memory required for future kernel dump registration. And therefore, unlike kdump, Fadump doesn't need a 2nd reboot to get back the system to the production configuration. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/fadump.h b/arch/powerpc/include/asm/fadump.h index 6768195..88dbf96 100644 --- a/arch/powerpc/include/asm/fadump.h +++ b/arch/powerpc/include/asm/fadump.h @@ -208,6 +208,9 @@ extern int fadump_reserve_mem(void); extern int setup_fadump(void); extern int is_fadump_active(void); extern void crash_fadump(struct pt_regs *, const char *); +extern void fadump_cleanup(void); + +extern void vmcore_cleanup(void); #else /* CONFIG_FA_DUMP */ static inline int is_fadump_active(void) { return 0; } static inline void crash_fadump(struct pt_regs *regs, const char *str) { } diff --git a/arch/powerpc/kernel/fadump.c b/arch/powerpc/kernel/fadump.c index a83bc90..cfe7a38 100644 --- a/arch/powerpc/kernel/fadump.c +++ b/arch/powerpc/kernel/fadump.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -984,6 +986,132 @@ static int fadump_unregister_dump(struct fadump_mem_struct *fdm) return 0; } +static int fadump_invalidate_dump(struct fadump_mem_struct *fdm) +{ + int rc = 0; + unsigned int wait_time; + + pr_debug("Invalidating firmware-assisted dump registration\n"); + + /* TODO: Add upper time limit for the delay */ + do { + rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL, + FADUMP_INVALIDATE, fdm, + sizeof(struct fadump_mem_struct)); + + wait_time = rtas_busy_delay_time(rc); + if (wait_time) + mdelay(wait_time); + } while (wait_time); + + if (rc) { + printk(KERN_ERR "Failed to invalidate firmware-assisted dump " + "rgistration. unexpected error(%d).\n", rc); + return rc; + } + fw_dump.dump_active = 0; + fdm_active = NULL; + return 0; +} + +void fadump_cleanup(void) +{ + /* Invalidate the registration only if dump is active. */ + if (fw_dump.dump_active) { + init_fadump_mem_struct(&fdm, + fdm_active->cpu_state_data.destination_address); + fadump_invalidate_dump(&fdm); + } +} + +/* + * Release the memory that was reserved in early boot to preserve the memory + * contents. The released memory will be available for general use. + */ +static void fadump_release_memory(unsigned long begin, unsigned long end) +{ + unsigned long addr; + unsigned long ra_start, ra_end; + + ra_start = fw_dump.reserve_dump_area_start; + ra_end = ra_start + fw_dump.reserve_dump_area_size; + + for (addr = begin; addr < end; addr += PAGE_SIZE) { + /* + * exclude the dump reserve area. Will reuse it for next + * fadump registration. + */ + if (addr <= ra_end && ((addr + PAGE_SIZE) > ra_start)) + continue; + + ClearPageReserved(pfn_to_page(addr >> PAGE_SHIFT)); + init_page_count(pfn_to_page(addr >> PAGE_SHIFT)); + free_page((unsigned long)__va(addr)); + totalram_pages++; + } +} + +static void fadump_invalidate_release_mem(void) +{ + unsigned long reserved_area_start, reserved_area_end; + unsigned long destination_address; + + mutex_lock(&fadump_mutex); + if (!fw_dump.dump_active) { + mutex_unlock(&fadump_mutex); + return; + } + + destination_address = fdm_active->cpu_state_data.destination_address; + fadump_cleanup(); + mutex_unlock(&fadump_mutex); + + /* + * Save the current reserved memory bounds we will require them + * later for releasing the memory for general use. + */ + reserved_area_start = fw_dump.reserve_dump_area_start; + reserved_area_end = reserved_area_start + + fw_dump.reserve_dump_area_size; + /* + * Setup reserve_dump_area_start and its size so that we can + * reuse this reserved memory for Re-registration. + */ + fw_dump.reserve_dump_area_start = destination_address; + fw_dump.reserve_dump_area_size = get_fadump_area_size(); + + fadump_release_memory(reserved_area_start, reserved_area_end); + if (fw_dump.cpu_notes_buf) { + fadump_cpu_notes_buf_free( + (unsigned long)__va(fw_dump.cpu_notes_buf), + fw_dump.cpu_notes_buf_size); + fw_dump.cpu_notes_buf = 0; + fw_dump.cpu_notes_buf_size = 0; + } + /* Initialize the kernel dump memory structure for FAD registration. */ + init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start); +} + +static ssize_t fadump_release_memory_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + if (!fw_dump.dump_active) + return -EPERM; + + if (buf[0] == '1') { + /* + * Take away the '/proc/vmcore'. We are releasing the dump + * memory, hence it will not be valid anymore. + */ + vmcore_cleanup(); + fadump_invalidate_release_mem(); + + } else + return -EINVAL; + return count; +} + static ssize_t fadump_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -1043,10 +1171,13 @@ static int fadump_region_show(struct seq_file *m, void *private) if (!fw_dump.fadump_enabled) return 0; + mutex_lock(&fadump_mutex); if (fdm_active) fdm_ptr = fdm_active; - else + else { + mutex_unlock(&fadump_mutex); fdm_ptr = &fdm; + } seq_printf(m, "CPU : [%#016llx-%#016llx] %#llx bytes, " @@ -1076,7 +1207,7 @@ static int fadump_region_show(struct seq_file *m, void *private) if (!fdm_active || (fw_dump.reserve_dump_area_start == fdm_ptr->cpu_state_data.destination_address)) - return 0; + goto out; /* Dump is active. Show reserved memory region. */ seq_printf(m, @@ -1088,9 +1219,15 @@ static int fadump_region_show(struct seq_file *m, void *private) fw_dump.reserve_dump_area_start, fdm_ptr->cpu_state_data.destination_address - fw_dump.reserve_dump_area_start); +out: + if (fdm_active) + mutex_unlock(&fadump_mutex); return 0; } +static struct kobj_attribute fadump_release_attr = __ATTR(fadump_release_mem, + 0200, NULL, + fadump_release_memory_store); static struct kobj_attribute fadump_attr = __ATTR(fadump_enabled, 0444, fadump_enabled_show, NULL); @@ -1131,6 +1268,13 @@ static void fadump_init_files(void) if (!debugfs_file) printk(KERN_ERR "fadump: unable to create debugfs file" " fadump_region\n"); + + if (fw_dump.dump_active) { + rc = sysfs_create_file(kernel_kobj, &fadump_release_attr.attr); + if (rc) + printk(KERN_ERR "fadump: unable to create sysfs file" + " fadump_release_mem (%d)\n", rc); + } return; } @@ -1153,8 +1297,14 @@ int __init setup_fadump(void) * If dump data is available then see if it is valid and prepare for * saving it to the disk. */ - if (fw_dump.dump_active) - process_fadump(fdm_active); + if (fw_dump.dump_active) { + /* + * if dump process fails then invalidate the registration + * and release memory before proceeding for re-registration. + */ + if (process_fadump(fdm_active) < 0) + fadump_invalidate_release_mem(); + } /* Initialize the kernel dump memory structure for FAD registration. */ else if (fw_dump.reserve_dump_area_size) init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start); -- cgit v0.10.2 From 67b43b9d7ced37a2e72e2c3e06464aa0a5be95f9 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:15:15 +0000 Subject: fadump: Invalidate the fadump registration during machine shutdown. If dump is active during system reboot, shutdown or halt then invalidate the fadump registration as it does not get invalidated automatically. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 4e62a56..b0ebdea 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -110,6 +110,14 @@ EXPORT_SYMBOL(ppc_do_canonicalize_irqs); /* also used by kexec */ void machine_shutdown(void) { +#ifdef CONFIG_FA_DUMP + /* + * if fadump is active, cleanup the fadump registration before we + * shutdown. + */ + fadump_cleanup(); +#endif + if (ppc_md.machine_shutdown) ppc_md.machine_shutdown(); } -- cgit v0.10.2 From 12d9299241241200e4f34f3b02f206fa8384a923 Mon Sep 17 00:00:00 2001 From: Mahesh Salgaonkar Date: Thu, 16 Feb 2012 01:15:23 +0000 Subject: fadump: Remove the phyp assisted dump code. Remove the phyp assisted dump implementation which is not is use. Signed-off-by: Mahesh Salgaonkar Signed-off-by: Benjamin Herrenschmidt diff --git a/Documentation/powerpc/phyp-assisted-dump.txt b/Documentation/powerpc/phyp-assisted-dump.txt deleted file mode 100644 index ad34020..0000000 --- a/Documentation/powerpc/phyp-assisted-dump.txt +++ /dev/null @@ -1,127 +0,0 @@ - - Hypervisor-Assisted Dump - ------------------------ - November 2007 - -The goal of hypervisor-assisted dump is to enable the dump of -a crashed system, and to do so from a fully-reset system, and -to minimize the total elapsed time until the system is back -in production use. - -As compared to kdump or other strategies, hypervisor-assisted -dump offers several strong, practical advantages: - --- Unlike kdump, the system has been reset, and loaded - with a fresh copy of the kernel. In particular, - PCI and I/O devices have been reinitialized and are - in a clean, consistent state. --- As the dump is performed, the dumped memory becomes - immediately available to the system for normal use. --- After the dump is completed, no further reboots are - required; the system will be fully usable, and running - in its normal, production mode on its normal kernel. - -The above can only be accomplished by coordination with, -and assistance from the hypervisor. The procedure is -as follows: - --- When a system crashes, the hypervisor will save - the low 256MB of RAM to a previously registered - save region. It will also save system state, system - registers, and hardware PTE's. - --- After the low 256MB area has been saved, the - hypervisor will reset PCI and other hardware state. - It will *not* clear RAM. It will then launch the - bootloader, as normal. - --- The freshly booted kernel will notice that there - is a new node (ibm,dump-kernel) in the device tree, - indicating that there is crash data available from - a previous boot. It will boot into only 256MB of RAM, - reserving the rest of system memory. - --- Userspace tools will parse /sys/kernel/release_region - and read /proc/vmcore to obtain the contents of memory, - which holds the previous crashed kernel. The userspace - tools may copy this info to disk, or network, nas, san, - iscsi, etc. as desired. - - For Example: the values in /sys/kernel/release-region - would look something like this (address-range pairs). - CPU:0x177fee000-0x10000: HPTE:0x177ffe020-0x1000: / - DUMP:0x177fff020-0x10000000, 0x10000000-0x16F1D370A - --- As the userspace tools complete saving a portion of - dump, they echo an offset and size to - /sys/kernel/release_region to release the reserved - memory back to general use. - - An example of this is: - "echo 0x40000000 0x10000000 > /sys/kernel/release_region" - which will release 256MB at the 1GB boundary. - -Please note that the hypervisor-assisted dump feature -is only available on Power6-based systems with recent -firmware versions. - -Implementation details: ----------------------- - -During boot, a check is made to see if firmware supports -this feature on this particular machine. If it does, then -we check to see if a active dump is waiting for us. If yes -then everything but 256 MB of RAM is reserved during early -boot. This area is released once we collect a dump from user -land scripts that are run. If there is dump data, then -the /sys/kernel/release_region file is created, and -the reserved memory is held. - -If there is no waiting dump data, then only the highest -256MB of the ram is reserved as a scratch area. This area -is *not* released: this region will be kept permanently -reserved, so that it can act as a receptacle for a copy -of the low 256MB in the case a crash does occur. See, -however, "open issues" below, as to whether -such a reserved region is really needed. - -Currently the dump will be copied from /proc/vmcore to a -a new file upon user intervention. The starting address -to be read and the range for each data point in provided -in /sys/kernel/release_region. - -The tools to examine the dump will be same as the ones -used for kdump. - -General notes: --------------- -Security: please note that there are potential security issues -with any sort of dump mechanism. In particular, plaintext -(unencrypted) data, and possibly passwords, may be present in -the dump data. Userspace tools must take adequate precautions to -preserve security. - -Open issues/ToDo: ------------- - o The various code paths that tell the hypervisor that a crash - occurred, vs. it simply being a normal reboot, should be - reviewed, and possibly clarified/fixed. - - o Instead of using /sys/kernel, should there be a /sys/dump - instead? There is a dump_subsys being created by the s390 code, - perhaps the pseries code should use a similar layout as well. - - o Is reserving a 256MB region really required? The goal of - reserving a 256MB scratch area is to make sure that no - important crash data is clobbered when the hypervisor - save low mem to the scratch area. But, if one could assure - that nothing important is located in some 256MB area, then - it would not need to be reserved. Something that can be - improved in subsequent versions. - - o Still working the kdump team to integrate this with kdump, - some work remains but this would not affect the current - patches. - - o Still need to write a shell script, to copy the dump away. - Currently I am parsing it manually. diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index afa4dab..c713111 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -376,16 +376,6 @@ config CRASH_DUMP The same kernel binary can be used as production kernel and dump capture kernel. -config PHYP_DUMP - bool "Hypervisor-assisted dump (EXPERIMENTAL)" - depends on PPC_PSERIES && EXPERIMENTAL - help - Hypervisor-assisted dump is meant to be a kdump replacement - offering robustness and speed not possible without system - hypervisor assistance. - - If unsure, say "N" - config FA_DUMP bool "Firmware-assisted dump" depends on PPC64 && PPC_RTAS && CRASH_DUMP diff --git a/arch/powerpc/include/asm/phyp_dump.h b/arch/powerpc/include/asm/phyp_dump.h deleted file mode 100644 index fa74c6c..0000000 --- a/arch/powerpc/include/asm/phyp_dump.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Hypervisor-assisted dump - * - * Linas Vepstas, Manish Ahuja 2008 - * Copyright 2008 IBM Corp. - * - * 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. - */ - -#ifndef _PPC64_PHYP_DUMP_H -#define _PPC64_PHYP_DUMP_H - -#ifdef CONFIG_PHYP_DUMP - -/* The RMR region will be saved for later dumping - * whenever the kernel crashes. Set this to 256MB. */ -#define PHYP_DUMP_RMR_START 0x0 -#define PHYP_DUMP_RMR_END (1UL<<28) - -struct phyp_dump { - /* Memory that is reserved during very early boot. */ - unsigned long init_reserve_start; - unsigned long init_reserve_size; - /* cmd line options during boot */ - unsigned long reserve_bootvar; - unsigned long phyp_dump_at_boot; - /* Check status during boot if dump supported, active & present*/ - unsigned long phyp_dump_configured; - unsigned long phyp_dump_is_active; - /* store cpu & hpte size */ - unsigned long cpu_state_size; - unsigned long hpte_region_size; - /* previous scratch area values */ - unsigned long reserved_scratch_addr; - unsigned long reserved_scratch_size; -}; - -extern struct phyp_dump *phyp_dump_info; - -int early_init_dt_scan_phyp_dump(unsigned long node, - const char *uname, int depth, void *data); - -#endif /* CONFIG_PHYP_DUMP */ -#endif /* _PPC64_PHYP_DUMP_H */ diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 70222b3..89e850a 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include @@ -616,86 +615,6 @@ static void __init early_reserve_mem(void) } } -#ifdef CONFIG_PHYP_DUMP -/** - * phyp_dump_calculate_reserve_size() - reserve variable boot area 5% or arg - * - * Function to find the largest size we need to reserve - * during early boot process. - * - * It either looks for boot param and returns that OR - * returns larger of 256 or 5% rounded down to multiples of 256MB. - * - */ -static inline unsigned long phyp_dump_calculate_reserve_size(void) -{ - unsigned long tmp; - - if (phyp_dump_info->reserve_bootvar) - return phyp_dump_info->reserve_bootvar; - - /* divide by 20 to get 5% of value */ - tmp = memblock_end_of_DRAM(); - do_div(tmp, 20); - - /* round it down in multiples of 256 */ - tmp = tmp & ~0x0FFFFFFFUL; - - return (tmp > PHYP_DUMP_RMR_END ? tmp : PHYP_DUMP_RMR_END); -} - -/** - * phyp_dump_reserve_mem() - reserve all not-yet-dumped mmemory - * - * This routine may reserve memory regions in the kernel only - * if the system is supported and a dump was taken in last - * boot instance or if the hardware is supported and the - * scratch area needs to be setup. In other instances it returns - * without reserving anything. The memory in case of dump being - * active is freed when the dump is collected (by userland tools). - */ -static void __init phyp_dump_reserve_mem(void) -{ - unsigned long base, size; - unsigned long variable_reserve_size; - - if (!phyp_dump_info->phyp_dump_configured) { - printk(KERN_ERR "Phyp-dump not supported on this hardware\n"); - return; - } - - if (!phyp_dump_info->phyp_dump_at_boot) { - printk(KERN_INFO "Phyp-dump disabled at boot time\n"); - return; - } - - variable_reserve_size = phyp_dump_calculate_reserve_size(); - - if (phyp_dump_info->phyp_dump_is_active) { - /* Reserve *everything* above RMR.Area freed by userland tools*/ - base = variable_reserve_size; - size = memblock_end_of_DRAM() - base; - - /* XXX crashed_ram_end is wrong, since it may be beyond - * the memory_limit, it will need to be adjusted. */ - memblock_reserve(base, size); - - phyp_dump_info->init_reserve_start = base; - phyp_dump_info->init_reserve_size = size; - } else { - size = phyp_dump_info->cpu_state_size + - phyp_dump_info->hpte_region_size + - variable_reserve_size; - base = memblock_end_of_DRAM() - size; - memblock_reserve(base, size); - phyp_dump_info->init_reserve_start = base; - phyp_dump_info->init_reserve_size = size; - } -} -#else -static inline void __init phyp_dump_reserve_mem(void) {} -#endif /* CONFIG_PHYP_DUMP && CONFIG_PPC_RTAS */ - void __init early_init_devtree(void *params) { phys_addr_t limit; @@ -715,11 +634,6 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_opal, NULL); #endif -#ifdef CONFIG_PHYP_DUMP - /* scan tree to see if dump occurred during last boot */ - of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL); -#endif - #ifdef CONFIG_FA_DUMP /* scan tree to see if dump is active during last boot */ of_scan_flat_dt(early_init_dt_scan_fw_dump, NULL); @@ -765,7 +679,6 @@ void __init early_init_devtree(void *params) #endif reserve_crashkernel(); early_reserve_mem(); - phyp_dump_reserve_mem(); /* * Ensure that total memory size is page-aligned, because otherwise diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 236db46..67b3e6e 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o obj-$(CONFIG_HVCS) += hvcserver.o obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o -obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o obj-$(CONFIG_CMM) += cmm.o obj-$(CONFIG_DTL) += dtl.o obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o diff --git a/arch/powerpc/platforms/pseries/phyp_dump.c b/arch/powerpc/platforms/pseries/phyp_dump.c deleted file mode 100644 index 6e7742d..0000000 --- a/arch/powerpc/platforms/pseries/phyp_dump.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Hypervisor-assisted dump - * - * Linas Vepstas, Manish Ahuja 2008 - * Copyright 2008 IBM Corp. - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* Variables, used to communicate data between early boot and late boot */ -static struct phyp_dump phyp_dump_vars; -struct phyp_dump *phyp_dump_info = &phyp_dump_vars; - -static int ibm_configure_kernel_dump; -/* ------------------------------------------------- */ -/* RTAS interfaces to declare the dump regions */ - -struct dump_section { - u32 dump_flags; - u16 source_type; - u16 error_flags; - u64 source_address; - u64 source_length; - u64 length_copied; - u64 destination_address; -}; - -struct phyp_dump_header { - u32 version; - u16 num_of_sections; - u16 status; - - u32 first_offset_section; - u32 dump_disk_section; - u64 block_num_dd; - u64 num_of_blocks_dd; - u32 offset_dd; - u32 maxtime_to_auto; - /* No dump disk path string used */ - - struct dump_section cpu_data; - struct dump_section hpte_data; - struct dump_section kernel_data; -}; - -/* The dump header *must be* in low memory, so .bss it */ -static struct phyp_dump_header phdr; - -#define NUM_DUMP_SECTIONS 3 -#define DUMP_HEADER_VERSION 0x1 -#define DUMP_REQUEST_FLAG 0x1 -#define DUMP_SOURCE_CPU 0x0001 -#define DUMP_SOURCE_HPTE 0x0002 -#define DUMP_SOURCE_RMO 0x0011 -#define DUMP_ERROR_FLAG 0x2000 -#define DUMP_TRIGGERED 0x4000 -#define DUMP_PERFORMED 0x8000 - - -/** - * init_dump_header() - initialize the header declaring a dump - * Returns: length of dump save area. - * - * When the hypervisor saves crashed state, it needs to put - * it somewhere. The dump header tells the hypervisor where - * the data can be saved. - */ -static unsigned long init_dump_header(struct phyp_dump_header *ph) -{ - unsigned long addr_offset = 0; - - /* Set up the dump header */ - ph->version = DUMP_HEADER_VERSION; - ph->num_of_sections = NUM_DUMP_SECTIONS; - ph->status = 0; - - ph->first_offset_section = - (u32)offsetof(struct phyp_dump_header, cpu_data); - ph->dump_disk_section = 0; - ph->block_num_dd = 0; - ph->num_of_blocks_dd = 0; - ph->offset_dd = 0; - - ph->maxtime_to_auto = 0; /* disabled */ - - /* The first two sections are mandatory */ - ph->cpu_data.dump_flags = DUMP_REQUEST_FLAG; - ph->cpu_data.source_type = DUMP_SOURCE_CPU; - ph->cpu_data.source_address = 0; - ph->cpu_data.source_length = phyp_dump_info->cpu_state_size; - ph->cpu_data.destination_address = addr_offset; - addr_offset += phyp_dump_info->cpu_state_size; - - ph->hpte_data.dump_flags = DUMP_REQUEST_FLAG; - ph->hpte_data.source_type = DUMP_SOURCE_HPTE; - ph->hpte_data.source_address = 0; - ph->hpte_data.source_length = phyp_dump_info->hpte_region_size; - ph->hpte_data.destination_address = addr_offset; - addr_offset += phyp_dump_info->hpte_region_size; - - /* This section describes the low kernel region */ - ph->kernel_data.dump_flags = DUMP_REQUEST_FLAG; - ph->kernel_data.source_type = DUMP_SOURCE_RMO; - ph->kernel_data.source_address = PHYP_DUMP_RMR_START; - ph->kernel_data.source_length = PHYP_DUMP_RMR_END; - ph->kernel_data.destination_address = addr_offset; - addr_offset += ph->kernel_data.source_length; - - return addr_offset; -} - -static void print_dump_header(const struct phyp_dump_header *ph) -{ -#ifdef DEBUG - if (ph == NULL) - return; - - printk(KERN_INFO "dump header:\n"); - /* setup some ph->sections required */ - printk(KERN_INFO "version = %d\n", ph->version); - printk(KERN_INFO "Sections = %d\n", ph->num_of_sections); - printk(KERN_INFO "Status = 0x%x\n", ph->status); - - /* No ph->disk, so all should be set to 0 */ - printk(KERN_INFO "Offset to first section 0x%x\n", - ph->first_offset_section); - printk(KERN_INFO "dump disk sections should be zero\n"); - printk(KERN_INFO "dump disk section = %d\n", ph->dump_disk_section); - printk(KERN_INFO "block num = %lld\n", ph->block_num_dd); - printk(KERN_INFO "number of blocks = %lld\n", ph->num_of_blocks_dd); - printk(KERN_INFO "dump disk offset = %d\n", ph->offset_dd); - printk(KERN_INFO "Max auto time= %d\n", ph->maxtime_to_auto); - - /*set cpu state and hpte states as well scratch pad area */ - printk(KERN_INFO " CPU AREA\n"); - printk(KERN_INFO "cpu dump_flags =%d\n", ph->cpu_data.dump_flags); - printk(KERN_INFO "cpu source_type =%d\n", ph->cpu_data.source_type); - printk(KERN_INFO "cpu error_flags =%d\n", ph->cpu_data.error_flags); - printk(KERN_INFO "cpu source_address =%llx\n", - ph->cpu_data.source_address); - printk(KERN_INFO "cpu source_length =%llx\n", - ph->cpu_data.source_length); - printk(KERN_INFO "cpu length_copied =%llx\n", - ph->cpu_data.length_copied); - - printk(KERN_INFO " HPTE AREA\n"); - printk(KERN_INFO "HPTE dump_flags =%d\n", ph->hpte_data.dump_flags); - printk(KERN_INFO "HPTE source_type =%d\n", ph->hpte_data.source_type); - printk(KERN_INFO "HPTE error_flags =%d\n", ph->hpte_data.error_flags); - printk(KERN_INFO "HPTE source_address =%llx\n", - ph->hpte_data.source_address); - printk(KERN_INFO "HPTE source_length =%llx\n", - ph->hpte_data.source_length); - printk(KERN_INFO "HPTE length_copied =%llx\n", - ph->hpte_data.length_copied); - - printk(KERN_INFO " SRSD AREA\n"); - printk(KERN_INFO "SRSD dump_flags =%d\n", ph->kernel_data.dump_flags); - printk(KERN_INFO "SRSD source_type =%d\n", ph->kernel_data.source_type); - printk(KERN_INFO "SRSD error_flags =%d\n", ph->kernel_data.error_flags); - printk(KERN_INFO "SRSD source_address =%llx\n", - ph->kernel_data.source_address); - printk(KERN_INFO "SRSD source_length =%llx\n", - ph->kernel_data.source_length); - printk(KERN_INFO "SRSD length_copied =%llx\n", - ph->kernel_data.length_copied); -#endif -} - -static ssize_t show_phyp_dump_active(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - - /* create filesystem entry so kdump is phyp-dump aware */ - return sprintf(buf, "%lx\n", phyp_dump_info->phyp_dump_at_boot); -} - -static struct kobj_attribute pdl = __ATTR(phyp_dump_active, 0600, - show_phyp_dump_active, - NULL); - -static void register_dump_area(struct phyp_dump_header *ph, unsigned long addr) -{ - int rc; - - /* Add addr value if not initialized before */ - if (ph->cpu_data.destination_address == 0) { - ph->cpu_data.destination_address += addr; - ph->hpte_data.destination_address += addr; - ph->kernel_data.destination_address += addr; - } - - /* ToDo Invalidate kdump and free memory range. */ - - do { - rc = rtas_call(ibm_configure_kernel_dump, 3, 1, NULL, - 1, ph, sizeof(struct phyp_dump_header)); - } while (rtas_busy_delay(rc)); - - if (rc) { - printk(KERN_ERR "phyp-dump: unexpected error (%d) on " - "register\n", rc); - print_dump_header(ph); - return; - } - - rc = sysfs_create_file(kernel_kobj, &pdl.attr); - if (rc) - printk(KERN_ERR "phyp-dump: unable to create sysfs" - " file (%d)\n", rc); -} - -static -void invalidate_last_dump(struct phyp_dump_header *ph, unsigned long addr) -{ - int rc; - - /* Add addr value if not initialized before */ - if (ph->cpu_data.destination_address == 0) { - ph->cpu_data.destination_address += addr; - ph->hpte_data.destination_address += addr; - ph->kernel_data.destination_address += addr; - } - - do { - rc = rtas_call(ibm_configure_kernel_dump, 3, 1, NULL, - 2, ph, sizeof(struct phyp_dump_header)); - } while (rtas_busy_delay(rc)); - - if (rc) { - printk(KERN_ERR "phyp-dump: unexpected error (%d) " - "on invalidate\n", rc); - print_dump_header(ph); - } -} - -/* ------------------------------------------------- */ -/** - * release_memory_range -- release memory previously memblock_reserved - * @start_pfn: starting physical frame number - * @nr_pages: number of pages to free. - * - * This routine will release memory that had been previously - * memblock_reserved in early boot. The released memory becomes - * available for genreal use. - */ -static void release_memory_range(unsigned long start_pfn, - unsigned long nr_pages) -{ - struct page *rpage; - unsigned long end_pfn; - long i; - - end_pfn = start_pfn + nr_pages; - - for (i = start_pfn; i <= end_pfn; i++) { - rpage = pfn_to_page(i); - if (PageReserved(rpage)) { - ClearPageReserved(rpage); - init_page_count(rpage); - __free_page(rpage); - totalram_pages++; - } - } -} - -/** - * track_freed_range -- Counts the range being freed. - * Once the counter goes to zero, it re-registers dump for - * future use. - */ -static void -track_freed_range(unsigned long addr, unsigned long length) -{ - static unsigned long scratch_area_size, reserved_area_size; - - if (addr < phyp_dump_info->init_reserve_start) - return; - - if ((addr >= phyp_dump_info->init_reserve_start) && - (addr <= phyp_dump_info->init_reserve_start + - phyp_dump_info->init_reserve_size)) - reserved_area_size += length; - - if ((addr >= phyp_dump_info->reserved_scratch_addr) && - (addr <= phyp_dump_info->reserved_scratch_addr + - phyp_dump_info->reserved_scratch_size)) - scratch_area_size += length; - - if ((reserved_area_size == phyp_dump_info->init_reserve_size) && - (scratch_area_size == phyp_dump_info->reserved_scratch_size)) { - - invalidate_last_dump(&phdr, - phyp_dump_info->reserved_scratch_addr); - register_dump_area(&phdr, - phyp_dump_info->reserved_scratch_addr); - } -} - -/* ------------------------------------------------- */ -/** - * sysfs_release_region -- sysfs interface to release memory range. - * - * Usage: - * "echo > /sys/kernel/release_region" - * - * Example: - * "echo 0x40000000 0x10000000 > /sys/kernel/release_region" - * - * will release 256MB starting at 1GB. - */ -static ssize_t store_release_region(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t count) -{ - unsigned long start_addr, length, end_addr; - unsigned long start_pfn, nr_pages; - ssize_t ret; - - ret = sscanf(buf, "%lx %lx", &start_addr, &length); - if (ret != 2) - return -EINVAL; - - track_freed_range(start_addr, length); - - /* Range-check - don't free any reserved memory that - * wasn't reserved for phyp-dump */ - if (start_addr < phyp_dump_info->init_reserve_start) - start_addr = phyp_dump_info->init_reserve_start; - - end_addr = phyp_dump_info->init_reserve_start + - phyp_dump_info->init_reserve_size; - if (start_addr+length > end_addr) - length = end_addr - start_addr; - - /* Release the region of memory assed in by user */ - start_pfn = PFN_DOWN(start_addr); - nr_pages = PFN_DOWN(length); - release_memory_range(start_pfn, nr_pages); - - return count; -} - -static ssize_t show_release_region(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -{ - u64 second_addr_range; - - /* total reserved size - start of scratch area */ - second_addr_range = phyp_dump_info->init_reserve_size - - phyp_dump_info->reserved_scratch_size; - return sprintf(buf, "CPU:0x%llx-0x%llx: HPTE:0x%llx-0x%llx:" - " DUMP:0x%llx-0x%llx, 0x%lx-0x%llx:\n", - phdr.cpu_data.destination_address, - phdr.cpu_data.length_copied, - phdr.hpte_data.destination_address, - phdr.hpte_data.length_copied, - phdr.kernel_data.destination_address, - phdr.kernel_data.length_copied, - phyp_dump_info->init_reserve_start, - second_addr_range); -} - -static struct kobj_attribute rr = __ATTR(release_region, 0600, - show_release_region, - store_release_region); - -static int __init phyp_dump_setup(void) -{ - struct device_node *rtas; - const struct phyp_dump_header *dump_header = NULL; - unsigned long dump_area_start; - unsigned long dump_area_length; - int header_len = 0; - int rc; - - /* If no memory was reserved in early boot, there is nothing to do */ - if (phyp_dump_info->init_reserve_size == 0) - return 0; - - /* Return if phyp dump not supported */ - if (!phyp_dump_info->phyp_dump_configured) - return -ENOSYS; - - /* Is there dump data waiting for us? If there isn't, - * then register a new dump area, and release all of - * the rest of the reserved ram. - * - * The /rtas/ibm,kernel-dump rtas node is present only - * if there is dump data waiting for us. - */ - rtas = of_find_node_by_path("/rtas"); - if (rtas) { - dump_header = of_get_property(rtas, "ibm,kernel-dump", - &header_len); - of_node_put(rtas); - } - - ibm_configure_kernel_dump = rtas_token("ibm,configure-kernel-dump"); - - print_dump_header(dump_header); - dump_area_length = init_dump_header(&phdr); - /* align down */ - dump_area_start = phyp_dump_info->init_reserve_start & PAGE_MASK; - - if (dump_header == NULL) { - register_dump_area(&phdr, dump_area_start); - return 0; - } - - /* re-register the dump area, if old dump was invalid */ - if ((dump_header) && (dump_header->status & DUMP_ERROR_FLAG)) { - invalidate_last_dump(&phdr, dump_area_start); - register_dump_area(&phdr, dump_area_start); - return 0; - } - - if (dump_header) { - phyp_dump_info->reserved_scratch_addr = - dump_header->cpu_data.destination_address; - phyp_dump_info->reserved_scratch_size = - dump_header->cpu_data.source_length + - dump_header->hpte_data.source_length + - dump_header->kernel_data.source_length; - } - - /* Should we create a dump_subsys, analogous to s390/ipl.c ? */ - rc = sysfs_create_file(kernel_kobj, &rr.attr); - if (rc) - printk(KERN_ERR "phyp-dump: unable to create sysfs file (%d)\n", - rc); - - /* ToDo: re-register the dump area, for next time. */ - return 0; -} -machine_subsys_initcall(pseries, phyp_dump_setup); - -int __init early_init_dt_scan_phyp_dump(unsigned long node, - const char *uname, int depth, void *data) -{ - const unsigned int *sizes; - - phyp_dump_info->phyp_dump_configured = 0; - phyp_dump_info->phyp_dump_is_active = 0; - - if (depth != 1 || strcmp(uname, "rtas") != 0) - return 0; - - if (of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL)) - phyp_dump_info->phyp_dump_configured++; - - if (of_get_flat_dt_prop(node, "ibm,dump-kernel", NULL)) - phyp_dump_info->phyp_dump_is_active++; - - sizes = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", - NULL); - if (!sizes) - return 0; - - if (sizes[0] == 1) - phyp_dump_info->cpu_state_size = *((unsigned long *)&sizes[1]); - - if (sizes[3] == 2) - phyp_dump_info->hpte_region_size = - *((unsigned long *)&sizes[4]); - return 1; -} - -/* Look for phyp_dump= cmdline option */ -static int __init early_phyp_dump_enabled(char *p) -{ - phyp_dump_info->phyp_dump_at_boot = 1; - - if (!p) - return 0; - - if (strncmp(p, "1", 1) == 0) - phyp_dump_info->phyp_dump_at_boot = 1; - else if (strncmp(p, "0", 1) == 0) - phyp_dump_info->phyp_dump_at_boot = 0; - - return 0; -} -early_param("phyp_dump", early_phyp_dump_enabled); - -/* Look for phyp_dump_reserve_size= cmdline option */ -static int __init early_phyp_dump_reserve_size(char *p) -{ - if (p) - phyp_dump_info->reserve_bootvar = memparse(p, &p); - - return 0; -} -early_param("phyp_dump_reserve_size", early_phyp_dump_reserve_size); -- cgit v0.10.2 From f2699491e06584a2ebb0939f108ad29f3b151456 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 20 Feb 2012 17:02:09 +0000 Subject: powerpc/perf: Move perf core & PMU code into a subdirectory The perf code has grown a lot since it started, and is big enough to warrant its own subdirectory. For reference it's ~60% bigger than the oprofile code. It declutters the kernel directory, makes it simpler to grep for "just perf stuff", and allows us to shorten some filenames. While we're at it, make it more obvious that we have two implementations of the core perf logic. One for (roughly) Book3S CPUs, which was the original implementation, and the other for Freescale embedded CPUs. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index b8b105c..6524c6e 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -157,6 +157,7 @@ core-y += arch/powerpc/kernel/ \ arch/powerpc/net/ core-$(CONFIG_XMON) += arch/powerpc/xmon/ core-$(CONFIG_KVM) += arch/powerpc/kvm/ +core-$(CONFIG_PERF_EVENTS) += arch/powerpc/perf/ drivers-$(CONFIG_OPROFILE) += arch/powerpc/oprofile/ diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 391bf7e..f5808a3 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -114,15 +114,6 @@ obj-$(CONFIG_PPC_IO_WORKAROUNDS) += io-workarounds.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o -obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o - -obj-$(CONFIG_PPC_PERF_CTRS) += perf_event.o -obj64-$(CONFIG_PPC_PERF_CTRS) += power4-pmu.o ppc970-pmu.o power5-pmu.o \ - power5+-pmu.o power6-pmu.o power7-pmu.o -obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o - -obj-$(CONFIG_FSL_EMB_PERF_EVENT) += perf_event_fsl_emb.o -obj-$(CONFIG_FSL_EMB_PERF_EVENT_E500) += e500-pmu.o obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o diff --git a/arch/powerpc/kernel/e500-pmu.c b/arch/powerpc/kernel/e500-pmu.c deleted file mode 100644 index cb2e294..0000000 --- a/arch/powerpc/kernel/e500-pmu.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Performance counter support for e500 family processors. - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * Copyright 2010 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 -#include -#include -#include - -/* - * Map of generic hardware event types to hardware events - * Zero if unsupported - */ -static int e500_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 1, - [PERF_COUNT_HW_INSTRUCTIONS] = 2, - [PERF_COUNT_HW_CACHE_MISSES] = 41, /* Data L1 cache reloads */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 12, - [PERF_COUNT_HW_BRANCH_MISSES] = 15, -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - */ -static int e500_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - /* - * D-cache misses are not split into read/write/prefetch; - * use raw event 41. - */ - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 27, 0 }, - [C(OP_WRITE)] = { 28, 0 }, - [C(OP_PREFETCH)] = { 29, 0 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 2, 60 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - /* - * Assuming LL means L2, it's not a good match for this model. - * It allocates only on L1 castout or explicit prefetch, and - * does not have separate read/write events (but it does have - * separate instruction/data events). - */ - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { 0, 0 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - /* - * There are data/instruction MMU misses, but that's a miss on - * the chip's internal level-one TLB which is probably not - * what the user wants. Instead, unified level-two TLB misses - * are reported here. - */ - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 26, 66 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 12, 15 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -static int num_events = 128; - -/* Upper half of event id is PMLCb, for threshold events */ -static u64 e500_xlate_event(u64 event_id) -{ - u32 event_low = (u32)event_id; - u64 ret; - - if (event_low >= num_events) - return 0; - - ret = FSL_EMB_EVENT_VALID; - - if (event_low >= 76 && event_low <= 81) { - ret |= FSL_EMB_EVENT_RESTRICTED; - ret |= event_id & - (FSL_EMB_EVENT_THRESHMUL | FSL_EMB_EVENT_THRESH); - } else if (event_id & - (FSL_EMB_EVENT_THRESHMUL | FSL_EMB_EVENT_THRESH)) { - /* Threshold requested on non-threshold event */ - return 0; - } - - return ret; -} - -static struct fsl_emb_pmu e500_pmu = { - .name = "e500 family", - .n_counter = 4, - .n_restricted = 2, - .xlate_event = e500_xlate_event, - .n_generic = ARRAY_SIZE(e500_generic_events), - .generic_events = e500_generic_events, - .cache_events = &e500_cache_events, -}; - -static int init_e500_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type) - return -ENODEV; - - if (!strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/e500mc")) - num_events = 256; - else if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/e500")) - return -ENODEV; - - return register_fsl_emb_pmu(&e500_pmu); -} - -early_initcall(init_e500_pmu); diff --git a/arch/powerpc/kernel/mpc7450-pmu.c b/arch/powerpc/kernel/mpc7450-pmu.c deleted file mode 100644 index fe21b51..0000000 --- a/arch/powerpc/kernel/mpc7450-pmu.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Performance counter support for MPC7450-family processors. - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include - -#define N_COUNTER 6 /* Number of hardware counters */ -#define MAX_ALT 3 /* Maximum number of event alternative codes */ - -/* - * Bits in event code for MPC7450 family - */ -#define PM_THRMULT_MSKS 0x40000 -#define PM_THRESH_SH 12 -#define PM_THRESH_MSK 0x3f -#define PM_PMC_SH 8 -#define PM_PMC_MSK 7 -#define PM_PMCSEL_MSK 0x7f - -/* - * Classify events according to how specific their PMC requirements are. - * Result is: - * 0: can go on any PMC - * 1: can go on PMCs 1-4 - * 2: can go on PMCs 1,2,4 - * 3: can go on PMCs 1 or 2 - * 4: can only go on one PMC - * -1: event code is invalid - */ -#define N_CLASSES 5 - -static int mpc7450_classify_event(u32 event) -{ - int pmc; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > N_COUNTER) - return -1; - return 4; - } - event &= PM_PMCSEL_MSK; - if (event <= 1) - return 0; - if (event <= 7) - return 1; - if (event <= 13) - return 2; - if (event <= 22) - return 3; - return -1; -} - -/* - * Events using threshold and possible threshold scale: - * code scale? name - * 11e N PM_INSTQ_EXCEED_CYC - * 11f N PM_ALTV_IQ_EXCEED_CYC - * 128 Y PM_DTLB_SEARCH_EXCEED_CYC - * 12b Y PM_LD_MISS_EXCEED_L1_CYC - * 220 N PM_CQ_EXCEED_CYC - * 30c N PM_GPR_RB_EXCEED_CYC - * 30d ? PM_FPR_IQ_EXCEED_CYC ? - * 311 Y PM_ITLB_SEARCH_EXCEED - * 410 N PM_GPR_IQ_EXCEED_CYC - */ - -/* - * Return use of threshold and threshold scale bits: - * 0 = uses neither, 1 = uses threshold, 2 = uses both - */ -static int mpc7450_threshold_use(u32 event) -{ - int pmc, sel; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - sel = event & PM_PMCSEL_MSK; - switch (pmc) { - case 1: - if (sel == 0x1e || sel == 0x1f) - return 1; - if (sel == 0x28 || sel == 0x2b) - return 2; - break; - case 2: - if (sel == 0x20) - return 1; - break; - case 3: - if (sel == 0xc || sel == 0xd) - return 1; - if (sel == 0x11) - return 2; - break; - case 4: - if (sel == 0x10) - return 1; - break; - } - return 0; -} - -/* - * Layout of constraint bits: - * 33222222222211111111110000000000 - * 10987654321098765432109876543210 - * |< >< > < > < ><><><><><><> - * TS TV G4 G3 G2P6P5P4P3P2P1 - * - * P1 - P6 - * 0 - 11: Count of events needing PMC1 .. PMC6 - * - * G2 - * 12 - 14: Count of events needing PMC1 or PMC2 - * - * G3 - * 16 - 18: Count of events needing PMC1, PMC2 or PMC4 - * - * G4 - * 20 - 23: Count of events needing PMC1, PMC2, PMC3 or PMC4 - * - * TV - * 24 - 29: Threshold value requested - * - * TS - * 30: Threshold scale value requested - */ - -static u32 pmcbits[N_COUNTER][2] = { - { 0x00844002, 0x00111001 }, /* PMC1 mask, value: P1,G2,G3,G4 */ - { 0x00844008, 0x00111004 }, /* PMC2: P2,G2,G3,G4 */ - { 0x00800020, 0x00100010 }, /* PMC3: P3,G4 */ - { 0x00840080, 0x00110040 }, /* PMC4: P4,G3,G4 */ - { 0x00000200, 0x00000100 }, /* PMC5: P5 */ - { 0x00000800, 0x00000400 } /* PMC6: P6 */ -}; - -static u32 classbits[N_CLASSES - 1][2] = { - { 0x00000000, 0x00000000 }, /* class 0: no constraint */ - { 0x00800000, 0x00100000 }, /* class 1: G4 */ - { 0x00040000, 0x00010000 }, /* class 2: G3 */ - { 0x00004000, 0x00001000 }, /* class 3: G2 */ -}; - -static int mpc7450_get_constraint(u64 event, unsigned long *maskp, - unsigned long *valp) -{ - int pmc, class; - u32 mask, value; - int thresh, tuse; - - class = mpc7450_classify_event(event); - if (class < 0) - return -1; - if (class == 4) { - pmc = ((unsigned int)event >> PM_PMC_SH) & PM_PMC_MSK; - mask = pmcbits[pmc - 1][0]; - value = pmcbits[pmc - 1][1]; - } else { - mask = classbits[class][0]; - value = classbits[class][1]; - } - - tuse = mpc7450_threshold_use(event); - if (tuse) { - thresh = ((unsigned int)event >> PM_THRESH_SH) & PM_THRESH_MSK; - mask |= 0x3f << 24; - value |= thresh << 24; - if (tuse == 2) { - mask |= 0x40000000; - if ((unsigned int)event & PM_THRMULT_MSKS) - value |= 0x40000000; - } - } - - *maskp = mask; - *valp = value; - return 0; -} - -static const unsigned int event_alternatives[][MAX_ALT] = { - { 0x217, 0x317 }, /* PM_L1_DCACHE_MISS */ - { 0x418, 0x50f, 0x60f }, /* PM_SNOOP_RETRY */ - { 0x502, 0x602 }, /* PM_L2_HIT */ - { 0x503, 0x603 }, /* PM_L3_HIT */ - { 0x504, 0x604 }, /* PM_L2_ICACHE_MISS */ - { 0x505, 0x605 }, /* PM_L3_ICACHE_MISS */ - { 0x506, 0x606 }, /* PM_L2_DCACHE_MISS */ - { 0x507, 0x607 }, /* PM_L3_DCACHE_MISS */ - { 0x50a, 0x623 }, /* PM_LD_HIT_L3 */ - { 0x50b, 0x624 }, /* PM_ST_HIT_L3 */ - { 0x50d, 0x60d }, /* PM_L2_TOUCH_HIT */ - { 0x50e, 0x60e }, /* PM_L3_TOUCH_HIT */ - { 0x512, 0x612 }, /* PM_INT_LOCAL */ - { 0x513, 0x61d }, /* PM_L2_MISS */ - { 0x514, 0x61e }, /* PM_L3_MISS */ -}; - -/* - * Scan the alternatives table for a match and return the - * index into the alternatives table if found, else -1. - */ -static int find_alternative(u32 event) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { - if (event < event_alternatives[i][0]) - break; - for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) - if (event == event_alternatives[i][j]) - return i; - } - return -1; -} - -static int mpc7450_get_alternatives(u64 event, unsigned int flags, u64 alt[]) -{ - int i, j, nalt = 1; - u32 ae; - - alt[0] = event; - nalt = 1; - i = find_alternative((u32)event); - if (i >= 0) { - for (j = 0; j < MAX_ALT; ++j) { - ae = event_alternatives[i][j]; - if (ae && ae != (u32)event) - alt[nalt++] = ae; - } - } - return nalt; -} - -/* - * Bitmaps of which PMCs each class can use for classes 0 - 3. - * Bit i is set if PMC i+1 is usable. - */ -static const u8 classmap[N_CLASSES] = { - 0x3f, 0x0f, 0x0b, 0x03, 0 -}; - -/* Bit position and width of each PMCSEL field */ -static const int pmcsel_shift[N_COUNTER] = { - 6, 0, 27, 22, 17, 11 -}; -static const u32 pmcsel_mask[N_COUNTER] = { - 0x7f, 0x3f, 0x1f, 0x1f, 0x1f, 0x3f -}; - -/* - * Compute MMCR0/1/2 values for a set of events. - */ -static int mpc7450_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]) -{ - u8 event_index[N_CLASSES][N_COUNTER]; - int n_classevent[N_CLASSES]; - int i, j, class, tuse; - u32 pmc_inuse = 0, pmc_avail; - u32 mmcr0 = 0, mmcr1 = 0, mmcr2 = 0; - u32 ev, pmc, thresh; - - if (n_ev > N_COUNTER) - return -1; - - /* First pass: count usage in each class */ - for (i = 0; i < N_CLASSES; ++i) - n_classevent[i] = 0; - for (i = 0; i < n_ev; ++i) { - class = mpc7450_classify_event(event[i]); - if (class < 0) - return -1; - j = n_classevent[class]++; - event_index[class][j] = i; - } - - /* Second pass: allocate PMCs from most specific event to least */ - for (class = N_CLASSES - 1; class >= 0; --class) { - for (i = 0; i < n_classevent[class]; ++i) { - ev = event[event_index[class][i]]; - if (class == 4) { - pmc = (ev >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc_inuse & (1 << (pmc - 1))) - return -1; - } else { - /* Find a suitable PMC */ - pmc_avail = classmap[class] & ~pmc_inuse; - if (!pmc_avail) - return -1; - pmc = ffs(pmc_avail); - } - pmc_inuse |= 1 << (pmc - 1); - - tuse = mpc7450_threshold_use(ev); - if (tuse) { - thresh = (ev >> PM_THRESH_SH) & PM_THRESH_MSK; - mmcr0 |= thresh << 16; - if (tuse == 2 && (ev & PM_THRMULT_MSKS)) - mmcr2 = 0x80000000; - } - ev &= pmcsel_mask[pmc - 1]; - ev <<= pmcsel_shift[pmc - 1]; - if (pmc <= 2) - mmcr0 |= ev; - else - mmcr1 |= ev; - hwc[event_index[class][i]] = pmc - 1; - } - } - - if (pmc_inuse & 1) - mmcr0 |= MMCR0_PMC1CE; - if (pmc_inuse & 0x3e) - mmcr0 |= MMCR0_PMCnCE; - - /* Return MMCRx values */ - mmcr[0] = mmcr0; - mmcr[1] = mmcr1; - mmcr[2] = mmcr2; - return 0; -} - -/* - * Disable counting by a PMC. - * Note that the pmc argument is 0-based here, not 1-based. - */ -static void mpc7450_disable_pmc(unsigned int pmc, unsigned long mmcr[]) -{ - if (pmc <= 1) - mmcr[0] &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); - else - mmcr[1] &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); -} - -static int mpc7450_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 1, - [PERF_COUNT_HW_INSTRUCTIONS] = 2, - [PERF_COUNT_HW_CACHE_MISSES] = 0x217, /* PM_L1_DCACHE_MISS */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x122, /* PM_BR_CMPL */ - [PERF_COUNT_HW_BRANCH_MISSES] = 0x41c, /* PM_BR_MPRED */ -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - */ -static int mpc7450_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x225 }, - [C(OP_WRITE)] = { 0, 0x227 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x129, 0x115 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0x634, 0 }, - }, - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { 0, 0 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x312 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x223 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x122, 0x41c }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -struct power_pmu mpc7450_pmu = { - .name = "MPC7450 family", - .n_counter = N_COUNTER, - .max_alternatives = MAX_ALT, - .add_fields = 0x00111555ul, - .test_adder = 0x00301000ul, - .compute_mmcr = mpc7450_compute_mmcr, - .get_constraint = mpc7450_get_constraint, - .get_alternatives = mpc7450_get_alternatives, - .disable_pmc = mpc7450_disable_pmc, - .n_generic = ARRAY_SIZE(mpc7450_generic_events), - .generic_events = mpc7450_generic_events, - .cache_events = &mpc7450_cache_events, -}; - -static int __init init_mpc7450_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type || - strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/7450")) - return -ENODEV; - - return register_power_pmu(&mpc7450_pmu); -} - -early_initcall(init_mpc7450_pmu); diff --git a/arch/powerpc/kernel/perf_callchain.c b/arch/powerpc/kernel/perf_callchain.c deleted file mode 100644 index 564c1d8..0000000 --- a/arch/powerpc/kernel/perf_callchain.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * Performance counter callchain support - powerpc architecture code - * - * Copyright © 2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_PPC64 -#include "ppc32.h" -#endif - - -/* - * Is sp valid as the address of the next kernel stack frame after prev_sp? - * The next frame may be in a different stack area but should not go - * back down in the same stack area. - */ -static int valid_next_sp(unsigned long sp, unsigned long prev_sp) -{ - if (sp & 0xf) - return 0; /* must be 16-byte aligned */ - if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD)) - return 0; - if (sp >= prev_sp + STACK_FRAME_OVERHEAD) - return 1; - /* - * sp could decrease when we jump off an interrupt stack - * back to the regular process stack. - */ - if ((sp & ~(THREAD_SIZE - 1)) != (prev_sp & ~(THREAD_SIZE - 1))) - return 1; - return 0; -} - -void -perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) -{ - unsigned long sp, next_sp; - unsigned long next_ip; - unsigned long lr; - long level = 0; - unsigned long *fp; - - lr = regs->link; - sp = regs->gpr[1]; - perf_callchain_store(entry, regs->nip); - - if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD)) - return; - - for (;;) { - fp = (unsigned long *) sp; - next_sp = fp[0]; - - if (next_sp == sp + STACK_INT_FRAME_SIZE && - fp[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) { - /* - * This looks like an interrupt frame for an - * interrupt that occurred in the kernel - */ - regs = (struct pt_regs *)(sp + STACK_FRAME_OVERHEAD); - next_ip = regs->nip; - lr = regs->link; - level = 0; - perf_callchain_store(entry, PERF_CONTEXT_KERNEL); - - } else { - if (level == 0) - next_ip = lr; - else - next_ip = fp[STACK_FRAME_LR_SAVE]; - - /* - * We can't tell which of the first two addresses - * we get are valid, but we can filter out the - * obviously bogus ones here. We replace them - * with 0 rather than removing them entirely so - * that userspace can tell which is which. - */ - if ((level == 1 && next_ip == lr) || - (level <= 1 && !kernel_text_address(next_ip))) - next_ip = 0; - - ++level; - } - - perf_callchain_store(entry, next_ip); - if (!valid_next_sp(next_sp, sp)) - return; - sp = next_sp; - } -} - -#ifdef CONFIG_PPC64 -/* - * On 64-bit we don't want to invoke hash_page on user addresses from - * interrupt context, so if the access faults, we read the page tables - * to find which page (if any) is mapped and access it directly. - */ -static int read_user_stack_slow(void __user *ptr, void *ret, int nb) -{ - pgd_t *pgdir; - pte_t *ptep, pte; - unsigned shift; - unsigned long addr = (unsigned long) ptr; - unsigned long offset; - unsigned long pfn; - void *kaddr; - - pgdir = current->mm->pgd; - if (!pgdir) - return -EFAULT; - - ptep = find_linux_pte_or_hugepte(pgdir, addr, &shift); - if (!shift) - shift = PAGE_SHIFT; - - /* align address to page boundary */ - offset = addr & ((1UL << shift) - 1); - addr -= offset; - - if (ptep == NULL) - return -EFAULT; - pte = *ptep; - if (!pte_present(pte) || !(pte_val(pte) & _PAGE_USER)) - return -EFAULT; - pfn = pte_pfn(pte); - if (!page_is_ram(pfn)) - return -EFAULT; - - /* no highmem to worry about here */ - kaddr = pfn_to_kaddr(pfn); - memcpy(ret, kaddr + offset, nb); - return 0; -} - -static int read_user_stack_64(unsigned long __user *ptr, unsigned long *ret) -{ - if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned long) || - ((unsigned long)ptr & 7)) - return -EFAULT; - - pagefault_disable(); - if (!__get_user_inatomic(*ret, ptr)) { - pagefault_enable(); - return 0; - } - pagefault_enable(); - - return read_user_stack_slow(ptr, ret, 8); -} - -static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret) -{ - if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) || - ((unsigned long)ptr & 3)) - return -EFAULT; - - pagefault_disable(); - if (!__get_user_inatomic(*ret, ptr)) { - pagefault_enable(); - return 0; - } - pagefault_enable(); - - return read_user_stack_slow(ptr, ret, 4); -} - -static inline int valid_user_sp(unsigned long sp, int is_64) -{ - if (!sp || (sp & 7) || sp > (is_64 ? TASK_SIZE : 0x100000000UL) - 32) - return 0; - return 1; -} - -/* - * 64-bit user processes use the same stack frame for RT and non-RT signals. - */ -struct signal_frame_64 { - char dummy[__SIGNAL_FRAMESIZE]; - struct ucontext uc; - unsigned long unused[2]; - unsigned int tramp[6]; - struct siginfo *pinfo; - void *puc; - struct siginfo info; - char abigap[288]; -}; - -static int is_sigreturn_64_address(unsigned long nip, unsigned long fp) -{ - if (nip == fp + offsetof(struct signal_frame_64, tramp)) - return 1; - if (vdso64_rt_sigtramp && current->mm->context.vdso_base && - nip == current->mm->context.vdso_base + vdso64_rt_sigtramp) - return 1; - return 0; -} - -/* - * Do some sanity checking on the signal frame pointed to by sp. - * We check the pinfo and puc pointers in the frame. - */ -static int sane_signal_64_frame(unsigned long sp) -{ - struct signal_frame_64 __user *sf; - unsigned long pinfo, puc; - - sf = (struct signal_frame_64 __user *) sp; - if (read_user_stack_64((unsigned long __user *) &sf->pinfo, &pinfo) || - read_user_stack_64((unsigned long __user *) &sf->puc, &puc)) - return 0; - return pinfo == (unsigned long) &sf->info && - puc == (unsigned long) &sf->uc; -} - -static void perf_callchain_user_64(struct perf_callchain_entry *entry, - struct pt_regs *regs) -{ - unsigned long sp, next_sp; - unsigned long next_ip; - unsigned long lr; - long level = 0; - struct signal_frame_64 __user *sigframe; - unsigned long __user *fp, *uregs; - - next_ip = regs->nip; - lr = regs->link; - sp = regs->gpr[1]; - perf_callchain_store(entry, next_ip); - - for (;;) { - fp = (unsigned long __user *) sp; - if (!valid_user_sp(sp, 1) || read_user_stack_64(fp, &next_sp)) - return; - if (level > 0 && read_user_stack_64(&fp[2], &next_ip)) - return; - - /* - * Note: the next_sp - sp >= signal frame size check - * is true when next_sp < sp, which can happen when - * transitioning from an alternate signal stack to the - * normal stack. - */ - if (next_sp - sp >= sizeof(struct signal_frame_64) && - (is_sigreturn_64_address(next_ip, sp) || - (level <= 1 && is_sigreturn_64_address(lr, sp))) && - sane_signal_64_frame(sp)) { - /* - * This looks like an signal frame - */ - sigframe = (struct signal_frame_64 __user *) sp; - uregs = sigframe->uc.uc_mcontext.gp_regs; - if (read_user_stack_64(&uregs[PT_NIP], &next_ip) || - read_user_stack_64(&uregs[PT_LNK], &lr) || - read_user_stack_64(&uregs[PT_R1], &sp)) - return; - level = 0; - perf_callchain_store(entry, PERF_CONTEXT_USER); - perf_callchain_store(entry, next_ip); - continue; - } - - if (level == 0) - next_ip = lr; - perf_callchain_store(entry, next_ip); - ++level; - sp = next_sp; - } -} - -static inline int current_is_64bit(void) -{ - /* - * We can't use test_thread_flag() here because we may be on an - * interrupt stack, and the thread flags don't get copied over - * from the thread_info on the main stack to the interrupt stack. - */ - return !test_ti_thread_flag(task_thread_info(current), TIF_32BIT); -} - -#else /* CONFIG_PPC64 */ -/* - * On 32-bit we just access the address and let hash_page create a - * HPTE if necessary, so there is no need to fall back to reading - * the page tables. Since this is called at interrupt level, - * do_page_fault() won't treat a DSI as a page fault. - */ -static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret) -{ - int rc; - - if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) || - ((unsigned long)ptr & 3)) - return -EFAULT; - - pagefault_disable(); - rc = __get_user_inatomic(*ret, ptr); - pagefault_enable(); - - return rc; -} - -static inline void perf_callchain_user_64(struct perf_callchain_entry *entry, - struct pt_regs *regs) -{ -} - -static inline int current_is_64bit(void) -{ - return 0; -} - -static inline int valid_user_sp(unsigned long sp, int is_64) -{ - if (!sp || (sp & 7) || sp > TASK_SIZE - 32) - return 0; - return 1; -} - -#define __SIGNAL_FRAMESIZE32 __SIGNAL_FRAMESIZE -#define sigcontext32 sigcontext -#define mcontext32 mcontext -#define ucontext32 ucontext -#define compat_siginfo_t struct siginfo - -#endif /* CONFIG_PPC64 */ - -/* - * Layout for non-RT signal frames - */ -struct signal_frame_32 { - char dummy[__SIGNAL_FRAMESIZE32]; - struct sigcontext32 sctx; - struct mcontext32 mctx; - int abigap[56]; -}; - -/* - * Layout for RT signal frames - */ -struct rt_signal_frame_32 { - char dummy[__SIGNAL_FRAMESIZE32 + 16]; - compat_siginfo_t info; - struct ucontext32 uc; - int abigap[56]; -}; - -static int is_sigreturn_32_address(unsigned int nip, unsigned int fp) -{ - if (nip == fp + offsetof(struct signal_frame_32, mctx.mc_pad)) - return 1; - if (vdso32_sigtramp && current->mm->context.vdso_base && - nip == current->mm->context.vdso_base + vdso32_sigtramp) - return 1; - return 0; -} - -static int is_rt_sigreturn_32_address(unsigned int nip, unsigned int fp) -{ - if (nip == fp + offsetof(struct rt_signal_frame_32, - uc.uc_mcontext.mc_pad)) - return 1; - if (vdso32_rt_sigtramp && current->mm->context.vdso_base && - nip == current->mm->context.vdso_base + vdso32_rt_sigtramp) - return 1; - return 0; -} - -static int sane_signal_32_frame(unsigned int sp) -{ - struct signal_frame_32 __user *sf; - unsigned int regs; - - sf = (struct signal_frame_32 __user *) (unsigned long) sp; - if (read_user_stack_32((unsigned int __user *) &sf->sctx.regs, ®s)) - return 0; - return regs == (unsigned long) &sf->mctx; -} - -static int sane_rt_signal_32_frame(unsigned int sp) -{ - struct rt_signal_frame_32 __user *sf; - unsigned int regs; - - sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp; - if (read_user_stack_32((unsigned int __user *) &sf->uc.uc_regs, ®s)) - return 0; - return regs == (unsigned long) &sf->uc.uc_mcontext; -} - -static unsigned int __user *signal_frame_32_regs(unsigned int sp, - unsigned int next_sp, unsigned int next_ip) -{ - struct mcontext32 __user *mctx = NULL; - struct signal_frame_32 __user *sf; - struct rt_signal_frame_32 __user *rt_sf; - - /* - * Note: the next_sp - sp >= signal frame size check - * is true when next_sp < sp, for example, when - * transitioning from an alternate signal stack to the - * normal stack. - */ - if (next_sp - sp >= sizeof(struct signal_frame_32) && - is_sigreturn_32_address(next_ip, sp) && - sane_signal_32_frame(sp)) { - sf = (struct signal_frame_32 __user *) (unsigned long) sp; - mctx = &sf->mctx; - } - - if (!mctx && next_sp - sp >= sizeof(struct rt_signal_frame_32) && - is_rt_sigreturn_32_address(next_ip, sp) && - sane_rt_signal_32_frame(sp)) { - rt_sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp; - mctx = &rt_sf->uc.uc_mcontext; - } - - if (!mctx) - return NULL; - return mctx->mc_gregs; -} - -static void perf_callchain_user_32(struct perf_callchain_entry *entry, - struct pt_regs *regs) -{ - unsigned int sp, next_sp; - unsigned int next_ip; - unsigned int lr; - long level = 0; - unsigned int __user *fp, *uregs; - - next_ip = regs->nip; - lr = regs->link; - sp = regs->gpr[1]; - perf_callchain_store(entry, next_ip); - - while (entry->nr < PERF_MAX_STACK_DEPTH) { - fp = (unsigned int __user *) (unsigned long) sp; - if (!valid_user_sp(sp, 0) || read_user_stack_32(fp, &next_sp)) - return; - if (level > 0 && read_user_stack_32(&fp[1], &next_ip)) - return; - - uregs = signal_frame_32_regs(sp, next_sp, next_ip); - if (!uregs && level <= 1) - uregs = signal_frame_32_regs(sp, next_sp, lr); - if (uregs) { - /* - * This looks like an signal frame, so restart - * the stack trace with the values in it. - */ - if (read_user_stack_32(&uregs[PT_NIP], &next_ip) || - read_user_stack_32(&uregs[PT_LNK], &lr) || - read_user_stack_32(&uregs[PT_R1], &sp)) - return; - level = 0; - perf_callchain_store(entry, PERF_CONTEXT_USER); - perf_callchain_store(entry, next_ip); - continue; - } - - if (level == 0) - next_ip = lr; - perf_callchain_store(entry, next_ip); - ++level; - sp = next_sp; - } -} - -void -perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) -{ - if (current_is_64bit()) - perf_callchain_user_64(entry, regs); - else - perf_callchain_user_32(entry, regs); -} diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c deleted file mode 100644 index 64483fd..0000000 --- a/arch/powerpc/kernel/perf_event.c +++ /dev/null @@ -1,1438 +0,0 @@ -/* - * Performance event support - powerpc architecture code - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct cpu_hw_events { - int n_events; - int n_percpu; - int disabled; - int n_added; - int n_limited; - u8 pmcs_enabled; - struct perf_event *event[MAX_HWEVENTS]; - u64 events[MAX_HWEVENTS]; - unsigned int flags[MAX_HWEVENTS]; - unsigned long mmcr[3]; - struct perf_event *limited_counter[MAX_LIMITED_HWCOUNTERS]; - u8 limited_hwidx[MAX_LIMITED_HWCOUNTERS]; - u64 alternatives[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; - unsigned long amasks[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; - unsigned long avalues[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; - - unsigned int group_flag; - int n_txn_start; -}; -DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); - -struct power_pmu *ppmu; - -/* - * Normally, to ignore kernel events we set the FCS (freeze counters - * in supervisor mode) bit in MMCR0, but if the kernel runs with the - * hypervisor bit set in the MSR, or if we are running on a processor - * where the hypervisor bit is forced to 1 (as on Apple G5 processors), - * then we need to use the FCHV bit to ignore kernel events. - */ -static unsigned int freeze_events_kernel = MMCR0_FCS; - -/* - * 32-bit doesn't have MMCRA but does have an MMCR2, - * and a few other names are different. - */ -#ifdef CONFIG_PPC32 - -#define MMCR0_FCHV 0 -#define MMCR0_PMCjCE MMCR0_PMCnCE - -#define SPRN_MMCRA SPRN_MMCR2 -#define MMCRA_SAMPLE_ENABLE 0 - -static inline unsigned long perf_ip_adjust(struct pt_regs *regs) -{ - return 0; -} -static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) { } -static inline u32 perf_get_misc_flags(struct pt_regs *regs) -{ - return 0; -} -static inline void perf_read_regs(struct pt_regs *regs) { } -static inline int perf_intr_is_nmi(struct pt_regs *regs) -{ - return 0; -} - -#endif /* CONFIG_PPC32 */ - -/* - * Things that are specific to 64-bit implementations. - */ -#ifdef CONFIG_PPC64 - -static inline unsigned long perf_ip_adjust(struct pt_regs *regs) -{ - unsigned long mmcra = regs->dsisr; - - if ((mmcra & MMCRA_SAMPLE_ENABLE) && !(ppmu->flags & PPMU_ALT_SIPR)) { - unsigned long slot = (mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT; - if (slot > 1) - return 4 * (slot - 1); - } - return 0; -} - -/* - * The user wants a data address recorded. - * If we're not doing instruction sampling, give them the SDAR - * (sampled data address). If we are doing instruction sampling, then - * only give them the SDAR if it corresponds to the instruction - * pointed to by SIAR; this is indicated by the [POWER6_]MMCRA_SDSYNC - * bit in MMCRA. - */ -static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) -{ - unsigned long mmcra = regs->dsisr; - unsigned long sdsync = (ppmu->flags & PPMU_ALT_SIPR) ? - POWER6_MMCRA_SDSYNC : MMCRA_SDSYNC; - - if (!(mmcra & MMCRA_SAMPLE_ENABLE) || (mmcra & sdsync)) - *addrp = mfspr(SPRN_SDAR); -} - -static inline u32 perf_get_misc_flags(struct pt_regs *regs) -{ - unsigned long mmcra = regs->dsisr; - unsigned long sihv = MMCRA_SIHV; - unsigned long sipr = MMCRA_SIPR; - - if (TRAP(regs) != 0xf00) - return 0; /* not a PMU interrupt */ - - if (ppmu->flags & PPMU_ALT_SIPR) { - sihv = POWER6_MMCRA_SIHV; - sipr = POWER6_MMCRA_SIPR; - } - - /* PR has priority over HV, so order below is important */ - if (mmcra & sipr) - return PERF_RECORD_MISC_USER; - if ((mmcra & sihv) && (freeze_events_kernel != MMCR0_FCHV)) - return PERF_RECORD_MISC_HYPERVISOR; - return PERF_RECORD_MISC_KERNEL; -} - -/* - * Overload regs->dsisr to store MMCRA so we only need to read it once - * on each interrupt. - */ -static inline void perf_read_regs(struct pt_regs *regs) -{ - regs->dsisr = mfspr(SPRN_MMCRA); -} - -/* - * If interrupts were soft-disabled when a PMU interrupt occurs, treat - * it as an NMI. - */ -static inline int perf_intr_is_nmi(struct pt_regs *regs) -{ - return !regs->softe; -} - -#endif /* CONFIG_PPC64 */ - -static void perf_event_interrupt(struct pt_regs *regs); - -void perf_event_print_debug(void) -{ -} - -/* - * Read one performance monitor counter (PMC). - */ -static unsigned long read_pmc(int idx) -{ - unsigned long val; - - switch (idx) { - case 1: - val = mfspr(SPRN_PMC1); - break; - case 2: - val = mfspr(SPRN_PMC2); - break; - case 3: - val = mfspr(SPRN_PMC3); - break; - case 4: - val = mfspr(SPRN_PMC4); - break; - case 5: - val = mfspr(SPRN_PMC5); - break; - case 6: - val = mfspr(SPRN_PMC6); - break; -#ifdef CONFIG_PPC64 - case 7: - val = mfspr(SPRN_PMC7); - break; - case 8: - val = mfspr(SPRN_PMC8); - break; -#endif /* CONFIG_PPC64 */ - default: - printk(KERN_ERR "oops trying to read PMC%d\n", idx); - val = 0; - } - return val; -} - -/* - * Write one PMC. - */ -static void write_pmc(int idx, unsigned long val) -{ - switch (idx) { - case 1: - mtspr(SPRN_PMC1, val); - break; - case 2: - mtspr(SPRN_PMC2, val); - break; - case 3: - mtspr(SPRN_PMC3, val); - break; - case 4: - mtspr(SPRN_PMC4, val); - break; - case 5: - mtspr(SPRN_PMC5, val); - break; - case 6: - mtspr(SPRN_PMC6, val); - break; -#ifdef CONFIG_PPC64 - case 7: - mtspr(SPRN_PMC7, val); - break; - case 8: - mtspr(SPRN_PMC8, val); - break; -#endif /* CONFIG_PPC64 */ - default: - printk(KERN_ERR "oops trying to write PMC%d\n", idx); - } -} - -/* - * Check if a set of events can all go on the PMU at once. - * If they can't, this will look at alternative codes for the events - * and see if any combination of alternative codes is feasible. - * The feasible set is returned in event_id[]. - */ -static int power_check_constraints(struct cpu_hw_events *cpuhw, - u64 event_id[], unsigned int cflags[], - int n_ev) -{ - unsigned long mask, value, nv; - unsigned long smasks[MAX_HWEVENTS], svalues[MAX_HWEVENTS]; - int n_alt[MAX_HWEVENTS], choice[MAX_HWEVENTS]; - int i, j; - unsigned long addf = ppmu->add_fields; - unsigned long tadd = ppmu->test_adder; - - if (n_ev > ppmu->n_counter) - return -1; - - /* First see if the events will go on as-is */ - for (i = 0; i < n_ev; ++i) { - if ((cflags[i] & PPMU_LIMITED_PMC_REQD) - && !ppmu->limited_pmc_event(event_id[i])) { - ppmu->get_alternatives(event_id[i], cflags[i], - cpuhw->alternatives[i]); - event_id[i] = cpuhw->alternatives[i][0]; - } - if (ppmu->get_constraint(event_id[i], &cpuhw->amasks[i][0], - &cpuhw->avalues[i][0])) - return -1; - } - value = mask = 0; - for (i = 0; i < n_ev; ++i) { - nv = (value | cpuhw->avalues[i][0]) + - (value & cpuhw->avalues[i][0] & addf); - if ((((nv + tadd) ^ value) & mask) != 0 || - (((nv + tadd) ^ cpuhw->avalues[i][0]) & - cpuhw->amasks[i][0]) != 0) - break; - value = nv; - mask |= cpuhw->amasks[i][0]; - } - if (i == n_ev) - return 0; /* all OK */ - - /* doesn't work, gather alternatives... */ - if (!ppmu->get_alternatives) - return -1; - for (i = 0; i < n_ev; ++i) { - choice[i] = 0; - n_alt[i] = ppmu->get_alternatives(event_id[i], cflags[i], - cpuhw->alternatives[i]); - for (j = 1; j < n_alt[i]; ++j) - ppmu->get_constraint(cpuhw->alternatives[i][j], - &cpuhw->amasks[i][j], - &cpuhw->avalues[i][j]); - } - - /* enumerate all possibilities and see if any will work */ - i = 0; - j = -1; - value = mask = nv = 0; - while (i < n_ev) { - if (j >= 0) { - /* we're backtracking, restore context */ - value = svalues[i]; - mask = smasks[i]; - j = choice[i]; - } - /* - * See if any alternative k for event_id i, - * where k > j, will satisfy the constraints. - */ - while (++j < n_alt[i]) { - nv = (value | cpuhw->avalues[i][j]) + - (value & cpuhw->avalues[i][j] & addf); - if ((((nv + tadd) ^ value) & mask) == 0 && - (((nv + tadd) ^ cpuhw->avalues[i][j]) - & cpuhw->amasks[i][j]) == 0) - break; - } - if (j >= n_alt[i]) { - /* - * No feasible alternative, backtrack - * to event_id i-1 and continue enumerating its - * alternatives from where we got up to. - */ - if (--i < 0) - return -1; - } else { - /* - * Found a feasible alternative for event_id i, - * remember where we got up to with this event_id, - * go on to the next event_id, and start with - * the first alternative for it. - */ - choice[i] = j; - svalues[i] = value; - smasks[i] = mask; - value = nv; - mask |= cpuhw->amasks[i][j]; - ++i; - j = -1; - } - } - - /* OK, we have a feasible combination, tell the caller the solution */ - for (i = 0; i < n_ev; ++i) - event_id[i] = cpuhw->alternatives[i][choice[i]]; - return 0; -} - -/* - * Check if newly-added events have consistent settings for - * exclude_{user,kernel,hv} with each other and any previously - * added events. - */ -static int check_excludes(struct perf_event **ctrs, unsigned int cflags[], - int n_prev, int n_new) -{ - int eu = 0, ek = 0, eh = 0; - int i, n, first; - struct perf_event *event; - - n = n_prev + n_new; - if (n <= 1) - return 0; - - first = 1; - for (i = 0; i < n; ++i) { - if (cflags[i] & PPMU_LIMITED_PMC_OK) { - cflags[i] &= ~PPMU_LIMITED_PMC_REQD; - continue; - } - event = ctrs[i]; - if (first) { - eu = event->attr.exclude_user; - ek = event->attr.exclude_kernel; - eh = event->attr.exclude_hv; - first = 0; - } else if (event->attr.exclude_user != eu || - event->attr.exclude_kernel != ek || - event->attr.exclude_hv != eh) { - return -EAGAIN; - } - } - - if (eu || ek || eh) - for (i = 0; i < n; ++i) - if (cflags[i] & PPMU_LIMITED_PMC_OK) - cflags[i] |= PPMU_LIMITED_PMC_REQD; - - return 0; -} - -static u64 check_and_compute_delta(u64 prev, u64 val) -{ - u64 delta = (val - prev) & 0xfffffffful; - - /* - * POWER7 can roll back counter values, if the new value is smaller - * than the previous value it will cause the delta and the counter to - * have bogus values unless we rolled a counter over. If a coutner is - * rolled back, it will be smaller, but within 256, which is the maximum - * number of events to rollback at once. If we dectect a rollback - * return 0. This can lead to a small lack of precision in the - * counters. - */ - if (prev > val && (prev - val) < 256) - delta = 0; - - return delta; -} - -static void power_pmu_read(struct perf_event *event) -{ - s64 val, delta, prev; - - if (event->hw.state & PERF_HES_STOPPED) - return; - - if (!event->hw.idx) - return; - /* - * Performance monitor interrupts come even when interrupts - * are soft-disabled, as long as interrupts are hard-enabled. - * Therefore we treat them like NMIs. - */ - do { - prev = local64_read(&event->hw.prev_count); - barrier(); - val = read_pmc(event->hw.idx); - delta = check_and_compute_delta(prev, val); - if (!delta) - return; - } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); - - local64_add(delta, &event->count); - local64_sub(delta, &event->hw.period_left); -} - -/* - * On some machines, PMC5 and PMC6 can't be written, don't respect - * the freeze conditions, and don't generate interrupts. This tells - * us if `event' is using such a PMC. - */ -static int is_limited_pmc(int pmcnum) -{ - return (ppmu->flags & PPMU_LIMITED_PMC5_6) - && (pmcnum == 5 || pmcnum == 6); -} - -static void freeze_limited_counters(struct cpu_hw_events *cpuhw, - unsigned long pmc5, unsigned long pmc6) -{ - struct perf_event *event; - u64 val, prev, delta; - int i; - - for (i = 0; i < cpuhw->n_limited; ++i) { - event = cpuhw->limited_counter[i]; - if (!event->hw.idx) - continue; - val = (event->hw.idx == 5) ? pmc5 : pmc6; - prev = local64_read(&event->hw.prev_count); - event->hw.idx = 0; - delta = check_and_compute_delta(prev, val); - if (delta) - local64_add(delta, &event->count); - } -} - -static void thaw_limited_counters(struct cpu_hw_events *cpuhw, - unsigned long pmc5, unsigned long pmc6) -{ - struct perf_event *event; - u64 val, prev; - int i; - - for (i = 0; i < cpuhw->n_limited; ++i) { - event = cpuhw->limited_counter[i]; - event->hw.idx = cpuhw->limited_hwidx[i]; - val = (event->hw.idx == 5) ? pmc5 : pmc6; - prev = local64_read(&event->hw.prev_count); - if (check_and_compute_delta(prev, val)) - local64_set(&event->hw.prev_count, val); - perf_event_update_userpage(event); - } -} - -/* - * Since limited events don't respect the freeze conditions, we - * have to read them immediately after freezing or unfreezing the - * other events. We try to keep the values from the limited - * events as consistent as possible by keeping the delay (in - * cycles and instructions) between freezing/unfreezing and reading - * the limited events as small and consistent as possible. - * Therefore, if any limited events are in use, we read them - * both, and always in the same order, to minimize variability, - * and do it inside the same asm that writes MMCR0. - */ -static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0) -{ - unsigned long pmc5, pmc6; - - if (!cpuhw->n_limited) { - mtspr(SPRN_MMCR0, mmcr0); - return; - } - - /* - * Write MMCR0, then read PMC5 and PMC6 immediately. - * To ensure we don't get a performance monitor interrupt - * between writing MMCR0 and freezing/thawing the limited - * events, we first write MMCR0 with the event overflow - * interrupt enable bits turned off. - */ - asm volatile("mtspr %3,%2; mfspr %0,%4; mfspr %1,%5" - : "=&r" (pmc5), "=&r" (pmc6) - : "r" (mmcr0 & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)), - "i" (SPRN_MMCR0), - "i" (SPRN_PMC5), "i" (SPRN_PMC6)); - - if (mmcr0 & MMCR0_FC) - freeze_limited_counters(cpuhw, pmc5, pmc6); - else - thaw_limited_counters(cpuhw, pmc5, pmc6); - - /* - * Write the full MMCR0 including the event overflow interrupt - * enable bits, if necessary. - */ - if (mmcr0 & (MMCR0_PMC1CE | MMCR0_PMCjCE)) - mtspr(SPRN_MMCR0, mmcr0); -} - -/* - * Disable all events to prevent PMU interrupts and to allow - * events to be added or removed. - */ -static void power_pmu_disable(struct pmu *pmu) -{ - struct cpu_hw_events *cpuhw; - unsigned long flags; - - if (!ppmu) - return; - local_irq_save(flags); - cpuhw = &__get_cpu_var(cpu_hw_events); - - if (!cpuhw->disabled) { - cpuhw->disabled = 1; - cpuhw->n_added = 0; - - /* - * Check if we ever enabled the PMU on this cpu. - */ - if (!cpuhw->pmcs_enabled) { - ppc_enable_pmcs(); - cpuhw->pmcs_enabled = 1; - } - - /* - * Disable instruction sampling if it was enabled - */ - if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { - mtspr(SPRN_MMCRA, - cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mb(); - } - - /* - * Set the 'freeze counters' bit. - * The barrier is to make sure the mtspr has been - * executed and the PMU has frozen the events - * before we return. - */ - write_mmcr0(cpuhw, mfspr(SPRN_MMCR0) | MMCR0_FC); - mb(); - } - local_irq_restore(flags); -} - -/* - * Re-enable all events if disable == 0. - * If we were previously disabled and events were added, then - * put the new config on the PMU. - */ -static void power_pmu_enable(struct pmu *pmu) -{ - struct perf_event *event; - struct cpu_hw_events *cpuhw; - unsigned long flags; - long i; - unsigned long val; - s64 left; - unsigned int hwc_index[MAX_HWEVENTS]; - int n_lim; - int idx; - - if (!ppmu) - return; - local_irq_save(flags); - cpuhw = &__get_cpu_var(cpu_hw_events); - if (!cpuhw->disabled) { - local_irq_restore(flags); - return; - } - cpuhw->disabled = 0; - - /* - * If we didn't change anything, or only removed events, - * no need to recalculate MMCR* settings and reset the PMCs. - * Just reenable the PMU with the current MMCR* settings - * (possibly updated for removal of events). - */ - if (!cpuhw->n_added) { - mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); - if (cpuhw->n_events == 0) - ppc_set_pmu_inuse(0); - goto out_enable; - } - - /* - * Compute MMCR* values for the new set of events - */ - if (ppmu->compute_mmcr(cpuhw->events, cpuhw->n_events, hwc_index, - cpuhw->mmcr)) { - /* shouldn't ever get here */ - printk(KERN_ERR "oops compute_mmcr failed\n"); - goto out; - } - - /* - * Add in MMCR0 freeze bits corresponding to the - * attr.exclude_* bits for the first event. - * We have already checked that all events have the - * same values for these bits as the first event. - */ - event = cpuhw->event[0]; - if (event->attr.exclude_user) - cpuhw->mmcr[0] |= MMCR0_FCP; - if (event->attr.exclude_kernel) - cpuhw->mmcr[0] |= freeze_events_kernel; - if (event->attr.exclude_hv) - cpuhw->mmcr[0] |= MMCR0_FCHV; - - /* - * Write the new configuration to MMCR* with the freeze - * bit set and set the hardware events to their initial values. - * Then unfreeze the events. - */ - ppc_set_pmu_inuse(1); - mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); - mtspr(SPRN_MMCR0, (cpuhw->mmcr[0] & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)) - | MMCR0_FC); - - /* - * Read off any pre-existing events that need to move - * to another PMC. - */ - for (i = 0; i < cpuhw->n_events; ++i) { - event = cpuhw->event[i]; - if (event->hw.idx && event->hw.idx != hwc_index[i] + 1) { - power_pmu_read(event); - write_pmc(event->hw.idx, 0); - event->hw.idx = 0; - } - } - - /* - * Initialize the PMCs for all the new and moved events. - */ - cpuhw->n_limited = n_lim = 0; - for (i = 0; i < cpuhw->n_events; ++i) { - event = cpuhw->event[i]; - if (event->hw.idx) - continue; - idx = hwc_index[i] + 1; - if (is_limited_pmc(idx)) { - cpuhw->limited_counter[n_lim] = event; - cpuhw->limited_hwidx[n_lim] = idx; - ++n_lim; - continue; - } - val = 0; - if (event->hw.sample_period) { - left = local64_read(&event->hw.period_left); - if (left < 0x80000000L) - val = 0x80000000L - left; - } - local64_set(&event->hw.prev_count, val); - event->hw.idx = idx; - if (event->hw.state & PERF_HES_STOPPED) - val = 0; - write_pmc(idx, val); - perf_event_update_userpage(event); - } - cpuhw->n_limited = n_lim; - cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; - - out_enable: - mb(); - write_mmcr0(cpuhw, cpuhw->mmcr[0]); - - /* - * Enable instruction sampling if necessary - */ - if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { - mb(); - mtspr(SPRN_MMCRA, cpuhw->mmcr[2]); - } - - out: - local_irq_restore(flags); -} - -static int collect_events(struct perf_event *group, int max_count, - struct perf_event *ctrs[], u64 *events, - unsigned int *flags) -{ - int n = 0; - struct perf_event *event; - - if (!is_software_event(group)) { - if (n >= max_count) - return -1; - ctrs[n] = group; - flags[n] = group->hw.event_base; - events[n++] = group->hw.config; - } - list_for_each_entry(event, &group->sibling_list, group_entry) { - if (!is_software_event(event) && - event->state != PERF_EVENT_STATE_OFF) { - if (n >= max_count) - return -1; - ctrs[n] = event; - flags[n] = event->hw.event_base; - events[n++] = event->hw.config; - } - } - return n; -} - -/* - * Add a event to the PMU. - * If all events are not already frozen, then we disable and - * re-enable the PMU in order to get hw_perf_enable to do the - * actual work of reconfiguring the PMU. - */ -static int power_pmu_add(struct perf_event *event, int ef_flags) -{ - struct cpu_hw_events *cpuhw; - unsigned long flags; - int n0; - int ret = -EAGAIN; - - local_irq_save(flags); - perf_pmu_disable(event->pmu); - - /* - * Add the event to the list (if there is room) - * and check whether the total set is still feasible. - */ - cpuhw = &__get_cpu_var(cpu_hw_events); - n0 = cpuhw->n_events; - if (n0 >= ppmu->n_counter) - goto out; - cpuhw->event[n0] = event; - cpuhw->events[n0] = event->hw.config; - cpuhw->flags[n0] = event->hw.event_base; - - if (!(ef_flags & PERF_EF_START)) - event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; - - /* - * If group events scheduling transaction was started, - * skip the schedulability test here, it will be performed - * at commit time(->commit_txn) as a whole - */ - if (cpuhw->group_flag & PERF_EVENT_TXN) - goto nocheck; - - if (check_excludes(cpuhw->event, cpuhw->flags, n0, 1)) - goto out; - if (power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n0 + 1)) - goto out; - event->hw.config = cpuhw->events[n0]; - -nocheck: - ++cpuhw->n_events; - ++cpuhw->n_added; - - ret = 0; - out: - perf_pmu_enable(event->pmu); - local_irq_restore(flags); - return ret; -} - -/* - * Remove a event from the PMU. - */ -static void power_pmu_del(struct perf_event *event, int ef_flags) -{ - struct cpu_hw_events *cpuhw; - long i; - unsigned long flags; - - local_irq_save(flags); - perf_pmu_disable(event->pmu); - - power_pmu_read(event); - - cpuhw = &__get_cpu_var(cpu_hw_events); - for (i = 0; i < cpuhw->n_events; ++i) { - if (event == cpuhw->event[i]) { - while (++i < cpuhw->n_events) { - cpuhw->event[i-1] = cpuhw->event[i]; - cpuhw->events[i-1] = cpuhw->events[i]; - cpuhw->flags[i-1] = cpuhw->flags[i]; - } - --cpuhw->n_events; - ppmu->disable_pmc(event->hw.idx - 1, cpuhw->mmcr); - if (event->hw.idx) { - write_pmc(event->hw.idx, 0); - event->hw.idx = 0; - } - perf_event_update_userpage(event); - break; - } - } - for (i = 0; i < cpuhw->n_limited; ++i) - if (event == cpuhw->limited_counter[i]) - break; - if (i < cpuhw->n_limited) { - while (++i < cpuhw->n_limited) { - cpuhw->limited_counter[i-1] = cpuhw->limited_counter[i]; - cpuhw->limited_hwidx[i-1] = cpuhw->limited_hwidx[i]; - } - --cpuhw->n_limited; - } - if (cpuhw->n_events == 0) { - /* disable exceptions if no events are running */ - cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); - } - - perf_pmu_enable(event->pmu); - local_irq_restore(flags); -} - -/* - * POWER-PMU does not support disabling individual counters, hence - * program their cycle counter to their max value and ignore the interrupts. - */ - -static void power_pmu_start(struct perf_event *event, int ef_flags) -{ - unsigned long flags; - s64 left; - unsigned long val; - - if (!event->hw.idx || !event->hw.sample_period) - return; - - if (!(event->hw.state & PERF_HES_STOPPED)) - return; - - if (ef_flags & PERF_EF_RELOAD) - WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); - - local_irq_save(flags); - perf_pmu_disable(event->pmu); - - event->hw.state = 0; - left = local64_read(&event->hw.period_left); - - val = 0; - if (left < 0x80000000L) - val = 0x80000000L - left; - - write_pmc(event->hw.idx, val); - - perf_event_update_userpage(event); - perf_pmu_enable(event->pmu); - local_irq_restore(flags); -} - -static void power_pmu_stop(struct perf_event *event, int ef_flags) -{ - unsigned long flags; - - if (!event->hw.idx || !event->hw.sample_period) - return; - - if (event->hw.state & PERF_HES_STOPPED) - return; - - local_irq_save(flags); - perf_pmu_disable(event->pmu); - - power_pmu_read(event); - event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; - write_pmc(event->hw.idx, 0); - - perf_event_update_userpage(event); - perf_pmu_enable(event->pmu); - local_irq_restore(flags); -} - -/* - * Start group events scheduling transaction - * Set the flag to make pmu::enable() not perform the - * schedulability test, it will be performed at commit time - */ -void power_pmu_start_txn(struct pmu *pmu) -{ - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); - - perf_pmu_disable(pmu); - cpuhw->group_flag |= PERF_EVENT_TXN; - cpuhw->n_txn_start = cpuhw->n_events; -} - -/* - * Stop group events scheduling transaction - * Clear the flag and pmu::enable() will perform the - * schedulability test. - */ -void power_pmu_cancel_txn(struct pmu *pmu) -{ - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); - - cpuhw->group_flag &= ~PERF_EVENT_TXN; - perf_pmu_enable(pmu); -} - -/* - * Commit group events scheduling transaction - * Perform the group schedulability test as a whole - * Return 0 if success - */ -int power_pmu_commit_txn(struct pmu *pmu) -{ - struct cpu_hw_events *cpuhw; - long i, n; - - if (!ppmu) - return -EAGAIN; - cpuhw = &__get_cpu_var(cpu_hw_events); - n = cpuhw->n_events; - if (check_excludes(cpuhw->event, cpuhw->flags, 0, n)) - return -EAGAIN; - i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n); - if (i < 0) - return -EAGAIN; - - for (i = cpuhw->n_txn_start; i < n; ++i) - cpuhw->event[i]->hw.config = cpuhw->events[i]; - - cpuhw->group_flag &= ~PERF_EVENT_TXN; - perf_pmu_enable(pmu); - return 0; -} - -/* - * Return 1 if we might be able to put event on a limited PMC, - * or 0 if not. - * A event can only go on a limited PMC if it counts something - * that a limited PMC can count, doesn't require interrupts, and - * doesn't exclude any processor mode. - */ -static int can_go_on_limited_pmc(struct perf_event *event, u64 ev, - unsigned int flags) -{ - int n; - u64 alt[MAX_EVENT_ALTERNATIVES]; - - if (event->attr.exclude_user - || event->attr.exclude_kernel - || event->attr.exclude_hv - || event->attr.sample_period) - return 0; - - if (ppmu->limited_pmc_event(ev)) - return 1; - - /* - * The requested event_id isn't on a limited PMC already; - * see if any alternative code goes on a limited PMC. - */ - if (!ppmu->get_alternatives) - return 0; - - flags |= PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD; - n = ppmu->get_alternatives(ev, flags, alt); - - return n > 0; -} - -/* - * Find an alternative event_id that goes on a normal PMC, if possible, - * and return the event_id code, or 0 if there is no such alternative. - * (Note: event_id code 0 is "don't count" on all machines.) - */ -static u64 normal_pmc_alternative(u64 ev, unsigned long flags) -{ - u64 alt[MAX_EVENT_ALTERNATIVES]; - int n; - - flags &= ~(PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD); - n = ppmu->get_alternatives(ev, flags, alt); - if (!n) - return 0; - return alt[0]; -} - -/* Number of perf_events counting hardware events */ -static atomic_t num_events; -/* Used to avoid races in calling reserve/release_pmc_hardware */ -static DEFINE_MUTEX(pmc_reserve_mutex); - -/* - * Release the PMU if this is the last perf_event. - */ -static void hw_perf_event_destroy(struct perf_event *event) -{ - if (!atomic_add_unless(&num_events, -1, 1)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_dec_return(&num_events) == 0) - release_pmc_hardware(); - mutex_unlock(&pmc_reserve_mutex); - } -} - -/* - * Translate a generic cache event_id config to a raw event_id code. - */ -static int hw_perf_cache_event(u64 config, u64 *eventp) -{ - unsigned long type, op, result; - int ev; - - if (!ppmu->cache_events) - return -EINVAL; - - /* unpack config */ - type = config & 0xff; - op = (config >> 8) & 0xff; - result = (config >> 16) & 0xff; - - if (type >= PERF_COUNT_HW_CACHE_MAX || - op >= PERF_COUNT_HW_CACHE_OP_MAX || - result >= PERF_COUNT_HW_CACHE_RESULT_MAX) - return -EINVAL; - - ev = (*ppmu->cache_events)[type][op][result]; - if (ev == 0) - return -EOPNOTSUPP; - if (ev == -1) - return -EINVAL; - *eventp = ev; - return 0; -} - -static int power_pmu_event_init(struct perf_event *event) -{ - u64 ev; - unsigned long flags; - struct perf_event *ctrs[MAX_HWEVENTS]; - u64 events[MAX_HWEVENTS]; - unsigned int cflags[MAX_HWEVENTS]; - int n; - int err; - struct cpu_hw_events *cpuhw; - - if (!ppmu) - return -ENOENT; - - switch (event->attr.type) { - case PERF_TYPE_HARDWARE: - ev = event->attr.config; - if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0) - return -EOPNOTSUPP; - ev = ppmu->generic_events[ev]; - break; - case PERF_TYPE_HW_CACHE: - err = hw_perf_cache_event(event->attr.config, &ev); - if (err) - return err; - break; - case PERF_TYPE_RAW: - ev = event->attr.config; - break; - default: - return -ENOENT; - } - - event->hw.config_base = ev; - event->hw.idx = 0; - - /* - * If we are not running on a hypervisor, force the - * exclude_hv bit to 0 so that we don't care what - * the user set it to. - */ - if (!firmware_has_feature(FW_FEATURE_LPAR)) - event->attr.exclude_hv = 0; - - /* - * If this is a per-task event, then we can use - * PM_RUN_* events interchangeably with their non RUN_* - * equivalents, e.g. PM_RUN_CYC instead of PM_CYC. - * XXX we should check if the task is an idle task. - */ - flags = 0; - if (event->attach_state & PERF_ATTACH_TASK) - flags |= PPMU_ONLY_COUNT_RUN; - - /* - * If this machine has limited events, check whether this - * event_id could go on a limited event. - */ - if (ppmu->flags & PPMU_LIMITED_PMC5_6) { - if (can_go_on_limited_pmc(event, ev, flags)) { - flags |= PPMU_LIMITED_PMC_OK; - } else if (ppmu->limited_pmc_event(ev)) { - /* - * The requested event_id is on a limited PMC, - * but we can't use a limited PMC; see if any - * alternative goes on a normal PMC. - */ - ev = normal_pmc_alternative(ev, flags); - if (!ev) - return -EINVAL; - } - } - - /* - * If this is in a group, check if it can go on with all the - * other hardware events in the group. We assume the event - * hasn't been linked into its leader's sibling list at this point. - */ - n = 0; - if (event->group_leader != event) { - n = collect_events(event->group_leader, ppmu->n_counter - 1, - ctrs, events, cflags); - if (n < 0) - return -EINVAL; - } - events[n] = ev; - ctrs[n] = event; - cflags[n] = flags; - if (check_excludes(ctrs, cflags, n, 1)) - return -EINVAL; - - cpuhw = &get_cpu_var(cpu_hw_events); - err = power_check_constraints(cpuhw, events, cflags, n + 1); - put_cpu_var(cpu_hw_events); - if (err) - return -EINVAL; - - event->hw.config = events[n]; - event->hw.event_base = cflags[n]; - event->hw.last_period = event->hw.sample_period; - local64_set(&event->hw.period_left, event->hw.last_period); - - /* - * See if we need to reserve the PMU. - * If no events are currently in use, then we have to take a - * mutex to ensure that we don't race with another task doing - * reserve_pmc_hardware or release_pmc_hardware. - */ - err = 0; - if (!atomic_inc_not_zero(&num_events)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_read(&num_events) == 0 && - reserve_pmc_hardware(perf_event_interrupt)) - err = -EBUSY; - else - atomic_inc(&num_events); - mutex_unlock(&pmc_reserve_mutex); - } - event->destroy = hw_perf_event_destroy; - - return err; -} - -struct pmu power_pmu = { - .pmu_enable = power_pmu_enable, - .pmu_disable = power_pmu_disable, - .event_init = power_pmu_event_init, - .add = power_pmu_add, - .del = power_pmu_del, - .start = power_pmu_start, - .stop = power_pmu_stop, - .read = power_pmu_read, - .start_txn = power_pmu_start_txn, - .cancel_txn = power_pmu_cancel_txn, - .commit_txn = power_pmu_commit_txn, -}; - -/* - * A counter has overflowed; update its count and record - * things if requested. Note that interrupts are hard-disabled - * here so there is no possibility of being interrupted. - */ -static void record_and_restart(struct perf_event *event, unsigned long val, - struct pt_regs *regs) -{ - u64 period = event->hw.sample_period; - s64 prev, delta, left; - int record = 0; - - if (event->hw.state & PERF_HES_STOPPED) { - write_pmc(event->hw.idx, 0); - return; - } - - /* we don't have to worry about interrupts here */ - prev = local64_read(&event->hw.prev_count); - delta = check_and_compute_delta(prev, val); - local64_add(delta, &event->count); - - /* - * See if the total period for this event has expired, - * and update for the next period. - */ - val = 0; - left = local64_read(&event->hw.period_left) - delta; - if (period) { - if (left <= 0) { - left += period; - if (left <= 0) - left = period; - record = 1; - event->hw.last_period = event->hw.sample_period; - } - if (left < 0x80000000LL) - val = 0x80000000LL - left; - } - - write_pmc(event->hw.idx, val); - local64_set(&event->hw.prev_count, val); - local64_set(&event->hw.period_left, left); - perf_event_update_userpage(event); - - /* - * Finally record data if requested. - */ - if (record) { - struct perf_sample_data data; - - perf_sample_data_init(&data, ~0ULL); - data.period = event->hw.last_period; - - if (event->attr.sample_type & PERF_SAMPLE_ADDR) - perf_get_data_addr(regs, &data.addr); - - if (perf_event_overflow(event, &data, regs)) - power_pmu_stop(event, 0); - } -} - -/* - * Called from generic code to get the misc flags (i.e. processor mode) - * for an event_id. - */ -unsigned long perf_misc_flags(struct pt_regs *regs) -{ - u32 flags = perf_get_misc_flags(regs); - - if (flags) - return flags; - return user_mode(regs) ? PERF_RECORD_MISC_USER : - PERF_RECORD_MISC_KERNEL; -} - -/* - * Called from generic code to get the instruction pointer - * for an event_id. - */ -unsigned long perf_instruction_pointer(struct pt_regs *regs) -{ - unsigned long ip; - - if (TRAP(regs) != 0xf00) - return regs->nip; /* not a PMU interrupt */ - - ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs); - return ip; -} - -static bool pmc_overflow(unsigned long val) -{ - if ((int)val < 0) - return true; - - /* - * Events on POWER7 can roll back if a speculative event doesn't - * eventually complete. Unfortunately in some rare cases they will - * raise a performance monitor exception. We need to catch this to - * ensure we reset the PMC. In all cases the PMC will be 256 or less - * cycles from overflow. - * - * We only do this if the first pass fails to find any overflowing - * PMCs because a user might set a period of less than 256 and we - * don't want to mistakenly reset them. - */ - if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256)) - return true; - - return false; -} - -/* - * Performance monitor interrupt stuff - */ -static void perf_event_interrupt(struct pt_regs *regs) -{ - int i; - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); - struct perf_event *event; - unsigned long val; - int found = 0; - int nmi; - - if (cpuhw->n_limited) - freeze_limited_counters(cpuhw, mfspr(SPRN_PMC5), - mfspr(SPRN_PMC6)); - - perf_read_regs(regs); - - nmi = perf_intr_is_nmi(regs); - if (nmi) - nmi_enter(); - else - irq_enter(); - - for (i = 0; i < cpuhw->n_events; ++i) { - event = cpuhw->event[i]; - if (!event->hw.idx || is_limited_pmc(event->hw.idx)) - continue; - val = read_pmc(event->hw.idx); - if ((int)val < 0) { - /* event has overflowed */ - found = 1; - record_and_restart(event, val, regs); - } - } - - /* - * In case we didn't find and reset the event that caused - * the interrupt, scan all events and reset any that are - * negative, to avoid getting continual interrupts. - * Any that we processed in the previous loop will not be negative. - */ - if (!found) { - for (i = 0; i < ppmu->n_counter; ++i) { - if (is_limited_pmc(i + 1)) - continue; - val = read_pmc(i + 1); - if (pmc_overflow(val)) - write_pmc(i + 1, 0); - } - } - - /* - * Reset MMCR0 to its normal value. This will set PMXE and - * clear FC (freeze counters) and PMAO (perf mon alert occurred) - * and thus allow interrupts to occur again. - * XXX might want to use MSR.PM to keep the events frozen until - * we get back out of this interrupt. - */ - write_mmcr0(cpuhw, cpuhw->mmcr[0]); - - if (nmi) - nmi_exit(); - else - irq_exit(); -} - -static void power_pmu_setup(int cpu) -{ - struct cpu_hw_events *cpuhw = &per_cpu(cpu_hw_events, cpu); - - if (!ppmu) - return; - memset(cpuhw, 0, sizeof(*cpuhw)); - cpuhw->mmcr[0] = MMCR0_FC; -} - -static int __cpuinit -power_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) -{ - unsigned int cpu = (long)hcpu; - - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_UP_PREPARE: - power_pmu_setup(cpu); - break; - - default: - break; - } - - return NOTIFY_OK; -} - -int __cpuinit register_power_pmu(struct power_pmu *pmu) -{ - if (ppmu) - return -EBUSY; /* something's already registered */ - - ppmu = pmu; - pr_info("%s performance monitor hardware support registered\n", - pmu->name); - -#ifdef MSR_HV - /* - * Use FCHV to ignore kernel events if MSR.HV is set. - */ - if (mfmsr() & MSR_HV) - freeze_events_kernel = MMCR0_FCHV; -#endif /* CONFIG_PPC64 */ - - perf_pmu_register(&power_pmu, "cpu", PERF_TYPE_RAW); - perf_cpu_notifier(power_pmu_notifier); - - return 0; -} diff --git a/arch/powerpc/kernel/perf_event_fsl_emb.c b/arch/powerpc/kernel/perf_event_fsl_emb.c deleted file mode 100644 index 0a6d2a9..0000000 --- a/arch/powerpc/kernel/perf_event_fsl_emb.c +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Performance event support - Freescale Embedded Performance Monitor - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * Copyright 2010 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct cpu_hw_events { - int n_events; - int disabled; - u8 pmcs_enabled; - struct perf_event *event[MAX_HWEVENTS]; -}; -static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); - -static struct fsl_emb_pmu *ppmu; - -/* Number of perf_events counting hardware events */ -static atomic_t num_events; -/* Used to avoid races in calling reserve/release_pmc_hardware */ -static DEFINE_MUTEX(pmc_reserve_mutex); - -/* - * If interrupts were soft-disabled when a PMU interrupt occurs, treat - * it as an NMI. - */ -static inline int perf_intr_is_nmi(struct pt_regs *regs) -{ -#ifdef __powerpc64__ - return !regs->softe; -#else - return 0; -#endif -} - -static void perf_event_interrupt(struct pt_regs *regs); - -/* - * Read one performance monitor counter (PMC). - */ -static unsigned long read_pmc(int idx) -{ - unsigned long val; - - switch (idx) { - case 0: - val = mfpmr(PMRN_PMC0); - break; - case 1: - val = mfpmr(PMRN_PMC1); - break; - case 2: - val = mfpmr(PMRN_PMC2); - break; - case 3: - val = mfpmr(PMRN_PMC3); - break; - default: - printk(KERN_ERR "oops trying to read PMC%d\n", idx); - val = 0; - } - return val; -} - -/* - * Write one PMC. - */ -static void write_pmc(int idx, unsigned long val) -{ - switch (idx) { - case 0: - mtpmr(PMRN_PMC0, val); - break; - case 1: - mtpmr(PMRN_PMC1, val); - break; - case 2: - mtpmr(PMRN_PMC2, val); - break; - case 3: - mtpmr(PMRN_PMC3, val); - break; - default: - printk(KERN_ERR "oops trying to write PMC%d\n", idx); - } - - isync(); -} - -/* - * Write one local control A register - */ -static void write_pmlca(int idx, unsigned long val) -{ - switch (idx) { - case 0: - mtpmr(PMRN_PMLCA0, val); - break; - case 1: - mtpmr(PMRN_PMLCA1, val); - break; - case 2: - mtpmr(PMRN_PMLCA2, val); - break; - case 3: - mtpmr(PMRN_PMLCA3, val); - break; - default: - printk(KERN_ERR "oops trying to write PMLCA%d\n", idx); - } - - isync(); -} - -/* - * Write one local control B register - */ -static void write_pmlcb(int idx, unsigned long val) -{ - switch (idx) { - case 0: - mtpmr(PMRN_PMLCB0, val); - break; - case 1: - mtpmr(PMRN_PMLCB1, val); - break; - case 2: - mtpmr(PMRN_PMLCB2, val); - break; - case 3: - mtpmr(PMRN_PMLCB3, val); - break; - default: - printk(KERN_ERR "oops trying to write PMLCB%d\n", idx); - } - - isync(); -} - -static void fsl_emb_pmu_read(struct perf_event *event) -{ - s64 val, delta, prev; - - if (event->hw.state & PERF_HES_STOPPED) - return; - - /* - * Performance monitor interrupts come even when interrupts - * are soft-disabled, as long as interrupts are hard-enabled. - * Therefore we treat them like NMIs. - */ - do { - prev = local64_read(&event->hw.prev_count); - barrier(); - val = read_pmc(event->hw.idx); - } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); - - /* The counters are only 32 bits wide */ - delta = (val - prev) & 0xfffffffful; - local64_add(delta, &event->count); - local64_sub(delta, &event->hw.period_left); -} - -/* - * Disable all events to prevent PMU interrupts and to allow - * events to be added or removed. - */ -static void fsl_emb_pmu_disable(struct pmu *pmu) -{ - struct cpu_hw_events *cpuhw; - unsigned long flags; - - local_irq_save(flags); - cpuhw = &__get_cpu_var(cpu_hw_events); - - if (!cpuhw->disabled) { - cpuhw->disabled = 1; - - /* - * Check if we ever enabled the PMU on this cpu. - */ - if (!cpuhw->pmcs_enabled) { - ppc_enable_pmcs(); - cpuhw->pmcs_enabled = 1; - } - - if (atomic_read(&num_events)) { - /* - * Set the 'freeze all counters' bit, and disable - * interrupts. The barrier is to make sure the - * mtpmr has been executed and the PMU has frozen - * the events before we return. - */ - - mtpmr(PMRN_PMGC0, PMGC0_FAC); - isync(); - } - } - local_irq_restore(flags); -} - -/* - * Re-enable all events if disable == 0. - * If we were previously disabled and events were added, then - * put the new config on the PMU. - */ -static void fsl_emb_pmu_enable(struct pmu *pmu) -{ - struct cpu_hw_events *cpuhw; - unsigned long flags; - - local_irq_save(flags); - cpuhw = &__get_cpu_var(cpu_hw_events); - if (!cpuhw->disabled) - goto out; - - cpuhw->disabled = 0; - ppc_set_pmu_inuse(cpuhw->n_events != 0); - - if (cpuhw->n_events > 0) { - mtpmr(PMRN_PMGC0, PMGC0_PMIE | PMGC0_FCECE); - isync(); - } - - out: - local_irq_restore(flags); -} - -static int collect_events(struct perf_event *group, int max_count, - struct perf_event *ctrs[]) -{ - int n = 0; - struct perf_event *event; - - if (!is_software_event(group)) { - if (n >= max_count) - return -1; - ctrs[n] = group; - n++; - } - list_for_each_entry(event, &group->sibling_list, group_entry) { - if (!is_software_event(event) && - event->state != PERF_EVENT_STATE_OFF) { - if (n >= max_count) - return -1; - ctrs[n] = event; - n++; - } - } - return n; -} - -/* context locked on entry */ -static int fsl_emb_pmu_add(struct perf_event *event, int flags) -{ - struct cpu_hw_events *cpuhw; - int ret = -EAGAIN; - int num_counters = ppmu->n_counter; - u64 val; - int i; - - perf_pmu_disable(event->pmu); - cpuhw = &get_cpu_var(cpu_hw_events); - - if (event->hw.config & FSL_EMB_EVENT_RESTRICTED) - num_counters = ppmu->n_restricted; - - /* - * Allocate counters from top-down, so that restricted-capable - * counters are kept free as long as possible. - */ - for (i = num_counters - 1; i >= 0; i--) { - if (cpuhw->event[i]) - continue; - - break; - } - - if (i < 0) - goto out; - - event->hw.idx = i; - cpuhw->event[i] = event; - ++cpuhw->n_events; - - val = 0; - if (event->hw.sample_period) { - s64 left = local64_read(&event->hw.period_left); - if (left < 0x80000000L) - val = 0x80000000L - left; - } - local64_set(&event->hw.prev_count, val); - - if (!(flags & PERF_EF_START)) { - event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; - val = 0; - } - - write_pmc(i, val); - perf_event_update_userpage(event); - - write_pmlcb(i, event->hw.config >> 32); - write_pmlca(i, event->hw.config_base); - - ret = 0; - out: - put_cpu_var(cpu_hw_events); - perf_pmu_enable(event->pmu); - return ret; -} - -/* context locked on entry */ -static void fsl_emb_pmu_del(struct perf_event *event, int flags) -{ - struct cpu_hw_events *cpuhw; - int i = event->hw.idx; - - perf_pmu_disable(event->pmu); - if (i < 0) - goto out; - - fsl_emb_pmu_read(event); - - cpuhw = &get_cpu_var(cpu_hw_events); - - WARN_ON(event != cpuhw->event[event->hw.idx]); - - write_pmlca(i, 0); - write_pmlcb(i, 0); - write_pmc(i, 0); - - cpuhw->event[i] = NULL; - event->hw.idx = -1; - - /* - * TODO: if at least one restricted event exists, and we - * just freed up a non-restricted-capable counter, and - * there is a restricted-capable counter occupied by - * a non-restricted event, migrate that event to the - * vacated counter. - */ - - cpuhw->n_events--; - - out: - perf_pmu_enable(event->pmu); - put_cpu_var(cpu_hw_events); -} - -static void fsl_emb_pmu_start(struct perf_event *event, int ef_flags) -{ - unsigned long flags; - s64 left; - - if (event->hw.idx < 0 || !event->hw.sample_period) - return; - - if (!(event->hw.state & PERF_HES_STOPPED)) - return; - - if (ef_flags & PERF_EF_RELOAD) - WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); - - local_irq_save(flags); - perf_pmu_disable(event->pmu); - - event->hw.state = 0; - left = local64_read(&event->hw.period_left); - write_pmc(event->hw.idx, left); - - perf_event_update_userpage(event); - perf_pmu_enable(event->pmu); - local_irq_restore(flags); -} - -static void fsl_emb_pmu_stop(struct perf_event *event, int ef_flags) -{ - unsigned long flags; - - if (event->hw.idx < 0 || !event->hw.sample_period) - return; - - if (event->hw.state & PERF_HES_STOPPED) - return; - - local_irq_save(flags); - perf_pmu_disable(event->pmu); - - fsl_emb_pmu_read(event); - event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; - write_pmc(event->hw.idx, 0); - - perf_event_update_userpage(event); - perf_pmu_enable(event->pmu); - local_irq_restore(flags); -} - -/* - * Release the PMU if this is the last perf_event. - */ -static void hw_perf_event_destroy(struct perf_event *event) -{ - if (!atomic_add_unless(&num_events, -1, 1)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_dec_return(&num_events) == 0) - release_pmc_hardware(); - mutex_unlock(&pmc_reserve_mutex); - } -} - -/* - * Translate a generic cache event_id config to a raw event_id code. - */ -static int hw_perf_cache_event(u64 config, u64 *eventp) -{ - unsigned long type, op, result; - int ev; - - if (!ppmu->cache_events) - return -EINVAL; - - /* unpack config */ - type = config & 0xff; - op = (config >> 8) & 0xff; - result = (config >> 16) & 0xff; - - if (type >= PERF_COUNT_HW_CACHE_MAX || - op >= PERF_COUNT_HW_CACHE_OP_MAX || - result >= PERF_COUNT_HW_CACHE_RESULT_MAX) - return -EINVAL; - - ev = (*ppmu->cache_events)[type][op][result]; - if (ev == 0) - return -EOPNOTSUPP; - if (ev == -1) - return -EINVAL; - *eventp = ev; - return 0; -} - -static int fsl_emb_pmu_event_init(struct perf_event *event) -{ - u64 ev; - struct perf_event *events[MAX_HWEVENTS]; - int n; - int err; - int num_restricted; - int i; - - switch (event->attr.type) { - case PERF_TYPE_HARDWARE: - ev = event->attr.config; - if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0) - return -EOPNOTSUPP; - ev = ppmu->generic_events[ev]; - break; - - case PERF_TYPE_HW_CACHE: - err = hw_perf_cache_event(event->attr.config, &ev); - if (err) - return err; - break; - - case PERF_TYPE_RAW: - ev = event->attr.config; - break; - - default: - return -ENOENT; - } - - event->hw.config = ppmu->xlate_event(ev); - if (!(event->hw.config & FSL_EMB_EVENT_VALID)) - return -EINVAL; - - /* - * If this is in a group, check if it can go on with all the - * other hardware events in the group. We assume the event - * hasn't been linked into its leader's sibling list at this point. - */ - n = 0; - if (event->group_leader != event) { - n = collect_events(event->group_leader, - ppmu->n_counter - 1, events); - if (n < 0) - return -EINVAL; - } - - if (event->hw.config & FSL_EMB_EVENT_RESTRICTED) { - num_restricted = 0; - for (i = 0; i < n; i++) { - if (events[i]->hw.config & FSL_EMB_EVENT_RESTRICTED) - num_restricted++; - } - - if (num_restricted >= ppmu->n_restricted) - return -EINVAL; - } - - event->hw.idx = -1; - - event->hw.config_base = PMLCA_CE | PMLCA_FCM1 | - (u32)((ev << 16) & PMLCA_EVENT_MASK); - - if (event->attr.exclude_user) - event->hw.config_base |= PMLCA_FCU; - if (event->attr.exclude_kernel) - event->hw.config_base |= PMLCA_FCS; - if (event->attr.exclude_idle) - return -ENOTSUPP; - - event->hw.last_period = event->hw.sample_period; - local64_set(&event->hw.period_left, event->hw.last_period); - - /* - * See if we need to reserve the PMU. - * If no events are currently in use, then we have to take a - * mutex to ensure that we don't race with another task doing - * reserve_pmc_hardware or release_pmc_hardware. - */ - err = 0; - if (!atomic_inc_not_zero(&num_events)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_read(&num_events) == 0 && - reserve_pmc_hardware(perf_event_interrupt)) - err = -EBUSY; - else - atomic_inc(&num_events); - mutex_unlock(&pmc_reserve_mutex); - - mtpmr(PMRN_PMGC0, PMGC0_FAC); - isync(); - } - event->destroy = hw_perf_event_destroy; - - return err; -} - -static struct pmu fsl_emb_pmu = { - .pmu_enable = fsl_emb_pmu_enable, - .pmu_disable = fsl_emb_pmu_disable, - .event_init = fsl_emb_pmu_event_init, - .add = fsl_emb_pmu_add, - .del = fsl_emb_pmu_del, - .start = fsl_emb_pmu_start, - .stop = fsl_emb_pmu_stop, - .read = fsl_emb_pmu_read, -}; - -/* - * A counter has overflowed; update its count and record - * things if requested. Note that interrupts are hard-disabled - * here so there is no possibility of being interrupted. - */ -static void record_and_restart(struct perf_event *event, unsigned long val, - struct pt_regs *regs) -{ - u64 period = event->hw.sample_period; - s64 prev, delta, left; - int record = 0; - - if (event->hw.state & PERF_HES_STOPPED) { - write_pmc(event->hw.idx, 0); - return; - } - - /* we don't have to worry about interrupts here */ - prev = local64_read(&event->hw.prev_count); - delta = (val - prev) & 0xfffffffful; - local64_add(delta, &event->count); - - /* - * See if the total period for this event has expired, - * and update for the next period. - */ - val = 0; - left = local64_read(&event->hw.period_left) - delta; - if (period) { - if (left <= 0) { - left += period; - if (left <= 0) - left = period; - record = 1; - event->hw.last_period = event->hw.sample_period; - } - if (left < 0x80000000LL) - val = 0x80000000LL - left; - } - - write_pmc(event->hw.idx, val); - local64_set(&event->hw.prev_count, val); - local64_set(&event->hw.period_left, left); - perf_event_update_userpage(event); - - /* - * Finally record data if requested. - */ - if (record) { - struct perf_sample_data data; - - perf_sample_data_init(&data, 0); - data.period = event->hw.last_period; - - if (perf_event_overflow(event, &data, regs)) - fsl_emb_pmu_stop(event, 0); - } -} - -static void perf_event_interrupt(struct pt_regs *regs) -{ - int i; - struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); - struct perf_event *event; - unsigned long val; - int found = 0; - int nmi; - - nmi = perf_intr_is_nmi(regs); - if (nmi) - nmi_enter(); - else - irq_enter(); - - for (i = 0; i < ppmu->n_counter; ++i) { - event = cpuhw->event[i]; - - val = read_pmc(i); - if ((int)val < 0) { - if (event) { - /* event has overflowed */ - found = 1; - record_and_restart(event, val, regs); - } else { - /* - * Disabled counter is negative, - * reset it just in case. - */ - write_pmc(i, 0); - } - } - } - - /* PMM will keep counters frozen until we return from the interrupt. */ - mtmsr(mfmsr() | MSR_PMM); - mtpmr(PMRN_PMGC0, PMGC0_PMIE | PMGC0_FCECE); - isync(); - - if (nmi) - nmi_exit(); - else - irq_exit(); -} - -void hw_perf_event_setup(int cpu) -{ - struct cpu_hw_events *cpuhw = &per_cpu(cpu_hw_events, cpu); - - memset(cpuhw, 0, sizeof(*cpuhw)); -} - -int register_fsl_emb_pmu(struct fsl_emb_pmu *pmu) -{ - if (ppmu) - return -EBUSY; /* something's already registered */ - - ppmu = pmu; - pr_info("%s performance monitor hardware support registered\n", - pmu->name); - - perf_pmu_register(&fsl_emb_pmu, "cpu", PERF_TYPE_RAW); - - return 0; -} diff --git a/arch/powerpc/kernel/power4-pmu.c b/arch/powerpc/kernel/power4-pmu.c deleted file mode 100644 index b4f1dda..0000000 --- a/arch/powerpc/kernel/power4-pmu.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Performance counter support for POWER4 (GP) and POWER4+ (GQ) processors. - * - * Copyright 2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include -#include - -/* - * Bits in event code for POWER4 - */ -#define PM_PMC_SH 12 /* PMC number (1-based) for direct events */ -#define PM_PMC_MSK 0xf -#define PM_UNIT_SH 8 /* TTMMUX number and setting - unit select */ -#define PM_UNIT_MSK 0xf -#define PM_LOWER_SH 6 -#define PM_LOWER_MSK 1 -#define PM_LOWER_MSKS 0x40 -#define PM_BYTE_SH 4 /* Byte number of event bus to use */ -#define PM_BYTE_MSK 3 -#define PM_PMCSEL_MSK 7 - -/* - * Unit code values - */ -#define PM_FPU 1 -#define PM_ISU1 2 -#define PM_IFU 3 -#define PM_IDU0 4 -#define PM_ISU1_ALT 6 -#define PM_ISU2 7 -#define PM_IFU_ALT 8 -#define PM_LSU0 9 -#define PM_LSU1 0xc -#define PM_GPS 0xf - -/* - * Bits in MMCR0 for POWER4 - */ -#define MMCR0_PMC1SEL_SH 8 -#define MMCR0_PMC2SEL_SH 1 -#define MMCR_PMCSEL_MSK 0x1f - -/* - * Bits in MMCR1 for POWER4 - */ -#define MMCR1_TTM0SEL_SH 62 -#define MMCR1_TTC0SEL_SH 61 -#define MMCR1_TTM1SEL_SH 59 -#define MMCR1_TTC1SEL_SH 58 -#define MMCR1_TTM2SEL_SH 56 -#define MMCR1_TTC2SEL_SH 55 -#define MMCR1_TTM3SEL_SH 53 -#define MMCR1_TTC3SEL_SH 52 -#define MMCR1_TTMSEL_MSK 3 -#define MMCR1_TD_CP_DBG0SEL_SH 50 -#define MMCR1_TD_CP_DBG1SEL_SH 48 -#define MMCR1_TD_CP_DBG2SEL_SH 46 -#define MMCR1_TD_CP_DBG3SEL_SH 44 -#define MMCR1_DEBUG0SEL_SH 43 -#define MMCR1_DEBUG1SEL_SH 42 -#define MMCR1_DEBUG2SEL_SH 41 -#define MMCR1_DEBUG3SEL_SH 40 -#define MMCR1_PMC1_ADDER_SEL_SH 39 -#define MMCR1_PMC2_ADDER_SEL_SH 38 -#define MMCR1_PMC6_ADDER_SEL_SH 37 -#define MMCR1_PMC5_ADDER_SEL_SH 36 -#define MMCR1_PMC8_ADDER_SEL_SH 35 -#define MMCR1_PMC7_ADDER_SEL_SH 34 -#define MMCR1_PMC3_ADDER_SEL_SH 33 -#define MMCR1_PMC4_ADDER_SEL_SH 32 -#define MMCR1_PMC3SEL_SH 27 -#define MMCR1_PMC4SEL_SH 22 -#define MMCR1_PMC5SEL_SH 17 -#define MMCR1_PMC6SEL_SH 12 -#define MMCR1_PMC7SEL_SH 7 -#define MMCR1_PMC8SEL_SH 2 /* note bit 0 is in MMCRA for GP */ - -static short mmcr1_adder_bits[8] = { - MMCR1_PMC1_ADDER_SEL_SH, - MMCR1_PMC2_ADDER_SEL_SH, - MMCR1_PMC3_ADDER_SEL_SH, - MMCR1_PMC4_ADDER_SEL_SH, - MMCR1_PMC5_ADDER_SEL_SH, - MMCR1_PMC6_ADDER_SEL_SH, - MMCR1_PMC7_ADDER_SEL_SH, - MMCR1_PMC8_ADDER_SEL_SH -}; - -/* - * Bits in MMCRA - */ -#define MMCRA_PMC8SEL0_SH 17 /* PMC8SEL bit 0 for GP */ - -/* - * Layout of constraint bits: - * 6666555555555544444444443333333333222222222211111111110000000000 - * 3210987654321098765432109876543210987654321098765432109876543210 - * |[ >[ >[ >|||[ >[ >< >< >< >< ><><><><><><><><> - * | UC1 UC2 UC3 ||| PS1 PS2 B0 B1 B2 B3 P1P2P3P4P5P6P7P8 - * \SMPL ||\TTC3SEL - * |\TTC_IFU_SEL - * \TTM2SEL0 - * - * SMPL - SAMPLE_ENABLE constraint - * 56: SAMPLE_ENABLE value 0x0100_0000_0000_0000 - * - * UC1 - unit constraint 1: can't have all three of FPU/ISU1/IDU0|ISU2 - * 55: UC1 error 0x0080_0000_0000_0000 - * 54: FPU events needed 0x0040_0000_0000_0000 - * 53: ISU1 events needed 0x0020_0000_0000_0000 - * 52: IDU0|ISU2 events needed 0x0010_0000_0000_0000 - * - * UC2 - unit constraint 2: can't have all three of FPU/IFU/LSU0 - * 51: UC2 error 0x0008_0000_0000_0000 - * 50: FPU events needed 0x0004_0000_0000_0000 - * 49: IFU events needed 0x0002_0000_0000_0000 - * 48: LSU0 events needed 0x0001_0000_0000_0000 - * - * UC3 - unit constraint 3: can't have all four of LSU0/IFU/IDU0|ISU2/ISU1 - * 47: UC3 error 0x8000_0000_0000 - * 46: LSU0 events needed 0x4000_0000_0000 - * 45: IFU events needed 0x2000_0000_0000 - * 44: IDU0|ISU2 events needed 0x1000_0000_0000 - * 43: ISU1 events needed 0x0800_0000_0000 - * - * TTM2SEL0 - * 42: 0 = IDU0 events needed - * 1 = ISU2 events needed 0x0400_0000_0000 - * - * TTC_IFU_SEL - * 41: 0 = IFU.U events needed - * 1 = IFU.L events needed 0x0200_0000_0000 - * - * TTC3SEL - * 40: 0 = LSU1.U events needed - * 1 = LSU1.L events needed 0x0100_0000_0000 - * - * PS1 - * 39: PS1 error 0x0080_0000_0000 - * 36-38: count of events needing PMC1/2/5/6 0x0070_0000_0000 - * - * PS2 - * 35: PS2 error 0x0008_0000_0000 - * 32-34: count of events needing PMC3/4/7/8 0x0007_0000_0000 - * - * B0 - * 28-31: Byte 0 event source 0xf000_0000 - * 1 = FPU - * 2 = ISU1 - * 3 = IFU - * 4 = IDU0 - * 7 = ISU2 - * 9 = LSU0 - * c = LSU1 - * f = GPS - * - * B1, B2, B3 - * 24-27, 20-23, 16-19: Byte 1, 2, 3 event sources - * - * P8 - * 15: P8 error 0x8000 - * 14-15: Count of events needing PMC8 - * - * P1..P7 - * 0-13: Count of events needing PMC1..PMC7 - * - * Note: this doesn't allow events using IFU.U to be combined with events - * using IFU.L, though that is feasible (using TTM0 and TTM2). However - * there are no listed events for IFU.L (they are debug events not - * verified for performance monitoring) so this shouldn't cause a - * problem. - */ - -static struct unitinfo { - unsigned long value, mask; - int unit; - int lowerbit; -} p4_unitinfo[16] = { - [PM_FPU] = { 0x44000000000000ul, 0x88000000000000ul, PM_FPU, 0 }, - [PM_ISU1] = { 0x20080000000000ul, 0x88000000000000ul, PM_ISU1, 0 }, - [PM_ISU1_ALT] = - { 0x20080000000000ul, 0x88000000000000ul, PM_ISU1, 0 }, - [PM_IFU] = { 0x02200000000000ul, 0x08820000000000ul, PM_IFU, 41 }, - [PM_IFU_ALT] = - { 0x02200000000000ul, 0x08820000000000ul, PM_IFU, 41 }, - [PM_IDU0] = { 0x10100000000000ul, 0x80840000000000ul, PM_IDU0, 1 }, - [PM_ISU2] = { 0x10140000000000ul, 0x80840000000000ul, PM_ISU2, 0 }, - [PM_LSU0] = { 0x01400000000000ul, 0x08800000000000ul, PM_LSU0, 0 }, - [PM_LSU1] = { 0x00000000000000ul, 0x00010000000000ul, PM_LSU1, 40 }, - [PM_GPS] = { 0x00000000000000ul, 0x00000000000000ul, PM_GPS, 0 } -}; - -static unsigned char direct_marked_event[8] = { - (1<<2) | (1<<3), /* PMC1: PM_MRK_GRP_DISP, PM_MRK_ST_CMPL */ - (1<<3) | (1<<5), /* PMC2: PM_THRESH_TIMEO, PM_MRK_BRU_FIN */ - (1<<3), /* PMC3: PM_MRK_ST_CMPL_INT */ - (1<<4) | (1<<5), /* PMC4: PM_MRK_GRP_CMPL, PM_MRK_CRU_FIN */ - (1<<4) | (1<<5), /* PMC5: PM_MRK_GRP_TIMEO */ - (1<<3) | (1<<4) | (1<<5), - /* PMC6: PM_MRK_ST_GPS, PM_MRK_FXU_FIN, PM_MRK_GRP_ISSUED */ - (1<<4) | (1<<5), /* PMC7: PM_MRK_FPU_FIN, PM_MRK_INST_FIN */ - (1<<4), /* PMC8: PM_MRK_LSU_FIN */ -}; - -/* - * Returns 1 if event counts things relating to marked instructions - * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. - */ -static int p4_marked_instr_event(u64 event) -{ - int pmc, psel, unit, byte, bit; - unsigned int mask; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - psel = event & PM_PMCSEL_MSK; - if (pmc) { - if (direct_marked_event[pmc - 1] & (1 << psel)) - return 1; - if (psel == 0) /* add events */ - bit = (pmc <= 4)? pmc - 1: 8 - pmc; - else if (psel == 6) /* decode events */ - bit = 4; - else - return 0; - } else - bit = psel; - - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - mask = 0; - switch (unit) { - case PM_LSU1: - if (event & PM_LOWER_MSKS) - mask = 1 << 28; /* byte 7 bit 4 */ - else - mask = 6 << 24; /* byte 3 bits 1 and 2 */ - break; - case PM_LSU0: - /* byte 3, bit 3; byte 2 bits 0,2,3,4,5; byte 1 */ - mask = 0x083dff00; - } - return (mask >> (byte * 8 + bit)) & 1; -} - -static int p4_get_constraint(u64 event, unsigned long *maskp, - unsigned long *valp) -{ - int pmc, byte, unit, lower, sh; - unsigned long mask = 0, value = 0; - int grp = -1; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 8) - return -1; - sh = (pmc - 1) * 2; - mask |= 2 << sh; - value |= 1 << sh; - grp = ((pmc - 1) >> 1) & 1; - } - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - if (unit) { - lower = (event >> PM_LOWER_SH) & PM_LOWER_MSK; - - /* - * Bus events on bytes 0 and 2 can be counted - * on PMC1/2/5/6; bytes 1 and 3 on PMC3/4/7/8. - */ - if (!pmc) - grp = byte & 1; - - if (!p4_unitinfo[unit].unit) - return -1; - mask |= p4_unitinfo[unit].mask; - value |= p4_unitinfo[unit].value; - sh = p4_unitinfo[unit].lowerbit; - if (sh > 1) - value |= (unsigned long)lower << sh; - else if (lower != sh) - return -1; - unit = p4_unitinfo[unit].unit; - - /* Set byte lane select field */ - mask |= 0xfULL << (28 - 4 * byte); - value |= (unsigned long)unit << (28 - 4 * byte); - } - if (grp == 0) { - /* increment PMC1/2/5/6 field */ - mask |= 0x8000000000ull; - value |= 0x1000000000ull; - } else { - /* increment PMC3/4/7/8 field */ - mask |= 0x800000000ull; - value |= 0x100000000ull; - } - - /* Marked instruction events need sample_enable set */ - if (p4_marked_instr_event(event)) { - mask |= 1ull << 56; - value |= 1ull << 56; - } - - /* PMCSEL=6 decode events on byte 2 need sample_enable clear */ - if (pmc && (event & PM_PMCSEL_MSK) == 6 && byte == 2) - mask |= 1ull << 56; - - *maskp = mask; - *valp = value; - return 0; -} - -static unsigned int ppc_inst_cmpl[] = { - 0x1001, 0x4001, 0x6001, 0x7001, 0x8001 -}; - -static int p4_get_alternatives(u64 event, unsigned int flags, u64 alt[]) -{ - int i, j, na; - - alt[0] = event; - na = 1; - - /* 2 possibilities for PM_GRP_DISP_REJECT */ - if (event == 0x8003 || event == 0x0224) { - alt[1] = event ^ (0x8003 ^ 0x0224); - return 2; - } - - /* 2 possibilities for PM_ST_MISS_L1 */ - if (event == 0x0c13 || event == 0x0c23) { - alt[1] = event ^ (0x0c13 ^ 0x0c23); - return 2; - } - - /* several possibilities for PM_INST_CMPL */ - for (i = 0; i < ARRAY_SIZE(ppc_inst_cmpl); ++i) { - if (event == ppc_inst_cmpl[i]) { - for (j = 0; j < ARRAY_SIZE(ppc_inst_cmpl); ++j) - if (j != i) - alt[na++] = ppc_inst_cmpl[j]; - break; - } - } - - return na; -} - -static int p4_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]) -{ - unsigned long mmcr0 = 0, mmcr1 = 0, mmcra = 0; - unsigned int pmc, unit, byte, psel, lower; - unsigned int ttm, grp; - unsigned int pmc_inuse = 0; - unsigned int pmc_grp_use[2]; - unsigned char busbyte[4]; - unsigned char unituse[16]; - unsigned int unitlower = 0; - int i; - - if (n_ev > 8) - return -1; - - /* First pass to count resource use */ - pmc_grp_use[0] = pmc_grp_use[1] = 0; - memset(busbyte, 0, sizeof(busbyte)); - memset(unituse, 0, sizeof(unituse)); - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc_inuse & (1 << (pmc - 1))) - return -1; - pmc_inuse |= 1 << (pmc - 1); - /* count 1/2/5/6 vs 3/4/7/8 use */ - ++pmc_grp_use[((pmc - 1) >> 1) & 1]; - } - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - lower = (event[i] >> PM_LOWER_SH) & PM_LOWER_MSK; - if (unit) { - if (!pmc) - ++pmc_grp_use[byte & 1]; - if (unit == 6 || unit == 8) - /* map alt ISU1/IFU codes: 6->2, 8->3 */ - unit = (unit >> 1) - 1; - if (busbyte[byte] && busbyte[byte] != unit) - return -1; - busbyte[byte] = unit; - lower <<= unit; - if (unituse[unit] && lower != (unitlower & lower)) - return -1; - unituse[unit] = 1; - unitlower |= lower; - } - } - if (pmc_grp_use[0] > 4 || pmc_grp_use[1] > 4) - return -1; - - /* - * Assign resources and set multiplexer selects. - * - * Units 1,2,3 are on TTM0, 4,6,7 on TTM1, 8,10 on TTM2. - * Each TTMx can only select one unit, but since - * units 2 and 6 are both ISU1, and 3 and 8 are both IFU, - * we have some choices. - */ - if (unituse[2] & (unituse[1] | (unituse[3] & unituse[9]))) { - unituse[6] = 1; /* Move 2 to 6 */ - unituse[2] = 0; - } - if (unituse[3] & (unituse[1] | unituse[2])) { - unituse[8] = 1; /* Move 3 to 8 */ - unituse[3] = 0; - unitlower = (unitlower & ~8) | ((unitlower & 8) << 5); - } - /* Check only one unit per TTMx */ - if (unituse[1] + unituse[2] + unituse[3] > 1 || - unituse[4] + unituse[6] + unituse[7] > 1 || - unituse[8] + unituse[9] > 1 || - (unituse[5] | unituse[10] | unituse[11] | - unituse[13] | unituse[14])) - return -1; - - /* Set TTMxSEL fields. Note, units 1-3 => TTM0SEL codes 0-2 */ - mmcr1 |= (unsigned long)(unituse[3] * 2 + unituse[2]) - << MMCR1_TTM0SEL_SH; - mmcr1 |= (unsigned long)(unituse[7] * 3 + unituse[6] * 2) - << MMCR1_TTM1SEL_SH; - mmcr1 |= (unsigned long)unituse[9] << MMCR1_TTM2SEL_SH; - - /* Set TTCxSEL fields. */ - if (unitlower & 0xe) - mmcr1 |= 1ull << MMCR1_TTC0SEL_SH; - if (unitlower & 0xf0) - mmcr1 |= 1ull << MMCR1_TTC1SEL_SH; - if (unitlower & 0xf00) - mmcr1 |= 1ull << MMCR1_TTC2SEL_SH; - if (unitlower & 0x7000) - mmcr1 |= 1ull << MMCR1_TTC3SEL_SH; - - /* Set byte lane select fields. */ - for (byte = 0; byte < 4; ++byte) { - unit = busbyte[byte]; - if (!unit) - continue; - if (unit == 0xf) { - /* special case for GPS */ - mmcr1 |= 1ull << (MMCR1_DEBUG0SEL_SH - byte); - } else { - if (!unituse[unit]) - ttm = unit - 1; /* 2->1, 3->2 */ - else - ttm = unit >> 2; - mmcr1 |= (unsigned long)ttm - << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); - } - } - - /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - psel = event[i] & PM_PMCSEL_MSK; - if (!pmc) { - /* Bus event or 00xxx direct event (off or cycles) */ - if (unit) - psel |= 0x10 | ((byte & 2) << 2); - for (pmc = 0; pmc < 8; ++pmc) { - if (pmc_inuse & (1 << pmc)) - continue; - grp = (pmc >> 1) & 1; - if (unit) { - if (grp == (byte & 1)) - break; - } else if (pmc_grp_use[grp] < 4) { - ++pmc_grp_use[grp]; - break; - } - } - pmc_inuse |= 1 << pmc; - } else { - /* Direct event */ - --pmc; - if (psel == 0 && (byte & 2)) - /* add events on higher-numbered bus */ - mmcr1 |= 1ull << mmcr1_adder_bits[pmc]; - else if (psel == 6 && byte == 3) - /* seem to need to set sample_enable here */ - mmcra |= MMCRA_SAMPLE_ENABLE; - psel |= 8; - } - if (pmc <= 1) - mmcr0 |= psel << (MMCR0_PMC1SEL_SH - 7 * pmc); - else - mmcr1 |= psel << (MMCR1_PMC3SEL_SH - 5 * (pmc - 2)); - if (pmc == 7) /* PMC8 */ - mmcra |= (psel & 1) << MMCRA_PMC8SEL0_SH; - hwc[i] = pmc; - if (p4_marked_instr_event(event[i])) - mmcra |= MMCRA_SAMPLE_ENABLE; - } - - if (pmc_inuse & 1) - mmcr0 |= MMCR0_PMC1CE; - if (pmc_inuse & 0xfe) - mmcr0 |= MMCR0_PMCjCE; - - mmcra |= 0x2000; /* mark only one IOP per PPC instruction */ - - /* Return MMCRx values */ - mmcr[0] = mmcr0; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; - return 0; -} - -static void p4_disable_pmc(unsigned int pmc, unsigned long mmcr[]) -{ - /* - * Setting the PMCxSEL field to 0 disables PMC x. - * (Note that pmc is 0-based here, not 1-based.) - */ - if (pmc <= 1) { - mmcr[0] &= ~(0x1fUL << (MMCR0_PMC1SEL_SH - 7 * pmc)); - } else { - mmcr[1] &= ~(0x1fUL << (MMCR1_PMC3SEL_SH - 5 * (pmc - 2))); - if (pmc == 7) - mmcr[2] &= ~(1UL << MMCRA_PMC8SEL0_SH); - } -} - -static int p4_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 7, - [PERF_COUNT_HW_INSTRUCTIONS] = 0x1001, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x8c10, /* PM_LD_REF_L1 */ - [PERF_COUNT_HW_CACHE_MISSES] = 0x3c10, /* PM_LD_MISS_L1 */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x330, /* PM_BR_ISSUED */ - [PERF_COUNT_HW_BRANCH_MISSES] = 0x331, /* PM_BR_MPRED_CR */ -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - */ -static int power4_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x8c10, 0x3c10 }, - [C(OP_WRITE)] = { 0x7c10, 0xc13 }, - [C(OP_PREFETCH)] = { 0xc35, 0 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { 0, 0 }, - [C(OP_PREFETCH)] = { 0xc34, 0 }, - }, - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x904 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x900 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x330, 0x331 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -static struct power_pmu power4_pmu = { - .name = "POWER4/4+", - .n_counter = 8, - .max_alternatives = 5, - .add_fields = 0x0000001100005555ul, - .test_adder = 0x0011083300000000ul, - .compute_mmcr = p4_compute_mmcr, - .get_constraint = p4_get_constraint, - .get_alternatives = p4_get_alternatives, - .disable_pmc = p4_disable_pmc, - .n_generic = ARRAY_SIZE(p4_generic_events), - .generic_events = p4_generic_events, - .cache_events = &power4_cache_events, -}; - -static int __init init_power4_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type || - strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power4")) - return -ENODEV; - - return register_power_pmu(&power4_pmu); -} - -early_initcall(init_power4_pmu); diff --git a/arch/powerpc/kernel/power5+-pmu.c b/arch/powerpc/kernel/power5+-pmu.c deleted file mode 100644 index a8757ba..0000000 --- a/arch/powerpc/kernel/power5+-pmu.c +++ /dev/null @@ -1,690 +0,0 @@ -/* - * Performance counter support for POWER5+/++ (not POWER5) processors. - * - * Copyright 2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include -#include - -/* - * Bits in event code for POWER5+ (POWER5 GS) and POWER5++ (POWER5 GS DD3) - */ -#define PM_PMC_SH 20 /* PMC number (1-based) for direct events */ -#define PM_PMC_MSK 0xf -#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) -#define PM_UNIT_SH 16 /* TTMMUX number and setting - unit select */ -#define PM_UNIT_MSK 0xf -#define PM_BYTE_SH 12 /* Byte number of event bus to use */ -#define PM_BYTE_MSK 7 -#define PM_GRS_SH 8 /* Storage subsystem mux select */ -#define PM_GRS_MSK 7 -#define PM_BUSEVENT_MSK 0x80 /* Set if event uses event bus */ -#define PM_PMCSEL_MSK 0x7f - -/* Values in PM_UNIT field */ -#define PM_FPU 0 -#define PM_ISU0 1 -#define PM_IFU 2 -#define PM_ISU1 3 -#define PM_IDU 4 -#define PM_ISU0_ALT 6 -#define PM_GRS 7 -#define PM_LSU0 8 -#define PM_LSU1 0xc -#define PM_LASTUNIT 0xc - -/* - * Bits in MMCR1 for POWER5+ - */ -#define MMCR1_TTM0SEL_SH 62 -#define MMCR1_TTM1SEL_SH 60 -#define MMCR1_TTM2SEL_SH 58 -#define MMCR1_TTM3SEL_SH 56 -#define MMCR1_TTMSEL_MSK 3 -#define MMCR1_TD_CP_DBG0SEL_SH 54 -#define MMCR1_TD_CP_DBG1SEL_SH 52 -#define MMCR1_TD_CP_DBG2SEL_SH 50 -#define MMCR1_TD_CP_DBG3SEL_SH 48 -#define MMCR1_GRS_L2SEL_SH 46 -#define MMCR1_GRS_L2SEL_MSK 3 -#define MMCR1_GRS_L3SEL_SH 44 -#define MMCR1_GRS_L3SEL_MSK 3 -#define MMCR1_GRS_MCSEL_SH 41 -#define MMCR1_GRS_MCSEL_MSK 7 -#define MMCR1_GRS_FABSEL_SH 39 -#define MMCR1_GRS_FABSEL_MSK 3 -#define MMCR1_PMC1_ADDER_SEL_SH 35 -#define MMCR1_PMC2_ADDER_SEL_SH 34 -#define MMCR1_PMC3_ADDER_SEL_SH 33 -#define MMCR1_PMC4_ADDER_SEL_SH 32 -#define MMCR1_PMC1SEL_SH 25 -#define MMCR1_PMC2SEL_SH 17 -#define MMCR1_PMC3SEL_SH 9 -#define MMCR1_PMC4SEL_SH 1 -#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) -#define MMCR1_PMCSEL_MSK 0x7f - -/* - * Layout of constraint bits: - * 6666555555555544444444443333333333222222222211111111110000000000 - * 3210987654321098765432109876543210987654321098765432109876543210 - * [ ><><>< ><> <><>[ > < >< >< >< ><><><><><><> - * NC G0G1G2 G3 T0T1 UC B0 B1 B2 B3 P6P5P4P3P2P1 - * - * NC - number of counters - * 51: NC error 0x0008_0000_0000_0000 - * 48-50: number of events needing PMC1-4 0x0007_0000_0000_0000 - * - * G0..G3 - GRS mux constraints - * 46-47: GRS_L2SEL value - * 44-45: GRS_L3SEL value - * 41-44: GRS_MCSEL value - * 39-40: GRS_FABSEL value - * Note that these match up with their bit positions in MMCR1 - * - * T0 - TTM0 constraint - * 36-37: TTM0SEL value (0=FPU, 2=IFU, 3=ISU1) 0x30_0000_0000 - * - * T1 - TTM1 constraint - * 34-35: TTM1SEL value (0=IDU, 3=GRS) 0x0c_0000_0000 - * - * UC - unit constraint: can't have all three of FPU|IFU|ISU1, ISU0, IDU|GRS - * 33: UC3 error 0x02_0000_0000 - * 32: FPU|IFU|ISU1 events needed 0x01_0000_0000 - * 31: ISU0 events needed 0x01_8000_0000 - * 30: IDU|GRS events needed 0x00_4000_0000 - * - * B0 - * 24-27: Byte 0 event source 0x0f00_0000 - * Encoding as for the event code - * - * B1, B2, B3 - * 20-23, 16-19, 12-15: Byte 1, 2, 3 event sources - * - * P6 - * 11: P6 error 0x800 - * 10-11: Count of events needing PMC6 - * - * P1..P5 - * 0-9: Count of events needing PMC1..PMC5 - */ - -static const int grsel_shift[8] = { - MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, - MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, - MMCR1_GRS_MCSEL_SH, MMCR1_GRS_FABSEL_SH -}; - -/* Masks and values for using events from the various units */ -static unsigned long unit_cons[PM_LASTUNIT+1][2] = { - [PM_FPU] = { 0x3200000000ul, 0x0100000000ul }, - [PM_ISU0] = { 0x0200000000ul, 0x0080000000ul }, - [PM_ISU1] = { 0x3200000000ul, 0x3100000000ul }, - [PM_IFU] = { 0x3200000000ul, 0x2100000000ul }, - [PM_IDU] = { 0x0e00000000ul, 0x0040000000ul }, - [PM_GRS] = { 0x0e00000000ul, 0x0c40000000ul }, -}; - -static int power5p_get_constraint(u64 event, unsigned long *maskp, - unsigned long *valp) -{ - int pmc, byte, unit, sh; - int bit, fmask; - unsigned long mask = 0, value = 0; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 6) - return -1; - sh = (pmc - 1) * 2; - mask |= 2 << sh; - value |= 1 << sh; - if (pmc >= 5 && !(event == 0x500009 || event == 0x600005)) - return -1; - } - if (event & PM_BUSEVENT_MSK) { - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - if (unit > PM_LASTUNIT) - return -1; - if (unit == PM_ISU0_ALT) - unit = PM_ISU0; - mask |= unit_cons[unit][0]; - value |= unit_cons[unit][1]; - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - if (byte >= 4) { - if (unit != PM_LSU1) - return -1; - /* Map LSU1 low word (bytes 4-7) to unit LSU1+1 */ - ++unit; - byte &= 3; - } - if (unit == PM_GRS) { - bit = event & 7; - fmask = (bit == 6)? 7: 3; - sh = grsel_shift[bit]; - mask |= (unsigned long)fmask << sh; - value |= (unsigned long)((event >> PM_GRS_SH) & fmask) - << sh; - } - /* Set byte lane select field */ - mask |= 0xfUL << (24 - 4 * byte); - value |= (unsigned long)unit << (24 - 4 * byte); - } - if (pmc < 5) { - /* need a counter from PMC1-4 set */ - mask |= 0x8000000000000ul; - value |= 0x1000000000000ul; - } - *maskp = mask; - *valp = value; - return 0; -} - -static int power5p_limited_pmc_event(u64 event) -{ - int pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - - return pmc == 5 || pmc == 6; -} - -#define MAX_ALT 3 /* at most 3 alternatives for any event */ - -static const unsigned int event_alternatives[][MAX_ALT] = { - { 0x100c0, 0x40001f }, /* PM_GCT_FULL_CYC */ - { 0x120e4, 0x400002 }, /* PM_GRP_DISP_REJECT */ - { 0x230e2, 0x323087 }, /* PM_BR_PRED_CR */ - { 0x230e3, 0x223087, 0x3230a0 }, /* PM_BR_PRED_TA */ - { 0x410c7, 0x441084 }, /* PM_THRD_L2MISS_BOTH_CYC */ - { 0x800c4, 0xc20e0 }, /* PM_DTLB_MISS */ - { 0xc50c6, 0xc60e0 }, /* PM_MRK_DTLB_MISS */ - { 0x100005, 0x600005 }, /* PM_RUN_CYC */ - { 0x100009, 0x200009 }, /* PM_INST_CMPL */ - { 0x200015, 0x300015 }, /* PM_LSU_LMQ_SRQ_EMPTY_CYC */ - { 0x300009, 0x400009 }, /* PM_INST_DISP */ -}; - -/* - * Scan the alternatives table for a match and return the - * index into the alternatives table if found, else -1. - */ -static int find_alternative(unsigned int event) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { - if (event < event_alternatives[i][0]) - break; - for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) - if (event == event_alternatives[i][j]) - return i; - } - return -1; -} - -static const unsigned char bytedecode_alternatives[4][4] = { - /* PMC 1 */ { 0x21, 0x23, 0x25, 0x27 }, - /* PMC 2 */ { 0x07, 0x17, 0x0e, 0x1e }, - /* PMC 3 */ { 0x20, 0x22, 0x24, 0x26 }, - /* PMC 4 */ { 0x07, 0x17, 0x0e, 0x1e } -}; - -/* - * Some direct events for decodes of event bus byte 3 have alternative - * PMCSEL values on other counters. This returns the alternative - * event code for those that do, or -1 otherwise. This also handles - * alternative PCMSEL values for add events. - */ -static s64 find_alternative_bdecode(u64 event) -{ - int pmc, altpmc, pp, j; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc == 0 || pmc > 4) - return -1; - altpmc = 5 - pmc; /* 1 <-> 4, 2 <-> 3 */ - pp = event & PM_PMCSEL_MSK; - for (j = 0; j < 4; ++j) { - if (bytedecode_alternatives[pmc - 1][j] == pp) { - return (event & ~(PM_PMC_MSKS | PM_PMCSEL_MSK)) | - (altpmc << PM_PMC_SH) | - bytedecode_alternatives[altpmc - 1][j]; - } - } - - /* new decode alternatives for power5+ */ - if (pmc == 1 && (pp == 0x0d || pp == 0x0e)) - return event + (2 << PM_PMC_SH) + (0x2e - 0x0d); - if (pmc == 3 && (pp == 0x2e || pp == 0x2f)) - return event - (2 << PM_PMC_SH) - (0x2e - 0x0d); - - /* alternative add event encodings */ - if (pp == 0x10 || pp == 0x28) - return ((event ^ (0x10 ^ 0x28)) & ~PM_PMC_MSKS) | - (altpmc << PM_PMC_SH); - - return -1; -} - -static int power5p_get_alternatives(u64 event, unsigned int flags, u64 alt[]) -{ - int i, j, nalt = 1; - int nlim; - s64 ae; - - alt[0] = event; - nalt = 1; - nlim = power5p_limited_pmc_event(event); - i = find_alternative(event); - if (i >= 0) { - for (j = 0; j < MAX_ALT; ++j) { - ae = event_alternatives[i][j]; - if (ae && ae != event) - alt[nalt++] = ae; - nlim += power5p_limited_pmc_event(ae); - } - } else { - ae = find_alternative_bdecode(event); - if (ae > 0) - alt[nalt++] = ae; - } - - if (flags & PPMU_ONLY_COUNT_RUN) { - /* - * We're only counting in RUN state, - * so PM_CYC is equivalent to PM_RUN_CYC - * and PM_INST_CMPL === PM_RUN_INST_CMPL. - * This doesn't include alternatives that don't provide - * any extra flexibility in assigning PMCs (e.g. - * 0x100005 for PM_RUN_CYC vs. 0xf for PM_CYC). - * Note that even with these additional alternatives - * we never end up with more than 3 alternatives for any event. - */ - j = nalt; - for (i = 0; i < nalt; ++i) { - switch (alt[i]) { - case 0xf: /* PM_CYC */ - alt[j++] = 0x600005; /* PM_RUN_CYC */ - ++nlim; - break; - case 0x600005: /* PM_RUN_CYC */ - alt[j++] = 0xf; - break; - case 0x100009: /* PM_INST_CMPL */ - alt[j++] = 0x500009; /* PM_RUN_INST_CMPL */ - ++nlim; - break; - case 0x500009: /* PM_RUN_INST_CMPL */ - alt[j++] = 0x100009; /* PM_INST_CMPL */ - alt[j++] = 0x200009; - break; - } - } - nalt = j; - } - - if (!(flags & PPMU_LIMITED_PMC_OK) && nlim) { - /* remove the limited PMC events */ - j = 0; - for (i = 0; i < nalt; ++i) { - if (!power5p_limited_pmc_event(alt[i])) { - alt[j] = alt[i]; - ++j; - } - } - nalt = j; - } else if ((flags & PPMU_LIMITED_PMC_REQD) && nlim < nalt) { - /* remove all but the limited PMC events */ - j = 0; - for (i = 0; i < nalt; ++i) { - if (power5p_limited_pmc_event(alt[i])) { - alt[j] = alt[i]; - ++j; - } - } - nalt = j; - } - - return nalt; -} - -/* - * Map of which direct events on which PMCs are marked instruction events. - * Indexed by PMCSEL value, bit i (LE) set if PMC i is a marked event. - * Bit 0 is set if it is marked for all PMCs. - * The 0x80 bit indicates a byte decode PMCSEL value. - */ -static unsigned char direct_event_is_marked[0x28] = { - 0, /* 00 */ - 0x1f, /* 01 PM_IOPS_CMPL */ - 0x2, /* 02 PM_MRK_GRP_DISP */ - 0xe, /* 03 PM_MRK_ST_CMPL, PM_MRK_ST_GPS, PM_MRK_ST_CMPL_INT */ - 0, /* 04 */ - 0x1c, /* 05 PM_MRK_BRU_FIN, PM_MRK_INST_FIN, PM_MRK_CRU_FIN */ - 0x80, /* 06 */ - 0x80, /* 07 */ - 0, 0, 0,/* 08 - 0a */ - 0x18, /* 0b PM_THRESH_TIMEO, PM_MRK_GRP_TIMEO */ - 0, /* 0c */ - 0x80, /* 0d */ - 0x80, /* 0e */ - 0, /* 0f */ - 0, /* 10 */ - 0x14, /* 11 PM_MRK_GRP_BR_REDIR, PM_MRK_GRP_IC_MISS */ - 0, /* 12 */ - 0x10, /* 13 PM_MRK_GRP_CMPL */ - 0x1f, /* 14 PM_GRP_MRK, PM_MRK_{FXU,FPU,LSU}_FIN */ - 0x2, /* 15 PM_MRK_GRP_ISSUED */ - 0x80, /* 16 */ - 0x80, /* 17 */ - 0, 0, 0, 0, 0, - 0x80, /* 1d */ - 0x80, /* 1e */ - 0, /* 1f */ - 0x80, /* 20 */ - 0x80, /* 21 */ - 0x80, /* 22 */ - 0x80, /* 23 */ - 0x80, /* 24 */ - 0x80, /* 25 */ - 0x80, /* 26 */ - 0x80, /* 27 */ -}; - -/* - * Returns 1 if event counts things relating to marked instructions - * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. - */ -static int power5p_marked_instr_event(u64 event) -{ - int pmc, psel; - int bit, byte, unit; - u32 mask; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - psel = event & PM_PMCSEL_MSK; - if (pmc >= 5) - return 0; - - bit = -1; - if (psel < sizeof(direct_event_is_marked)) { - if (direct_event_is_marked[psel] & (1 << pmc)) - return 1; - if (direct_event_is_marked[psel] & 0x80) - bit = 4; - else if (psel == 0x08) - bit = pmc - 1; - else if (psel == 0x10) - bit = 4 - pmc; - else if (psel == 0x1b && (pmc == 1 || pmc == 3)) - bit = 4; - } else if ((psel & 0x48) == 0x40) { - bit = psel & 7; - } else if (psel == 0x28) { - bit = pmc - 1; - } else if (pmc == 3 && (psel == 0x2e || psel == 0x2f)) { - bit = 4; - } - - if (!(event & PM_BUSEVENT_MSK) || bit == -1) - return 0; - - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - if (unit == PM_LSU0) { - /* byte 1 bits 0-7, byte 2 bits 0,2-4,6 */ - mask = 0x5dff00; - } else if (unit == PM_LSU1 && byte >= 4) { - byte -= 4; - /* byte 5 bits 6-7, byte 6 bits 0,4, byte 7 bits 0-4,6 */ - mask = 0x5f11c000; - } else - return 0; - - return (mask >> (byte * 8 + bit)) & 1; -} - -static int power5p_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]) -{ - unsigned long mmcr1 = 0; - unsigned long mmcra = 0; - unsigned int pmc, unit, byte, psel; - unsigned int ttm; - int i, isbus, bit, grsel; - unsigned int pmc_inuse = 0; - unsigned char busbyte[4]; - unsigned char unituse[16]; - int ttmuse; - - if (n_ev > 6) - return -1; - - /* First pass to count resource use */ - memset(busbyte, 0, sizeof(busbyte)); - memset(unituse, 0, sizeof(unituse)); - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 6) - return -1; - if (pmc_inuse & (1 << (pmc - 1))) - return -1; - pmc_inuse |= 1 << (pmc - 1); - } - if (event[i] & PM_BUSEVENT_MSK) { - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - if (unit > PM_LASTUNIT) - return -1; - if (unit == PM_ISU0_ALT) - unit = PM_ISU0; - if (byte >= 4) { - if (unit != PM_LSU1) - return -1; - ++unit; - byte &= 3; - } - if (busbyte[byte] && busbyte[byte] != unit) - return -1; - busbyte[byte] = unit; - unituse[unit] = 1; - } - } - - /* - * Assign resources and set multiplexer selects. - * - * PM_ISU0 can go either on TTM0 or TTM1, but that's the only - * choice we have to deal with. - */ - if (unituse[PM_ISU0] & - (unituse[PM_FPU] | unituse[PM_IFU] | unituse[PM_ISU1])) { - unituse[PM_ISU0_ALT] = 1; /* move ISU to TTM1 */ - unituse[PM_ISU0] = 0; - } - /* Set TTM[01]SEL fields. */ - ttmuse = 0; - for (i = PM_FPU; i <= PM_ISU1; ++i) { - if (!unituse[i]) - continue; - if (ttmuse++) - return -1; - mmcr1 |= (unsigned long)i << MMCR1_TTM0SEL_SH; - } - ttmuse = 0; - for (; i <= PM_GRS; ++i) { - if (!unituse[i]) - continue; - if (ttmuse++) - return -1; - mmcr1 |= (unsigned long)(i & 3) << MMCR1_TTM1SEL_SH; - } - if (ttmuse > 1) - return -1; - - /* Set byte lane select fields, TTM[23]SEL and GRS_*SEL. */ - for (byte = 0; byte < 4; ++byte) { - unit = busbyte[byte]; - if (!unit) - continue; - if (unit == PM_ISU0 && unituse[PM_ISU0_ALT]) { - /* get ISU0 through TTM1 rather than TTM0 */ - unit = PM_ISU0_ALT; - } else if (unit == PM_LSU1 + 1) { - /* select lower word of LSU1 for this byte */ - mmcr1 |= 1ul << (MMCR1_TTM3SEL_SH + 3 - byte); - } - ttm = unit >> 2; - mmcr1 |= (unsigned long)ttm - << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); - } - - /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - psel = event[i] & PM_PMCSEL_MSK; - isbus = event[i] & PM_BUSEVENT_MSK; - if (!pmc) { - /* Bus event or any-PMC direct event */ - for (pmc = 0; pmc < 4; ++pmc) { - if (!(pmc_inuse & (1 << pmc))) - break; - } - if (pmc >= 4) - return -1; - pmc_inuse |= 1 << pmc; - } else if (pmc <= 4) { - /* Direct event */ - --pmc; - if (isbus && (byte & 2) && - (psel == 8 || psel == 0x10 || psel == 0x28)) - /* add events on higher-numbered bus */ - mmcr1 |= 1ul << (MMCR1_PMC1_ADDER_SEL_SH - pmc); - } else { - /* Instructions or run cycles on PMC5/6 */ - --pmc; - } - if (isbus && unit == PM_GRS) { - bit = psel & 7; - grsel = (event[i] >> PM_GRS_SH) & PM_GRS_MSK; - mmcr1 |= (unsigned long)grsel << grsel_shift[bit]; - } - if (power5p_marked_instr_event(event[i])) - mmcra |= MMCRA_SAMPLE_ENABLE; - if ((psel & 0x58) == 0x40 && (byte & 1) != ((pmc >> 1) & 1)) - /* select alternate byte lane */ - psel |= 0x10; - if (pmc <= 3) - mmcr1 |= psel << MMCR1_PMCSEL_SH(pmc); - hwc[i] = pmc; - } - - /* Return MMCRx values */ - mmcr[0] = 0; - if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; - if (pmc_inuse & 0x3e) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; - return 0; -} - -static void power5p_disable_pmc(unsigned int pmc, unsigned long mmcr[]) -{ - if (pmc <= 3) - mmcr[1] &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); -} - -static int power5p_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 0xf, - [PERF_COUNT_HW_INSTRUCTIONS] = 0x100009, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x1c10a8, /* LD_REF_L1 */ - [PERF_COUNT_HW_CACHE_MISSES] = 0x3c1088, /* LD_MISS_L1 */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x230e4, /* BR_ISSUED */ - [PERF_COUNT_HW_BRANCH_MISSES] = 0x230e5, /* BR_MPRED_CR */ -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - */ -static int power5p_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x1c10a8, 0x3c1088 }, - [C(OP_WRITE)] = { 0x2c10a8, 0xc10c3 }, - [C(OP_PREFETCH)] = { 0xc70e7, -1 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { 0, 0 }, - [C(OP_PREFETCH)] = { 0xc50c3, 0 }, - }, - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0xc20e4, 0x800c4 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x800c0 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x230e4, 0x230e5 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -static struct power_pmu power5p_pmu = { - .name = "POWER5+/++", - .n_counter = 6, - .max_alternatives = MAX_ALT, - .add_fields = 0x7000000000055ul, - .test_adder = 0x3000040000000ul, - .compute_mmcr = power5p_compute_mmcr, - .get_constraint = power5p_get_constraint, - .get_alternatives = power5p_get_alternatives, - .disable_pmc = power5p_disable_pmc, - .limited_pmc_event = power5p_limited_pmc_event, - .flags = PPMU_LIMITED_PMC5_6, - .n_generic = ARRAY_SIZE(power5p_generic_events), - .generic_events = power5p_generic_events, - .cache_events = &power5p_cache_events, -}; - -static int __init init_power5p_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type || - (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5+") - && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5++"))) - return -ENODEV; - - return register_power_pmu(&power5p_pmu); -} - -early_initcall(init_power5p_pmu); diff --git a/arch/powerpc/kernel/power5-pmu.c b/arch/powerpc/kernel/power5-pmu.c deleted file mode 100644 index e7f06eb..0000000 --- a/arch/powerpc/kernel/power5-pmu.c +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Performance counter support for POWER5 (not POWER5++) processors. - * - * Copyright 2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include -#include - -/* - * Bits in event code for POWER5 (not POWER5++) - */ -#define PM_PMC_SH 20 /* PMC number (1-based) for direct events */ -#define PM_PMC_MSK 0xf -#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) -#define PM_UNIT_SH 16 /* TTMMUX number and setting - unit select */ -#define PM_UNIT_MSK 0xf -#define PM_BYTE_SH 12 /* Byte number of event bus to use */ -#define PM_BYTE_MSK 7 -#define PM_GRS_SH 8 /* Storage subsystem mux select */ -#define PM_GRS_MSK 7 -#define PM_BUSEVENT_MSK 0x80 /* Set if event uses event bus */ -#define PM_PMCSEL_MSK 0x7f - -/* Values in PM_UNIT field */ -#define PM_FPU 0 -#define PM_ISU0 1 -#define PM_IFU 2 -#define PM_ISU1 3 -#define PM_IDU 4 -#define PM_ISU0_ALT 6 -#define PM_GRS 7 -#define PM_LSU0 8 -#define PM_LSU1 0xc -#define PM_LASTUNIT 0xc - -/* - * Bits in MMCR1 for POWER5 - */ -#define MMCR1_TTM0SEL_SH 62 -#define MMCR1_TTM1SEL_SH 60 -#define MMCR1_TTM2SEL_SH 58 -#define MMCR1_TTM3SEL_SH 56 -#define MMCR1_TTMSEL_MSK 3 -#define MMCR1_TD_CP_DBG0SEL_SH 54 -#define MMCR1_TD_CP_DBG1SEL_SH 52 -#define MMCR1_TD_CP_DBG2SEL_SH 50 -#define MMCR1_TD_CP_DBG3SEL_SH 48 -#define MMCR1_GRS_L2SEL_SH 46 -#define MMCR1_GRS_L2SEL_MSK 3 -#define MMCR1_GRS_L3SEL_SH 44 -#define MMCR1_GRS_L3SEL_MSK 3 -#define MMCR1_GRS_MCSEL_SH 41 -#define MMCR1_GRS_MCSEL_MSK 7 -#define MMCR1_GRS_FABSEL_SH 39 -#define MMCR1_GRS_FABSEL_MSK 3 -#define MMCR1_PMC1_ADDER_SEL_SH 35 -#define MMCR1_PMC2_ADDER_SEL_SH 34 -#define MMCR1_PMC3_ADDER_SEL_SH 33 -#define MMCR1_PMC4_ADDER_SEL_SH 32 -#define MMCR1_PMC1SEL_SH 25 -#define MMCR1_PMC2SEL_SH 17 -#define MMCR1_PMC3SEL_SH 9 -#define MMCR1_PMC4SEL_SH 1 -#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) -#define MMCR1_PMCSEL_MSK 0x7f - -/* - * Layout of constraint bits: - * 6666555555555544444444443333333333222222222211111111110000000000 - * 3210987654321098765432109876543210987654321098765432109876543210 - * <><>[ ><><>< ><> [ >[ >[ >< >< >< >< ><><><><><><> - * T0T1 NC G0G1G2 G3 UC PS1PS2 B0 B1 B2 B3 P6P5P4P3P2P1 - * - * T0 - TTM0 constraint - * 54-55: TTM0SEL value (0=FPU, 2=IFU, 3=ISU1) 0xc0_0000_0000_0000 - * - * T1 - TTM1 constraint - * 52-53: TTM1SEL value (0=IDU, 3=GRS) 0x30_0000_0000_0000 - * - * NC - number of counters - * 51: NC error 0x0008_0000_0000_0000 - * 48-50: number of events needing PMC1-4 0x0007_0000_0000_0000 - * - * G0..G3 - GRS mux constraints - * 46-47: GRS_L2SEL value - * 44-45: GRS_L3SEL value - * 41-44: GRS_MCSEL value - * 39-40: GRS_FABSEL value - * Note that these match up with their bit positions in MMCR1 - * - * UC - unit constraint: can't have all three of FPU|IFU|ISU1, ISU0, IDU|GRS - * 37: UC3 error 0x20_0000_0000 - * 36: FPU|IFU|ISU1 events needed 0x10_0000_0000 - * 35: ISU0 events needed 0x08_0000_0000 - * 34: IDU|GRS events needed 0x04_0000_0000 - * - * PS1 - * 33: PS1 error 0x2_0000_0000 - * 31-32: count of events needing PMC1/2 0x1_8000_0000 - * - * PS2 - * 30: PS2 error 0x4000_0000 - * 28-29: count of events needing PMC3/4 0x3000_0000 - * - * B0 - * 24-27: Byte 0 event source 0x0f00_0000 - * Encoding as for the event code - * - * B1, B2, B3 - * 20-23, 16-19, 12-15: Byte 1, 2, 3 event sources - * - * P1..P6 - * 0-11: Count of events needing PMC1..PMC6 - */ - -static const int grsel_shift[8] = { - MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, - MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, - MMCR1_GRS_MCSEL_SH, MMCR1_GRS_FABSEL_SH -}; - -/* Masks and values for using events from the various units */ -static unsigned long unit_cons[PM_LASTUNIT+1][2] = { - [PM_FPU] = { 0xc0002000000000ul, 0x00001000000000ul }, - [PM_ISU0] = { 0x00002000000000ul, 0x00000800000000ul }, - [PM_ISU1] = { 0xc0002000000000ul, 0xc0001000000000ul }, - [PM_IFU] = { 0xc0002000000000ul, 0x80001000000000ul }, - [PM_IDU] = { 0x30002000000000ul, 0x00000400000000ul }, - [PM_GRS] = { 0x30002000000000ul, 0x30000400000000ul }, -}; - -static int power5_get_constraint(u64 event, unsigned long *maskp, - unsigned long *valp) -{ - int pmc, byte, unit, sh; - int bit, fmask; - unsigned long mask = 0, value = 0; - int grp = -1; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 6) - return -1; - sh = (pmc - 1) * 2; - mask |= 2 << sh; - value |= 1 << sh; - if (pmc <= 4) - grp = (pmc - 1) >> 1; - else if (event != 0x500009 && event != 0x600005) - return -1; - } - if (event & PM_BUSEVENT_MSK) { - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - if (unit > PM_LASTUNIT) - return -1; - if (unit == PM_ISU0_ALT) - unit = PM_ISU0; - mask |= unit_cons[unit][0]; - value |= unit_cons[unit][1]; - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - if (byte >= 4) { - if (unit != PM_LSU1) - return -1; - /* Map LSU1 low word (bytes 4-7) to unit LSU1+1 */ - ++unit; - byte &= 3; - } - if (unit == PM_GRS) { - bit = event & 7; - fmask = (bit == 6)? 7: 3; - sh = grsel_shift[bit]; - mask |= (unsigned long)fmask << sh; - value |= (unsigned long)((event >> PM_GRS_SH) & fmask) - << sh; - } - /* - * Bus events on bytes 0 and 2 can be counted - * on PMC1/2; bytes 1 and 3 on PMC3/4. - */ - if (!pmc) - grp = byte & 1; - /* Set byte lane select field */ - mask |= 0xfUL << (24 - 4 * byte); - value |= (unsigned long)unit << (24 - 4 * byte); - } - if (grp == 0) { - /* increment PMC1/2 field */ - mask |= 0x200000000ul; - value |= 0x080000000ul; - } else if (grp == 1) { - /* increment PMC3/4 field */ - mask |= 0x40000000ul; - value |= 0x10000000ul; - } - if (pmc < 5) { - /* need a counter from PMC1-4 set */ - mask |= 0x8000000000000ul; - value |= 0x1000000000000ul; - } - *maskp = mask; - *valp = value; - return 0; -} - -#define MAX_ALT 3 /* at most 3 alternatives for any event */ - -static const unsigned int event_alternatives[][MAX_ALT] = { - { 0x120e4, 0x400002 }, /* PM_GRP_DISP_REJECT */ - { 0x410c7, 0x441084 }, /* PM_THRD_L2MISS_BOTH_CYC */ - { 0x100005, 0x600005 }, /* PM_RUN_CYC */ - { 0x100009, 0x200009, 0x500009 }, /* PM_INST_CMPL */ - { 0x300009, 0x400009 }, /* PM_INST_DISP */ -}; - -/* - * Scan the alternatives table for a match and return the - * index into the alternatives table if found, else -1. - */ -static int find_alternative(u64 event) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { - if (event < event_alternatives[i][0]) - break; - for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) - if (event == event_alternatives[i][j]) - return i; - } - return -1; -} - -static const unsigned char bytedecode_alternatives[4][4] = { - /* PMC 1 */ { 0x21, 0x23, 0x25, 0x27 }, - /* PMC 2 */ { 0x07, 0x17, 0x0e, 0x1e }, - /* PMC 3 */ { 0x20, 0x22, 0x24, 0x26 }, - /* PMC 4 */ { 0x07, 0x17, 0x0e, 0x1e } -}; - -/* - * Some direct events for decodes of event bus byte 3 have alternative - * PMCSEL values on other counters. This returns the alternative - * event code for those that do, or -1 otherwise. - */ -static s64 find_alternative_bdecode(u64 event) -{ - int pmc, altpmc, pp, j; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc == 0 || pmc > 4) - return -1; - altpmc = 5 - pmc; /* 1 <-> 4, 2 <-> 3 */ - pp = event & PM_PMCSEL_MSK; - for (j = 0; j < 4; ++j) { - if (bytedecode_alternatives[pmc - 1][j] == pp) { - return (event & ~(PM_PMC_MSKS | PM_PMCSEL_MSK)) | - (altpmc << PM_PMC_SH) | - bytedecode_alternatives[altpmc - 1][j]; - } - } - return -1; -} - -static int power5_get_alternatives(u64 event, unsigned int flags, u64 alt[]) -{ - int i, j, nalt = 1; - s64 ae; - - alt[0] = event; - nalt = 1; - i = find_alternative(event); - if (i >= 0) { - for (j = 0; j < MAX_ALT; ++j) { - ae = event_alternatives[i][j]; - if (ae && ae != event) - alt[nalt++] = ae; - } - } else { - ae = find_alternative_bdecode(event); - if (ae > 0) - alt[nalt++] = ae; - } - return nalt; -} - -/* - * Map of which direct events on which PMCs are marked instruction events. - * Indexed by PMCSEL value, bit i (LE) set if PMC i is a marked event. - * Bit 0 is set if it is marked for all PMCs. - * The 0x80 bit indicates a byte decode PMCSEL value. - */ -static unsigned char direct_event_is_marked[0x28] = { - 0, /* 00 */ - 0x1f, /* 01 PM_IOPS_CMPL */ - 0x2, /* 02 PM_MRK_GRP_DISP */ - 0xe, /* 03 PM_MRK_ST_CMPL, PM_MRK_ST_GPS, PM_MRK_ST_CMPL_INT */ - 0, /* 04 */ - 0x1c, /* 05 PM_MRK_BRU_FIN, PM_MRK_INST_FIN, PM_MRK_CRU_FIN */ - 0x80, /* 06 */ - 0x80, /* 07 */ - 0, 0, 0,/* 08 - 0a */ - 0x18, /* 0b PM_THRESH_TIMEO, PM_MRK_GRP_TIMEO */ - 0, /* 0c */ - 0x80, /* 0d */ - 0x80, /* 0e */ - 0, /* 0f */ - 0, /* 10 */ - 0x14, /* 11 PM_MRK_GRP_BR_REDIR, PM_MRK_GRP_IC_MISS */ - 0, /* 12 */ - 0x10, /* 13 PM_MRK_GRP_CMPL */ - 0x1f, /* 14 PM_GRP_MRK, PM_MRK_{FXU,FPU,LSU}_FIN */ - 0x2, /* 15 PM_MRK_GRP_ISSUED */ - 0x80, /* 16 */ - 0x80, /* 17 */ - 0, 0, 0, 0, 0, - 0x80, /* 1d */ - 0x80, /* 1e */ - 0, /* 1f */ - 0x80, /* 20 */ - 0x80, /* 21 */ - 0x80, /* 22 */ - 0x80, /* 23 */ - 0x80, /* 24 */ - 0x80, /* 25 */ - 0x80, /* 26 */ - 0x80, /* 27 */ -}; - -/* - * Returns 1 if event counts things relating to marked instructions - * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. - */ -static int power5_marked_instr_event(u64 event) -{ - int pmc, psel; - int bit, byte, unit; - u32 mask; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - psel = event & PM_PMCSEL_MSK; - if (pmc >= 5) - return 0; - - bit = -1; - if (psel < sizeof(direct_event_is_marked)) { - if (direct_event_is_marked[psel] & (1 << pmc)) - return 1; - if (direct_event_is_marked[psel] & 0x80) - bit = 4; - else if (psel == 0x08) - bit = pmc - 1; - else if (psel == 0x10) - bit = 4 - pmc; - else if (psel == 0x1b && (pmc == 1 || pmc == 3)) - bit = 4; - } else if ((psel & 0x58) == 0x40) - bit = psel & 7; - - if (!(event & PM_BUSEVENT_MSK)) - return 0; - - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - if (unit == PM_LSU0) { - /* byte 1 bits 0-7, byte 2 bits 0,2-4,6 */ - mask = 0x5dff00; - } else if (unit == PM_LSU1 && byte >= 4) { - byte -= 4; - /* byte 4 bits 1,3,5,7, byte 5 bits 6-7, byte 7 bits 0-4,6 */ - mask = 0x5f00c0aa; - } else - return 0; - - return (mask >> (byte * 8 + bit)) & 1; -} - -static int power5_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]) -{ - unsigned long mmcr1 = 0; - unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; - unsigned int pmc, unit, byte, psel; - unsigned int ttm, grp; - int i, isbus, bit, grsel; - unsigned int pmc_inuse = 0; - unsigned int pmc_grp_use[2]; - unsigned char busbyte[4]; - unsigned char unituse[16]; - int ttmuse; - - if (n_ev > 6) - return -1; - - /* First pass to count resource use */ - pmc_grp_use[0] = pmc_grp_use[1] = 0; - memset(busbyte, 0, sizeof(busbyte)); - memset(unituse, 0, sizeof(unituse)); - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 6) - return -1; - if (pmc_inuse & (1 << (pmc - 1))) - return -1; - pmc_inuse |= 1 << (pmc - 1); - /* count 1/2 vs 3/4 use */ - if (pmc <= 4) - ++pmc_grp_use[(pmc - 1) >> 1]; - } - if (event[i] & PM_BUSEVENT_MSK) { - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - if (unit > PM_LASTUNIT) - return -1; - if (unit == PM_ISU0_ALT) - unit = PM_ISU0; - if (byte >= 4) { - if (unit != PM_LSU1) - return -1; - ++unit; - byte &= 3; - } - if (!pmc) - ++pmc_grp_use[byte & 1]; - if (busbyte[byte] && busbyte[byte] != unit) - return -1; - busbyte[byte] = unit; - unituse[unit] = 1; - } - } - if (pmc_grp_use[0] > 2 || pmc_grp_use[1] > 2) - return -1; - - /* - * Assign resources and set multiplexer selects. - * - * PM_ISU0 can go either on TTM0 or TTM1, but that's the only - * choice we have to deal with. - */ - if (unituse[PM_ISU0] & - (unituse[PM_FPU] | unituse[PM_IFU] | unituse[PM_ISU1])) { - unituse[PM_ISU0_ALT] = 1; /* move ISU to TTM1 */ - unituse[PM_ISU0] = 0; - } - /* Set TTM[01]SEL fields. */ - ttmuse = 0; - for (i = PM_FPU; i <= PM_ISU1; ++i) { - if (!unituse[i]) - continue; - if (ttmuse++) - return -1; - mmcr1 |= (unsigned long)i << MMCR1_TTM0SEL_SH; - } - ttmuse = 0; - for (; i <= PM_GRS; ++i) { - if (!unituse[i]) - continue; - if (ttmuse++) - return -1; - mmcr1 |= (unsigned long)(i & 3) << MMCR1_TTM1SEL_SH; - } - if (ttmuse > 1) - return -1; - - /* Set byte lane select fields, TTM[23]SEL and GRS_*SEL. */ - for (byte = 0; byte < 4; ++byte) { - unit = busbyte[byte]; - if (!unit) - continue; - if (unit == PM_ISU0 && unituse[PM_ISU0_ALT]) { - /* get ISU0 through TTM1 rather than TTM0 */ - unit = PM_ISU0_ALT; - } else if (unit == PM_LSU1 + 1) { - /* select lower word of LSU1 for this byte */ - mmcr1 |= 1ul << (MMCR1_TTM3SEL_SH + 3 - byte); - } - ttm = unit >> 2; - mmcr1 |= (unsigned long)ttm - << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); - } - - /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - psel = event[i] & PM_PMCSEL_MSK; - isbus = event[i] & PM_BUSEVENT_MSK; - if (!pmc) { - /* Bus event or any-PMC direct event */ - for (pmc = 0; pmc < 4; ++pmc) { - if (pmc_inuse & (1 << pmc)) - continue; - grp = (pmc >> 1) & 1; - if (isbus) { - if (grp == (byte & 1)) - break; - } else if (pmc_grp_use[grp] < 2) { - ++pmc_grp_use[grp]; - break; - } - } - pmc_inuse |= 1 << pmc; - } else if (pmc <= 4) { - /* Direct event */ - --pmc; - if ((psel == 8 || psel == 0x10) && isbus && (byte & 2)) - /* add events on higher-numbered bus */ - mmcr1 |= 1ul << (MMCR1_PMC1_ADDER_SEL_SH - pmc); - } else { - /* Instructions or run cycles on PMC5/6 */ - --pmc; - } - if (isbus && unit == PM_GRS) { - bit = psel & 7; - grsel = (event[i] >> PM_GRS_SH) & PM_GRS_MSK; - mmcr1 |= (unsigned long)grsel << grsel_shift[bit]; - } - if (power5_marked_instr_event(event[i])) - mmcra |= MMCRA_SAMPLE_ENABLE; - if (pmc <= 3) - mmcr1 |= psel << MMCR1_PMCSEL_SH(pmc); - hwc[i] = pmc; - } - - /* Return MMCRx values */ - mmcr[0] = 0; - if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; - if (pmc_inuse & 0x3e) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; - return 0; -} - -static void power5_disable_pmc(unsigned int pmc, unsigned long mmcr[]) -{ - if (pmc <= 3) - mmcr[1] &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); -} - -static int power5_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 0xf, - [PERF_COUNT_HW_INSTRUCTIONS] = 0x100009, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x4c1090, /* LD_REF_L1 */ - [PERF_COUNT_HW_CACHE_MISSES] = 0x3c1088, /* LD_MISS_L1 */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x230e4, /* BR_ISSUED */ - [PERF_COUNT_HW_BRANCH_MISSES] = 0x230e5, /* BR_MPRED_CR */ -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - */ -static int power5_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x4c1090, 0x3c1088 }, - [C(OP_WRITE)] = { 0x3c1090, 0xc10c3 }, - [C(OP_PREFETCH)] = { 0xc70e7, 0 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x3c309b }, - [C(OP_WRITE)] = { 0, 0 }, - [C(OP_PREFETCH)] = { 0xc50c3, 0 }, - }, - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x2c4090, 0x800c4 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x800c0 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x230e4, 0x230e5 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -static struct power_pmu power5_pmu = { - .name = "POWER5", - .n_counter = 6, - .max_alternatives = MAX_ALT, - .add_fields = 0x7000090000555ul, - .test_adder = 0x3000490000000ul, - .compute_mmcr = power5_compute_mmcr, - .get_constraint = power5_get_constraint, - .get_alternatives = power5_get_alternatives, - .disable_pmc = power5_disable_pmc, - .n_generic = ARRAY_SIZE(power5_generic_events), - .generic_events = power5_generic_events, - .cache_events = &power5_cache_events, -}; - -static int __init init_power5_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type || - strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5")) - return -ENODEV; - - return register_power_pmu(&power5_pmu); -} - -early_initcall(init_power5_pmu); diff --git a/arch/powerpc/kernel/power6-pmu.c b/arch/powerpc/kernel/power6-pmu.c deleted file mode 100644 index 0bbc901..0000000 --- a/arch/powerpc/kernel/power6-pmu.c +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Performance counter support for POWER6 processors. - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include -#include - -/* - * Bits in event code for POWER6 - */ -#define PM_PMC_SH 20 /* PMC number (1-based) for direct events */ -#define PM_PMC_MSK 0x7 -#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) -#define PM_UNIT_SH 16 /* Unit event comes (TTMxSEL encoding) */ -#define PM_UNIT_MSK 0xf -#define PM_UNIT_MSKS (PM_UNIT_MSK << PM_UNIT_SH) -#define PM_LLAV 0x8000 /* Load lookahead match value */ -#define PM_LLA 0x4000 /* Load lookahead match enable */ -#define PM_BYTE_SH 12 /* Byte of event bus to use */ -#define PM_BYTE_MSK 3 -#define PM_SUBUNIT_SH 8 /* Subunit event comes from (NEST_SEL enc.) */ -#define PM_SUBUNIT_MSK 7 -#define PM_SUBUNIT_MSKS (PM_SUBUNIT_MSK << PM_SUBUNIT_SH) -#define PM_PMCSEL_MSK 0xff /* PMCxSEL value */ -#define PM_BUSEVENT_MSK 0xf3700 - -/* - * Bits in MMCR1 for POWER6 - */ -#define MMCR1_TTM0SEL_SH 60 -#define MMCR1_TTMSEL_SH(n) (MMCR1_TTM0SEL_SH - (n) * 4) -#define MMCR1_TTMSEL_MSK 0xf -#define MMCR1_TTMSEL(m, n) (((m) >> MMCR1_TTMSEL_SH(n)) & MMCR1_TTMSEL_MSK) -#define MMCR1_NESTSEL_SH 45 -#define MMCR1_NESTSEL_MSK 0x7 -#define MMCR1_NESTSEL(m) (((m) >> MMCR1_NESTSEL_SH) & MMCR1_NESTSEL_MSK) -#define MMCR1_PMC1_LLA (1ul << 44) -#define MMCR1_PMC1_LLA_VALUE (1ul << 39) -#define MMCR1_PMC1_ADDR_SEL (1ul << 35) -#define MMCR1_PMC1SEL_SH 24 -#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) -#define MMCR1_PMCSEL_MSK 0xff - -/* - * Map of which direct events on which PMCs are marked instruction events. - * Indexed by PMCSEL value >> 1. - * Bottom 4 bits are a map of which PMCs are interesting, - * top 4 bits say what sort of event: - * 0 = direct marked event, - * 1 = byte decode event, - * 4 = add/and event (PMC1 -> bits 0 & 4), - * 5 = add/and event (PMC1 -> bits 1 & 5), - * 6 = add/and event (PMC1 -> bits 2 & 6), - * 7 = add/and event (PMC1 -> bits 3 & 7). - */ -static unsigned char direct_event_is_marked[0x60 >> 1] = { - 0, /* 00 */ - 0, /* 02 */ - 0, /* 04 */ - 0x07, /* 06 PM_MRK_ST_CMPL, PM_MRK_ST_GPS, PM_MRK_ST_CMPL_INT */ - 0x04, /* 08 PM_MRK_DFU_FIN */ - 0x06, /* 0a PM_MRK_IFU_FIN, PM_MRK_INST_FIN */ - 0, /* 0c */ - 0, /* 0e */ - 0x02, /* 10 PM_MRK_INST_DISP */ - 0x08, /* 12 PM_MRK_LSU_DERAT_MISS */ - 0, /* 14 */ - 0, /* 16 */ - 0x0c, /* 18 PM_THRESH_TIMEO, PM_MRK_INST_FIN */ - 0x0f, /* 1a PM_MRK_INST_DISP, PM_MRK_{FXU,FPU,LSU}_FIN */ - 0x01, /* 1c PM_MRK_INST_ISSUED */ - 0, /* 1e */ - 0, /* 20 */ - 0, /* 22 */ - 0, /* 24 */ - 0, /* 26 */ - 0x15, /* 28 PM_MRK_DATA_FROM_L2MISS, PM_MRK_DATA_FROM_L3MISS */ - 0, /* 2a */ - 0, /* 2c */ - 0, /* 2e */ - 0x4f, /* 30 */ - 0x7f, /* 32 */ - 0x4f, /* 34 */ - 0x5f, /* 36 */ - 0x6f, /* 38 */ - 0x4f, /* 3a */ - 0, /* 3c */ - 0x08, /* 3e PM_MRK_INST_TIMEO */ - 0x1f, /* 40 */ - 0x1f, /* 42 */ - 0x1f, /* 44 */ - 0x1f, /* 46 */ - 0x1f, /* 48 */ - 0x1f, /* 4a */ - 0x1f, /* 4c */ - 0x1f, /* 4e */ - 0, /* 50 */ - 0x05, /* 52 PM_MRK_BR_TAKEN, PM_MRK_BR_MPRED */ - 0x1c, /* 54 PM_MRK_PTEG_FROM_L3MISS, PM_MRK_PTEG_FROM_L2MISS */ - 0x02, /* 56 PM_MRK_LD_MISS_L1 */ - 0, /* 58 */ - 0, /* 5a */ - 0, /* 5c */ - 0, /* 5e */ -}; - -/* - * Masks showing for each unit which bits are marked events. - * These masks are in LE order, i.e. 0x00000001 is byte 0, bit 0. - */ -static u32 marked_bus_events[16] = { - 0x01000000, /* direct events set 1: byte 3 bit 0 */ - 0x00010000, /* direct events set 2: byte 2 bit 0 */ - 0, 0, 0, 0, /* IDU, IFU, nest: nothing */ - 0x00000088, /* VMX set 1: byte 0 bits 3, 7 */ - 0x000000c0, /* VMX set 2: byte 0 bits 4-7 */ - 0x04010000, /* LSU set 1: byte 2 bit 0, byte 3 bit 2 */ - 0xff010000u, /* LSU set 2: byte 2 bit 0, all of byte 3 */ - 0, /* LSU set 3 */ - 0x00000010, /* VMX set 3: byte 0 bit 4 */ - 0, /* BFP set 1 */ - 0x00000022, /* BFP set 2: byte 0 bits 1, 5 */ - 0, 0 -}; - -/* - * Returns 1 if event counts things relating to marked instructions - * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. - */ -static int power6_marked_instr_event(u64 event) -{ - int pmc, psel, ptype; - int bit, byte, unit; - u32 mask; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - psel = (event & PM_PMCSEL_MSK) >> 1; /* drop edge/level bit */ - if (pmc >= 5) - return 0; - - bit = -1; - if (psel < sizeof(direct_event_is_marked)) { - ptype = direct_event_is_marked[psel]; - if (pmc == 0 || !(ptype & (1 << (pmc - 1)))) - return 0; - ptype >>= 4; - if (ptype == 0) - return 1; - if (ptype == 1) - bit = 0; - else - bit = ptype ^ (pmc - 1); - } else if ((psel & 0x48) == 0x40) - bit = psel & 7; - - if (!(event & PM_BUSEVENT_MSK) || bit == -1) - return 0; - - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - mask = marked_bus_events[unit]; - return (mask >> (byte * 8 + bit)) & 1; -} - -/* - * Assign PMC numbers and compute MMCR1 value for a set of events - */ -static int p6_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]) -{ - unsigned long mmcr1 = 0; - unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; - int i; - unsigned int pmc, ev, b, u, s, psel; - unsigned int ttmset = 0; - unsigned int pmc_inuse = 0; - - if (n_ev > 6) - return -1; - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc_inuse & (1 << (pmc - 1))) - return -1; /* collision! */ - pmc_inuse |= 1 << (pmc - 1); - } - } - for (i = 0; i < n_ev; ++i) { - ev = event[i]; - pmc = (ev >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - --pmc; - } else { - /* can go on any PMC; find a free one */ - for (pmc = 0; pmc < 4; ++pmc) - if (!(pmc_inuse & (1 << pmc))) - break; - if (pmc >= 4) - return -1; - pmc_inuse |= 1 << pmc; - } - hwc[i] = pmc; - psel = ev & PM_PMCSEL_MSK; - if (ev & PM_BUSEVENT_MSK) { - /* this event uses the event bus */ - b = (ev >> PM_BYTE_SH) & PM_BYTE_MSK; - u = (ev >> PM_UNIT_SH) & PM_UNIT_MSK; - /* check for conflict on this byte of event bus */ - if ((ttmset & (1 << b)) && MMCR1_TTMSEL(mmcr1, b) != u) - return -1; - mmcr1 |= (unsigned long)u << MMCR1_TTMSEL_SH(b); - ttmset |= 1 << b; - if (u == 5) { - /* Nest events have a further mux */ - s = (ev >> PM_SUBUNIT_SH) & PM_SUBUNIT_MSK; - if ((ttmset & 0x10) && - MMCR1_NESTSEL(mmcr1) != s) - return -1; - ttmset |= 0x10; - mmcr1 |= (unsigned long)s << MMCR1_NESTSEL_SH; - } - if (0x30 <= psel && psel <= 0x3d) { - /* these need the PMCx_ADDR_SEL bits */ - if (b >= 2) - mmcr1 |= MMCR1_PMC1_ADDR_SEL >> pmc; - } - /* bus select values are different for PMC3/4 */ - if (pmc >= 2 && (psel & 0x90) == 0x80) - psel ^= 0x20; - } - if (ev & PM_LLA) { - mmcr1 |= MMCR1_PMC1_LLA >> pmc; - if (ev & PM_LLAV) - mmcr1 |= MMCR1_PMC1_LLA_VALUE >> pmc; - } - if (power6_marked_instr_event(event[i])) - mmcra |= MMCRA_SAMPLE_ENABLE; - if (pmc < 4) - mmcr1 |= (unsigned long)psel << MMCR1_PMCSEL_SH(pmc); - } - mmcr[0] = 0; - if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; - if (pmc_inuse & 0xe) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; - return 0; -} - -/* - * Layout of constraint bits: - * - * 0-1 add field: number of uses of PMC1 (max 1) - * 2-3, 4-5, 6-7, 8-9, 10-11: ditto for PMC2, 3, 4, 5, 6 - * 12-15 add field: number of uses of PMC1-4 (max 4) - * 16-19 select field: unit on byte 0 of event bus - * 20-23, 24-27, 28-31 ditto for bytes 1, 2, 3 - * 32-34 select field: nest (subunit) event selector - */ -static int p6_get_constraint(u64 event, unsigned long *maskp, - unsigned long *valp) -{ - int pmc, byte, sh, subunit; - unsigned long mask = 0, value = 0; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 4 && !(event == 0x500009 || event == 0x600005)) - return -1; - sh = (pmc - 1) * 2; - mask |= 2 << sh; - value |= 1 << sh; - } - if (event & PM_BUSEVENT_MSK) { - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - sh = byte * 4 + (16 - PM_UNIT_SH); - mask |= PM_UNIT_MSKS << sh; - value |= (unsigned long)(event & PM_UNIT_MSKS) << sh; - if ((event & PM_UNIT_MSKS) == (5 << PM_UNIT_SH)) { - subunit = (event >> PM_SUBUNIT_SH) & PM_SUBUNIT_MSK; - mask |= (unsigned long)PM_SUBUNIT_MSK << 32; - value |= (unsigned long)subunit << 32; - } - } - if (pmc <= 4) { - mask |= 0x8000; /* add field for count of PMC1-4 uses */ - value |= 0x1000; - } - *maskp = mask; - *valp = value; - return 0; -} - -static int p6_limited_pmc_event(u64 event) -{ - int pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - - return pmc == 5 || pmc == 6; -} - -#define MAX_ALT 4 /* at most 4 alternatives for any event */ - -static const unsigned int event_alternatives[][MAX_ALT] = { - { 0x0130e8, 0x2000f6, 0x3000fc }, /* PM_PTEG_RELOAD_VALID */ - { 0x080080, 0x10000d, 0x30000c, 0x4000f0 }, /* PM_LD_MISS_L1 */ - { 0x080088, 0x200054, 0x3000f0 }, /* PM_ST_MISS_L1 */ - { 0x10000a, 0x2000f4, 0x600005 }, /* PM_RUN_CYC */ - { 0x10000b, 0x2000f5 }, /* PM_RUN_COUNT */ - { 0x10000e, 0x400010 }, /* PM_PURR */ - { 0x100010, 0x4000f8 }, /* PM_FLUSH */ - { 0x10001a, 0x200010 }, /* PM_MRK_INST_DISP */ - { 0x100026, 0x3000f8 }, /* PM_TB_BIT_TRANS */ - { 0x100054, 0x2000f0 }, /* PM_ST_FIN */ - { 0x100056, 0x2000fc }, /* PM_L1_ICACHE_MISS */ - { 0x1000f0, 0x40000a }, /* PM_INST_IMC_MATCH_CMPL */ - { 0x1000f8, 0x200008 }, /* PM_GCT_EMPTY_CYC */ - { 0x1000fc, 0x400006 }, /* PM_LSU_DERAT_MISS_CYC */ - { 0x20000e, 0x400007 }, /* PM_LSU_DERAT_MISS */ - { 0x200012, 0x300012 }, /* PM_INST_DISP */ - { 0x2000f2, 0x3000f2 }, /* PM_INST_DISP */ - { 0x2000f8, 0x300010 }, /* PM_EXT_INT */ - { 0x2000fe, 0x300056 }, /* PM_DATA_FROM_L2MISS */ - { 0x2d0030, 0x30001a }, /* PM_MRK_FPU_FIN */ - { 0x30000a, 0x400018 }, /* PM_MRK_INST_FIN */ - { 0x3000f6, 0x40000e }, /* PM_L1_DCACHE_RELOAD_VALID */ - { 0x3000fe, 0x400056 }, /* PM_DATA_FROM_L3MISS */ -}; - -/* - * This could be made more efficient with a binary search on - * a presorted list, if necessary - */ -static int find_alternatives_list(u64 event) -{ - int i, j; - unsigned int alt; - - for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { - if (event < event_alternatives[i][0]) - return -1; - for (j = 0; j < MAX_ALT; ++j) { - alt = event_alternatives[i][j]; - if (!alt || event < alt) - break; - if (event == alt) - return i; - } - } - return -1; -} - -static int p6_get_alternatives(u64 event, unsigned int flags, u64 alt[]) -{ - int i, j, nlim; - unsigned int psel, pmc; - unsigned int nalt = 1; - u64 aevent; - - alt[0] = event; - nlim = p6_limited_pmc_event(event); - - /* check the alternatives table */ - i = find_alternatives_list(event); - if (i >= 0) { - /* copy out alternatives from list */ - for (j = 0; j < MAX_ALT; ++j) { - aevent = event_alternatives[i][j]; - if (!aevent) - break; - if (aevent != event) - alt[nalt++] = aevent; - nlim += p6_limited_pmc_event(aevent); - } - - } else { - /* Check for alternative ways of computing sum events */ - /* PMCSEL 0x32 counter N == PMCSEL 0x34 counter 5-N */ - psel = event & (PM_PMCSEL_MSK & ~1); /* ignore edge bit */ - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc && (psel == 0x32 || psel == 0x34)) - alt[nalt++] = ((event ^ 0x6) & ~PM_PMC_MSKS) | - ((5 - pmc) << PM_PMC_SH); - - /* PMCSEL 0x38 counter N == PMCSEL 0x3a counter N+/-2 */ - if (pmc && (psel == 0x38 || psel == 0x3a)) - alt[nalt++] = ((event ^ 0x2) & ~PM_PMC_MSKS) | - ((pmc > 2? pmc - 2: pmc + 2) << PM_PMC_SH); - } - - if (flags & PPMU_ONLY_COUNT_RUN) { - /* - * We're only counting in RUN state, - * so PM_CYC is equivalent to PM_RUN_CYC, - * PM_INST_CMPL === PM_RUN_INST_CMPL, PM_PURR === PM_RUN_PURR. - * This doesn't include alternatives that don't provide - * any extra flexibility in assigning PMCs (e.g. - * 0x10000a for PM_RUN_CYC vs. 0x1e for PM_CYC). - * Note that even with these additional alternatives - * we never end up with more than 4 alternatives for any event. - */ - j = nalt; - for (i = 0; i < nalt; ++i) { - switch (alt[i]) { - case 0x1e: /* PM_CYC */ - alt[j++] = 0x600005; /* PM_RUN_CYC */ - ++nlim; - break; - case 0x10000a: /* PM_RUN_CYC */ - alt[j++] = 0x1e; /* PM_CYC */ - break; - case 2: /* PM_INST_CMPL */ - alt[j++] = 0x500009; /* PM_RUN_INST_CMPL */ - ++nlim; - break; - case 0x500009: /* PM_RUN_INST_CMPL */ - alt[j++] = 2; /* PM_INST_CMPL */ - break; - case 0x10000e: /* PM_PURR */ - alt[j++] = 0x4000f4; /* PM_RUN_PURR */ - break; - case 0x4000f4: /* PM_RUN_PURR */ - alt[j++] = 0x10000e; /* PM_PURR */ - break; - } - } - nalt = j; - } - - if (!(flags & PPMU_LIMITED_PMC_OK) && nlim) { - /* remove the limited PMC events */ - j = 0; - for (i = 0; i < nalt; ++i) { - if (!p6_limited_pmc_event(alt[i])) { - alt[j] = alt[i]; - ++j; - } - } - nalt = j; - } else if ((flags & PPMU_LIMITED_PMC_REQD) && nlim < nalt) { - /* remove all but the limited PMC events */ - j = 0; - for (i = 0; i < nalt; ++i) { - if (p6_limited_pmc_event(alt[i])) { - alt[j] = alt[i]; - ++j; - } - } - nalt = j; - } - - return nalt; -} - -static void p6_disable_pmc(unsigned int pmc, unsigned long mmcr[]) -{ - /* Set PMCxSEL to 0 to disable PMCx */ - if (pmc <= 3) - mmcr[1] &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); -} - -static int power6_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 0x1e, - [PERF_COUNT_HW_INSTRUCTIONS] = 2, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x280030, /* LD_REF_L1 */ - [PERF_COUNT_HW_CACHE_MISSES] = 0x30000c, /* LD_MISS_L1 */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x410a0, /* BR_PRED */ - [PERF_COUNT_HW_BRANCH_MISSES] = 0x400052, /* BR_MPRED */ -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - * The "DTLB" and "ITLB" events relate to the DERAT and IERAT. - */ -static int power6_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x280030, 0x80080 }, - [C(OP_WRITE)] = { 0x180032, 0x80088 }, - [C(OP_PREFETCH)] = { 0x810a4, 0 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x100056 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0x4008c, 0 }, - }, - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x150730, 0x250532 }, - [C(OP_WRITE)] = { 0x250432, 0x150432 }, - [C(OP_PREFETCH)] = { 0x810a6, 0 }, - }, - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x20000e }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x420ce }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x430e6, 0x400052 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -static struct power_pmu power6_pmu = { - .name = "POWER6", - .n_counter = 6, - .max_alternatives = MAX_ALT, - .add_fields = 0x1555, - .test_adder = 0x3000, - .compute_mmcr = p6_compute_mmcr, - .get_constraint = p6_get_constraint, - .get_alternatives = p6_get_alternatives, - .disable_pmc = p6_disable_pmc, - .limited_pmc_event = p6_limited_pmc_event, - .flags = PPMU_LIMITED_PMC5_6 | PPMU_ALT_SIPR, - .n_generic = ARRAY_SIZE(power6_generic_events), - .generic_events = power6_generic_events, - .cache_events = &power6_cache_events, -}; - -static int __init init_power6_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type || - strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power6")) - return -ENODEV; - - return register_power_pmu(&power6_pmu); -} - -early_initcall(init_power6_pmu); diff --git a/arch/powerpc/kernel/power7-pmu.c b/arch/powerpc/kernel/power7-pmu.c deleted file mode 100644 index 1251e4d..0000000 --- a/arch/powerpc/kernel/power7-pmu.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Performance counter support for POWER7 processors. - * - * Copyright 2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include -#include - -/* - * Bits in event code for POWER7 - */ -#define PM_PMC_SH 16 /* PMC number (1-based) for direct events */ -#define PM_PMC_MSK 0xf -#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) -#define PM_UNIT_SH 12 /* TTMMUX number and setting - unit select */ -#define PM_UNIT_MSK 0xf -#define PM_COMBINE_SH 11 /* Combined event bit */ -#define PM_COMBINE_MSK 1 -#define PM_COMBINE_MSKS 0x800 -#define PM_L2SEL_SH 8 /* L2 event select */ -#define PM_L2SEL_MSK 7 -#define PM_PMCSEL_MSK 0xff - -/* - * Bits in MMCR1 for POWER7 - */ -#define MMCR1_TTM0SEL_SH 60 -#define MMCR1_TTM1SEL_SH 56 -#define MMCR1_TTM2SEL_SH 52 -#define MMCR1_TTM3SEL_SH 48 -#define MMCR1_TTMSEL_MSK 0xf -#define MMCR1_L2SEL_SH 45 -#define MMCR1_L2SEL_MSK 7 -#define MMCR1_PMC1_COMBINE_SH 35 -#define MMCR1_PMC2_COMBINE_SH 34 -#define MMCR1_PMC3_COMBINE_SH 33 -#define MMCR1_PMC4_COMBINE_SH 32 -#define MMCR1_PMC1SEL_SH 24 -#define MMCR1_PMC2SEL_SH 16 -#define MMCR1_PMC3SEL_SH 8 -#define MMCR1_PMC4SEL_SH 0 -#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) -#define MMCR1_PMCSEL_MSK 0xff - -/* - * Layout of constraint bits: - * 6666555555555544444444443333333333222222222211111111110000000000 - * 3210987654321098765432109876543210987654321098765432109876543210 - * [ ><><><><><><> - * NC P6P5P4P3P2P1 - * - * NC - number of counters - * 15: NC error 0x8000 - * 12-14: number of events needing PMC1-4 0x7000 - * - * P6 - * 11: P6 error 0x800 - * 10-11: Count of events needing PMC6 - * - * P1..P5 - * 0-9: Count of events needing PMC1..PMC5 - */ - -static int power7_get_constraint(u64 event, unsigned long *maskp, - unsigned long *valp) -{ - int pmc, sh; - unsigned long mask = 0, value = 0; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 6) - return -1; - sh = (pmc - 1) * 2; - mask |= 2 << sh; - value |= 1 << sh; - if (pmc >= 5 && !(event == 0x500fa || event == 0x600f4)) - return -1; - } - if (pmc < 5) { - /* need a counter from PMC1-4 set */ - mask |= 0x8000; - value |= 0x1000; - } - *maskp = mask; - *valp = value; - return 0; -} - -#define MAX_ALT 2 /* at most 2 alternatives for any event */ - -static const unsigned int event_alternatives[][MAX_ALT] = { - { 0x200f2, 0x300f2 }, /* PM_INST_DISP */ - { 0x200f4, 0x600f4 }, /* PM_RUN_CYC */ - { 0x400fa, 0x500fa }, /* PM_RUN_INST_CMPL */ -}; - -/* - * Scan the alternatives table for a match and return the - * index into the alternatives table if found, else -1. - */ -static int find_alternative(u64 event) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { - if (event < event_alternatives[i][0]) - break; - for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) - if (event == event_alternatives[i][j]) - return i; - } - return -1; -} - -static s64 find_alternative_decode(u64 event) -{ - int pmc, psel; - - /* this only handles the 4x decode events */ - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - psel = event & PM_PMCSEL_MSK; - if ((pmc == 2 || pmc == 4) && (psel & ~7) == 0x40) - return event - (1 << PM_PMC_SH) + 8; - if ((pmc == 1 || pmc == 3) && (psel & ~7) == 0x48) - return event + (1 << PM_PMC_SH) - 8; - return -1; -} - -static int power7_get_alternatives(u64 event, unsigned int flags, u64 alt[]) -{ - int i, j, nalt = 1; - s64 ae; - - alt[0] = event; - nalt = 1; - i = find_alternative(event); - if (i >= 0) { - for (j = 0; j < MAX_ALT; ++j) { - ae = event_alternatives[i][j]; - if (ae && ae != event) - alt[nalt++] = ae; - } - } else { - ae = find_alternative_decode(event); - if (ae > 0) - alt[nalt++] = ae; - } - - if (flags & PPMU_ONLY_COUNT_RUN) { - /* - * We're only counting in RUN state, - * so PM_CYC is equivalent to PM_RUN_CYC - * and PM_INST_CMPL === PM_RUN_INST_CMPL. - * This doesn't include alternatives that don't provide - * any extra flexibility in assigning PMCs. - */ - j = nalt; - for (i = 0; i < nalt; ++i) { - switch (alt[i]) { - case 0x1e: /* PM_CYC */ - alt[j++] = 0x600f4; /* PM_RUN_CYC */ - break; - case 0x600f4: /* PM_RUN_CYC */ - alt[j++] = 0x1e; - break; - case 0x2: /* PM_PPC_CMPL */ - alt[j++] = 0x500fa; /* PM_RUN_INST_CMPL */ - break; - case 0x500fa: /* PM_RUN_INST_CMPL */ - alt[j++] = 0x2; /* PM_PPC_CMPL */ - break; - } - } - nalt = j; - } - - return nalt; -} - -/* - * Returns 1 if event counts things relating to marked instructions - * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. - */ -static int power7_marked_instr_event(u64 event) -{ - int pmc, psel; - int unit; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - psel = event & PM_PMCSEL_MSK & ~1; /* trim off edge/level bit */ - if (pmc >= 5) - return 0; - - switch (psel >> 4) { - case 2: - return pmc == 2 || pmc == 4; - case 3: - if (psel == 0x3c) - return pmc == 1; - if (psel == 0x3e) - return pmc != 2; - return 1; - case 4: - case 5: - return unit == 0xd; - case 6: - if (psel == 0x64) - return pmc >= 3; - case 8: - return unit == 0xd; - } - return 0; -} - -static int power7_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]) -{ - unsigned long mmcr1 = 0; - unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; - unsigned int pmc, unit, combine, l2sel, psel; - unsigned int pmc_inuse = 0; - int i; - - /* First pass to count resource use */ - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 6) - return -1; - if (pmc_inuse & (1 << (pmc - 1))) - return -1; - pmc_inuse |= 1 << (pmc - 1); - } - } - - /* Second pass: assign PMCs, set all MMCR1 fields */ - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - combine = (event[i] >> PM_COMBINE_SH) & PM_COMBINE_MSK; - l2sel = (event[i] >> PM_L2SEL_SH) & PM_L2SEL_MSK; - psel = event[i] & PM_PMCSEL_MSK; - if (!pmc) { - /* Bus event or any-PMC direct event */ - for (pmc = 0; pmc < 4; ++pmc) { - if (!(pmc_inuse & (1 << pmc))) - break; - } - if (pmc >= 4) - return -1; - pmc_inuse |= 1 << pmc; - } else { - /* Direct or decoded event */ - --pmc; - } - if (pmc <= 3) { - mmcr1 |= (unsigned long) unit - << (MMCR1_TTM0SEL_SH - 4 * pmc); - mmcr1 |= (unsigned long) combine - << (MMCR1_PMC1_COMBINE_SH - pmc); - mmcr1 |= psel << MMCR1_PMCSEL_SH(pmc); - if (unit == 6) /* L2 events */ - mmcr1 |= (unsigned long) l2sel - << MMCR1_L2SEL_SH; - } - if (power7_marked_instr_event(event[i])) - mmcra |= MMCRA_SAMPLE_ENABLE; - hwc[i] = pmc; - } - - /* Return MMCRx values */ - mmcr[0] = 0; - if (pmc_inuse & 1) - mmcr[0] = MMCR0_PMC1CE; - if (pmc_inuse & 0x3e) - mmcr[0] |= MMCR0_PMCjCE; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; - return 0; -} - -static void power7_disable_pmc(unsigned int pmc, unsigned long mmcr[]) -{ - if (pmc <= 3) - mmcr[1] &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); -} - -static int power7_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 0x1e, - [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x100f8, /* GCT_NOSLOT_CYC */ - [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x4000a, /* CMPLU_STALL */ - [PERF_COUNT_HW_INSTRUCTIONS] = 2, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0xc880, /* LD_REF_L1_LSU*/ - [PERF_COUNT_HW_CACHE_MISSES] = 0x400f0, /* LD_MISS_L1 */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x10068, /* BRU_FIN */ - [PERF_COUNT_HW_BRANCH_MISSES] = 0x400f6, /* BR_MPRED */ -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - */ -static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0xc880, 0x400f0 }, - [C(OP_WRITE)] = { 0, 0x300f0 }, - [C(OP_PREFETCH)] = { 0xd8b8, 0 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x200fc }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0x408a, 0 }, - }, - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x16080, 0x26080 }, - [C(OP_WRITE)] = { 0x16082, 0x26082 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x300fc }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x400fc }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x10068, 0x400f6 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -static struct power_pmu power7_pmu = { - .name = "POWER7", - .n_counter = 6, - .max_alternatives = MAX_ALT + 1, - .add_fields = 0x1555ul, - .test_adder = 0x3000ul, - .compute_mmcr = power7_compute_mmcr, - .get_constraint = power7_get_constraint, - .get_alternatives = power7_get_alternatives, - .disable_pmc = power7_disable_pmc, - .flags = PPMU_ALT_SIPR, - .n_generic = ARRAY_SIZE(power7_generic_events), - .generic_events = power7_generic_events, - .cache_events = &power7_cache_events, -}; - -static int __init init_power7_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type || - strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power7")) - return -ENODEV; - - return register_power_pmu(&power7_pmu); -} - -early_initcall(init_power7_pmu); diff --git a/arch/powerpc/kernel/ppc970-pmu.c b/arch/powerpc/kernel/ppc970-pmu.c deleted file mode 100644 index 8c21902..0000000 --- a/arch/powerpc/kernel/ppc970-pmu.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Performance counter support for PPC970-family processors. - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * - * 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 -#include -#include -#include - -/* - * Bits in event code for PPC970 - */ -#define PM_PMC_SH 12 /* PMC number (1-based) for direct events */ -#define PM_PMC_MSK 0xf -#define PM_UNIT_SH 8 /* TTMMUX number and setting - unit select */ -#define PM_UNIT_MSK 0xf -#define PM_SPCSEL_SH 6 -#define PM_SPCSEL_MSK 3 -#define PM_BYTE_SH 4 /* Byte number of event bus to use */ -#define PM_BYTE_MSK 3 -#define PM_PMCSEL_MSK 0xf - -/* Values in PM_UNIT field */ -#define PM_NONE 0 -#define PM_FPU 1 -#define PM_VPU 2 -#define PM_ISU 3 -#define PM_IFU 4 -#define PM_IDU 5 -#define PM_STS 6 -#define PM_LSU0 7 -#define PM_LSU1U 8 -#define PM_LSU1L 9 -#define PM_LASTUNIT 9 - -/* - * Bits in MMCR0 for PPC970 - */ -#define MMCR0_PMC1SEL_SH 8 -#define MMCR0_PMC2SEL_SH 1 -#define MMCR_PMCSEL_MSK 0x1f - -/* - * Bits in MMCR1 for PPC970 - */ -#define MMCR1_TTM0SEL_SH 62 -#define MMCR1_TTM1SEL_SH 59 -#define MMCR1_TTM3SEL_SH 53 -#define MMCR1_TTMSEL_MSK 3 -#define MMCR1_TD_CP_DBG0SEL_SH 50 -#define MMCR1_TD_CP_DBG1SEL_SH 48 -#define MMCR1_TD_CP_DBG2SEL_SH 46 -#define MMCR1_TD_CP_DBG3SEL_SH 44 -#define MMCR1_PMC1_ADDER_SEL_SH 39 -#define MMCR1_PMC2_ADDER_SEL_SH 38 -#define MMCR1_PMC6_ADDER_SEL_SH 37 -#define MMCR1_PMC5_ADDER_SEL_SH 36 -#define MMCR1_PMC8_ADDER_SEL_SH 35 -#define MMCR1_PMC7_ADDER_SEL_SH 34 -#define MMCR1_PMC3_ADDER_SEL_SH 33 -#define MMCR1_PMC4_ADDER_SEL_SH 32 -#define MMCR1_PMC3SEL_SH 27 -#define MMCR1_PMC4SEL_SH 22 -#define MMCR1_PMC5SEL_SH 17 -#define MMCR1_PMC6SEL_SH 12 -#define MMCR1_PMC7SEL_SH 7 -#define MMCR1_PMC8SEL_SH 2 - -static short mmcr1_adder_bits[8] = { - MMCR1_PMC1_ADDER_SEL_SH, - MMCR1_PMC2_ADDER_SEL_SH, - MMCR1_PMC3_ADDER_SEL_SH, - MMCR1_PMC4_ADDER_SEL_SH, - MMCR1_PMC5_ADDER_SEL_SH, - MMCR1_PMC6_ADDER_SEL_SH, - MMCR1_PMC7_ADDER_SEL_SH, - MMCR1_PMC8_ADDER_SEL_SH -}; - -/* - * Layout of constraint bits: - * 6666555555555544444444443333333333222222222211111111110000000000 - * 3210987654321098765432109876543210987654321098765432109876543210 - * <><><>[ >[ >[ >< >< >< >< ><><><><><><><><> - * SPT0T1 UC PS1 PS2 B0 B1 B2 B3 P1P2P3P4P5P6P7P8 - * - * SP - SPCSEL constraint - * 48-49: SPCSEL value 0x3_0000_0000_0000 - * - * T0 - TTM0 constraint - * 46-47: TTM0SEL value (0=FPU, 2=IFU, 3=VPU) 0xC000_0000_0000 - * - * T1 - TTM1 constraint - * 44-45: TTM1SEL value (0=IDU, 3=STS) 0x3000_0000_0000 - * - * UC - unit constraint: can't have all three of FPU|IFU|VPU, ISU, IDU|STS - * 43: UC3 error 0x0800_0000_0000 - * 42: FPU|IFU|VPU events needed 0x0400_0000_0000 - * 41: ISU events needed 0x0200_0000_0000 - * 40: IDU|STS events needed 0x0100_0000_0000 - * - * PS1 - * 39: PS1 error 0x0080_0000_0000 - * 36-38: count of events needing PMC1/2/5/6 0x0070_0000_0000 - * - * PS2 - * 35: PS2 error 0x0008_0000_0000 - * 32-34: count of events needing PMC3/4/7/8 0x0007_0000_0000 - * - * B0 - * 28-31: Byte 0 event source 0xf000_0000 - * Encoding as for the event code - * - * B1, B2, B3 - * 24-27, 20-23, 16-19: Byte 1, 2, 3 event sources - * - * P1 - * 15: P1 error 0x8000 - * 14-15: Count of events needing PMC1 - * - * P2..P8 - * 0-13: Count of events needing PMC2..PMC8 - */ - -static unsigned char direct_marked_event[8] = { - (1<<2) | (1<<3), /* PMC1: PM_MRK_GRP_DISP, PM_MRK_ST_CMPL */ - (1<<3) | (1<<5), /* PMC2: PM_THRESH_TIMEO, PM_MRK_BRU_FIN */ - (1<<3) | (1<<5), /* PMC3: PM_MRK_ST_CMPL_INT, PM_MRK_VMX_FIN */ - (1<<4) | (1<<5), /* PMC4: PM_MRK_GRP_CMPL, PM_MRK_CRU_FIN */ - (1<<4) | (1<<5), /* PMC5: PM_GRP_MRK, PM_MRK_GRP_TIMEO */ - (1<<3) | (1<<4) | (1<<5), - /* PMC6: PM_MRK_ST_STS, PM_MRK_FXU_FIN, PM_MRK_GRP_ISSUED */ - (1<<4) | (1<<5), /* PMC7: PM_MRK_FPU_FIN, PM_MRK_INST_FIN */ - (1<<4) /* PMC8: PM_MRK_LSU_FIN */ -}; - -/* - * Returns 1 if event counts things relating to marked instructions - * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. - */ -static int p970_marked_instr_event(u64 event) -{ - int pmc, psel, unit, byte, bit; - unsigned int mask; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - psel = event & PM_PMCSEL_MSK; - if (pmc) { - if (direct_marked_event[pmc - 1] & (1 << psel)) - return 1; - if (psel == 0) /* add events */ - bit = (pmc <= 4)? pmc - 1: 8 - pmc; - else if (psel == 7 || psel == 13) /* decode events */ - bit = 4; - else - return 0; - } else - bit = psel; - - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - mask = 0; - switch (unit) { - case PM_VPU: - mask = 0x4c; /* byte 0 bits 2,3,6 */ - break; - case PM_LSU0: - /* byte 2 bits 0,2,3,4,6; all of byte 1 */ - mask = 0x085dff00; - break; - case PM_LSU1L: - mask = 0x50 << 24; /* byte 3 bits 4,6 */ - break; - } - return (mask >> (byte * 8 + bit)) & 1; -} - -/* Masks and values for using events from the various units */ -static unsigned long unit_cons[PM_LASTUNIT+1][2] = { - [PM_FPU] = { 0xc80000000000ull, 0x040000000000ull }, - [PM_VPU] = { 0xc80000000000ull, 0xc40000000000ull }, - [PM_ISU] = { 0x080000000000ull, 0x020000000000ull }, - [PM_IFU] = { 0xc80000000000ull, 0x840000000000ull }, - [PM_IDU] = { 0x380000000000ull, 0x010000000000ull }, - [PM_STS] = { 0x380000000000ull, 0x310000000000ull }, -}; - -static int p970_get_constraint(u64 event, unsigned long *maskp, - unsigned long *valp) -{ - int pmc, byte, unit, sh, spcsel; - unsigned long mask = 0, value = 0; - int grp = -1; - - pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc > 8) - return -1; - sh = (pmc - 1) * 2; - mask |= 2 << sh; - value |= 1 << sh; - grp = ((pmc - 1) >> 1) & 1; - } - unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; - if (unit) { - if (unit > PM_LASTUNIT) - return -1; - mask |= unit_cons[unit][0]; - value |= unit_cons[unit][1]; - byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; - /* - * Bus events on bytes 0 and 2 can be counted - * on PMC1/2/5/6; bytes 1 and 3 on PMC3/4/7/8. - */ - if (!pmc) - grp = byte & 1; - /* Set byte lane select field */ - mask |= 0xfULL << (28 - 4 * byte); - value |= (unsigned long)unit << (28 - 4 * byte); - } - if (grp == 0) { - /* increment PMC1/2/5/6 field */ - mask |= 0x8000000000ull; - value |= 0x1000000000ull; - } else if (grp == 1) { - /* increment PMC3/4/7/8 field */ - mask |= 0x800000000ull; - value |= 0x100000000ull; - } - spcsel = (event >> PM_SPCSEL_SH) & PM_SPCSEL_MSK; - if (spcsel) { - mask |= 3ull << 48; - value |= (unsigned long)spcsel << 48; - } - *maskp = mask; - *valp = value; - return 0; -} - -static int p970_get_alternatives(u64 event, unsigned int flags, u64 alt[]) -{ - alt[0] = event; - - /* 2 alternatives for LSU empty */ - if (event == 0x2002 || event == 0x3002) { - alt[1] = event ^ 0x1000; - return 2; - } - - return 1; -} - -static int p970_compute_mmcr(u64 event[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]) -{ - unsigned long mmcr0 = 0, mmcr1 = 0, mmcra = 0; - unsigned int pmc, unit, byte, psel; - unsigned int ttm, grp; - unsigned int pmc_inuse = 0; - unsigned int pmc_grp_use[2]; - unsigned char busbyte[4]; - unsigned char unituse[16]; - unsigned char unitmap[] = { 0, 0<<3, 3<<3, 1<<3, 2<<3, 0|4, 3|4 }; - unsigned char ttmuse[2]; - unsigned char pmcsel[8]; - int i; - int spcsel; - - if (n_ev > 8) - return -1; - - /* First pass to count resource use */ - pmc_grp_use[0] = pmc_grp_use[1] = 0; - memset(busbyte, 0, sizeof(busbyte)); - memset(unituse, 0, sizeof(unituse)); - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - if (pmc) { - if (pmc_inuse & (1 << (pmc - 1))) - return -1; - pmc_inuse |= 1 << (pmc - 1); - /* count 1/2/5/6 vs 3/4/7/8 use */ - ++pmc_grp_use[((pmc - 1) >> 1) & 1]; - } - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - if (unit) { - if (unit > PM_LASTUNIT) - return -1; - if (!pmc) - ++pmc_grp_use[byte & 1]; - if (busbyte[byte] && busbyte[byte] != unit) - return -1; - busbyte[byte] = unit; - unituse[unit] = 1; - } - } - if (pmc_grp_use[0] > 4 || pmc_grp_use[1] > 4) - return -1; - - /* - * Assign resources and set multiplexer selects. - * - * PM_ISU can go either on TTM0 or TTM1, but that's the only - * choice we have to deal with. - */ - if (unituse[PM_ISU] & - (unituse[PM_FPU] | unituse[PM_IFU] | unituse[PM_VPU])) - unitmap[PM_ISU] = 2 | 4; /* move ISU to TTM1 */ - /* Set TTM[01]SEL fields. */ - ttmuse[0] = ttmuse[1] = 0; - for (i = PM_FPU; i <= PM_STS; ++i) { - if (!unituse[i]) - continue; - ttm = unitmap[i]; - ++ttmuse[(ttm >> 2) & 1]; - mmcr1 |= (unsigned long)(ttm & ~4) << MMCR1_TTM1SEL_SH; - } - /* Check only one unit per TTMx */ - if (ttmuse[0] > 1 || ttmuse[1] > 1) - return -1; - - /* Set byte lane select fields and TTM3SEL. */ - for (byte = 0; byte < 4; ++byte) { - unit = busbyte[byte]; - if (!unit) - continue; - if (unit <= PM_STS) - ttm = (unitmap[unit] >> 2) & 1; - else if (unit == PM_LSU0) - ttm = 2; - else { - ttm = 3; - if (unit == PM_LSU1L && byte >= 2) - mmcr1 |= 1ull << (MMCR1_TTM3SEL_SH + 3 - byte); - } - mmcr1 |= (unsigned long)ttm - << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); - } - - /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ - memset(pmcsel, 0x8, sizeof(pmcsel)); /* 8 means don't count */ - for (i = 0; i < n_ev; ++i) { - pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; - unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; - byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; - psel = event[i] & PM_PMCSEL_MSK; - if (!pmc) { - /* Bus event or any-PMC direct event */ - if (unit) - psel |= 0x10 | ((byte & 2) << 2); - else - psel |= 8; - for (pmc = 0; pmc < 8; ++pmc) { - if (pmc_inuse & (1 << pmc)) - continue; - grp = (pmc >> 1) & 1; - if (unit) { - if (grp == (byte & 1)) - break; - } else if (pmc_grp_use[grp] < 4) { - ++pmc_grp_use[grp]; - break; - } - } - pmc_inuse |= 1 << pmc; - } else { - /* Direct event */ - --pmc; - if (psel == 0 && (byte & 2)) - /* add events on higher-numbered bus */ - mmcr1 |= 1ull << mmcr1_adder_bits[pmc]; - } - pmcsel[pmc] = psel; - hwc[i] = pmc; - spcsel = (event[i] >> PM_SPCSEL_SH) & PM_SPCSEL_MSK; - mmcr1 |= spcsel; - if (p970_marked_instr_event(event[i])) - mmcra |= MMCRA_SAMPLE_ENABLE; - } - for (pmc = 0; pmc < 2; ++pmc) - mmcr0 |= pmcsel[pmc] << (MMCR0_PMC1SEL_SH - 7 * pmc); - for (; pmc < 8; ++pmc) - mmcr1 |= (unsigned long)pmcsel[pmc] - << (MMCR1_PMC3SEL_SH - 5 * (pmc - 2)); - if (pmc_inuse & 1) - mmcr0 |= MMCR0_PMC1CE; - if (pmc_inuse & 0xfe) - mmcr0 |= MMCR0_PMCjCE; - - mmcra |= 0x2000; /* mark only one IOP per PPC instruction */ - - /* Return MMCRx values */ - mmcr[0] = mmcr0; - mmcr[1] = mmcr1; - mmcr[2] = mmcra; - return 0; -} - -static void p970_disable_pmc(unsigned int pmc, unsigned long mmcr[]) -{ - int shift, i; - - if (pmc <= 1) { - shift = MMCR0_PMC1SEL_SH - 7 * pmc; - i = 0; - } else { - shift = MMCR1_PMC3SEL_SH - 5 * (pmc - 2); - i = 1; - } - /* - * Setting the PMCxSEL field to 0x08 disables PMC x. - */ - mmcr[i] = (mmcr[i] & ~(0x1fUL << shift)) | (0x08UL << shift); -} - -static int ppc970_generic_events[] = { - [PERF_COUNT_HW_CPU_CYCLES] = 7, - [PERF_COUNT_HW_INSTRUCTIONS] = 1, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x8810, /* PM_LD_REF_L1 */ - [PERF_COUNT_HW_CACHE_MISSES] = 0x3810, /* PM_LD_MISS_L1 */ - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x431, /* PM_BR_ISSUED */ - [PERF_COUNT_HW_BRANCH_MISSES] = 0x327, /* PM_GRP_BR_MPRED */ -}; - -#define C(x) PERF_COUNT_HW_CACHE_##x - -/* - * Table of generalized cache-related events. - * 0 means not supported, -1 means nonsensical, other values - * are event codes. - */ -static int ppc970_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { - [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x8810, 0x3810 }, - [C(OP_WRITE)] = { 0x7810, 0x813 }, - [C(OP_PREFETCH)] = { 0x731, 0 }, - }, - [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { 0, 0 }, - }, - [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0 }, - [C(OP_WRITE)] = { 0, 0 }, - [C(OP_PREFETCH)] = { 0x733, 0 }, - }, - [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x704 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0, 0x700 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x431, 0x327 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, - [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { -1, -1 }, - [C(OP_WRITE)] = { -1, -1 }, - [C(OP_PREFETCH)] = { -1, -1 }, - }, -}; - -static struct power_pmu ppc970_pmu = { - .name = "PPC970/FX/MP", - .n_counter = 8, - .max_alternatives = 2, - .add_fields = 0x001100005555ull, - .test_adder = 0x013300000000ull, - .compute_mmcr = p970_compute_mmcr, - .get_constraint = p970_get_constraint, - .get_alternatives = p970_get_alternatives, - .disable_pmc = p970_disable_pmc, - .n_generic = ARRAY_SIZE(ppc970_generic_events), - .generic_events = ppc970_generic_events, - .cache_events = &ppc970_cache_events, -}; - -static int __init init_ppc970_pmu(void) -{ - if (!cur_cpu_spec->oprofile_cpu_type || - (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970") - && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970MP"))) - return -ENODEV; - - return register_power_pmu(&ppc970_pmu); -} - -early_initcall(init_ppc970_pmu); diff --git a/arch/powerpc/perf/Makefile b/arch/powerpc/perf/Makefile new file mode 100644 index 0000000..af3fac2 --- /dev/null +++ b/arch/powerpc/perf/Makefile @@ -0,0 +1,14 @@ +subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror + +obj-$(CONFIG_PERF_EVENTS) += callchain.o + +obj-$(CONFIG_PPC_PERF_CTRS) += core-book3s.o +obj64-$(CONFIG_PPC_PERF_CTRS) += power4-pmu.o ppc970-pmu.o power5-pmu.o \ + power5+-pmu.o power6-pmu.o power7-pmu.o +obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o + +obj-$(CONFIG_FSL_EMB_PERF_EVENT) += core-fsl-emb.o +obj-$(CONFIG_FSL_EMB_PERF_EVENT_E500) += e500-pmu.o + +obj-$(CONFIG_PPC64) += $(obj64-y) +obj-$(CONFIG_PPC32) += $(obj32-y) diff --git a/arch/powerpc/perf/callchain.c b/arch/powerpc/perf/callchain.c new file mode 100644 index 0000000..e8a18d1 --- /dev/null +++ b/arch/powerpc/perf/callchain.c @@ -0,0 +1,492 @@ +/* + * Performance counter callchain support - powerpc architecture code + * + * Copyright © 2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PPC64 +#include "../kernel/ppc32.h" +#endif + + +/* + * Is sp valid as the address of the next kernel stack frame after prev_sp? + * The next frame may be in a different stack area but should not go + * back down in the same stack area. + */ +static int valid_next_sp(unsigned long sp, unsigned long prev_sp) +{ + if (sp & 0xf) + return 0; /* must be 16-byte aligned */ + if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD)) + return 0; + if (sp >= prev_sp + STACK_FRAME_OVERHEAD) + return 1; + /* + * sp could decrease when we jump off an interrupt stack + * back to the regular process stack. + */ + if ((sp & ~(THREAD_SIZE - 1)) != (prev_sp & ~(THREAD_SIZE - 1))) + return 1; + return 0; +} + +void +perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) +{ + unsigned long sp, next_sp; + unsigned long next_ip; + unsigned long lr; + long level = 0; + unsigned long *fp; + + lr = regs->link; + sp = regs->gpr[1]; + perf_callchain_store(entry, regs->nip); + + if (!validate_sp(sp, current, STACK_FRAME_OVERHEAD)) + return; + + for (;;) { + fp = (unsigned long *) sp; + next_sp = fp[0]; + + if (next_sp == sp + STACK_INT_FRAME_SIZE && + fp[STACK_FRAME_MARKER] == STACK_FRAME_REGS_MARKER) { + /* + * This looks like an interrupt frame for an + * interrupt that occurred in the kernel + */ + regs = (struct pt_regs *)(sp + STACK_FRAME_OVERHEAD); + next_ip = regs->nip; + lr = regs->link; + level = 0; + perf_callchain_store(entry, PERF_CONTEXT_KERNEL); + + } else { + if (level == 0) + next_ip = lr; + else + next_ip = fp[STACK_FRAME_LR_SAVE]; + + /* + * We can't tell which of the first two addresses + * we get are valid, but we can filter out the + * obviously bogus ones here. We replace them + * with 0 rather than removing them entirely so + * that userspace can tell which is which. + */ + if ((level == 1 && next_ip == lr) || + (level <= 1 && !kernel_text_address(next_ip))) + next_ip = 0; + + ++level; + } + + perf_callchain_store(entry, next_ip); + if (!valid_next_sp(next_sp, sp)) + return; + sp = next_sp; + } +} + +#ifdef CONFIG_PPC64 +/* + * On 64-bit we don't want to invoke hash_page on user addresses from + * interrupt context, so if the access faults, we read the page tables + * to find which page (if any) is mapped and access it directly. + */ +static int read_user_stack_slow(void __user *ptr, void *ret, int nb) +{ + pgd_t *pgdir; + pte_t *ptep, pte; + unsigned shift; + unsigned long addr = (unsigned long) ptr; + unsigned long offset; + unsigned long pfn; + void *kaddr; + + pgdir = current->mm->pgd; + if (!pgdir) + return -EFAULT; + + ptep = find_linux_pte_or_hugepte(pgdir, addr, &shift); + if (!shift) + shift = PAGE_SHIFT; + + /* align address to page boundary */ + offset = addr & ((1UL << shift) - 1); + addr -= offset; + + if (ptep == NULL) + return -EFAULT; + pte = *ptep; + if (!pte_present(pte) || !(pte_val(pte) & _PAGE_USER)) + return -EFAULT; + pfn = pte_pfn(pte); + if (!page_is_ram(pfn)) + return -EFAULT; + + /* no highmem to worry about here */ + kaddr = pfn_to_kaddr(pfn); + memcpy(ret, kaddr + offset, nb); + return 0; +} + +static int read_user_stack_64(unsigned long __user *ptr, unsigned long *ret) +{ + if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned long) || + ((unsigned long)ptr & 7)) + return -EFAULT; + + pagefault_disable(); + if (!__get_user_inatomic(*ret, ptr)) { + pagefault_enable(); + return 0; + } + pagefault_enable(); + + return read_user_stack_slow(ptr, ret, 8); +} + +static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret) +{ + if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) || + ((unsigned long)ptr & 3)) + return -EFAULT; + + pagefault_disable(); + if (!__get_user_inatomic(*ret, ptr)) { + pagefault_enable(); + return 0; + } + pagefault_enable(); + + return read_user_stack_slow(ptr, ret, 4); +} + +static inline int valid_user_sp(unsigned long sp, int is_64) +{ + if (!sp || (sp & 7) || sp > (is_64 ? TASK_SIZE : 0x100000000UL) - 32) + return 0; + return 1; +} + +/* + * 64-bit user processes use the same stack frame for RT and non-RT signals. + */ +struct signal_frame_64 { + char dummy[__SIGNAL_FRAMESIZE]; + struct ucontext uc; + unsigned long unused[2]; + unsigned int tramp[6]; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + char abigap[288]; +}; + +static int is_sigreturn_64_address(unsigned long nip, unsigned long fp) +{ + if (nip == fp + offsetof(struct signal_frame_64, tramp)) + return 1; + if (vdso64_rt_sigtramp && current->mm->context.vdso_base && + nip == current->mm->context.vdso_base + vdso64_rt_sigtramp) + return 1; + return 0; +} + +/* + * Do some sanity checking on the signal frame pointed to by sp. + * We check the pinfo and puc pointers in the frame. + */ +static int sane_signal_64_frame(unsigned long sp) +{ + struct signal_frame_64 __user *sf; + unsigned long pinfo, puc; + + sf = (struct signal_frame_64 __user *) sp; + if (read_user_stack_64((unsigned long __user *) &sf->pinfo, &pinfo) || + read_user_stack_64((unsigned long __user *) &sf->puc, &puc)) + return 0; + return pinfo == (unsigned long) &sf->info && + puc == (unsigned long) &sf->uc; +} + +static void perf_callchain_user_64(struct perf_callchain_entry *entry, + struct pt_regs *regs) +{ + unsigned long sp, next_sp; + unsigned long next_ip; + unsigned long lr; + long level = 0; + struct signal_frame_64 __user *sigframe; + unsigned long __user *fp, *uregs; + + next_ip = regs->nip; + lr = regs->link; + sp = regs->gpr[1]; + perf_callchain_store(entry, next_ip); + + for (;;) { + fp = (unsigned long __user *) sp; + if (!valid_user_sp(sp, 1) || read_user_stack_64(fp, &next_sp)) + return; + if (level > 0 && read_user_stack_64(&fp[2], &next_ip)) + return; + + /* + * Note: the next_sp - sp >= signal frame size check + * is true when next_sp < sp, which can happen when + * transitioning from an alternate signal stack to the + * normal stack. + */ + if (next_sp - sp >= sizeof(struct signal_frame_64) && + (is_sigreturn_64_address(next_ip, sp) || + (level <= 1 && is_sigreturn_64_address(lr, sp))) && + sane_signal_64_frame(sp)) { + /* + * This looks like an signal frame + */ + sigframe = (struct signal_frame_64 __user *) sp; + uregs = sigframe->uc.uc_mcontext.gp_regs; + if (read_user_stack_64(&uregs[PT_NIP], &next_ip) || + read_user_stack_64(&uregs[PT_LNK], &lr) || + read_user_stack_64(&uregs[PT_R1], &sp)) + return; + level = 0; + perf_callchain_store(entry, PERF_CONTEXT_USER); + perf_callchain_store(entry, next_ip); + continue; + } + + if (level == 0) + next_ip = lr; + perf_callchain_store(entry, next_ip); + ++level; + sp = next_sp; + } +} + +static inline int current_is_64bit(void) +{ + /* + * We can't use test_thread_flag() here because we may be on an + * interrupt stack, and the thread flags don't get copied over + * from the thread_info on the main stack to the interrupt stack. + */ + return !test_ti_thread_flag(task_thread_info(current), TIF_32BIT); +} + +#else /* CONFIG_PPC64 */ +/* + * On 32-bit we just access the address and let hash_page create a + * HPTE if necessary, so there is no need to fall back to reading + * the page tables. Since this is called at interrupt level, + * do_page_fault() won't treat a DSI as a page fault. + */ +static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret) +{ + int rc; + + if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) || + ((unsigned long)ptr & 3)) + return -EFAULT; + + pagefault_disable(); + rc = __get_user_inatomic(*ret, ptr); + pagefault_enable(); + + return rc; +} + +static inline void perf_callchain_user_64(struct perf_callchain_entry *entry, + struct pt_regs *regs) +{ +} + +static inline int current_is_64bit(void) +{ + return 0; +} + +static inline int valid_user_sp(unsigned long sp, int is_64) +{ + if (!sp || (sp & 7) || sp > TASK_SIZE - 32) + return 0; + return 1; +} + +#define __SIGNAL_FRAMESIZE32 __SIGNAL_FRAMESIZE +#define sigcontext32 sigcontext +#define mcontext32 mcontext +#define ucontext32 ucontext +#define compat_siginfo_t struct siginfo + +#endif /* CONFIG_PPC64 */ + +/* + * Layout for non-RT signal frames + */ +struct signal_frame_32 { + char dummy[__SIGNAL_FRAMESIZE32]; + struct sigcontext32 sctx; + struct mcontext32 mctx; + int abigap[56]; +}; + +/* + * Layout for RT signal frames + */ +struct rt_signal_frame_32 { + char dummy[__SIGNAL_FRAMESIZE32 + 16]; + compat_siginfo_t info; + struct ucontext32 uc; + int abigap[56]; +}; + +static int is_sigreturn_32_address(unsigned int nip, unsigned int fp) +{ + if (nip == fp + offsetof(struct signal_frame_32, mctx.mc_pad)) + return 1; + if (vdso32_sigtramp && current->mm->context.vdso_base && + nip == current->mm->context.vdso_base + vdso32_sigtramp) + return 1; + return 0; +} + +static int is_rt_sigreturn_32_address(unsigned int nip, unsigned int fp) +{ + if (nip == fp + offsetof(struct rt_signal_frame_32, + uc.uc_mcontext.mc_pad)) + return 1; + if (vdso32_rt_sigtramp && current->mm->context.vdso_base && + nip == current->mm->context.vdso_base + vdso32_rt_sigtramp) + return 1; + return 0; +} + +static int sane_signal_32_frame(unsigned int sp) +{ + struct signal_frame_32 __user *sf; + unsigned int regs; + + sf = (struct signal_frame_32 __user *) (unsigned long) sp; + if (read_user_stack_32((unsigned int __user *) &sf->sctx.regs, ®s)) + return 0; + return regs == (unsigned long) &sf->mctx; +} + +static int sane_rt_signal_32_frame(unsigned int sp) +{ + struct rt_signal_frame_32 __user *sf; + unsigned int regs; + + sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp; + if (read_user_stack_32((unsigned int __user *) &sf->uc.uc_regs, ®s)) + return 0; + return regs == (unsigned long) &sf->uc.uc_mcontext; +} + +static unsigned int __user *signal_frame_32_regs(unsigned int sp, + unsigned int next_sp, unsigned int next_ip) +{ + struct mcontext32 __user *mctx = NULL; + struct signal_frame_32 __user *sf; + struct rt_signal_frame_32 __user *rt_sf; + + /* + * Note: the next_sp - sp >= signal frame size check + * is true when next_sp < sp, for example, when + * transitioning from an alternate signal stack to the + * normal stack. + */ + if (next_sp - sp >= sizeof(struct signal_frame_32) && + is_sigreturn_32_address(next_ip, sp) && + sane_signal_32_frame(sp)) { + sf = (struct signal_frame_32 __user *) (unsigned long) sp; + mctx = &sf->mctx; + } + + if (!mctx && next_sp - sp >= sizeof(struct rt_signal_frame_32) && + is_rt_sigreturn_32_address(next_ip, sp) && + sane_rt_signal_32_frame(sp)) { + rt_sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp; + mctx = &rt_sf->uc.uc_mcontext; + } + + if (!mctx) + return NULL; + return mctx->mc_gregs; +} + +static void perf_callchain_user_32(struct perf_callchain_entry *entry, + struct pt_regs *regs) +{ + unsigned int sp, next_sp; + unsigned int next_ip; + unsigned int lr; + long level = 0; + unsigned int __user *fp, *uregs; + + next_ip = regs->nip; + lr = regs->link; + sp = regs->gpr[1]; + perf_callchain_store(entry, next_ip); + + while (entry->nr < PERF_MAX_STACK_DEPTH) { + fp = (unsigned int __user *) (unsigned long) sp; + if (!valid_user_sp(sp, 0) || read_user_stack_32(fp, &next_sp)) + return; + if (level > 0 && read_user_stack_32(&fp[1], &next_ip)) + return; + + uregs = signal_frame_32_regs(sp, next_sp, next_ip); + if (!uregs && level <= 1) + uregs = signal_frame_32_regs(sp, next_sp, lr); + if (uregs) { + /* + * This looks like an signal frame, so restart + * the stack trace with the values in it. + */ + if (read_user_stack_32(&uregs[PT_NIP], &next_ip) || + read_user_stack_32(&uregs[PT_LNK], &lr) || + read_user_stack_32(&uregs[PT_R1], &sp)) + return; + level = 0; + perf_callchain_store(entry, PERF_CONTEXT_USER); + perf_callchain_store(entry, next_ip); + continue; + } + + if (level == 0) + next_ip = lr; + perf_callchain_store(entry, next_ip); + ++level; + sp = next_sp; + } +} + +void +perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) +{ + if (current_is_64bit()) + perf_callchain_user_64(entry, regs); + else + perf_callchain_user_32(entry, regs); +} diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c new file mode 100644 index 0000000..64483fd --- /dev/null +++ b/arch/powerpc/perf/core-book3s.c @@ -0,0 +1,1438 @@ +/* + * Performance event support - powerpc architecture code + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cpu_hw_events { + int n_events; + int n_percpu; + int disabled; + int n_added; + int n_limited; + u8 pmcs_enabled; + struct perf_event *event[MAX_HWEVENTS]; + u64 events[MAX_HWEVENTS]; + unsigned int flags[MAX_HWEVENTS]; + unsigned long mmcr[3]; + struct perf_event *limited_counter[MAX_LIMITED_HWCOUNTERS]; + u8 limited_hwidx[MAX_LIMITED_HWCOUNTERS]; + u64 alternatives[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; + unsigned long amasks[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; + unsigned long avalues[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; + + unsigned int group_flag; + int n_txn_start; +}; +DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); + +struct power_pmu *ppmu; + +/* + * Normally, to ignore kernel events we set the FCS (freeze counters + * in supervisor mode) bit in MMCR0, but if the kernel runs with the + * hypervisor bit set in the MSR, or if we are running on a processor + * where the hypervisor bit is forced to 1 (as on Apple G5 processors), + * then we need to use the FCHV bit to ignore kernel events. + */ +static unsigned int freeze_events_kernel = MMCR0_FCS; + +/* + * 32-bit doesn't have MMCRA but does have an MMCR2, + * and a few other names are different. + */ +#ifdef CONFIG_PPC32 + +#define MMCR0_FCHV 0 +#define MMCR0_PMCjCE MMCR0_PMCnCE + +#define SPRN_MMCRA SPRN_MMCR2 +#define MMCRA_SAMPLE_ENABLE 0 + +static inline unsigned long perf_ip_adjust(struct pt_regs *regs) +{ + return 0; +} +static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) { } +static inline u32 perf_get_misc_flags(struct pt_regs *regs) +{ + return 0; +} +static inline void perf_read_regs(struct pt_regs *regs) { } +static inline int perf_intr_is_nmi(struct pt_regs *regs) +{ + return 0; +} + +#endif /* CONFIG_PPC32 */ + +/* + * Things that are specific to 64-bit implementations. + */ +#ifdef CONFIG_PPC64 + +static inline unsigned long perf_ip_adjust(struct pt_regs *regs) +{ + unsigned long mmcra = regs->dsisr; + + if ((mmcra & MMCRA_SAMPLE_ENABLE) && !(ppmu->flags & PPMU_ALT_SIPR)) { + unsigned long slot = (mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT; + if (slot > 1) + return 4 * (slot - 1); + } + return 0; +} + +/* + * The user wants a data address recorded. + * If we're not doing instruction sampling, give them the SDAR + * (sampled data address). If we are doing instruction sampling, then + * only give them the SDAR if it corresponds to the instruction + * pointed to by SIAR; this is indicated by the [POWER6_]MMCRA_SDSYNC + * bit in MMCRA. + */ +static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) +{ + unsigned long mmcra = regs->dsisr; + unsigned long sdsync = (ppmu->flags & PPMU_ALT_SIPR) ? + POWER6_MMCRA_SDSYNC : MMCRA_SDSYNC; + + if (!(mmcra & MMCRA_SAMPLE_ENABLE) || (mmcra & sdsync)) + *addrp = mfspr(SPRN_SDAR); +} + +static inline u32 perf_get_misc_flags(struct pt_regs *regs) +{ + unsigned long mmcra = regs->dsisr; + unsigned long sihv = MMCRA_SIHV; + unsigned long sipr = MMCRA_SIPR; + + if (TRAP(regs) != 0xf00) + return 0; /* not a PMU interrupt */ + + if (ppmu->flags & PPMU_ALT_SIPR) { + sihv = POWER6_MMCRA_SIHV; + sipr = POWER6_MMCRA_SIPR; + } + + /* PR has priority over HV, so order below is important */ + if (mmcra & sipr) + return PERF_RECORD_MISC_USER; + if ((mmcra & sihv) && (freeze_events_kernel != MMCR0_FCHV)) + return PERF_RECORD_MISC_HYPERVISOR; + return PERF_RECORD_MISC_KERNEL; +} + +/* + * Overload regs->dsisr to store MMCRA so we only need to read it once + * on each interrupt. + */ +static inline void perf_read_regs(struct pt_regs *regs) +{ + regs->dsisr = mfspr(SPRN_MMCRA); +} + +/* + * If interrupts were soft-disabled when a PMU interrupt occurs, treat + * it as an NMI. + */ +static inline int perf_intr_is_nmi(struct pt_regs *regs) +{ + return !regs->softe; +} + +#endif /* CONFIG_PPC64 */ + +static void perf_event_interrupt(struct pt_regs *regs); + +void perf_event_print_debug(void) +{ +} + +/* + * Read one performance monitor counter (PMC). + */ +static unsigned long read_pmc(int idx) +{ + unsigned long val; + + switch (idx) { + case 1: + val = mfspr(SPRN_PMC1); + break; + case 2: + val = mfspr(SPRN_PMC2); + break; + case 3: + val = mfspr(SPRN_PMC3); + break; + case 4: + val = mfspr(SPRN_PMC4); + break; + case 5: + val = mfspr(SPRN_PMC5); + break; + case 6: + val = mfspr(SPRN_PMC6); + break; +#ifdef CONFIG_PPC64 + case 7: + val = mfspr(SPRN_PMC7); + break; + case 8: + val = mfspr(SPRN_PMC8); + break; +#endif /* CONFIG_PPC64 */ + default: + printk(KERN_ERR "oops trying to read PMC%d\n", idx); + val = 0; + } + return val; +} + +/* + * Write one PMC. + */ +static void write_pmc(int idx, unsigned long val) +{ + switch (idx) { + case 1: + mtspr(SPRN_PMC1, val); + break; + case 2: + mtspr(SPRN_PMC2, val); + break; + case 3: + mtspr(SPRN_PMC3, val); + break; + case 4: + mtspr(SPRN_PMC4, val); + break; + case 5: + mtspr(SPRN_PMC5, val); + break; + case 6: + mtspr(SPRN_PMC6, val); + break; +#ifdef CONFIG_PPC64 + case 7: + mtspr(SPRN_PMC7, val); + break; + case 8: + mtspr(SPRN_PMC8, val); + break; +#endif /* CONFIG_PPC64 */ + default: + printk(KERN_ERR "oops trying to write PMC%d\n", idx); + } +} + +/* + * Check if a set of events can all go on the PMU at once. + * If they can't, this will look at alternative codes for the events + * and see if any combination of alternative codes is feasible. + * The feasible set is returned in event_id[]. + */ +static int power_check_constraints(struct cpu_hw_events *cpuhw, + u64 event_id[], unsigned int cflags[], + int n_ev) +{ + unsigned long mask, value, nv; + unsigned long smasks[MAX_HWEVENTS], svalues[MAX_HWEVENTS]; + int n_alt[MAX_HWEVENTS], choice[MAX_HWEVENTS]; + int i, j; + unsigned long addf = ppmu->add_fields; + unsigned long tadd = ppmu->test_adder; + + if (n_ev > ppmu->n_counter) + return -1; + + /* First see if the events will go on as-is */ + for (i = 0; i < n_ev; ++i) { + if ((cflags[i] & PPMU_LIMITED_PMC_REQD) + && !ppmu->limited_pmc_event(event_id[i])) { + ppmu->get_alternatives(event_id[i], cflags[i], + cpuhw->alternatives[i]); + event_id[i] = cpuhw->alternatives[i][0]; + } + if (ppmu->get_constraint(event_id[i], &cpuhw->amasks[i][0], + &cpuhw->avalues[i][0])) + return -1; + } + value = mask = 0; + for (i = 0; i < n_ev; ++i) { + nv = (value | cpuhw->avalues[i][0]) + + (value & cpuhw->avalues[i][0] & addf); + if ((((nv + tadd) ^ value) & mask) != 0 || + (((nv + tadd) ^ cpuhw->avalues[i][0]) & + cpuhw->amasks[i][0]) != 0) + break; + value = nv; + mask |= cpuhw->amasks[i][0]; + } + if (i == n_ev) + return 0; /* all OK */ + + /* doesn't work, gather alternatives... */ + if (!ppmu->get_alternatives) + return -1; + for (i = 0; i < n_ev; ++i) { + choice[i] = 0; + n_alt[i] = ppmu->get_alternatives(event_id[i], cflags[i], + cpuhw->alternatives[i]); + for (j = 1; j < n_alt[i]; ++j) + ppmu->get_constraint(cpuhw->alternatives[i][j], + &cpuhw->amasks[i][j], + &cpuhw->avalues[i][j]); + } + + /* enumerate all possibilities and see if any will work */ + i = 0; + j = -1; + value = mask = nv = 0; + while (i < n_ev) { + if (j >= 0) { + /* we're backtracking, restore context */ + value = svalues[i]; + mask = smasks[i]; + j = choice[i]; + } + /* + * See if any alternative k for event_id i, + * where k > j, will satisfy the constraints. + */ + while (++j < n_alt[i]) { + nv = (value | cpuhw->avalues[i][j]) + + (value & cpuhw->avalues[i][j] & addf); + if ((((nv + tadd) ^ value) & mask) == 0 && + (((nv + tadd) ^ cpuhw->avalues[i][j]) + & cpuhw->amasks[i][j]) == 0) + break; + } + if (j >= n_alt[i]) { + /* + * No feasible alternative, backtrack + * to event_id i-1 and continue enumerating its + * alternatives from where we got up to. + */ + if (--i < 0) + return -1; + } else { + /* + * Found a feasible alternative for event_id i, + * remember where we got up to with this event_id, + * go on to the next event_id, and start with + * the first alternative for it. + */ + choice[i] = j; + svalues[i] = value; + smasks[i] = mask; + value = nv; + mask |= cpuhw->amasks[i][j]; + ++i; + j = -1; + } + } + + /* OK, we have a feasible combination, tell the caller the solution */ + for (i = 0; i < n_ev; ++i) + event_id[i] = cpuhw->alternatives[i][choice[i]]; + return 0; +} + +/* + * Check if newly-added events have consistent settings for + * exclude_{user,kernel,hv} with each other and any previously + * added events. + */ +static int check_excludes(struct perf_event **ctrs, unsigned int cflags[], + int n_prev, int n_new) +{ + int eu = 0, ek = 0, eh = 0; + int i, n, first; + struct perf_event *event; + + n = n_prev + n_new; + if (n <= 1) + return 0; + + first = 1; + for (i = 0; i < n; ++i) { + if (cflags[i] & PPMU_LIMITED_PMC_OK) { + cflags[i] &= ~PPMU_LIMITED_PMC_REQD; + continue; + } + event = ctrs[i]; + if (first) { + eu = event->attr.exclude_user; + ek = event->attr.exclude_kernel; + eh = event->attr.exclude_hv; + first = 0; + } else if (event->attr.exclude_user != eu || + event->attr.exclude_kernel != ek || + event->attr.exclude_hv != eh) { + return -EAGAIN; + } + } + + if (eu || ek || eh) + for (i = 0; i < n; ++i) + if (cflags[i] & PPMU_LIMITED_PMC_OK) + cflags[i] |= PPMU_LIMITED_PMC_REQD; + + return 0; +} + +static u64 check_and_compute_delta(u64 prev, u64 val) +{ + u64 delta = (val - prev) & 0xfffffffful; + + /* + * POWER7 can roll back counter values, if the new value is smaller + * than the previous value it will cause the delta and the counter to + * have bogus values unless we rolled a counter over. If a coutner is + * rolled back, it will be smaller, but within 256, which is the maximum + * number of events to rollback at once. If we dectect a rollback + * return 0. This can lead to a small lack of precision in the + * counters. + */ + if (prev > val && (prev - val) < 256) + delta = 0; + + return delta; +} + +static void power_pmu_read(struct perf_event *event) +{ + s64 val, delta, prev; + + if (event->hw.state & PERF_HES_STOPPED) + return; + + if (!event->hw.idx) + return; + /* + * Performance monitor interrupts come even when interrupts + * are soft-disabled, as long as interrupts are hard-enabled. + * Therefore we treat them like NMIs. + */ + do { + prev = local64_read(&event->hw.prev_count); + barrier(); + val = read_pmc(event->hw.idx); + delta = check_and_compute_delta(prev, val); + if (!delta) + return; + } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); + + local64_add(delta, &event->count); + local64_sub(delta, &event->hw.period_left); +} + +/* + * On some machines, PMC5 and PMC6 can't be written, don't respect + * the freeze conditions, and don't generate interrupts. This tells + * us if `event' is using such a PMC. + */ +static int is_limited_pmc(int pmcnum) +{ + return (ppmu->flags & PPMU_LIMITED_PMC5_6) + && (pmcnum == 5 || pmcnum == 6); +} + +static void freeze_limited_counters(struct cpu_hw_events *cpuhw, + unsigned long pmc5, unsigned long pmc6) +{ + struct perf_event *event; + u64 val, prev, delta; + int i; + + for (i = 0; i < cpuhw->n_limited; ++i) { + event = cpuhw->limited_counter[i]; + if (!event->hw.idx) + continue; + val = (event->hw.idx == 5) ? pmc5 : pmc6; + prev = local64_read(&event->hw.prev_count); + event->hw.idx = 0; + delta = check_and_compute_delta(prev, val); + if (delta) + local64_add(delta, &event->count); + } +} + +static void thaw_limited_counters(struct cpu_hw_events *cpuhw, + unsigned long pmc5, unsigned long pmc6) +{ + struct perf_event *event; + u64 val, prev; + int i; + + for (i = 0; i < cpuhw->n_limited; ++i) { + event = cpuhw->limited_counter[i]; + event->hw.idx = cpuhw->limited_hwidx[i]; + val = (event->hw.idx == 5) ? pmc5 : pmc6; + prev = local64_read(&event->hw.prev_count); + if (check_and_compute_delta(prev, val)) + local64_set(&event->hw.prev_count, val); + perf_event_update_userpage(event); + } +} + +/* + * Since limited events don't respect the freeze conditions, we + * have to read them immediately after freezing or unfreezing the + * other events. We try to keep the values from the limited + * events as consistent as possible by keeping the delay (in + * cycles and instructions) between freezing/unfreezing and reading + * the limited events as small and consistent as possible. + * Therefore, if any limited events are in use, we read them + * both, and always in the same order, to minimize variability, + * and do it inside the same asm that writes MMCR0. + */ +static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0) +{ + unsigned long pmc5, pmc6; + + if (!cpuhw->n_limited) { + mtspr(SPRN_MMCR0, mmcr0); + return; + } + + /* + * Write MMCR0, then read PMC5 and PMC6 immediately. + * To ensure we don't get a performance monitor interrupt + * between writing MMCR0 and freezing/thawing the limited + * events, we first write MMCR0 with the event overflow + * interrupt enable bits turned off. + */ + asm volatile("mtspr %3,%2; mfspr %0,%4; mfspr %1,%5" + : "=&r" (pmc5), "=&r" (pmc6) + : "r" (mmcr0 & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)), + "i" (SPRN_MMCR0), + "i" (SPRN_PMC5), "i" (SPRN_PMC6)); + + if (mmcr0 & MMCR0_FC) + freeze_limited_counters(cpuhw, pmc5, pmc6); + else + thaw_limited_counters(cpuhw, pmc5, pmc6); + + /* + * Write the full MMCR0 including the event overflow interrupt + * enable bits, if necessary. + */ + if (mmcr0 & (MMCR0_PMC1CE | MMCR0_PMCjCE)) + mtspr(SPRN_MMCR0, mmcr0); +} + +/* + * Disable all events to prevent PMU interrupts and to allow + * events to be added or removed. + */ +static void power_pmu_disable(struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw; + unsigned long flags; + + if (!ppmu) + return; + local_irq_save(flags); + cpuhw = &__get_cpu_var(cpu_hw_events); + + if (!cpuhw->disabled) { + cpuhw->disabled = 1; + cpuhw->n_added = 0; + + /* + * Check if we ever enabled the PMU on this cpu. + */ + if (!cpuhw->pmcs_enabled) { + ppc_enable_pmcs(); + cpuhw->pmcs_enabled = 1; + } + + /* + * Disable instruction sampling if it was enabled + */ + if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { + mtspr(SPRN_MMCRA, + cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); + mb(); + } + + /* + * Set the 'freeze counters' bit. + * The barrier is to make sure the mtspr has been + * executed and the PMU has frozen the events + * before we return. + */ + write_mmcr0(cpuhw, mfspr(SPRN_MMCR0) | MMCR0_FC); + mb(); + } + local_irq_restore(flags); +} + +/* + * Re-enable all events if disable == 0. + * If we were previously disabled and events were added, then + * put the new config on the PMU. + */ +static void power_pmu_enable(struct pmu *pmu) +{ + struct perf_event *event; + struct cpu_hw_events *cpuhw; + unsigned long flags; + long i; + unsigned long val; + s64 left; + unsigned int hwc_index[MAX_HWEVENTS]; + int n_lim; + int idx; + + if (!ppmu) + return; + local_irq_save(flags); + cpuhw = &__get_cpu_var(cpu_hw_events); + if (!cpuhw->disabled) { + local_irq_restore(flags); + return; + } + cpuhw->disabled = 0; + + /* + * If we didn't change anything, or only removed events, + * no need to recalculate MMCR* settings and reset the PMCs. + * Just reenable the PMU with the current MMCR* settings + * (possibly updated for removal of events). + */ + if (!cpuhw->n_added) { + mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); + mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); + if (cpuhw->n_events == 0) + ppc_set_pmu_inuse(0); + goto out_enable; + } + + /* + * Compute MMCR* values for the new set of events + */ + if (ppmu->compute_mmcr(cpuhw->events, cpuhw->n_events, hwc_index, + cpuhw->mmcr)) { + /* shouldn't ever get here */ + printk(KERN_ERR "oops compute_mmcr failed\n"); + goto out; + } + + /* + * Add in MMCR0 freeze bits corresponding to the + * attr.exclude_* bits for the first event. + * We have already checked that all events have the + * same values for these bits as the first event. + */ + event = cpuhw->event[0]; + if (event->attr.exclude_user) + cpuhw->mmcr[0] |= MMCR0_FCP; + if (event->attr.exclude_kernel) + cpuhw->mmcr[0] |= freeze_events_kernel; + if (event->attr.exclude_hv) + cpuhw->mmcr[0] |= MMCR0_FCHV; + + /* + * Write the new configuration to MMCR* with the freeze + * bit set and set the hardware events to their initial values. + * Then unfreeze the events. + */ + ppc_set_pmu_inuse(1); + mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); + mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); + mtspr(SPRN_MMCR0, (cpuhw->mmcr[0] & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)) + | MMCR0_FC); + + /* + * Read off any pre-existing events that need to move + * to another PMC. + */ + for (i = 0; i < cpuhw->n_events; ++i) { + event = cpuhw->event[i]; + if (event->hw.idx && event->hw.idx != hwc_index[i] + 1) { + power_pmu_read(event); + write_pmc(event->hw.idx, 0); + event->hw.idx = 0; + } + } + + /* + * Initialize the PMCs for all the new and moved events. + */ + cpuhw->n_limited = n_lim = 0; + for (i = 0; i < cpuhw->n_events; ++i) { + event = cpuhw->event[i]; + if (event->hw.idx) + continue; + idx = hwc_index[i] + 1; + if (is_limited_pmc(idx)) { + cpuhw->limited_counter[n_lim] = event; + cpuhw->limited_hwidx[n_lim] = idx; + ++n_lim; + continue; + } + val = 0; + if (event->hw.sample_period) { + left = local64_read(&event->hw.period_left); + if (left < 0x80000000L) + val = 0x80000000L - left; + } + local64_set(&event->hw.prev_count, val); + event->hw.idx = idx; + if (event->hw.state & PERF_HES_STOPPED) + val = 0; + write_pmc(idx, val); + perf_event_update_userpage(event); + } + cpuhw->n_limited = n_lim; + cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; + + out_enable: + mb(); + write_mmcr0(cpuhw, cpuhw->mmcr[0]); + + /* + * Enable instruction sampling if necessary + */ + if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { + mb(); + mtspr(SPRN_MMCRA, cpuhw->mmcr[2]); + } + + out: + local_irq_restore(flags); +} + +static int collect_events(struct perf_event *group, int max_count, + struct perf_event *ctrs[], u64 *events, + unsigned int *flags) +{ + int n = 0; + struct perf_event *event; + + if (!is_software_event(group)) { + if (n >= max_count) + return -1; + ctrs[n] = group; + flags[n] = group->hw.event_base; + events[n++] = group->hw.config; + } + list_for_each_entry(event, &group->sibling_list, group_entry) { + if (!is_software_event(event) && + event->state != PERF_EVENT_STATE_OFF) { + if (n >= max_count) + return -1; + ctrs[n] = event; + flags[n] = event->hw.event_base; + events[n++] = event->hw.config; + } + } + return n; +} + +/* + * Add a event to the PMU. + * If all events are not already frozen, then we disable and + * re-enable the PMU in order to get hw_perf_enable to do the + * actual work of reconfiguring the PMU. + */ +static int power_pmu_add(struct perf_event *event, int ef_flags) +{ + struct cpu_hw_events *cpuhw; + unsigned long flags; + int n0; + int ret = -EAGAIN; + + local_irq_save(flags); + perf_pmu_disable(event->pmu); + + /* + * Add the event to the list (if there is room) + * and check whether the total set is still feasible. + */ + cpuhw = &__get_cpu_var(cpu_hw_events); + n0 = cpuhw->n_events; + if (n0 >= ppmu->n_counter) + goto out; + cpuhw->event[n0] = event; + cpuhw->events[n0] = event->hw.config; + cpuhw->flags[n0] = event->hw.event_base; + + if (!(ef_flags & PERF_EF_START)) + event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + + /* + * If group events scheduling transaction was started, + * skip the schedulability test here, it will be performed + * at commit time(->commit_txn) as a whole + */ + if (cpuhw->group_flag & PERF_EVENT_TXN) + goto nocheck; + + if (check_excludes(cpuhw->event, cpuhw->flags, n0, 1)) + goto out; + if (power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n0 + 1)) + goto out; + event->hw.config = cpuhw->events[n0]; + +nocheck: + ++cpuhw->n_events; + ++cpuhw->n_added; + + ret = 0; + out: + perf_pmu_enable(event->pmu); + local_irq_restore(flags); + return ret; +} + +/* + * Remove a event from the PMU. + */ +static void power_pmu_del(struct perf_event *event, int ef_flags) +{ + struct cpu_hw_events *cpuhw; + long i; + unsigned long flags; + + local_irq_save(flags); + perf_pmu_disable(event->pmu); + + power_pmu_read(event); + + cpuhw = &__get_cpu_var(cpu_hw_events); + for (i = 0; i < cpuhw->n_events; ++i) { + if (event == cpuhw->event[i]) { + while (++i < cpuhw->n_events) { + cpuhw->event[i-1] = cpuhw->event[i]; + cpuhw->events[i-1] = cpuhw->events[i]; + cpuhw->flags[i-1] = cpuhw->flags[i]; + } + --cpuhw->n_events; + ppmu->disable_pmc(event->hw.idx - 1, cpuhw->mmcr); + if (event->hw.idx) { + write_pmc(event->hw.idx, 0); + event->hw.idx = 0; + } + perf_event_update_userpage(event); + break; + } + } + for (i = 0; i < cpuhw->n_limited; ++i) + if (event == cpuhw->limited_counter[i]) + break; + if (i < cpuhw->n_limited) { + while (++i < cpuhw->n_limited) { + cpuhw->limited_counter[i-1] = cpuhw->limited_counter[i]; + cpuhw->limited_hwidx[i-1] = cpuhw->limited_hwidx[i]; + } + --cpuhw->n_limited; + } + if (cpuhw->n_events == 0) { + /* disable exceptions if no events are running */ + cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); + } + + perf_pmu_enable(event->pmu); + local_irq_restore(flags); +} + +/* + * POWER-PMU does not support disabling individual counters, hence + * program their cycle counter to their max value and ignore the interrupts. + */ + +static void power_pmu_start(struct perf_event *event, int ef_flags) +{ + unsigned long flags; + s64 left; + unsigned long val; + + if (!event->hw.idx || !event->hw.sample_period) + return; + + if (!(event->hw.state & PERF_HES_STOPPED)) + return; + + if (ef_flags & PERF_EF_RELOAD) + WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); + + local_irq_save(flags); + perf_pmu_disable(event->pmu); + + event->hw.state = 0; + left = local64_read(&event->hw.period_left); + + val = 0; + if (left < 0x80000000L) + val = 0x80000000L - left; + + write_pmc(event->hw.idx, val); + + perf_event_update_userpage(event); + perf_pmu_enable(event->pmu); + local_irq_restore(flags); +} + +static void power_pmu_stop(struct perf_event *event, int ef_flags) +{ + unsigned long flags; + + if (!event->hw.idx || !event->hw.sample_period) + return; + + if (event->hw.state & PERF_HES_STOPPED) + return; + + local_irq_save(flags); + perf_pmu_disable(event->pmu); + + power_pmu_read(event); + event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; + write_pmc(event->hw.idx, 0); + + perf_event_update_userpage(event); + perf_pmu_enable(event->pmu); + local_irq_restore(flags); +} + +/* + * Start group events scheduling transaction + * Set the flag to make pmu::enable() not perform the + * schedulability test, it will be performed at commit time + */ +void power_pmu_start_txn(struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + + perf_pmu_disable(pmu); + cpuhw->group_flag |= PERF_EVENT_TXN; + cpuhw->n_txn_start = cpuhw->n_events; +} + +/* + * Stop group events scheduling transaction + * Clear the flag and pmu::enable() will perform the + * schedulability test. + */ +void power_pmu_cancel_txn(struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + + cpuhw->group_flag &= ~PERF_EVENT_TXN; + perf_pmu_enable(pmu); +} + +/* + * Commit group events scheduling transaction + * Perform the group schedulability test as a whole + * Return 0 if success + */ +int power_pmu_commit_txn(struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw; + long i, n; + + if (!ppmu) + return -EAGAIN; + cpuhw = &__get_cpu_var(cpu_hw_events); + n = cpuhw->n_events; + if (check_excludes(cpuhw->event, cpuhw->flags, 0, n)) + return -EAGAIN; + i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n); + if (i < 0) + return -EAGAIN; + + for (i = cpuhw->n_txn_start; i < n; ++i) + cpuhw->event[i]->hw.config = cpuhw->events[i]; + + cpuhw->group_flag &= ~PERF_EVENT_TXN; + perf_pmu_enable(pmu); + return 0; +} + +/* + * Return 1 if we might be able to put event on a limited PMC, + * or 0 if not. + * A event can only go on a limited PMC if it counts something + * that a limited PMC can count, doesn't require interrupts, and + * doesn't exclude any processor mode. + */ +static int can_go_on_limited_pmc(struct perf_event *event, u64 ev, + unsigned int flags) +{ + int n; + u64 alt[MAX_EVENT_ALTERNATIVES]; + + if (event->attr.exclude_user + || event->attr.exclude_kernel + || event->attr.exclude_hv + || event->attr.sample_period) + return 0; + + if (ppmu->limited_pmc_event(ev)) + return 1; + + /* + * The requested event_id isn't on a limited PMC already; + * see if any alternative code goes on a limited PMC. + */ + if (!ppmu->get_alternatives) + return 0; + + flags |= PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD; + n = ppmu->get_alternatives(ev, flags, alt); + + return n > 0; +} + +/* + * Find an alternative event_id that goes on a normal PMC, if possible, + * and return the event_id code, or 0 if there is no such alternative. + * (Note: event_id code 0 is "don't count" on all machines.) + */ +static u64 normal_pmc_alternative(u64 ev, unsigned long flags) +{ + u64 alt[MAX_EVENT_ALTERNATIVES]; + int n; + + flags &= ~(PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD); + n = ppmu->get_alternatives(ev, flags, alt); + if (!n) + return 0; + return alt[0]; +} + +/* Number of perf_events counting hardware events */ +static atomic_t num_events; +/* Used to avoid races in calling reserve/release_pmc_hardware */ +static DEFINE_MUTEX(pmc_reserve_mutex); + +/* + * Release the PMU if this is the last perf_event. + */ +static void hw_perf_event_destroy(struct perf_event *event) +{ + if (!atomic_add_unless(&num_events, -1, 1)) { + mutex_lock(&pmc_reserve_mutex); + if (atomic_dec_return(&num_events) == 0) + release_pmc_hardware(); + mutex_unlock(&pmc_reserve_mutex); + } +} + +/* + * Translate a generic cache event_id config to a raw event_id code. + */ +static int hw_perf_cache_event(u64 config, u64 *eventp) +{ + unsigned long type, op, result; + int ev; + + if (!ppmu->cache_events) + return -EINVAL; + + /* unpack config */ + type = config & 0xff; + op = (config >> 8) & 0xff; + result = (config >> 16) & 0xff; + + if (type >= PERF_COUNT_HW_CACHE_MAX || + op >= PERF_COUNT_HW_CACHE_OP_MAX || + result >= PERF_COUNT_HW_CACHE_RESULT_MAX) + return -EINVAL; + + ev = (*ppmu->cache_events)[type][op][result]; + if (ev == 0) + return -EOPNOTSUPP; + if (ev == -1) + return -EINVAL; + *eventp = ev; + return 0; +} + +static int power_pmu_event_init(struct perf_event *event) +{ + u64 ev; + unsigned long flags; + struct perf_event *ctrs[MAX_HWEVENTS]; + u64 events[MAX_HWEVENTS]; + unsigned int cflags[MAX_HWEVENTS]; + int n; + int err; + struct cpu_hw_events *cpuhw; + + if (!ppmu) + return -ENOENT; + + switch (event->attr.type) { + case PERF_TYPE_HARDWARE: + ev = event->attr.config; + if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0) + return -EOPNOTSUPP; + ev = ppmu->generic_events[ev]; + break; + case PERF_TYPE_HW_CACHE: + err = hw_perf_cache_event(event->attr.config, &ev); + if (err) + return err; + break; + case PERF_TYPE_RAW: + ev = event->attr.config; + break; + default: + return -ENOENT; + } + + event->hw.config_base = ev; + event->hw.idx = 0; + + /* + * If we are not running on a hypervisor, force the + * exclude_hv bit to 0 so that we don't care what + * the user set it to. + */ + if (!firmware_has_feature(FW_FEATURE_LPAR)) + event->attr.exclude_hv = 0; + + /* + * If this is a per-task event, then we can use + * PM_RUN_* events interchangeably with their non RUN_* + * equivalents, e.g. PM_RUN_CYC instead of PM_CYC. + * XXX we should check if the task is an idle task. + */ + flags = 0; + if (event->attach_state & PERF_ATTACH_TASK) + flags |= PPMU_ONLY_COUNT_RUN; + + /* + * If this machine has limited events, check whether this + * event_id could go on a limited event. + */ + if (ppmu->flags & PPMU_LIMITED_PMC5_6) { + if (can_go_on_limited_pmc(event, ev, flags)) { + flags |= PPMU_LIMITED_PMC_OK; + } else if (ppmu->limited_pmc_event(ev)) { + /* + * The requested event_id is on a limited PMC, + * but we can't use a limited PMC; see if any + * alternative goes on a normal PMC. + */ + ev = normal_pmc_alternative(ev, flags); + if (!ev) + return -EINVAL; + } + } + + /* + * If this is in a group, check if it can go on with all the + * other hardware events in the group. We assume the event + * hasn't been linked into its leader's sibling list at this point. + */ + n = 0; + if (event->group_leader != event) { + n = collect_events(event->group_leader, ppmu->n_counter - 1, + ctrs, events, cflags); + if (n < 0) + return -EINVAL; + } + events[n] = ev; + ctrs[n] = event; + cflags[n] = flags; + if (check_excludes(ctrs, cflags, n, 1)) + return -EINVAL; + + cpuhw = &get_cpu_var(cpu_hw_events); + err = power_check_constraints(cpuhw, events, cflags, n + 1); + put_cpu_var(cpu_hw_events); + if (err) + return -EINVAL; + + event->hw.config = events[n]; + event->hw.event_base = cflags[n]; + event->hw.last_period = event->hw.sample_period; + local64_set(&event->hw.period_left, event->hw.last_period); + + /* + * See if we need to reserve the PMU. + * If no events are currently in use, then we have to take a + * mutex to ensure that we don't race with another task doing + * reserve_pmc_hardware or release_pmc_hardware. + */ + err = 0; + if (!atomic_inc_not_zero(&num_events)) { + mutex_lock(&pmc_reserve_mutex); + if (atomic_read(&num_events) == 0 && + reserve_pmc_hardware(perf_event_interrupt)) + err = -EBUSY; + else + atomic_inc(&num_events); + mutex_unlock(&pmc_reserve_mutex); + } + event->destroy = hw_perf_event_destroy; + + return err; +} + +struct pmu power_pmu = { + .pmu_enable = power_pmu_enable, + .pmu_disable = power_pmu_disable, + .event_init = power_pmu_event_init, + .add = power_pmu_add, + .del = power_pmu_del, + .start = power_pmu_start, + .stop = power_pmu_stop, + .read = power_pmu_read, + .start_txn = power_pmu_start_txn, + .cancel_txn = power_pmu_cancel_txn, + .commit_txn = power_pmu_commit_txn, +}; + +/* + * A counter has overflowed; update its count and record + * things if requested. Note that interrupts are hard-disabled + * here so there is no possibility of being interrupted. + */ +static void record_and_restart(struct perf_event *event, unsigned long val, + struct pt_regs *regs) +{ + u64 period = event->hw.sample_period; + s64 prev, delta, left; + int record = 0; + + if (event->hw.state & PERF_HES_STOPPED) { + write_pmc(event->hw.idx, 0); + return; + } + + /* we don't have to worry about interrupts here */ + prev = local64_read(&event->hw.prev_count); + delta = check_and_compute_delta(prev, val); + local64_add(delta, &event->count); + + /* + * See if the total period for this event has expired, + * and update for the next period. + */ + val = 0; + left = local64_read(&event->hw.period_left) - delta; + if (period) { + if (left <= 0) { + left += period; + if (left <= 0) + left = period; + record = 1; + event->hw.last_period = event->hw.sample_period; + } + if (left < 0x80000000LL) + val = 0x80000000LL - left; + } + + write_pmc(event->hw.idx, val); + local64_set(&event->hw.prev_count, val); + local64_set(&event->hw.period_left, left); + perf_event_update_userpage(event); + + /* + * Finally record data if requested. + */ + if (record) { + struct perf_sample_data data; + + perf_sample_data_init(&data, ~0ULL); + data.period = event->hw.last_period; + + if (event->attr.sample_type & PERF_SAMPLE_ADDR) + perf_get_data_addr(regs, &data.addr); + + if (perf_event_overflow(event, &data, regs)) + power_pmu_stop(event, 0); + } +} + +/* + * Called from generic code to get the misc flags (i.e. processor mode) + * for an event_id. + */ +unsigned long perf_misc_flags(struct pt_regs *regs) +{ + u32 flags = perf_get_misc_flags(regs); + + if (flags) + return flags; + return user_mode(regs) ? PERF_RECORD_MISC_USER : + PERF_RECORD_MISC_KERNEL; +} + +/* + * Called from generic code to get the instruction pointer + * for an event_id. + */ +unsigned long perf_instruction_pointer(struct pt_regs *regs) +{ + unsigned long ip; + + if (TRAP(regs) != 0xf00) + return regs->nip; /* not a PMU interrupt */ + + ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs); + return ip; +} + +static bool pmc_overflow(unsigned long val) +{ + if ((int)val < 0) + return true; + + /* + * Events on POWER7 can roll back if a speculative event doesn't + * eventually complete. Unfortunately in some rare cases they will + * raise a performance monitor exception. We need to catch this to + * ensure we reset the PMC. In all cases the PMC will be 256 or less + * cycles from overflow. + * + * We only do this if the first pass fails to find any overflowing + * PMCs because a user might set a period of less than 256 and we + * don't want to mistakenly reset them. + */ + if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256)) + return true; + + return false; +} + +/* + * Performance monitor interrupt stuff + */ +static void perf_event_interrupt(struct pt_regs *regs) +{ + int i; + struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct perf_event *event; + unsigned long val; + int found = 0; + int nmi; + + if (cpuhw->n_limited) + freeze_limited_counters(cpuhw, mfspr(SPRN_PMC5), + mfspr(SPRN_PMC6)); + + perf_read_regs(regs); + + nmi = perf_intr_is_nmi(regs); + if (nmi) + nmi_enter(); + else + irq_enter(); + + for (i = 0; i < cpuhw->n_events; ++i) { + event = cpuhw->event[i]; + if (!event->hw.idx || is_limited_pmc(event->hw.idx)) + continue; + val = read_pmc(event->hw.idx); + if ((int)val < 0) { + /* event has overflowed */ + found = 1; + record_and_restart(event, val, regs); + } + } + + /* + * In case we didn't find and reset the event that caused + * the interrupt, scan all events and reset any that are + * negative, to avoid getting continual interrupts. + * Any that we processed in the previous loop will not be negative. + */ + if (!found) { + for (i = 0; i < ppmu->n_counter; ++i) { + if (is_limited_pmc(i + 1)) + continue; + val = read_pmc(i + 1); + if (pmc_overflow(val)) + write_pmc(i + 1, 0); + } + } + + /* + * Reset MMCR0 to its normal value. This will set PMXE and + * clear FC (freeze counters) and PMAO (perf mon alert occurred) + * and thus allow interrupts to occur again. + * XXX might want to use MSR.PM to keep the events frozen until + * we get back out of this interrupt. + */ + write_mmcr0(cpuhw, cpuhw->mmcr[0]); + + if (nmi) + nmi_exit(); + else + irq_exit(); +} + +static void power_pmu_setup(int cpu) +{ + struct cpu_hw_events *cpuhw = &per_cpu(cpu_hw_events, cpu); + + if (!ppmu) + return; + memset(cpuhw, 0, sizeof(*cpuhw)); + cpuhw->mmcr[0] = MMCR0_FC; +} + +static int __cpuinit +power_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) +{ + unsigned int cpu = (long)hcpu; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + power_pmu_setup(cpu); + break; + + default: + break; + } + + return NOTIFY_OK; +} + +int __cpuinit register_power_pmu(struct power_pmu *pmu) +{ + if (ppmu) + return -EBUSY; /* something's already registered */ + + ppmu = pmu; + pr_info("%s performance monitor hardware support registered\n", + pmu->name); + +#ifdef MSR_HV + /* + * Use FCHV to ignore kernel events if MSR.HV is set. + */ + if (mfmsr() & MSR_HV) + freeze_events_kernel = MMCR0_FCHV; +#endif /* CONFIG_PPC64 */ + + perf_pmu_register(&power_pmu, "cpu", PERF_TYPE_RAW); + perf_cpu_notifier(power_pmu_notifier); + + return 0; +} diff --git a/arch/powerpc/perf/core-fsl-emb.c b/arch/powerpc/perf/core-fsl-emb.c new file mode 100644 index 0000000..0a6d2a9 --- /dev/null +++ b/arch/powerpc/perf/core-fsl-emb.c @@ -0,0 +1,688 @@ +/* + * Performance event support - Freescale Embedded Performance Monitor + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * Copyright 2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cpu_hw_events { + int n_events; + int disabled; + u8 pmcs_enabled; + struct perf_event *event[MAX_HWEVENTS]; +}; +static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); + +static struct fsl_emb_pmu *ppmu; + +/* Number of perf_events counting hardware events */ +static atomic_t num_events; +/* Used to avoid races in calling reserve/release_pmc_hardware */ +static DEFINE_MUTEX(pmc_reserve_mutex); + +/* + * If interrupts were soft-disabled when a PMU interrupt occurs, treat + * it as an NMI. + */ +static inline int perf_intr_is_nmi(struct pt_regs *regs) +{ +#ifdef __powerpc64__ + return !regs->softe; +#else + return 0; +#endif +} + +static void perf_event_interrupt(struct pt_regs *regs); + +/* + * Read one performance monitor counter (PMC). + */ +static unsigned long read_pmc(int idx) +{ + unsigned long val; + + switch (idx) { + case 0: + val = mfpmr(PMRN_PMC0); + break; + case 1: + val = mfpmr(PMRN_PMC1); + break; + case 2: + val = mfpmr(PMRN_PMC2); + break; + case 3: + val = mfpmr(PMRN_PMC3); + break; + default: + printk(KERN_ERR "oops trying to read PMC%d\n", idx); + val = 0; + } + return val; +} + +/* + * Write one PMC. + */ +static void write_pmc(int idx, unsigned long val) +{ + switch (idx) { + case 0: + mtpmr(PMRN_PMC0, val); + break; + case 1: + mtpmr(PMRN_PMC1, val); + break; + case 2: + mtpmr(PMRN_PMC2, val); + break; + case 3: + mtpmr(PMRN_PMC3, val); + break; + default: + printk(KERN_ERR "oops trying to write PMC%d\n", idx); + } + + isync(); +} + +/* + * Write one local control A register + */ +static void write_pmlca(int idx, unsigned long val) +{ + switch (idx) { + case 0: + mtpmr(PMRN_PMLCA0, val); + break; + case 1: + mtpmr(PMRN_PMLCA1, val); + break; + case 2: + mtpmr(PMRN_PMLCA2, val); + break; + case 3: + mtpmr(PMRN_PMLCA3, val); + break; + default: + printk(KERN_ERR "oops trying to write PMLCA%d\n", idx); + } + + isync(); +} + +/* + * Write one local control B register + */ +static void write_pmlcb(int idx, unsigned long val) +{ + switch (idx) { + case 0: + mtpmr(PMRN_PMLCB0, val); + break; + case 1: + mtpmr(PMRN_PMLCB1, val); + break; + case 2: + mtpmr(PMRN_PMLCB2, val); + break; + case 3: + mtpmr(PMRN_PMLCB3, val); + break; + default: + printk(KERN_ERR "oops trying to write PMLCB%d\n", idx); + } + + isync(); +} + +static void fsl_emb_pmu_read(struct perf_event *event) +{ + s64 val, delta, prev; + + if (event->hw.state & PERF_HES_STOPPED) + return; + + /* + * Performance monitor interrupts come even when interrupts + * are soft-disabled, as long as interrupts are hard-enabled. + * Therefore we treat them like NMIs. + */ + do { + prev = local64_read(&event->hw.prev_count); + barrier(); + val = read_pmc(event->hw.idx); + } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); + + /* The counters are only 32 bits wide */ + delta = (val - prev) & 0xfffffffful; + local64_add(delta, &event->count); + local64_sub(delta, &event->hw.period_left); +} + +/* + * Disable all events to prevent PMU interrupts and to allow + * events to be added or removed. + */ +static void fsl_emb_pmu_disable(struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw; + unsigned long flags; + + local_irq_save(flags); + cpuhw = &__get_cpu_var(cpu_hw_events); + + if (!cpuhw->disabled) { + cpuhw->disabled = 1; + + /* + * Check if we ever enabled the PMU on this cpu. + */ + if (!cpuhw->pmcs_enabled) { + ppc_enable_pmcs(); + cpuhw->pmcs_enabled = 1; + } + + if (atomic_read(&num_events)) { + /* + * Set the 'freeze all counters' bit, and disable + * interrupts. The barrier is to make sure the + * mtpmr has been executed and the PMU has frozen + * the events before we return. + */ + + mtpmr(PMRN_PMGC0, PMGC0_FAC); + isync(); + } + } + local_irq_restore(flags); +} + +/* + * Re-enable all events if disable == 0. + * If we were previously disabled and events were added, then + * put the new config on the PMU. + */ +static void fsl_emb_pmu_enable(struct pmu *pmu) +{ + struct cpu_hw_events *cpuhw; + unsigned long flags; + + local_irq_save(flags); + cpuhw = &__get_cpu_var(cpu_hw_events); + if (!cpuhw->disabled) + goto out; + + cpuhw->disabled = 0; + ppc_set_pmu_inuse(cpuhw->n_events != 0); + + if (cpuhw->n_events > 0) { + mtpmr(PMRN_PMGC0, PMGC0_PMIE | PMGC0_FCECE); + isync(); + } + + out: + local_irq_restore(flags); +} + +static int collect_events(struct perf_event *group, int max_count, + struct perf_event *ctrs[]) +{ + int n = 0; + struct perf_event *event; + + if (!is_software_event(group)) { + if (n >= max_count) + return -1; + ctrs[n] = group; + n++; + } + list_for_each_entry(event, &group->sibling_list, group_entry) { + if (!is_software_event(event) && + event->state != PERF_EVENT_STATE_OFF) { + if (n >= max_count) + return -1; + ctrs[n] = event; + n++; + } + } + return n; +} + +/* context locked on entry */ +static int fsl_emb_pmu_add(struct perf_event *event, int flags) +{ + struct cpu_hw_events *cpuhw; + int ret = -EAGAIN; + int num_counters = ppmu->n_counter; + u64 val; + int i; + + perf_pmu_disable(event->pmu); + cpuhw = &get_cpu_var(cpu_hw_events); + + if (event->hw.config & FSL_EMB_EVENT_RESTRICTED) + num_counters = ppmu->n_restricted; + + /* + * Allocate counters from top-down, so that restricted-capable + * counters are kept free as long as possible. + */ + for (i = num_counters - 1; i >= 0; i--) { + if (cpuhw->event[i]) + continue; + + break; + } + + if (i < 0) + goto out; + + event->hw.idx = i; + cpuhw->event[i] = event; + ++cpuhw->n_events; + + val = 0; + if (event->hw.sample_period) { + s64 left = local64_read(&event->hw.period_left); + if (left < 0x80000000L) + val = 0x80000000L - left; + } + local64_set(&event->hw.prev_count, val); + + if (!(flags & PERF_EF_START)) { + event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + val = 0; + } + + write_pmc(i, val); + perf_event_update_userpage(event); + + write_pmlcb(i, event->hw.config >> 32); + write_pmlca(i, event->hw.config_base); + + ret = 0; + out: + put_cpu_var(cpu_hw_events); + perf_pmu_enable(event->pmu); + return ret; +} + +/* context locked on entry */ +static void fsl_emb_pmu_del(struct perf_event *event, int flags) +{ + struct cpu_hw_events *cpuhw; + int i = event->hw.idx; + + perf_pmu_disable(event->pmu); + if (i < 0) + goto out; + + fsl_emb_pmu_read(event); + + cpuhw = &get_cpu_var(cpu_hw_events); + + WARN_ON(event != cpuhw->event[event->hw.idx]); + + write_pmlca(i, 0); + write_pmlcb(i, 0); + write_pmc(i, 0); + + cpuhw->event[i] = NULL; + event->hw.idx = -1; + + /* + * TODO: if at least one restricted event exists, and we + * just freed up a non-restricted-capable counter, and + * there is a restricted-capable counter occupied by + * a non-restricted event, migrate that event to the + * vacated counter. + */ + + cpuhw->n_events--; + + out: + perf_pmu_enable(event->pmu); + put_cpu_var(cpu_hw_events); +} + +static void fsl_emb_pmu_start(struct perf_event *event, int ef_flags) +{ + unsigned long flags; + s64 left; + + if (event->hw.idx < 0 || !event->hw.sample_period) + return; + + if (!(event->hw.state & PERF_HES_STOPPED)) + return; + + if (ef_flags & PERF_EF_RELOAD) + WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); + + local_irq_save(flags); + perf_pmu_disable(event->pmu); + + event->hw.state = 0; + left = local64_read(&event->hw.period_left); + write_pmc(event->hw.idx, left); + + perf_event_update_userpage(event); + perf_pmu_enable(event->pmu); + local_irq_restore(flags); +} + +static void fsl_emb_pmu_stop(struct perf_event *event, int ef_flags) +{ + unsigned long flags; + + if (event->hw.idx < 0 || !event->hw.sample_period) + return; + + if (event->hw.state & PERF_HES_STOPPED) + return; + + local_irq_save(flags); + perf_pmu_disable(event->pmu); + + fsl_emb_pmu_read(event); + event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; + write_pmc(event->hw.idx, 0); + + perf_event_update_userpage(event); + perf_pmu_enable(event->pmu); + local_irq_restore(flags); +} + +/* + * Release the PMU if this is the last perf_event. + */ +static void hw_perf_event_destroy(struct perf_event *event) +{ + if (!atomic_add_unless(&num_events, -1, 1)) { + mutex_lock(&pmc_reserve_mutex); + if (atomic_dec_return(&num_events) == 0) + release_pmc_hardware(); + mutex_unlock(&pmc_reserve_mutex); + } +} + +/* + * Translate a generic cache event_id config to a raw event_id code. + */ +static int hw_perf_cache_event(u64 config, u64 *eventp) +{ + unsigned long type, op, result; + int ev; + + if (!ppmu->cache_events) + return -EINVAL; + + /* unpack config */ + type = config & 0xff; + op = (config >> 8) & 0xff; + result = (config >> 16) & 0xff; + + if (type >= PERF_COUNT_HW_CACHE_MAX || + op >= PERF_COUNT_HW_CACHE_OP_MAX || + result >= PERF_COUNT_HW_CACHE_RESULT_MAX) + return -EINVAL; + + ev = (*ppmu->cache_events)[type][op][result]; + if (ev == 0) + return -EOPNOTSUPP; + if (ev == -1) + return -EINVAL; + *eventp = ev; + return 0; +} + +static int fsl_emb_pmu_event_init(struct perf_event *event) +{ + u64 ev; + struct perf_event *events[MAX_HWEVENTS]; + int n; + int err; + int num_restricted; + int i; + + switch (event->attr.type) { + case PERF_TYPE_HARDWARE: + ev = event->attr.config; + if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0) + return -EOPNOTSUPP; + ev = ppmu->generic_events[ev]; + break; + + case PERF_TYPE_HW_CACHE: + err = hw_perf_cache_event(event->attr.config, &ev); + if (err) + return err; + break; + + case PERF_TYPE_RAW: + ev = event->attr.config; + break; + + default: + return -ENOENT; + } + + event->hw.config = ppmu->xlate_event(ev); + if (!(event->hw.config & FSL_EMB_EVENT_VALID)) + return -EINVAL; + + /* + * If this is in a group, check if it can go on with all the + * other hardware events in the group. We assume the event + * hasn't been linked into its leader's sibling list at this point. + */ + n = 0; + if (event->group_leader != event) { + n = collect_events(event->group_leader, + ppmu->n_counter - 1, events); + if (n < 0) + return -EINVAL; + } + + if (event->hw.config & FSL_EMB_EVENT_RESTRICTED) { + num_restricted = 0; + for (i = 0; i < n; i++) { + if (events[i]->hw.config & FSL_EMB_EVENT_RESTRICTED) + num_restricted++; + } + + if (num_restricted >= ppmu->n_restricted) + return -EINVAL; + } + + event->hw.idx = -1; + + event->hw.config_base = PMLCA_CE | PMLCA_FCM1 | + (u32)((ev << 16) & PMLCA_EVENT_MASK); + + if (event->attr.exclude_user) + event->hw.config_base |= PMLCA_FCU; + if (event->attr.exclude_kernel) + event->hw.config_base |= PMLCA_FCS; + if (event->attr.exclude_idle) + return -ENOTSUPP; + + event->hw.last_period = event->hw.sample_period; + local64_set(&event->hw.period_left, event->hw.last_period); + + /* + * See if we need to reserve the PMU. + * If no events are currently in use, then we have to take a + * mutex to ensure that we don't race with another task doing + * reserve_pmc_hardware or release_pmc_hardware. + */ + err = 0; + if (!atomic_inc_not_zero(&num_events)) { + mutex_lock(&pmc_reserve_mutex); + if (atomic_read(&num_events) == 0 && + reserve_pmc_hardware(perf_event_interrupt)) + err = -EBUSY; + else + atomic_inc(&num_events); + mutex_unlock(&pmc_reserve_mutex); + + mtpmr(PMRN_PMGC0, PMGC0_FAC); + isync(); + } + event->destroy = hw_perf_event_destroy; + + return err; +} + +static struct pmu fsl_emb_pmu = { + .pmu_enable = fsl_emb_pmu_enable, + .pmu_disable = fsl_emb_pmu_disable, + .event_init = fsl_emb_pmu_event_init, + .add = fsl_emb_pmu_add, + .del = fsl_emb_pmu_del, + .start = fsl_emb_pmu_start, + .stop = fsl_emb_pmu_stop, + .read = fsl_emb_pmu_read, +}; + +/* + * A counter has overflowed; update its count and record + * things if requested. Note that interrupts are hard-disabled + * here so there is no possibility of being interrupted. + */ +static void record_and_restart(struct perf_event *event, unsigned long val, + struct pt_regs *regs) +{ + u64 period = event->hw.sample_period; + s64 prev, delta, left; + int record = 0; + + if (event->hw.state & PERF_HES_STOPPED) { + write_pmc(event->hw.idx, 0); + return; + } + + /* we don't have to worry about interrupts here */ + prev = local64_read(&event->hw.prev_count); + delta = (val - prev) & 0xfffffffful; + local64_add(delta, &event->count); + + /* + * See if the total period for this event has expired, + * and update for the next period. + */ + val = 0; + left = local64_read(&event->hw.period_left) - delta; + if (period) { + if (left <= 0) { + left += period; + if (left <= 0) + left = period; + record = 1; + event->hw.last_period = event->hw.sample_period; + } + if (left < 0x80000000LL) + val = 0x80000000LL - left; + } + + write_pmc(event->hw.idx, val); + local64_set(&event->hw.prev_count, val); + local64_set(&event->hw.period_left, left); + perf_event_update_userpage(event); + + /* + * Finally record data if requested. + */ + if (record) { + struct perf_sample_data data; + + perf_sample_data_init(&data, 0); + data.period = event->hw.last_period; + + if (perf_event_overflow(event, &data, regs)) + fsl_emb_pmu_stop(event, 0); + } +} + +static void perf_event_interrupt(struct pt_regs *regs) +{ + int i; + struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct perf_event *event; + unsigned long val; + int found = 0; + int nmi; + + nmi = perf_intr_is_nmi(regs); + if (nmi) + nmi_enter(); + else + irq_enter(); + + for (i = 0; i < ppmu->n_counter; ++i) { + event = cpuhw->event[i]; + + val = read_pmc(i); + if ((int)val < 0) { + if (event) { + /* event has overflowed */ + found = 1; + record_and_restart(event, val, regs); + } else { + /* + * Disabled counter is negative, + * reset it just in case. + */ + write_pmc(i, 0); + } + } + } + + /* PMM will keep counters frozen until we return from the interrupt. */ + mtmsr(mfmsr() | MSR_PMM); + mtpmr(PMRN_PMGC0, PMGC0_PMIE | PMGC0_FCECE); + isync(); + + if (nmi) + nmi_exit(); + else + irq_exit(); +} + +void hw_perf_event_setup(int cpu) +{ + struct cpu_hw_events *cpuhw = &per_cpu(cpu_hw_events, cpu); + + memset(cpuhw, 0, sizeof(*cpuhw)); +} + +int register_fsl_emb_pmu(struct fsl_emb_pmu *pmu) +{ + if (ppmu) + return -EBUSY; /* something's already registered */ + + ppmu = pmu; + pr_info("%s performance monitor hardware support registered\n", + pmu->name); + + perf_pmu_register(&fsl_emb_pmu, "cpu", PERF_TYPE_RAW); + + return 0; +} diff --git a/arch/powerpc/perf/e500-pmu.c b/arch/powerpc/perf/e500-pmu.c new file mode 100644 index 0000000..cb2e294 --- /dev/null +++ b/arch/powerpc/perf/e500-pmu.c @@ -0,0 +1,134 @@ +/* + * Performance counter support for e500 family processors. + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * Copyright 2010 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 +#include +#include +#include + +/* + * Map of generic hardware event types to hardware events + * Zero if unsupported + */ +static int e500_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 1, + [PERF_COUNT_HW_INSTRUCTIONS] = 2, + [PERF_COUNT_HW_CACHE_MISSES] = 41, /* Data L1 cache reloads */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 12, + [PERF_COUNT_HW_BRANCH_MISSES] = 15, +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static int e500_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + /* + * D-cache misses are not split into read/write/prefetch; + * use raw event 41. + */ + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 27, 0 }, + [C(OP_WRITE)] = { 28, 0 }, + [C(OP_PREFETCH)] = { 29, 0 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 2, 60 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + /* + * Assuming LL means L2, it's not a good match for this model. + * It allocates only on L1 castout or explicit prefetch, and + * does not have separate read/write events (but it does have + * separate instruction/data events). + */ + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { 0, 0 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + /* + * There are data/instruction MMU misses, but that's a miss on + * the chip's internal level-one TLB which is probably not + * what the user wants. Instead, unified level-two TLB misses + * are reported here. + */ + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 26, 66 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 12, 15 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +static int num_events = 128; + +/* Upper half of event id is PMLCb, for threshold events */ +static u64 e500_xlate_event(u64 event_id) +{ + u32 event_low = (u32)event_id; + u64 ret; + + if (event_low >= num_events) + return 0; + + ret = FSL_EMB_EVENT_VALID; + + if (event_low >= 76 && event_low <= 81) { + ret |= FSL_EMB_EVENT_RESTRICTED; + ret |= event_id & + (FSL_EMB_EVENT_THRESHMUL | FSL_EMB_EVENT_THRESH); + } else if (event_id & + (FSL_EMB_EVENT_THRESHMUL | FSL_EMB_EVENT_THRESH)) { + /* Threshold requested on non-threshold event */ + return 0; + } + + return ret; +} + +static struct fsl_emb_pmu e500_pmu = { + .name = "e500 family", + .n_counter = 4, + .n_restricted = 2, + .xlate_event = e500_xlate_event, + .n_generic = ARRAY_SIZE(e500_generic_events), + .generic_events = e500_generic_events, + .cache_events = &e500_cache_events, +}; + +static int init_e500_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type) + return -ENODEV; + + if (!strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/e500mc")) + num_events = 256; + else if (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/e500")) + return -ENODEV; + + return register_fsl_emb_pmu(&e500_pmu); +} + +early_initcall(init_e500_pmu); diff --git a/arch/powerpc/perf/mpc7450-pmu.c b/arch/powerpc/perf/mpc7450-pmu.c new file mode 100644 index 0000000..fe21b51 --- /dev/null +++ b/arch/powerpc/perf/mpc7450-pmu.c @@ -0,0 +1,422 @@ +/* + * Performance counter support for MPC7450-family processors. + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include + +#define N_COUNTER 6 /* Number of hardware counters */ +#define MAX_ALT 3 /* Maximum number of event alternative codes */ + +/* + * Bits in event code for MPC7450 family + */ +#define PM_THRMULT_MSKS 0x40000 +#define PM_THRESH_SH 12 +#define PM_THRESH_MSK 0x3f +#define PM_PMC_SH 8 +#define PM_PMC_MSK 7 +#define PM_PMCSEL_MSK 0x7f + +/* + * Classify events according to how specific their PMC requirements are. + * Result is: + * 0: can go on any PMC + * 1: can go on PMCs 1-4 + * 2: can go on PMCs 1,2,4 + * 3: can go on PMCs 1 or 2 + * 4: can only go on one PMC + * -1: event code is invalid + */ +#define N_CLASSES 5 + +static int mpc7450_classify_event(u32 event) +{ + int pmc; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > N_COUNTER) + return -1; + return 4; + } + event &= PM_PMCSEL_MSK; + if (event <= 1) + return 0; + if (event <= 7) + return 1; + if (event <= 13) + return 2; + if (event <= 22) + return 3; + return -1; +} + +/* + * Events using threshold and possible threshold scale: + * code scale? name + * 11e N PM_INSTQ_EXCEED_CYC + * 11f N PM_ALTV_IQ_EXCEED_CYC + * 128 Y PM_DTLB_SEARCH_EXCEED_CYC + * 12b Y PM_LD_MISS_EXCEED_L1_CYC + * 220 N PM_CQ_EXCEED_CYC + * 30c N PM_GPR_RB_EXCEED_CYC + * 30d ? PM_FPR_IQ_EXCEED_CYC ? + * 311 Y PM_ITLB_SEARCH_EXCEED + * 410 N PM_GPR_IQ_EXCEED_CYC + */ + +/* + * Return use of threshold and threshold scale bits: + * 0 = uses neither, 1 = uses threshold, 2 = uses both + */ +static int mpc7450_threshold_use(u32 event) +{ + int pmc, sel; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + sel = event & PM_PMCSEL_MSK; + switch (pmc) { + case 1: + if (sel == 0x1e || sel == 0x1f) + return 1; + if (sel == 0x28 || sel == 0x2b) + return 2; + break; + case 2: + if (sel == 0x20) + return 1; + break; + case 3: + if (sel == 0xc || sel == 0xd) + return 1; + if (sel == 0x11) + return 2; + break; + case 4: + if (sel == 0x10) + return 1; + break; + } + return 0; +} + +/* + * Layout of constraint bits: + * 33222222222211111111110000000000 + * 10987654321098765432109876543210 + * |< >< > < > < ><><><><><><> + * TS TV G4 G3 G2P6P5P4P3P2P1 + * + * P1 - P6 + * 0 - 11: Count of events needing PMC1 .. PMC6 + * + * G2 + * 12 - 14: Count of events needing PMC1 or PMC2 + * + * G3 + * 16 - 18: Count of events needing PMC1, PMC2 or PMC4 + * + * G4 + * 20 - 23: Count of events needing PMC1, PMC2, PMC3 or PMC4 + * + * TV + * 24 - 29: Threshold value requested + * + * TS + * 30: Threshold scale value requested + */ + +static u32 pmcbits[N_COUNTER][2] = { + { 0x00844002, 0x00111001 }, /* PMC1 mask, value: P1,G2,G3,G4 */ + { 0x00844008, 0x00111004 }, /* PMC2: P2,G2,G3,G4 */ + { 0x00800020, 0x00100010 }, /* PMC3: P3,G4 */ + { 0x00840080, 0x00110040 }, /* PMC4: P4,G3,G4 */ + { 0x00000200, 0x00000100 }, /* PMC5: P5 */ + { 0x00000800, 0x00000400 } /* PMC6: P6 */ +}; + +static u32 classbits[N_CLASSES - 1][2] = { + { 0x00000000, 0x00000000 }, /* class 0: no constraint */ + { 0x00800000, 0x00100000 }, /* class 1: G4 */ + { 0x00040000, 0x00010000 }, /* class 2: G3 */ + { 0x00004000, 0x00001000 }, /* class 3: G2 */ +}; + +static int mpc7450_get_constraint(u64 event, unsigned long *maskp, + unsigned long *valp) +{ + int pmc, class; + u32 mask, value; + int thresh, tuse; + + class = mpc7450_classify_event(event); + if (class < 0) + return -1; + if (class == 4) { + pmc = ((unsigned int)event >> PM_PMC_SH) & PM_PMC_MSK; + mask = pmcbits[pmc - 1][0]; + value = pmcbits[pmc - 1][1]; + } else { + mask = classbits[class][0]; + value = classbits[class][1]; + } + + tuse = mpc7450_threshold_use(event); + if (tuse) { + thresh = ((unsigned int)event >> PM_THRESH_SH) & PM_THRESH_MSK; + mask |= 0x3f << 24; + value |= thresh << 24; + if (tuse == 2) { + mask |= 0x40000000; + if ((unsigned int)event & PM_THRMULT_MSKS) + value |= 0x40000000; + } + } + + *maskp = mask; + *valp = value; + return 0; +} + +static const unsigned int event_alternatives[][MAX_ALT] = { + { 0x217, 0x317 }, /* PM_L1_DCACHE_MISS */ + { 0x418, 0x50f, 0x60f }, /* PM_SNOOP_RETRY */ + { 0x502, 0x602 }, /* PM_L2_HIT */ + { 0x503, 0x603 }, /* PM_L3_HIT */ + { 0x504, 0x604 }, /* PM_L2_ICACHE_MISS */ + { 0x505, 0x605 }, /* PM_L3_ICACHE_MISS */ + { 0x506, 0x606 }, /* PM_L2_DCACHE_MISS */ + { 0x507, 0x607 }, /* PM_L3_DCACHE_MISS */ + { 0x50a, 0x623 }, /* PM_LD_HIT_L3 */ + { 0x50b, 0x624 }, /* PM_ST_HIT_L3 */ + { 0x50d, 0x60d }, /* PM_L2_TOUCH_HIT */ + { 0x50e, 0x60e }, /* PM_L3_TOUCH_HIT */ + { 0x512, 0x612 }, /* PM_INT_LOCAL */ + { 0x513, 0x61d }, /* PM_L2_MISS */ + { 0x514, 0x61e }, /* PM_L3_MISS */ +}; + +/* + * Scan the alternatives table for a match and return the + * index into the alternatives table if found, else -1. + */ +static int find_alternative(u32 event) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { + if (event < event_alternatives[i][0]) + break; + for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) + if (event == event_alternatives[i][j]) + return i; + } + return -1; +} + +static int mpc7450_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int i, j, nalt = 1; + u32 ae; + + alt[0] = event; + nalt = 1; + i = find_alternative((u32)event); + if (i >= 0) { + for (j = 0; j < MAX_ALT; ++j) { + ae = event_alternatives[i][j]; + if (ae && ae != (u32)event) + alt[nalt++] = ae; + } + } + return nalt; +} + +/* + * Bitmaps of which PMCs each class can use for classes 0 - 3. + * Bit i is set if PMC i+1 is usable. + */ +static const u8 classmap[N_CLASSES] = { + 0x3f, 0x0f, 0x0b, 0x03, 0 +}; + +/* Bit position and width of each PMCSEL field */ +static const int pmcsel_shift[N_COUNTER] = { + 6, 0, 27, 22, 17, 11 +}; +static const u32 pmcsel_mask[N_COUNTER] = { + 0x7f, 0x3f, 0x1f, 0x1f, 0x1f, 0x3f +}; + +/* + * Compute MMCR0/1/2 values for a set of events. + */ +static int mpc7450_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]) +{ + u8 event_index[N_CLASSES][N_COUNTER]; + int n_classevent[N_CLASSES]; + int i, j, class, tuse; + u32 pmc_inuse = 0, pmc_avail; + u32 mmcr0 = 0, mmcr1 = 0, mmcr2 = 0; + u32 ev, pmc, thresh; + + if (n_ev > N_COUNTER) + return -1; + + /* First pass: count usage in each class */ + for (i = 0; i < N_CLASSES; ++i) + n_classevent[i] = 0; + for (i = 0; i < n_ev; ++i) { + class = mpc7450_classify_event(event[i]); + if (class < 0) + return -1; + j = n_classevent[class]++; + event_index[class][j] = i; + } + + /* Second pass: allocate PMCs from most specific event to least */ + for (class = N_CLASSES - 1; class >= 0; --class) { + for (i = 0; i < n_classevent[class]; ++i) { + ev = event[event_index[class][i]]; + if (class == 4) { + pmc = (ev >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc_inuse & (1 << (pmc - 1))) + return -1; + } else { + /* Find a suitable PMC */ + pmc_avail = classmap[class] & ~pmc_inuse; + if (!pmc_avail) + return -1; + pmc = ffs(pmc_avail); + } + pmc_inuse |= 1 << (pmc - 1); + + tuse = mpc7450_threshold_use(ev); + if (tuse) { + thresh = (ev >> PM_THRESH_SH) & PM_THRESH_MSK; + mmcr0 |= thresh << 16; + if (tuse == 2 && (ev & PM_THRMULT_MSKS)) + mmcr2 = 0x80000000; + } + ev &= pmcsel_mask[pmc - 1]; + ev <<= pmcsel_shift[pmc - 1]; + if (pmc <= 2) + mmcr0 |= ev; + else + mmcr1 |= ev; + hwc[event_index[class][i]] = pmc - 1; + } + } + + if (pmc_inuse & 1) + mmcr0 |= MMCR0_PMC1CE; + if (pmc_inuse & 0x3e) + mmcr0 |= MMCR0_PMCnCE; + + /* Return MMCRx values */ + mmcr[0] = mmcr0; + mmcr[1] = mmcr1; + mmcr[2] = mmcr2; + return 0; +} + +/* + * Disable counting by a PMC. + * Note that the pmc argument is 0-based here, not 1-based. + */ +static void mpc7450_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +{ + if (pmc <= 1) + mmcr[0] &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); + else + mmcr[1] &= ~(pmcsel_mask[pmc] << pmcsel_shift[pmc]); +} + +static int mpc7450_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 1, + [PERF_COUNT_HW_INSTRUCTIONS] = 2, + [PERF_COUNT_HW_CACHE_MISSES] = 0x217, /* PM_L1_DCACHE_MISS */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x122, /* PM_BR_CMPL */ + [PERF_COUNT_HW_BRANCH_MISSES] = 0x41c, /* PM_BR_MPRED */ +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static int mpc7450_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x225 }, + [C(OP_WRITE)] = { 0, 0x227 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x129, 0x115 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0x634, 0 }, + }, + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { 0, 0 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x312 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x223 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x122, 0x41c }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +struct power_pmu mpc7450_pmu = { + .name = "MPC7450 family", + .n_counter = N_COUNTER, + .max_alternatives = MAX_ALT, + .add_fields = 0x00111555ul, + .test_adder = 0x00301000ul, + .compute_mmcr = mpc7450_compute_mmcr, + .get_constraint = mpc7450_get_constraint, + .get_alternatives = mpc7450_get_alternatives, + .disable_pmc = mpc7450_disable_pmc, + .n_generic = ARRAY_SIZE(mpc7450_generic_events), + .generic_events = mpc7450_generic_events, + .cache_events = &mpc7450_cache_events, +}; + +static int __init init_mpc7450_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type || + strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc/7450")) + return -ENODEV; + + return register_power_pmu(&mpc7450_pmu); +} + +early_initcall(init_mpc7450_pmu); diff --git a/arch/powerpc/perf/power4-pmu.c b/arch/powerpc/perf/power4-pmu.c new file mode 100644 index 0000000..b4f1dda --- /dev/null +++ b/arch/powerpc/perf/power4-pmu.c @@ -0,0 +1,621 @@ +/* + * Performance counter support for POWER4 (GP) and POWER4+ (GQ) processors. + * + * Copyright 2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include +#include + +/* + * Bits in event code for POWER4 + */ +#define PM_PMC_SH 12 /* PMC number (1-based) for direct events */ +#define PM_PMC_MSK 0xf +#define PM_UNIT_SH 8 /* TTMMUX number and setting - unit select */ +#define PM_UNIT_MSK 0xf +#define PM_LOWER_SH 6 +#define PM_LOWER_MSK 1 +#define PM_LOWER_MSKS 0x40 +#define PM_BYTE_SH 4 /* Byte number of event bus to use */ +#define PM_BYTE_MSK 3 +#define PM_PMCSEL_MSK 7 + +/* + * Unit code values + */ +#define PM_FPU 1 +#define PM_ISU1 2 +#define PM_IFU 3 +#define PM_IDU0 4 +#define PM_ISU1_ALT 6 +#define PM_ISU2 7 +#define PM_IFU_ALT 8 +#define PM_LSU0 9 +#define PM_LSU1 0xc +#define PM_GPS 0xf + +/* + * Bits in MMCR0 for POWER4 + */ +#define MMCR0_PMC1SEL_SH 8 +#define MMCR0_PMC2SEL_SH 1 +#define MMCR_PMCSEL_MSK 0x1f + +/* + * Bits in MMCR1 for POWER4 + */ +#define MMCR1_TTM0SEL_SH 62 +#define MMCR1_TTC0SEL_SH 61 +#define MMCR1_TTM1SEL_SH 59 +#define MMCR1_TTC1SEL_SH 58 +#define MMCR1_TTM2SEL_SH 56 +#define MMCR1_TTC2SEL_SH 55 +#define MMCR1_TTM3SEL_SH 53 +#define MMCR1_TTC3SEL_SH 52 +#define MMCR1_TTMSEL_MSK 3 +#define MMCR1_TD_CP_DBG0SEL_SH 50 +#define MMCR1_TD_CP_DBG1SEL_SH 48 +#define MMCR1_TD_CP_DBG2SEL_SH 46 +#define MMCR1_TD_CP_DBG3SEL_SH 44 +#define MMCR1_DEBUG0SEL_SH 43 +#define MMCR1_DEBUG1SEL_SH 42 +#define MMCR1_DEBUG2SEL_SH 41 +#define MMCR1_DEBUG3SEL_SH 40 +#define MMCR1_PMC1_ADDER_SEL_SH 39 +#define MMCR1_PMC2_ADDER_SEL_SH 38 +#define MMCR1_PMC6_ADDER_SEL_SH 37 +#define MMCR1_PMC5_ADDER_SEL_SH 36 +#define MMCR1_PMC8_ADDER_SEL_SH 35 +#define MMCR1_PMC7_ADDER_SEL_SH 34 +#define MMCR1_PMC3_ADDER_SEL_SH 33 +#define MMCR1_PMC4_ADDER_SEL_SH 32 +#define MMCR1_PMC3SEL_SH 27 +#define MMCR1_PMC4SEL_SH 22 +#define MMCR1_PMC5SEL_SH 17 +#define MMCR1_PMC6SEL_SH 12 +#define MMCR1_PMC7SEL_SH 7 +#define MMCR1_PMC8SEL_SH 2 /* note bit 0 is in MMCRA for GP */ + +static short mmcr1_adder_bits[8] = { + MMCR1_PMC1_ADDER_SEL_SH, + MMCR1_PMC2_ADDER_SEL_SH, + MMCR1_PMC3_ADDER_SEL_SH, + MMCR1_PMC4_ADDER_SEL_SH, + MMCR1_PMC5_ADDER_SEL_SH, + MMCR1_PMC6_ADDER_SEL_SH, + MMCR1_PMC7_ADDER_SEL_SH, + MMCR1_PMC8_ADDER_SEL_SH +}; + +/* + * Bits in MMCRA + */ +#define MMCRA_PMC8SEL0_SH 17 /* PMC8SEL bit 0 for GP */ + +/* + * Layout of constraint bits: + * 6666555555555544444444443333333333222222222211111111110000000000 + * 3210987654321098765432109876543210987654321098765432109876543210 + * |[ >[ >[ >|||[ >[ >< >< >< >< ><><><><><><><><> + * | UC1 UC2 UC3 ||| PS1 PS2 B0 B1 B2 B3 P1P2P3P4P5P6P7P8 + * \SMPL ||\TTC3SEL + * |\TTC_IFU_SEL + * \TTM2SEL0 + * + * SMPL - SAMPLE_ENABLE constraint + * 56: SAMPLE_ENABLE value 0x0100_0000_0000_0000 + * + * UC1 - unit constraint 1: can't have all three of FPU/ISU1/IDU0|ISU2 + * 55: UC1 error 0x0080_0000_0000_0000 + * 54: FPU events needed 0x0040_0000_0000_0000 + * 53: ISU1 events needed 0x0020_0000_0000_0000 + * 52: IDU0|ISU2 events needed 0x0010_0000_0000_0000 + * + * UC2 - unit constraint 2: can't have all three of FPU/IFU/LSU0 + * 51: UC2 error 0x0008_0000_0000_0000 + * 50: FPU events needed 0x0004_0000_0000_0000 + * 49: IFU events needed 0x0002_0000_0000_0000 + * 48: LSU0 events needed 0x0001_0000_0000_0000 + * + * UC3 - unit constraint 3: can't have all four of LSU0/IFU/IDU0|ISU2/ISU1 + * 47: UC3 error 0x8000_0000_0000 + * 46: LSU0 events needed 0x4000_0000_0000 + * 45: IFU events needed 0x2000_0000_0000 + * 44: IDU0|ISU2 events needed 0x1000_0000_0000 + * 43: ISU1 events needed 0x0800_0000_0000 + * + * TTM2SEL0 + * 42: 0 = IDU0 events needed + * 1 = ISU2 events needed 0x0400_0000_0000 + * + * TTC_IFU_SEL + * 41: 0 = IFU.U events needed + * 1 = IFU.L events needed 0x0200_0000_0000 + * + * TTC3SEL + * 40: 0 = LSU1.U events needed + * 1 = LSU1.L events needed 0x0100_0000_0000 + * + * PS1 + * 39: PS1 error 0x0080_0000_0000 + * 36-38: count of events needing PMC1/2/5/6 0x0070_0000_0000 + * + * PS2 + * 35: PS2 error 0x0008_0000_0000 + * 32-34: count of events needing PMC3/4/7/8 0x0007_0000_0000 + * + * B0 + * 28-31: Byte 0 event source 0xf000_0000 + * 1 = FPU + * 2 = ISU1 + * 3 = IFU + * 4 = IDU0 + * 7 = ISU2 + * 9 = LSU0 + * c = LSU1 + * f = GPS + * + * B1, B2, B3 + * 24-27, 20-23, 16-19: Byte 1, 2, 3 event sources + * + * P8 + * 15: P8 error 0x8000 + * 14-15: Count of events needing PMC8 + * + * P1..P7 + * 0-13: Count of events needing PMC1..PMC7 + * + * Note: this doesn't allow events using IFU.U to be combined with events + * using IFU.L, though that is feasible (using TTM0 and TTM2). However + * there are no listed events for IFU.L (they are debug events not + * verified for performance monitoring) so this shouldn't cause a + * problem. + */ + +static struct unitinfo { + unsigned long value, mask; + int unit; + int lowerbit; +} p4_unitinfo[16] = { + [PM_FPU] = { 0x44000000000000ul, 0x88000000000000ul, PM_FPU, 0 }, + [PM_ISU1] = { 0x20080000000000ul, 0x88000000000000ul, PM_ISU1, 0 }, + [PM_ISU1_ALT] = + { 0x20080000000000ul, 0x88000000000000ul, PM_ISU1, 0 }, + [PM_IFU] = { 0x02200000000000ul, 0x08820000000000ul, PM_IFU, 41 }, + [PM_IFU_ALT] = + { 0x02200000000000ul, 0x08820000000000ul, PM_IFU, 41 }, + [PM_IDU0] = { 0x10100000000000ul, 0x80840000000000ul, PM_IDU0, 1 }, + [PM_ISU2] = { 0x10140000000000ul, 0x80840000000000ul, PM_ISU2, 0 }, + [PM_LSU0] = { 0x01400000000000ul, 0x08800000000000ul, PM_LSU0, 0 }, + [PM_LSU1] = { 0x00000000000000ul, 0x00010000000000ul, PM_LSU1, 40 }, + [PM_GPS] = { 0x00000000000000ul, 0x00000000000000ul, PM_GPS, 0 } +}; + +static unsigned char direct_marked_event[8] = { + (1<<2) | (1<<3), /* PMC1: PM_MRK_GRP_DISP, PM_MRK_ST_CMPL */ + (1<<3) | (1<<5), /* PMC2: PM_THRESH_TIMEO, PM_MRK_BRU_FIN */ + (1<<3), /* PMC3: PM_MRK_ST_CMPL_INT */ + (1<<4) | (1<<5), /* PMC4: PM_MRK_GRP_CMPL, PM_MRK_CRU_FIN */ + (1<<4) | (1<<5), /* PMC5: PM_MRK_GRP_TIMEO */ + (1<<3) | (1<<4) | (1<<5), + /* PMC6: PM_MRK_ST_GPS, PM_MRK_FXU_FIN, PM_MRK_GRP_ISSUED */ + (1<<4) | (1<<5), /* PMC7: PM_MRK_FPU_FIN, PM_MRK_INST_FIN */ + (1<<4), /* PMC8: PM_MRK_LSU_FIN */ +}; + +/* + * Returns 1 if event counts things relating to marked instructions + * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. + */ +static int p4_marked_instr_event(u64 event) +{ + int pmc, psel, unit, byte, bit; + unsigned int mask; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + psel = event & PM_PMCSEL_MSK; + if (pmc) { + if (direct_marked_event[pmc - 1] & (1 << psel)) + return 1; + if (psel == 0) /* add events */ + bit = (pmc <= 4)? pmc - 1: 8 - pmc; + else if (psel == 6) /* decode events */ + bit = 4; + else + return 0; + } else + bit = psel; + + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + mask = 0; + switch (unit) { + case PM_LSU1: + if (event & PM_LOWER_MSKS) + mask = 1 << 28; /* byte 7 bit 4 */ + else + mask = 6 << 24; /* byte 3 bits 1 and 2 */ + break; + case PM_LSU0: + /* byte 3, bit 3; byte 2 bits 0,2,3,4,5; byte 1 */ + mask = 0x083dff00; + } + return (mask >> (byte * 8 + bit)) & 1; +} + +static int p4_get_constraint(u64 event, unsigned long *maskp, + unsigned long *valp) +{ + int pmc, byte, unit, lower, sh; + unsigned long mask = 0, value = 0; + int grp = -1; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 8) + return -1; + sh = (pmc - 1) * 2; + mask |= 2 << sh; + value |= 1 << sh; + grp = ((pmc - 1) >> 1) & 1; + } + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + if (unit) { + lower = (event >> PM_LOWER_SH) & PM_LOWER_MSK; + + /* + * Bus events on bytes 0 and 2 can be counted + * on PMC1/2/5/6; bytes 1 and 3 on PMC3/4/7/8. + */ + if (!pmc) + grp = byte & 1; + + if (!p4_unitinfo[unit].unit) + return -1; + mask |= p4_unitinfo[unit].mask; + value |= p4_unitinfo[unit].value; + sh = p4_unitinfo[unit].lowerbit; + if (sh > 1) + value |= (unsigned long)lower << sh; + else if (lower != sh) + return -1; + unit = p4_unitinfo[unit].unit; + + /* Set byte lane select field */ + mask |= 0xfULL << (28 - 4 * byte); + value |= (unsigned long)unit << (28 - 4 * byte); + } + if (grp == 0) { + /* increment PMC1/2/5/6 field */ + mask |= 0x8000000000ull; + value |= 0x1000000000ull; + } else { + /* increment PMC3/4/7/8 field */ + mask |= 0x800000000ull; + value |= 0x100000000ull; + } + + /* Marked instruction events need sample_enable set */ + if (p4_marked_instr_event(event)) { + mask |= 1ull << 56; + value |= 1ull << 56; + } + + /* PMCSEL=6 decode events on byte 2 need sample_enable clear */ + if (pmc && (event & PM_PMCSEL_MSK) == 6 && byte == 2) + mask |= 1ull << 56; + + *maskp = mask; + *valp = value; + return 0; +} + +static unsigned int ppc_inst_cmpl[] = { + 0x1001, 0x4001, 0x6001, 0x7001, 0x8001 +}; + +static int p4_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int i, j, na; + + alt[0] = event; + na = 1; + + /* 2 possibilities for PM_GRP_DISP_REJECT */ + if (event == 0x8003 || event == 0x0224) { + alt[1] = event ^ (0x8003 ^ 0x0224); + return 2; + } + + /* 2 possibilities for PM_ST_MISS_L1 */ + if (event == 0x0c13 || event == 0x0c23) { + alt[1] = event ^ (0x0c13 ^ 0x0c23); + return 2; + } + + /* several possibilities for PM_INST_CMPL */ + for (i = 0; i < ARRAY_SIZE(ppc_inst_cmpl); ++i) { + if (event == ppc_inst_cmpl[i]) { + for (j = 0; j < ARRAY_SIZE(ppc_inst_cmpl); ++j) + if (j != i) + alt[na++] = ppc_inst_cmpl[j]; + break; + } + } + + return na; +} + +static int p4_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]) +{ + unsigned long mmcr0 = 0, mmcr1 = 0, mmcra = 0; + unsigned int pmc, unit, byte, psel, lower; + unsigned int ttm, grp; + unsigned int pmc_inuse = 0; + unsigned int pmc_grp_use[2]; + unsigned char busbyte[4]; + unsigned char unituse[16]; + unsigned int unitlower = 0; + int i; + + if (n_ev > 8) + return -1; + + /* First pass to count resource use */ + pmc_grp_use[0] = pmc_grp_use[1] = 0; + memset(busbyte, 0, sizeof(busbyte)); + memset(unituse, 0, sizeof(unituse)); + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc_inuse & (1 << (pmc - 1))) + return -1; + pmc_inuse |= 1 << (pmc - 1); + /* count 1/2/5/6 vs 3/4/7/8 use */ + ++pmc_grp_use[((pmc - 1) >> 1) & 1]; + } + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + lower = (event[i] >> PM_LOWER_SH) & PM_LOWER_MSK; + if (unit) { + if (!pmc) + ++pmc_grp_use[byte & 1]; + if (unit == 6 || unit == 8) + /* map alt ISU1/IFU codes: 6->2, 8->3 */ + unit = (unit >> 1) - 1; + if (busbyte[byte] && busbyte[byte] != unit) + return -1; + busbyte[byte] = unit; + lower <<= unit; + if (unituse[unit] && lower != (unitlower & lower)) + return -1; + unituse[unit] = 1; + unitlower |= lower; + } + } + if (pmc_grp_use[0] > 4 || pmc_grp_use[1] > 4) + return -1; + + /* + * Assign resources and set multiplexer selects. + * + * Units 1,2,3 are on TTM0, 4,6,7 on TTM1, 8,10 on TTM2. + * Each TTMx can only select one unit, but since + * units 2 and 6 are both ISU1, and 3 and 8 are both IFU, + * we have some choices. + */ + if (unituse[2] & (unituse[1] | (unituse[3] & unituse[9]))) { + unituse[6] = 1; /* Move 2 to 6 */ + unituse[2] = 0; + } + if (unituse[3] & (unituse[1] | unituse[2])) { + unituse[8] = 1; /* Move 3 to 8 */ + unituse[3] = 0; + unitlower = (unitlower & ~8) | ((unitlower & 8) << 5); + } + /* Check only one unit per TTMx */ + if (unituse[1] + unituse[2] + unituse[3] > 1 || + unituse[4] + unituse[6] + unituse[7] > 1 || + unituse[8] + unituse[9] > 1 || + (unituse[5] | unituse[10] | unituse[11] | + unituse[13] | unituse[14])) + return -1; + + /* Set TTMxSEL fields. Note, units 1-3 => TTM0SEL codes 0-2 */ + mmcr1 |= (unsigned long)(unituse[3] * 2 + unituse[2]) + << MMCR1_TTM0SEL_SH; + mmcr1 |= (unsigned long)(unituse[7] * 3 + unituse[6] * 2) + << MMCR1_TTM1SEL_SH; + mmcr1 |= (unsigned long)unituse[9] << MMCR1_TTM2SEL_SH; + + /* Set TTCxSEL fields. */ + if (unitlower & 0xe) + mmcr1 |= 1ull << MMCR1_TTC0SEL_SH; + if (unitlower & 0xf0) + mmcr1 |= 1ull << MMCR1_TTC1SEL_SH; + if (unitlower & 0xf00) + mmcr1 |= 1ull << MMCR1_TTC2SEL_SH; + if (unitlower & 0x7000) + mmcr1 |= 1ull << MMCR1_TTC3SEL_SH; + + /* Set byte lane select fields. */ + for (byte = 0; byte < 4; ++byte) { + unit = busbyte[byte]; + if (!unit) + continue; + if (unit == 0xf) { + /* special case for GPS */ + mmcr1 |= 1ull << (MMCR1_DEBUG0SEL_SH - byte); + } else { + if (!unituse[unit]) + ttm = unit - 1; /* 2->1, 3->2 */ + else + ttm = unit >> 2; + mmcr1 |= (unsigned long)ttm + << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); + } + } + + /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + psel = event[i] & PM_PMCSEL_MSK; + if (!pmc) { + /* Bus event or 00xxx direct event (off or cycles) */ + if (unit) + psel |= 0x10 | ((byte & 2) << 2); + for (pmc = 0; pmc < 8; ++pmc) { + if (pmc_inuse & (1 << pmc)) + continue; + grp = (pmc >> 1) & 1; + if (unit) { + if (grp == (byte & 1)) + break; + } else if (pmc_grp_use[grp] < 4) { + ++pmc_grp_use[grp]; + break; + } + } + pmc_inuse |= 1 << pmc; + } else { + /* Direct event */ + --pmc; + if (psel == 0 && (byte & 2)) + /* add events on higher-numbered bus */ + mmcr1 |= 1ull << mmcr1_adder_bits[pmc]; + else if (psel == 6 && byte == 3) + /* seem to need to set sample_enable here */ + mmcra |= MMCRA_SAMPLE_ENABLE; + psel |= 8; + } + if (pmc <= 1) + mmcr0 |= psel << (MMCR0_PMC1SEL_SH - 7 * pmc); + else + mmcr1 |= psel << (MMCR1_PMC3SEL_SH - 5 * (pmc - 2)); + if (pmc == 7) /* PMC8 */ + mmcra |= (psel & 1) << MMCRA_PMC8SEL0_SH; + hwc[i] = pmc; + if (p4_marked_instr_event(event[i])) + mmcra |= MMCRA_SAMPLE_ENABLE; + } + + if (pmc_inuse & 1) + mmcr0 |= MMCR0_PMC1CE; + if (pmc_inuse & 0xfe) + mmcr0 |= MMCR0_PMCjCE; + + mmcra |= 0x2000; /* mark only one IOP per PPC instruction */ + + /* Return MMCRx values */ + mmcr[0] = mmcr0; + mmcr[1] = mmcr1; + mmcr[2] = mmcra; + return 0; +} + +static void p4_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +{ + /* + * Setting the PMCxSEL field to 0 disables PMC x. + * (Note that pmc is 0-based here, not 1-based.) + */ + if (pmc <= 1) { + mmcr[0] &= ~(0x1fUL << (MMCR0_PMC1SEL_SH - 7 * pmc)); + } else { + mmcr[1] &= ~(0x1fUL << (MMCR1_PMC3SEL_SH - 5 * (pmc - 2))); + if (pmc == 7) + mmcr[2] &= ~(1UL << MMCRA_PMC8SEL0_SH); + } +} + +static int p4_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 7, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x1001, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x8c10, /* PM_LD_REF_L1 */ + [PERF_COUNT_HW_CACHE_MISSES] = 0x3c10, /* PM_LD_MISS_L1 */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x330, /* PM_BR_ISSUED */ + [PERF_COUNT_HW_BRANCH_MISSES] = 0x331, /* PM_BR_MPRED_CR */ +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static int power4_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x8c10, 0x3c10 }, + [C(OP_WRITE)] = { 0x7c10, 0xc13 }, + [C(OP_PREFETCH)] = { 0xc35, 0 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { 0, 0 }, + [C(OP_PREFETCH)] = { 0xc34, 0 }, + }, + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x904 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x900 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x330, 0x331 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +static struct power_pmu power4_pmu = { + .name = "POWER4/4+", + .n_counter = 8, + .max_alternatives = 5, + .add_fields = 0x0000001100005555ul, + .test_adder = 0x0011083300000000ul, + .compute_mmcr = p4_compute_mmcr, + .get_constraint = p4_get_constraint, + .get_alternatives = p4_get_alternatives, + .disable_pmc = p4_disable_pmc, + .n_generic = ARRAY_SIZE(p4_generic_events), + .generic_events = p4_generic_events, + .cache_events = &power4_cache_events, +}; + +static int __init init_power4_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type || + strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power4")) + return -ENODEV; + + return register_power_pmu(&power4_pmu); +} + +early_initcall(init_power4_pmu); diff --git a/arch/powerpc/perf/power5+-pmu.c b/arch/powerpc/perf/power5+-pmu.c new file mode 100644 index 0000000..a8757ba --- /dev/null +++ b/arch/powerpc/perf/power5+-pmu.c @@ -0,0 +1,690 @@ +/* + * Performance counter support for POWER5+/++ (not POWER5) processors. + * + * Copyright 2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include +#include + +/* + * Bits in event code for POWER5+ (POWER5 GS) and POWER5++ (POWER5 GS DD3) + */ +#define PM_PMC_SH 20 /* PMC number (1-based) for direct events */ +#define PM_PMC_MSK 0xf +#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) +#define PM_UNIT_SH 16 /* TTMMUX number and setting - unit select */ +#define PM_UNIT_MSK 0xf +#define PM_BYTE_SH 12 /* Byte number of event bus to use */ +#define PM_BYTE_MSK 7 +#define PM_GRS_SH 8 /* Storage subsystem mux select */ +#define PM_GRS_MSK 7 +#define PM_BUSEVENT_MSK 0x80 /* Set if event uses event bus */ +#define PM_PMCSEL_MSK 0x7f + +/* Values in PM_UNIT field */ +#define PM_FPU 0 +#define PM_ISU0 1 +#define PM_IFU 2 +#define PM_ISU1 3 +#define PM_IDU 4 +#define PM_ISU0_ALT 6 +#define PM_GRS 7 +#define PM_LSU0 8 +#define PM_LSU1 0xc +#define PM_LASTUNIT 0xc + +/* + * Bits in MMCR1 for POWER5+ + */ +#define MMCR1_TTM0SEL_SH 62 +#define MMCR1_TTM1SEL_SH 60 +#define MMCR1_TTM2SEL_SH 58 +#define MMCR1_TTM3SEL_SH 56 +#define MMCR1_TTMSEL_MSK 3 +#define MMCR1_TD_CP_DBG0SEL_SH 54 +#define MMCR1_TD_CP_DBG1SEL_SH 52 +#define MMCR1_TD_CP_DBG2SEL_SH 50 +#define MMCR1_TD_CP_DBG3SEL_SH 48 +#define MMCR1_GRS_L2SEL_SH 46 +#define MMCR1_GRS_L2SEL_MSK 3 +#define MMCR1_GRS_L3SEL_SH 44 +#define MMCR1_GRS_L3SEL_MSK 3 +#define MMCR1_GRS_MCSEL_SH 41 +#define MMCR1_GRS_MCSEL_MSK 7 +#define MMCR1_GRS_FABSEL_SH 39 +#define MMCR1_GRS_FABSEL_MSK 3 +#define MMCR1_PMC1_ADDER_SEL_SH 35 +#define MMCR1_PMC2_ADDER_SEL_SH 34 +#define MMCR1_PMC3_ADDER_SEL_SH 33 +#define MMCR1_PMC4_ADDER_SEL_SH 32 +#define MMCR1_PMC1SEL_SH 25 +#define MMCR1_PMC2SEL_SH 17 +#define MMCR1_PMC3SEL_SH 9 +#define MMCR1_PMC4SEL_SH 1 +#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) +#define MMCR1_PMCSEL_MSK 0x7f + +/* + * Layout of constraint bits: + * 6666555555555544444444443333333333222222222211111111110000000000 + * 3210987654321098765432109876543210987654321098765432109876543210 + * [ ><><>< ><> <><>[ > < >< >< >< ><><><><><><> + * NC G0G1G2 G3 T0T1 UC B0 B1 B2 B3 P6P5P4P3P2P1 + * + * NC - number of counters + * 51: NC error 0x0008_0000_0000_0000 + * 48-50: number of events needing PMC1-4 0x0007_0000_0000_0000 + * + * G0..G3 - GRS mux constraints + * 46-47: GRS_L2SEL value + * 44-45: GRS_L3SEL value + * 41-44: GRS_MCSEL value + * 39-40: GRS_FABSEL value + * Note that these match up with their bit positions in MMCR1 + * + * T0 - TTM0 constraint + * 36-37: TTM0SEL value (0=FPU, 2=IFU, 3=ISU1) 0x30_0000_0000 + * + * T1 - TTM1 constraint + * 34-35: TTM1SEL value (0=IDU, 3=GRS) 0x0c_0000_0000 + * + * UC - unit constraint: can't have all three of FPU|IFU|ISU1, ISU0, IDU|GRS + * 33: UC3 error 0x02_0000_0000 + * 32: FPU|IFU|ISU1 events needed 0x01_0000_0000 + * 31: ISU0 events needed 0x01_8000_0000 + * 30: IDU|GRS events needed 0x00_4000_0000 + * + * B0 + * 24-27: Byte 0 event source 0x0f00_0000 + * Encoding as for the event code + * + * B1, B2, B3 + * 20-23, 16-19, 12-15: Byte 1, 2, 3 event sources + * + * P6 + * 11: P6 error 0x800 + * 10-11: Count of events needing PMC6 + * + * P1..P5 + * 0-9: Count of events needing PMC1..PMC5 + */ + +static const int grsel_shift[8] = { + MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, + MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, + MMCR1_GRS_MCSEL_SH, MMCR1_GRS_FABSEL_SH +}; + +/* Masks and values for using events from the various units */ +static unsigned long unit_cons[PM_LASTUNIT+1][2] = { + [PM_FPU] = { 0x3200000000ul, 0x0100000000ul }, + [PM_ISU0] = { 0x0200000000ul, 0x0080000000ul }, + [PM_ISU1] = { 0x3200000000ul, 0x3100000000ul }, + [PM_IFU] = { 0x3200000000ul, 0x2100000000ul }, + [PM_IDU] = { 0x0e00000000ul, 0x0040000000ul }, + [PM_GRS] = { 0x0e00000000ul, 0x0c40000000ul }, +}; + +static int power5p_get_constraint(u64 event, unsigned long *maskp, + unsigned long *valp) +{ + int pmc, byte, unit, sh; + int bit, fmask; + unsigned long mask = 0, value = 0; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 6) + return -1; + sh = (pmc - 1) * 2; + mask |= 2 << sh; + value |= 1 << sh; + if (pmc >= 5 && !(event == 0x500009 || event == 0x600005)) + return -1; + } + if (event & PM_BUSEVENT_MSK) { + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + if (unit > PM_LASTUNIT) + return -1; + if (unit == PM_ISU0_ALT) + unit = PM_ISU0; + mask |= unit_cons[unit][0]; + value |= unit_cons[unit][1]; + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + if (byte >= 4) { + if (unit != PM_LSU1) + return -1; + /* Map LSU1 low word (bytes 4-7) to unit LSU1+1 */ + ++unit; + byte &= 3; + } + if (unit == PM_GRS) { + bit = event & 7; + fmask = (bit == 6)? 7: 3; + sh = grsel_shift[bit]; + mask |= (unsigned long)fmask << sh; + value |= (unsigned long)((event >> PM_GRS_SH) & fmask) + << sh; + } + /* Set byte lane select field */ + mask |= 0xfUL << (24 - 4 * byte); + value |= (unsigned long)unit << (24 - 4 * byte); + } + if (pmc < 5) { + /* need a counter from PMC1-4 set */ + mask |= 0x8000000000000ul; + value |= 0x1000000000000ul; + } + *maskp = mask; + *valp = value; + return 0; +} + +static int power5p_limited_pmc_event(u64 event) +{ + int pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + + return pmc == 5 || pmc == 6; +} + +#define MAX_ALT 3 /* at most 3 alternatives for any event */ + +static const unsigned int event_alternatives[][MAX_ALT] = { + { 0x100c0, 0x40001f }, /* PM_GCT_FULL_CYC */ + { 0x120e4, 0x400002 }, /* PM_GRP_DISP_REJECT */ + { 0x230e2, 0x323087 }, /* PM_BR_PRED_CR */ + { 0x230e3, 0x223087, 0x3230a0 }, /* PM_BR_PRED_TA */ + { 0x410c7, 0x441084 }, /* PM_THRD_L2MISS_BOTH_CYC */ + { 0x800c4, 0xc20e0 }, /* PM_DTLB_MISS */ + { 0xc50c6, 0xc60e0 }, /* PM_MRK_DTLB_MISS */ + { 0x100005, 0x600005 }, /* PM_RUN_CYC */ + { 0x100009, 0x200009 }, /* PM_INST_CMPL */ + { 0x200015, 0x300015 }, /* PM_LSU_LMQ_SRQ_EMPTY_CYC */ + { 0x300009, 0x400009 }, /* PM_INST_DISP */ +}; + +/* + * Scan the alternatives table for a match and return the + * index into the alternatives table if found, else -1. + */ +static int find_alternative(unsigned int event) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { + if (event < event_alternatives[i][0]) + break; + for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) + if (event == event_alternatives[i][j]) + return i; + } + return -1; +} + +static const unsigned char bytedecode_alternatives[4][4] = { + /* PMC 1 */ { 0x21, 0x23, 0x25, 0x27 }, + /* PMC 2 */ { 0x07, 0x17, 0x0e, 0x1e }, + /* PMC 3 */ { 0x20, 0x22, 0x24, 0x26 }, + /* PMC 4 */ { 0x07, 0x17, 0x0e, 0x1e } +}; + +/* + * Some direct events for decodes of event bus byte 3 have alternative + * PMCSEL values on other counters. This returns the alternative + * event code for those that do, or -1 otherwise. This also handles + * alternative PCMSEL values for add events. + */ +static s64 find_alternative_bdecode(u64 event) +{ + int pmc, altpmc, pp, j; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc == 0 || pmc > 4) + return -1; + altpmc = 5 - pmc; /* 1 <-> 4, 2 <-> 3 */ + pp = event & PM_PMCSEL_MSK; + for (j = 0; j < 4; ++j) { + if (bytedecode_alternatives[pmc - 1][j] == pp) { + return (event & ~(PM_PMC_MSKS | PM_PMCSEL_MSK)) | + (altpmc << PM_PMC_SH) | + bytedecode_alternatives[altpmc - 1][j]; + } + } + + /* new decode alternatives for power5+ */ + if (pmc == 1 && (pp == 0x0d || pp == 0x0e)) + return event + (2 << PM_PMC_SH) + (0x2e - 0x0d); + if (pmc == 3 && (pp == 0x2e || pp == 0x2f)) + return event - (2 << PM_PMC_SH) - (0x2e - 0x0d); + + /* alternative add event encodings */ + if (pp == 0x10 || pp == 0x28) + return ((event ^ (0x10 ^ 0x28)) & ~PM_PMC_MSKS) | + (altpmc << PM_PMC_SH); + + return -1; +} + +static int power5p_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int i, j, nalt = 1; + int nlim; + s64 ae; + + alt[0] = event; + nalt = 1; + nlim = power5p_limited_pmc_event(event); + i = find_alternative(event); + if (i >= 0) { + for (j = 0; j < MAX_ALT; ++j) { + ae = event_alternatives[i][j]; + if (ae && ae != event) + alt[nalt++] = ae; + nlim += power5p_limited_pmc_event(ae); + } + } else { + ae = find_alternative_bdecode(event); + if (ae > 0) + alt[nalt++] = ae; + } + + if (flags & PPMU_ONLY_COUNT_RUN) { + /* + * We're only counting in RUN state, + * so PM_CYC is equivalent to PM_RUN_CYC + * and PM_INST_CMPL === PM_RUN_INST_CMPL. + * This doesn't include alternatives that don't provide + * any extra flexibility in assigning PMCs (e.g. + * 0x100005 for PM_RUN_CYC vs. 0xf for PM_CYC). + * Note that even with these additional alternatives + * we never end up with more than 3 alternatives for any event. + */ + j = nalt; + for (i = 0; i < nalt; ++i) { + switch (alt[i]) { + case 0xf: /* PM_CYC */ + alt[j++] = 0x600005; /* PM_RUN_CYC */ + ++nlim; + break; + case 0x600005: /* PM_RUN_CYC */ + alt[j++] = 0xf; + break; + case 0x100009: /* PM_INST_CMPL */ + alt[j++] = 0x500009; /* PM_RUN_INST_CMPL */ + ++nlim; + break; + case 0x500009: /* PM_RUN_INST_CMPL */ + alt[j++] = 0x100009; /* PM_INST_CMPL */ + alt[j++] = 0x200009; + break; + } + } + nalt = j; + } + + if (!(flags & PPMU_LIMITED_PMC_OK) && nlim) { + /* remove the limited PMC events */ + j = 0; + for (i = 0; i < nalt; ++i) { + if (!power5p_limited_pmc_event(alt[i])) { + alt[j] = alt[i]; + ++j; + } + } + nalt = j; + } else if ((flags & PPMU_LIMITED_PMC_REQD) && nlim < nalt) { + /* remove all but the limited PMC events */ + j = 0; + for (i = 0; i < nalt; ++i) { + if (power5p_limited_pmc_event(alt[i])) { + alt[j] = alt[i]; + ++j; + } + } + nalt = j; + } + + return nalt; +} + +/* + * Map of which direct events on which PMCs are marked instruction events. + * Indexed by PMCSEL value, bit i (LE) set if PMC i is a marked event. + * Bit 0 is set if it is marked for all PMCs. + * The 0x80 bit indicates a byte decode PMCSEL value. + */ +static unsigned char direct_event_is_marked[0x28] = { + 0, /* 00 */ + 0x1f, /* 01 PM_IOPS_CMPL */ + 0x2, /* 02 PM_MRK_GRP_DISP */ + 0xe, /* 03 PM_MRK_ST_CMPL, PM_MRK_ST_GPS, PM_MRK_ST_CMPL_INT */ + 0, /* 04 */ + 0x1c, /* 05 PM_MRK_BRU_FIN, PM_MRK_INST_FIN, PM_MRK_CRU_FIN */ + 0x80, /* 06 */ + 0x80, /* 07 */ + 0, 0, 0,/* 08 - 0a */ + 0x18, /* 0b PM_THRESH_TIMEO, PM_MRK_GRP_TIMEO */ + 0, /* 0c */ + 0x80, /* 0d */ + 0x80, /* 0e */ + 0, /* 0f */ + 0, /* 10 */ + 0x14, /* 11 PM_MRK_GRP_BR_REDIR, PM_MRK_GRP_IC_MISS */ + 0, /* 12 */ + 0x10, /* 13 PM_MRK_GRP_CMPL */ + 0x1f, /* 14 PM_GRP_MRK, PM_MRK_{FXU,FPU,LSU}_FIN */ + 0x2, /* 15 PM_MRK_GRP_ISSUED */ + 0x80, /* 16 */ + 0x80, /* 17 */ + 0, 0, 0, 0, 0, + 0x80, /* 1d */ + 0x80, /* 1e */ + 0, /* 1f */ + 0x80, /* 20 */ + 0x80, /* 21 */ + 0x80, /* 22 */ + 0x80, /* 23 */ + 0x80, /* 24 */ + 0x80, /* 25 */ + 0x80, /* 26 */ + 0x80, /* 27 */ +}; + +/* + * Returns 1 if event counts things relating to marked instructions + * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. + */ +static int power5p_marked_instr_event(u64 event) +{ + int pmc, psel; + int bit, byte, unit; + u32 mask; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + psel = event & PM_PMCSEL_MSK; + if (pmc >= 5) + return 0; + + bit = -1; + if (psel < sizeof(direct_event_is_marked)) { + if (direct_event_is_marked[psel] & (1 << pmc)) + return 1; + if (direct_event_is_marked[psel] & 0x80) + bit = 4; + else if (psel == 0x08) + bit = pmc - 1; + else if (psel == 0x10) + bit = 4 - pmc; + else if (psel == 0x1b && (pmc == 1 || pmc == 3)) + bit = 4; + } else if ((psel & 0x48) == 0x40) { + bit = psel & 7; + } else if (psel == 0x28) { + bit = pmc - 1; + } else if (pmc == 3 && (psel == 0x2e || psel == 0x2f)) { + bit = 4; + } + + if (!(event & PM_BUSEVENT_MSK) || bit == -1) + return 0; + + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + if (unit == PM_LSU0) { + /* byte 1 bits 0-7, byte 2 bits 0,2-4,6 */ + mask = 0x5dff00; + } else if (unit == PM_LSU1 && byte >= 4) { + byte -= 4; + /* byte 5 bits 6-7, byte 6 bits 0,4, byte 7 bits 0-4,6 */ + mask = 0x5f11c000; + } else + return 0; + + return (mask >> (byte * 8 + bit)) & 1; +} + +static int power5p_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]) +{ + unsigned long mmcr1 = 0; + unsigned long mmcra = 0; + unsigned int pmc, unit, byte, psel; + unsigned int ttm; + int i, isbus, bit, grsel; + unsigned int pmc_inuse = 0; + unsigned char busbyte[4]; + unsigned char unituse[16]; + int ttmuse; + + if (n_ev > 6) + return -1; + + /* First pass to count resource use */ + memset(busbyte, 0, sizeof(busbyte)); + memset(unituse, 0, sizeof(unituse)); + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 6) + return -1; + if (pmc_inuse & (1 << (pmc - 1))) + return -1; + pmc_inuse |= 1 << (pmc - 1); + } + if (event[i] & PM_BUSEVENT_MSK) { + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + if (unit > PM_LASTUNIT) + return -1; + if (unit == PM_ISU0_ALT) + unit = PM_ISU0; + if (byte >= 4) { + if (unit != PM_LSU1) + return -1; + ++unit; + byte &= 3; + } + if (busbyte[byte] && busbyte[byte] != unit) + return -1; + busbyte[byte] = unit; + unituse[unit] = 1; + } + } + + /* + * Assign resources and set multiplexer selects. + * + * PM_ISU0 can go either on TTM0 or TTM1, but that's the only + * choice we have to deal with. + */ + if (unituse[PM_ISU0] & + (unituse[PM_FPU] | unituse[PM_IFU] | unituse[PM_ISU1])) { + unituse[PM_ISU0_ALT] = 1; /* move ISU to TTM1 */ + unituse[PM_ISU0] = 0; + } + /* Set TTM[01]SEL fields. */ + ttmuse = 0; + for (i = PM_FPU; i <= PM_ISU1; ++i) { + if (!unituse[i]) + continue; + if (ttmuse++) + return -1; + mmcr1 |= (unsigned long)i << MMCR1_TTM0SEL_SH; + } + ttmuse = 0; + for (; i <= PM_GRS; ++i) { + if (!unituse[i]) + continue; + if (ttmuse++) + return -1; + mmcr1 |= (unsigned long)(i & 3) << MMCR1_TTM1SEL_SH; + } + if (ttmuse > 1) + return -1; + + /* Set byte lane select fields, TTM[23]SEL and GRS_*SEL. */ + for (byte = 0; byte < 4; ++byte) { + unit = busbyte[byte]; + if (!unit) + continue; + if (unit == PM_ISU0 && unituse[PM_ISU0_ALT]) { + /* get ISU0 through TTM1 rather than TTM0 */ + unit = PM_ISU0_ALT; + } else if (unit == PM_LSU1 + 1) { + /* select lower word of LSU1 for this byte */ + mmcr1 |= 1ul << (MMCR1_TTM3SEL_SH + 3 - byte); + } + ttm = unit >> 2; + mmcr1 |= (unsigned long)ttm + << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); + } + + /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + psel = event[i] & PM_PMCSEL_MSK; + isbus = event[i] & PM_BUSEVENT_MSK; + if (!pmc) { + /* Bus event or any-PMC direct event */ + for (pmc = 0; pmc < 4; ++pmc) { + if (!(pmc_inuse & (1 << pmc))) + break; + } + if (pmc >= 4) + return -1; + pmc_inuse |= 1 << pmc; + } else if (pmc <= 4) { + /* Direct event */ + --pmc; + if (isbus && (byte & 2) && + (psel == 8 || psel == 0x10 || psel == 0x28)) + /* add events on higher-numbered bus */ + mmcr1 |= 1ul << (MMCR1_PMC1_ADDER_SEL_SH - pmc); + } else { + /* Instructions or run cycles on PMC5/6 */ + --pmc; + } + if (isbus && unit == PM_GRS) { + bit = psel & 7; + grsel = (event[i] >> PM_GRS_SH) & PM_GRS_MSK; + mmcr1 |= (unsigned long)grsel << grsel_shift[bit]; + } + if (power5p_marked_instr_event(event[i])) + mmcra |= MMCRA_SAMPLE_ENABLE; + if ((psel & 0x58) == 0x40 && (byte & 1) != ((pmc >> 1) & 1)) + /* select alternate byte lane */ + psel |= 0x10; + if (pmc <= 3) + mmcr1 |= psel << MMCR1_PMCSEL_SH(pmc); + hwc[i] = pmc; + } + + /* Return MMCRx values */ + mmcr[0] = 0; + if (pmc_inuse & 1) + mmcr[0] = MMCR0_PMC1CE; + if (pmc_inuse & 0x3e) + mmcr[0] |= MMCR0_PMCjCE; + mmcr[1] = mmcr1; + mmcr[2] = mmcra; + return 0; +} + +static void power5p_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +{ + if (pmc <= 3) + mmcr[1] &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); +} + +static int power5p_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 0xf, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x100009, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x1c10a8, /* LD_REF_L1 */ + [PERF_COUNT_HW_CACHE_MISSES] = 0x3c1088, /* LD_MISS_L1 */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x230e4, /* BR_ISSUED */ + [PERF_COUNT_HW_BRANCH_MISSES] = 0x230e5, /* BR_MPRED_CR */ +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static int power5p_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x1c10a8, 0x3c1088 }, + [C(OP_WRITE)] = { 0x2c10a8, 0xc10c3 }, + [C(OP_PREFETCH)] = { 0xc70e7, -1 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { 0, 0 }, + [C(OP_PREFETCH)] = { 0xc50c3, 0 }, + }, + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0xc20e4, 0x800c4 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x800c0 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x230e4, 0x230e5 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +static struct power_pmu power5p_pmu = { + .name = "POWER5+/++", + .n_counter = 6, + .max_alternatives = MAX_ALT, + .add_fields = 0x7000000000055ul, + .test_adder = 0x3000040000000ul, + .compute_mmcr = power5p_compute_mmcr, + .get_constraint = power5p_get_constraint, + .get_alternatives = power5p_get_alternatives, + .disable_pmc = power5p_disable_pmc, + .limited_pmc_event = power5p_limited_pmc_event, + .flags = PPMU_LIMITED_PMC5_6, + .n_generic = ARRAY_SIZE(power5p_generic_events), + .generic_events = power5p_generic_events, + .cache_events = &power5p_cache_events, +}; + +static int __init init_power5p_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type || + (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5+") + && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5++"))) + return -ENODEV; + + return register_power_pmu(&power5p_pmu); +} + +early_initcall(init_power5p_pmu); diff --git a/arch/powerpc/perf/power5-pmu.c b/arch/powerpc/perf/power5-pmu.c new file mode 100644 index 0000000..e7f06eb --- /dev/null +++ b/arch/powerpc/perf/power5-pmu.c @@ -0,0 +1,629 @@ +/* + * Performance counter support for POWER5 (not POWER5++) processors. + * + * Copyright 2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include +#include + +/* + * Bits in event code for POWER5 (not POWER5++) + */ +#define PM_PMC_SH 20 /* PMC number (1-based) for direct events */ +#define PM_PMC_MSK 0xf +#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) +#define PM_UNIT_SH 16 /* TTMMUX number and setting - unit select */ +#define PM_UNIT_MSK 0xf +#define PM_BYTE_SH 12 /* Byte number of event bus to use */ +#define PM_BYTE_MSK 7 +#define PM_GRS_SH 8 /* Storage subsystem mux select */ +#define PM_GRS_MSK 7 +#define PM_BUSEVENT_MSK 0x80 /* Set if event uses event bus */ +#define PM_PMCSEL_MSK 0x7f + +/* Values in PM_UNIT field */ +#define PM_FPU 0 +#define PM_ISU0 1 +#define PM_IFU 2 +#define PM_ISU1 3 +#define PM_IDU 4 +#define PM_ISU0_ALT 6 +#define PM_GRS 7 +#define PM_LSU0 8 +#define PM_LSU1 0xc +#define PM_LASTUNIT 0xc + +/* + * Bits in MMCR1 for POWER5 + */ +#define MMCR1_TTM0SEL_SH 62 +#define MMCR1_TTM1SEL_SH 60 +#define MMCR1_TTM2SEL_SH 58 +#define MMCR1_TTM3SEL_SH 56 +#define MMCR1_TTMSEL_MSK 3 +#define MMCR1_TD_CP_DBG0SEL_SH 54 +#define MMCR1_TD_CP_DBG1SEL_SH 52 +#define MMCR1_TD_CP_DBG2SEL_SH 50 +#define MMCR1_TD_CP_DBG3SEL_SH 48 +#define MMCR1_GRS_L2SEL_SH 46 +#define MMCR1_GRS_L2SEL_MSK 3 +#define MMCR1_GRS_L3SEL_SH 44 +#define MMCR1_GRS_L3SEL_MSK 3 +#define MMCR1_GRS_MCSEL_SH 41 +#define MMCR1_GRS_MCSEL_MSK 7 +#define MMCR1_GRS_FABSEL_SH 39 +#define MMCR1_GRS_FABSEL_MSK 3 +#define MMCR1_PMC1_ADDER_SEL_SH 35 +#define MMCR1_PMC2_ADDER_SEL_SH 34 +#define MMCR1_PMC3_ADDER_SEL_SH 33 +#define MMCR1_PMC4_ADDER_SEL_SH 32 +#define MMCR1_PMC1SEL_SH 25 +#define MMCR1_PMC2SEL_SH 17 +#define MMCR1_PMC3SEL_SH 9 +#define MMCR1_PMC4SEL_SH 1 +#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) +#define MMCR1_PMCSEL_MSK 0x7f + +/* + * Layout of constraint bits: + * 6666555555555544444444443333333333222222222211111111110000000000 + * 3210987654321098765432109876543210987654321098765432109876543210 + * <><>[ ><><>< ><> [ >[ >[ >< >< >< >< ><><><><><><> + * T0T1 NC G0G1G2 G3 UC PS1PS2 B0 B1 B2 B3 P6P5P4P3P2P1 + * + * T0 - TTM0 constraint + * 54-55: TTM0SEL value (0=FPU, 2=IFU, 3=ISU1) 0xc0_0000_0000_0000 + * + * T1 - TTM1 constraint + * 52-53: TTM1SEL value (0=IDU, 3=GRS) 0x30_0000_0000_0000 + * + * NC - number of counters + * 51: NC error 0x0008_0000_0000_0000 + * 48-50: number of events needing PMC1-4 0x0007_0000_0000_0000 + * + * G0..G3 - GRS mux constraints + * 46-47: GRS_L2SEL value + * 44-45: GRS_L3SEL value + * 41-44: GRS_MCSEL value + * 39-40: GRS_FABSEL value + * Note that these match up with their bit positions in MMCR1 + * + * UC - unit constraint: can't have all three of FPU|IFU|ISU1, ISU0, IDU|GRS + * 37: UC3 error 0x20_0000_0000 + * 36: FPU|IFU|ISU1 events needed 0x10_0000_0000 + * 35: ISU0 events needed 0x08_0000_0000 + * 34: IDU|GRS events needed 0x04_0000_0000 + * + * PS1 + * 33: PS1 error 0x2_0000_0000 + * 31-32: count of events needing PMC1/2 0x1_8000_0000 + * + * PS2 + * 30: PS2 error 0x4000_0000 + * 28-29: count of events needing PMC3/4 0x3000_0000 + * + * B0 + * 24-27: Byte 0 event source 0x0f00_0000 + * Encoding as for the event code + * + * B1, B2, B3 + * 20-23, 16-19, 12-15: Byte 1, 2, 3 event sources + * + * P1..P6 + * 0-11: Count of events needing PMC1..PMC6 + */ + +static const int grsel_shift[8] = { + MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, MMCR1_GRS_L2SEL_SH, + MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, MMCR1_GRS_L3SEL_SH, + MMCR1_GRS_MCSEL_SH, MMCR1_GRS_FABSEL_SH +}; + +/* Masks and values for using events from the various units */ +static unsigned long unit_cons[PM_LASTUNIT+1][2] = { + [PM_FPU] = { 0xc0002000000000ul, 0x00001000000000ul }, + [PM_ISU0] = { 0x00002000000000ul, 0x00000800000000ul }, + [PM_ISU1] = { 0xc0002000000000ul, 0xc0001000000000ul }, + [PM_IFU] = { 0xc0002000000000ul, 0x80001000000000ul }, + [PM_IDU] = { 0x30002000000000ul, 0x00000400000000ul }, + [PM_GRS] = { 0x30002000000000ul, 0x30000400000000ul }, +}; + +static int power5_get_constraint(u64 event, unsigned long *maskp, + unsigned long *valp) +{ + int pmc, byte, unit, sh; + int bit, fmask; + unsigned long mask = 0, value = 0; + int grp = -1; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 6) + return -1; + sh = (pmc - 1) * 2; + mask |= 2 << sh; + value |= 1 << sh; + if (pmc <= 4) + grp = (pmc - 1) >> 1; + else if (event != 0x500009 && event != 0x600005) + return -1; + } + if (event & PM_BUSEVENT_MSK) { + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + if (unit > PM_LASTUNIT) + return -1; + if (unit == PM_ISU0_ALT) + unit = PM_ISU0; + mask |= unit_cons[unit][0]; + value |= unit_cons[unit][1]; + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + if (byte >= 4) { + if (unit != PM_LSU1) + return -1; + /* Map LSU1 low word (bytes 4-7) to unit LSU1+1 */ + ++unit; + byte &= 3; + } + if (unit == PM_GRS) { + bit = event & 7; + fmask = (bit == 6)? 7: 3; + sh = grsel_shift[bit]; + mask |= (unsigned long)fmask << sh; + value |= (unsigned long)((event >> PM_GRS_SH) & fmask) + << sh; + } + /* + * Bus events on bytes 0 and 2 can be counted + * on PMC1/2; bytes 1 and 3 on PMC3/4. + */ + if (!pmc) + grp = byte & 1; + /* Set byte lane select field */ + mask |= 0xfUL << (24 - 4 * byte); + value |= (unsigned long)unit << (24 - 4 * byte); + } + if (grp == 0) { + /* increment PMC1/2 field */ + mask |= 0x200000000ul; + value |= 0x080000000ul; + } else if (grp == 1) { + /* increment PMC3/4 field */ + mask |= 0x40000000ul; + value |= 0x10000000ul; + } + if (pmc < 5) { + /* need a counter from PMC1-4 set */ + mask |= 0x8000000000000ul; + value |= 0x1000000000000ul; + } + *maskp = mask; + *valp = value; + return 0; +} + +#define MAX_ALT 3 /* at most 3 alternatives for any event */ + +static const unsigned int event_alternatives[][MAX_ALT] = { + { 0x120e4, 0x400002 }, /* PM_GRP_DISP_REJECT */ + { 0x410c7, 0x441084 }, /* PM_THRD_L2MISS_BOTH_CYC */ + { 0x100005, 0x600005 }, /* PM_RUN_CYC */ + { 0x100009, 0x200009, 0x500009 }, /* PM_INST_CMPL */ + { 0x300009, 0x400009 }, /* PM_INST_DISP */ +}; + +/* + * Scan the alternatives table for a match and return the + * index into the alternatives table if found, else -1. + */ +static int find_alternative(u64 event) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { + if (event < event_alternatives[i][0]) + break; + for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) + if (event == event_alternatives[i][j]) + return i; + } + return -1; +} + +static const unsigned char bytedecode_alternatives[4][4] = { + /* PMC 1 */ { 0x21, 0x23, 0x25, 0x27 }, + /* PMC 2 */ { 0x07, 0x17, 0x0e, 0x1e }, + /* PMC 3 */ { 0x20, 0x22, 0x24, 0x26 }, + /* PMC 4 */ { 0x07, 0x17, 0x0e, 0x1e } +}; + +/* + * Some direct events for decodes of event bus byte 3 have alternative + * PMCSEL values on other counters. This returns the alternative + * event code for those that do, or -1 otherwise. + */ +static s64 find_alternative_bdecode(u64 event) +{ + int pmc, altpmc, pp, j; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc == 0 || pmc > 4) + return -1; + altpmc = 5 - pmc; /* 1 <-> 4, 2 <-> 3 */ + pp = event & PM_PMCSEL_MSK; + for (j = 0; j < 4; ++j) { + if (bytedecode_alternatives[pmc - 1][j] == pp) { + return (event & ~(PM_PMC_MSKS | PM_PMCSEL_MSK)) | + (altpmc << PM_PMC_SH) | + bytedecode_alternatives[altpmc - 1][j]; + } + } + return -1; +} + +static int power5_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int i, j, nalt = 1; + s64 ae; + + alt[0] = event; + nalt = 1; + i = find_alternative(event); + if (i >= 0) { + for (j = 0; j < MAX_ALT; ++j) { + ae = event_alternatives[i][j]; + if (ae && ae != event) + alt[nalt++] = ae; + } + } else { + ae = find_alternative_bdecode(event); + if (ae > 0) + alt[nalt++] = ae; + } + return nalt; +} + +/* + * Map of which direct events on which PMCs are marked instruction events. + * Indexed by PMCSEL value, bit i (LE) set if PMC i is a marked event. + * Bit 0 is set if it is marked for all PMCs. + * The 0x80 bit indicates a byte decode PMCSEL value. + */ +static unsigned char direct_event_is_marked[0x28] = { + 0, /* 00 */ + 0x1f, /* 01 PM_IOPS_CMPL */ + 0x2, /* 02 PM_MRK_GRP_DISP */ + 0xe, /* 03 PM_MRK_ST_CMPL, PM_MRK_ST_GPS, PM_MRK_ST_CMPL_INT */ + 0, /* 04 */ + 0x1c, /* 05 PM_MRK_BRU_FIN, PM_MRK_INST_FIN, PM_MRK_CRU_FIN */ + 0x80, /* 06 */ + 0x80, /* 07 */ + 0, 0, 0,/* 08 - 0a */ + 0x18, /* 0b PM_THRESH_TIMEO, PM_MRK_GRP_TIMEO */ + 0, /* 0c */ + 0x80, /* 0d */ + 0x80, /* 0e */ + 0, /* 0f */ + 0, /* 10 */ + 0x14, /* 11 PM_MRK_GRP_BR_REDIR, PM_MRK_GRP_IC_MISS */ + 0, /* 12 */ + 0x10, /* 13 PM_MRK_GRP_CMPL */ + 0x1f, /* 14 PM_GRP_MRK, PM_MRK_{FXU,FPU,LSU}_FIN */ + 0x2, /* 15 PM_MRK_GRP_ISSUED */ + 0x80, /* 16 */ + 0x80, /* 17 */ + 0, 0, 0, 0, 0, + 0x80, /* 1d */ + 0x80, /* 1e */ + 0, /* 1f */ + 0x80, /* 20 */ + 0x80, /* 21 */ + 0x80, /* 22 */ + 0x80, /* 23 */ + 0x80, /* 24 */ + 0x80, /* 25 */ + 0x80, /* 26 */ + 0x80, /* 27 */ +}; + +/* + * Returns 1 if event counts things relating to marked instructions + * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. + */ +static int power5_marked_instr_event(u64 event) +{ + int pmc, psel; + int bit, byte, unit; + u32 mask; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + psel = event & PM_PMCSEL_MSK; + if (pmc >= 5) + return 0; + + bit = -1; + if (psel < sizeof(direct_event_is_marked)) { + if (direct_event_is_marked[psel] & (1 << pmc)) + return 1; + if (direct_event_is_marked[psel] & 0x80) + bit = 4; + else if (psel == 0x08) + bit = pmc - 1; + else if (psel == 0x10) + bit = 4 - pmc; + else if (psel == 0x1b && (pmc == 1 || pmc == 3)) + bit = 4; + } else if ((psel & 0x58) == 0x40) + bit = psel & 7; + + if (!(event & PM_BUSEVENT_MSK)) + return 0; + + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + if (unit == PM_LSU0) { + /* byte 1 bits 0-7, byte 2 bits 0,2-4,6 */ + mask = 0x5dff00; + } else if (unit == PM_LSU1 && byte >= 4) { + byte -= 4; + /* byte 4 bits 1,3,5,7, byte 5 bits 6-7, byte 7 bits 0-4,6 */ + mask = 0x5f00c0aa; + } else + return 0; + + return (mask >> (byte * 8 + bit)) & 1; +} + +static int power5_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]) +{ + unsigned long mmcr1 = 0; + unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; + unsigned int pmc, unit, byte, psel; + unsigned int ttm, grp; + int i, isbus, bit, grsel; + unsigned int pmc_inuse = 0; + unsigned int pmc_grp_use[2]; + unsigned char busbyte[4]; + unsigned char unituse[16]; + int ttmuse; + + if (n_ev > 6) + return -1; + + /* First pass to count resource use */ + pmc_grp_use[0] = pmc_grp_use[1] = 0; + memset(busbyte, 0, sizeof(busbyte)); + memset(unituse, 0, sizeof(unituse)); + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 6) + return -1; + if (pmc_inuse & (1 << (pmc - 1))) + return -1; + pmc_inuse |= 1 << (pmc - 1); + /* count 1/2 vs 3/4 use */ + if (pmc <= 4) + ++pmc_grp_use[(pmc - 1) >> 1]; + } + if (event[i] & PM_BUSEVENT_MSK) { + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + if (unit > PM_LASTUNIT) + return -1; + if (unit == PM_ISU0_ALT) + unit = PM_ISU0; + if (byte >= 4) { + if (unit != PM_LSU1) + return -1; + ++unit; + byte &= 3; + } + if (!pmc) + ++pmc_grp_use[byte & 1]; + if (busbyte[byte] && busbyte[byte] != unit) + return -1; + busbyte[byte] = unit; + unituse[unit] = 1; + } + } + if (pmc_grp_use[0] > 2 || pmc_grp_use[1] > 2) + return -1; + + /* + * Assign resources and set multiplexer selects. + * + * PM_ISU0 can go either on TTM0 or TTM1, but that's the only + * choice we have to deal with. + */ + if (unituse[PM_ISU0] & + (unituse[PM_FPU] | unituse[PM_IFU] | unituse[PM_ISU1])) { + unituse[PM_ISU0_ALT] = 1; /* move ISU to TTM1 */ + unituse[PM_ISU0] = 0; + } + /* Set TTM[01]SEL fields. */ + ttmuse = 0; + for (i = PM_FPU; i <= PM_ISU1; ++i) { + if (!unituse[i]) + continue; + if (ttmuse++) + return -1; + mmcr1 |= (unsigned long)i << MMCR1_TTM0SEL_SH; + } + ttmuse = 0; + for (; i <= PM_GRS; ++i) { + if (!unituse[i]) + continue; + if (ttmuse++) + return -1; + mmcr1 |= (unsigned long)(i & 3) << MMCR1_TTM1SEL_SH; + } + if (ttmuse > 1) + return -1; + + /* Set byte lane select fields, TTM[23]SEL and GRS_*SEL. */ + for (byte = 0; byte < 4; ++byte) { + unit = busbyte[byte]; + if (!unit) + continue; + if (unit == PM_ISU0 && unituse[PM_ISU0_ALT]) { + /* get ISU0 through TTM1 rather than TTM0 */ + unit = PM_ISU0_ALT; + } else if (unit == PM_LSU1 + 1) { + /* select lower word of LSU1 for this byte */ + mmcr1 |= 1ul << (MMCR1_TTM3SEL_SH + 3 - byte); + } + ttm = unit >> 2; + mmcr1 |= (unsigned long)ttm + << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); + } + + /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + psel = event[i] & PM_PMCSEL_MSK; + isbus = event[i] & PM_BUSEVENT_MSK; + if (!pmc) { + /* Bus event or any-PMC direct event */ + for (pmc = 0; pmc < 4; ++pmc) { + if (pmc_inuse & (1 << pmc)) + continue; + grp = (pmc >> 1) & 1; + if (isbus) { + if (grp == (byte & 1)) + break; + } else if (pmc_grp_use[grp] < 2) { + ++pmc_grp_use[grp]; + break; + } + } + pmc_inuse |= 1 << pmc; + } else if (pmc <= 4) { + /* Direct event */ + --pmc; + if ((psel == 8 || psel == 0x10) && isbus && (byte & 2)) + /* add events on higher-numbered bus */ + mmcr1 |= 1ul << (MMCR1_PMC1_ADDER_SEL_SH - pmc); + } else { + /* Instructions or run cycles on PMC5/6 */ + --pmc; + } + if (isbus && unit == PM_GRS) { + bit = psel & 7; + grsel = (event[i] >> PM_GRS_SH) & PM_GRS_MSK; + mmcr1 |= (unsigned long)grsel << grsel_shift[bit]; + } + if (power5_marked_instr_event(event[i])) + mmcra |= MMCRA_SAMPLE_ENABLE; + if (pmc <= 3) + mmcr1 |= psel << MMCR1_PMCSEL_SH(pmc); + hwc[i] = pmc; + } + + /* Return MMCRx values */ + mmcr[0] = 0; + if (pmc_inuse & 1) + mmcr[0] = MMCR0_PMC1CE; + if (pmc_inuse & 0x3e) + mmcr[0] |= MMCR0_PMCjCE; + mmcr[1] = mmcr1; + mmcr[2] = mmcra; + return 0; +} + +static void power5_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +{ + if (pmc <= 3) + mmcr[1] &= ~(0x7fUL << MMCR1_PMCSEL_SH(pmc)); +} + +static int power5_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 0xf, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x100009, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x4c1090, /* LD_REF_L1 */ + [PERF_COUNT_HW_CACHE_MISSES] = 0x3c1088, /* LD_MISS_L1 */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x230e4, /* BR_ISSUED */ + [PERF_COUNT_HW_BRANCH_MISSES] = 0x230e5, /* BR_MPRED_CR */ +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static int power5_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x4c1090, 0x3c1088 }, + [C(OP_WRITE)] = { 0x3c1090, 0xc10c3 }, + [C(OP_PREFETCH)] = { 0xc70e7, 0 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x3c309b }, + [C(OP_WRITE)] = { 0, 0 }, + [C(OP_PREFETCH)] = { 0xc50c3, 0 }, + }, + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x2c4090, 0x800c4 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x800c0 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x230e4, 0x230e5 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +static struct power_pmu power5_pmu = { + .name = "POWER5", + .n_counter = 6, + .max_alternatives = MAX_ALT, + .add_fields = 0x7000090000555ul, + .test_adder = 0x3000490000000ul, + .compute_mmcr = power5_compute_mmcr, + .get_constraint = power5_get_constraint, + .get_alternatives = power5_get_alternatives, + .disable_pmc = power5_disable_pmc, + .n_generic = ARRAY_SIZE(power5_generic_events), + .generic_events = power5_generic_events, + .cache_events = &power5_cache_events, +}; + +static int __init init_power5_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type || + strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power5")) + return -ENODEV; + + return register_power_pmu(&power5_pmu); +} + +early_initcall(init_power5_pmu); diff --git a/arch/powerpc/perf/power6-pmu.c b/arch/powerpc/perf/power6-pmu.c new file mode 100644 index 0000000..31128e0 --- /dev/null +++ b/arch/powerpc/perf/power6-pmu.c @@ -0,0 +1,552 @@ +/* + * Performance counter support for POWER6 processors. + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include +#include + +/* + * Bits in event code for POWER6 + */ +#define PM_PMC_SH 20 /* PMC number (1-based) for direct events */ +#define PM_PMC_MSK 0x7 +#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) +#define PM_UNIT_SH 16 /* Unit event comes (TTMxSEL encoding) */ +#define PM_UNIT_MSK 0xf +#define PM_UNIT_MSKS (PM_UNIT_MSK << PM_UNIT_SH) +#define PM_LLAV 0x8000 /* Load lookahead match value */ +#define PM_LLA 0x4000 /* Load lookahead match enable */ +#define PM_BYTE_SH 12 /* Byte of event bus to use */ +#define PM_BYTE_MSK 3 +#define PM_SUBUNIT_SH 8 /* Subunit event comes from (NEST_SEL enc.) */ +#define PM_SUBUNIT_MSK 7 +#define PM_SUBUNIT_MSKS (PM_SUBUNIT_MSK << PM_SUBUNIT_SH) +#define PM_PMCSEL_MSK 0xff /* PMCxSEL value */ +#define PM_BUSEVENT_MSK 0xf3700 + +/* + * Bits in MMCR1 for POWER6 + */ +#define MMCR1_TTM0SEL_SH 60 +#define MMCR1_TTMSEL_SH(n) (MMCR1_TTM0SEL_SH - (n) * 4) +#define MMCR1_TTMSEL_MSK 0xf +#define MMCR1_TTMSEL(m, n) (((m) >> MMCR1_TTMSEL_SH(n)) & MMCR1_TTMSEL_MSK) +#define MMCR1_NESTSEL_SH 45 +#define MMCR1_NESTSEL_MSK 0x7 +#define MMCR1_NESTSEL(m) (((m) >> MMCR1_NESTSEL_SH) & MMCR1_NESTSEL_MSK) +#define MMCR1_PMC1_LLA (1ul << 44) +#define MMCR1_PMC1_LLA_VALUE (1ul << 39) +#define MMCR1_PMC1_ADDR_SEL (1ul << 35) +#define MMCR1_PMC1SEL_SH 24 +#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) +#define MMCR1_PMCSEL_MSK 0xff + +/* + * Map of which direct events on which PMCs are marked instruction events. + * Indexed by PMCSEL value >> 1. + * Bottom 4 bits are a map of which PMCs are interesting, + * top 4 bits say what sort of event: + * 0 = direct marked event, + * 1 = byte decode event, + * 4 = add/and event (PMC1 -> bits 0 & 4), + * 5 = add/and event (PMC1 -> bits 1 & 5), + * 6 = add/and event (PMC1 -> bits 2 & 6), + * 7 = add/and event (PMC1 -> bits 3 & 7). + */ +static unsigned char direct_event_is_marked[0x60 >> 1] = { + 0, /* 00 */ + 0, /* 02 */ + 0, /* 04 */ + 0x07, /* 06 PM_MRK_ST_CMPL, PM_MRK_ST_GPS, PM_MRK_ST_CMPL_INT */ + 0x04, /* 08 PM_MRK_DFU_FIN */ + 0x06, /* 0a PM_MRK_IFU_FIN, PM_MRK_INST_FIN */ + 0, /* 0c */ + 0, /* 0e */ + 0x02, /* 10 PM_MRK_INST_DISP */ + 0x08, /* 12 PM_MRK_LSU_DERAT_MISS */ + 0, /* 14 */ + 0, /* 16 */ + 0x0c, /* 18 PM_THRESH_TIMEO, PM_MRK_INST_FIN */ + 0x0f, /* 1a PM_MRK_INST_DISP, PM_MRK_{FXU,FPU,LSU}_FIN */ + 0x01, /* 1c PM_MRK_INST_ISSUED */ + 0, /* 1e */ + 0, /* 20 */ + 0, /* 22 */ + 0, /* 24 */ + 0, /* 26 */ + 0x15, /* 28 PM_MRK_DATA_FROM_L2MISS, PM_MRK_DATA_FROM_L3MISS */ + 0, /* 2a */ + 0, /* 2c */ + 0, /* 2e */ + 0x4f, /* 30 */ + 0x7f, /* 32 */ + 0x4f, /* 34 */ + 0x5f, /* 36 */ + 0x6f, /* 38 */ + 0x4f, /* 3a */ + 0, /* 3c */ + 0x08, /* 3e PM_MRK_INST_TIMEO */ + 0x1f, /* 40 */ + 0x1f, /* 42 */ + 0x1f, /* 44 */ + 0x1f, /* 46 */ + 0x1f, /* 48 */ + 0x1f, /* 4a */ + 0x1f, /* 4c */ + 0x1f, /* 4e */ + 0, /* 50 */ + 0x05, /* 52 PM_MRK_BR_TAKEN, PM_MRK_BR_MPRED */ + 0x1c, /* 54 PM_MRK_PTEG_FROM_L3MISS, PM_MRK_PTEG_FROM_L2MISS */ + 0x02, /* 56 PM_MRK_LD_MISS_L1 */ + 0, /* 58 */ + 0, /* 5a */ + 0, /* 5c */ + 0, /* 5e */ +}; + +/* + * Masks showing for each unit which bits are marked events. + * These masks are in LE order, i.e. 0x00000001 is byte 0, bit 0. + */ +static u32 marked_bus_events[16] = { + 0x01000000, /* direct events set 1: byte 3 bit 0 */ + 0x00010000, /* direct events set 2: byte 2 bit 0 */ + 0, 0, 0, 0, /* IDU, IFU, nest: nothing */ + 0x00000088, /* VMX set 1: byte 0 bits 3, 7 */ + 0x000000c0, /* VMX set 2: byte 0 bits 4-7 */ + 0x04010000, /* LSU set 1: byte 2 bit 0, byte 3 bit 2 */ + 0xff010000u, /* LSU set 2: byte 2 bit 0, all of byte 3 */ + 0, /* LSU set 3 */ + 0x00000010, /* VMX set 3: byte 0 bit 4 */ + 0, /* BFP set 1 */ + 0x00000022, /* BFP set 2: byte 0 bits 1, 5 */ + 0, 0 +}; + +/* + * Returns 1 if event counts things relating to marked instructions + * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. + */ +static int power6_marked_instr_event(u64 event) +{ + int pmc, psel, ptype; + int bit, byte, unit; + u32 mask; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + psel = (event & PM_PMCSEL_MSK) >> 1; /* drop edge/level bit */ + if (pmc >= 5) + return 0; + + bit = -1; + if (psel < sizeof(direct_event_is_marked)) { + ptype = direct_event_is_marked[psel]; + if (pmc == 0 || !(ptype & (1 << (pmc - 1)))) + return 0; + ptype >>= 4; + if (ptype == 0) + return 1; + if (ptype == 1) + bit = 0; + else + bit = ptype ^ (pmc - 1); + } else if ((psel & 0x48) == 0x40) + bit = psel & 7; + + if (!(event & PM_BUSEVENT_MSK) || bit == -1) + return 0; + + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + mask = marked_bus_events[unit]; + return (mask >> (byte * 8 + bit)) & 1; +} + +/* + * Assign PMC numbers and compute MMCR1 value for a set of events + */ +static int p6_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]) +{ + unsigned long mmcr1 = 0; + unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; + int i; + unsigned int pmc, ev, b, u, s, psel; + unsigned int ttmset = 0; + unsigned int pmc_inuse = 0; + + if (n_ev > 6) + return -1; + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc_inuse & (1 << (pmc - 1))) + return -1; /* collision! */ + pmc_inuse |= 1 << (pmc - 1); + } + } + for (i = 0; i < n_ev; ++i) { + ev = event[i]; + pmc = (ev >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + --pmc; + } else { + /* can go on any PMC; find a free one */ + for (pmc = 0; pmc < 4; ++pmc) + if (!(pmc_inuse & (1 << pmc))) + break; + if (pmc >= 4) + return -1; + pmc_inuse |= 1 << pmc; + } + hwc[i] = pmc; + psel = ev & PM_PMCSEL_MSK; + if (ev & PM_BUSEVENT_MSK) { + /* this event uses the event bus */ + b = (ev >> PM_BYTE_SH) & PM_BYTE_MSK; + u = (ev >> PM_UNIT_SH) & PM_UNIT_MSK; + /* check for conflict on this byte of event bus */ + if ((ttmset & (1 << b)) && MMCR1_TTMSEL(mmcr1, b) != u) + return -1; + mmcr1 |= (unsigned long)u << MMCR1_TTMSEL_SH(b); + ttmset |= 1 << b; + if (u == 5) { + /* Nest events have a further mux */ + s = (ev >> PM_SUBUNIT_SH) & PM_SUBUNIT_MSK; + if ((ttmset & 0x10) && + MMCR1_NESTSEL(mmcr1) != s) + return -1; + ttmset |= 0x10; + mmcr1 |= (unsigned long)s << MMCR1_NESTSEL_SH; + } + if (0x30 <= psel && psel <= 0x3d) { + /* these need the PMCx_ADDR_SEL bits */ + if (b >= 2) + mmcr1 |= MMCR1_PMC1_ADDR_SEL >> pmc; + } + /* bus select values are different for PMC3/4 */ + if (pmc >= 2 && (psel & 0x90) == 0x80) + psel ^= 0x20; + } + if (ev & PM_LLA) { + mmcr1 |= MMCR1_PMC1_LLA >> pmc; + if (ev & PM_LLAV) + mmcr1 |= MMCR1_PMC1_LLA_VALUE >> pmc; + } + if (power6_marked_instr_event(event[i])) + mmcra |= MMCRA_SAMPLE_ENABLE; + if (pmc < 4) + mmcr1 |= (unsigned long)psel << MMCR1_PMCSEL_SH(pmc); + } + mmcr[0] = 0; + if (pmc_inuse & 1) + mmcr[0] = MMCR0_PMC1CE; + if (pmc_inuse & 0xe) + mmcr[0] |= MMCR0_PMCjCE; + mmcr[1] = mmcr1; + mmcr[2] = mmcra; + return 0; +} + +/* + * Layout of constraint bits: + * + * 0-1 add field: number of uses of PMC1 (max 1) + * 2-3, 4-5, 6-7, 8-9, 10-11: ditto for PMC2, 3, 4, 5, 6 + * 12-15 add field: number of uses of PMC1-4 (max 4) + * 16-19 select field: unit on byte 0 of event bus + * 20-23, 24-27, 28-31 ditto for bytes 1, 2, 3 + * 32-34 select field: nest (subunit) event selector + */ +static int p6_get_constraint(u64 event, unsigned long *maskp, + unsigned long *valp) +{ + int pmc, byte, sh, subunit; + unsigned long mask = 0, value = 0; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 4 && !(event == 0x500009 || event == 0x600005)) + return -1; + sh = (pmc - 1) * 2; + mask |= 2 << sh; + value |= 1 << sh; + } + if (event & PM_BUSEVENT_MSK) { + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + sh = byte * 4 + (16 - PM_UNIT_SH); + mask |= PM_UNIT_MSKS << sh; + value |= (unsigned long)(event & PM_UNIT_MSKS) << sh; + if ((event & PM_UNIT_MSKS) == (5 << PM_UNIT_SH)) { + subunit = (event >> PM_SUBUNIT_SH) & PM_SUBUNIT_MSK; + mask |= (unsigned long)PM_SUBUNIT_MSK << 32; + value |= (unsigned long)subunit << 32; + } + } + if (pmc <= 4) { + mask |= 0x8000; /* add field for count of PMC1-4 uses */ + value |= 0x1000; + } + *maskp = mask; + *valp = value; + return 0; +} + +static int p6_limited_pmc_event(u64 event) +{ + int pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + + return pmc == 5 || pmc == 6; +} + +#define MAX_ALT 4 /* at most 4 alternatives for any event */ + +static const unsigned int event_alternatives[][MAX_ALT] = { + { 0x0130e8, 0x2000f6, 0x3000fc }, /* PM_PTEG_RELOAD_VALID */ + { 0x080080, 0x10000d, 0x30000c, 0x4000f0 }, /* PM_LD_MISS_L1 */ + { 0x080088, 0x200054, 0x3000f0 }, /* PM_ST_MISS_L1 */ + { 0x10000a, 0x2000f4, 0x600005 }, /* PM_RUN_CYC */ + { 0x10000b, 0x2000f5 }, /* PM_RUN_COUNT */ + { 0x10000e, 0x400010 }, /* PM_PURR */ + { 0x100010, 0x4000f8 }, /* PM_FLUSH */ + { 0x10001a, 0x200010 }, /* PM_MRK_INST_DISP */ + { 0x100026, 0x3000f8 }, /* PM_TB_BIT_TRANS */ + { 0x100054, 0x2000f0 }, /* PM_ST_FIN */ + { 0x100056, 0x2000fc }, /* PM_L1_ICACHE_MISS */ + { 0x1000f0, 0x40000a }, /* PM_INST_IMC_MATCH_CMPL */ + { 0x1000f8, 0x200008 }, /* PM_GCT_EMPTY_CYC */ + { 0x1000fc, 0x400006 }, /* PM_LSU_DERAT_MISS_CYC */ + { 0x20000e, 0x400007 }, /* PM_LSU_DERAT_MISS */ + { 0x200012, 0x300012 }, /* PM_INST_DISP */ + { 0x2000f2, 0x3000f2 }, /* PM_INST_DISP */ + { 0x2000f8, 0x300010 }, /* PM_EXT_INT */ + { 0x2000fe, 0x300056 }, /* PM_DATA_FROM_L2MISS */ + { 0x2d0030, 0x30001a }, /* PM_MRK_FPU_FIN */ + { 0x30000a, 0x400018 }, /* PM_MRK_INST_FIN */ + { 0x3000f6, 0x40000e }, /* PM_L1_DCACHE_RELOAD_VALID */ + { 0x3000fe, 0x400056 }, /* PM_DATA_FROM_L3MISS */ +}; + +/* + * This could be made more efficient with a binary search on + * a presorted list, if necessary + */ +static int find_alternatives_list(u64 event) +{ + int i, j; + unsigned int alt; + + for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { + if (event < event_alternatives[i][0]) + return -1; + for (j = 0; j < MAX_ALT; ++j) { + alt = event_alternatives[i][j]; + if (!alt || event < alt) + break; + if (event == alt) + return i; + } + } + return -1; +} + +static int p6_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int i, j, nlim; + unsigned int psel, pmc; + unsigned int nalt = 1; + u64 aevent; + + alt[0] = event; + nlim = p6_limited_pmc_event(event); + + /* check the alternatives table */ + i = find_alternatives_list(event); + if (i >= 0) { + /* copy out alternatives from list */ + for (j = 0; j < MAX_ALT; ++j) { + aevent = event_alternatives[i][j]; + if (!aevent) + break; + if (aevent != event) + alt[nalt++] = aevent; + nlim += p6_limited_pmc_event(aevent); + } + + } else { + /* Check for alternative ways of computing sum events */ + /* PMCSEL 0x32 counter N == PMCSEL 0x34 counter 5-N */ + psel = event & (PM_PMCSEL_MSK & ~1); /* ignore edge bit */ + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc && (psel == 0x32 || psel == 0x34)) + alt[nalt++] = ((event ^ 0x6) & ~PM_PMC_MSKS) | + ((5 - pmc) << PM_PMC_SH); + + /* PMCSEL 0x38 counter N == PMCSEL 0x3a counter N+/-2 */ + if (pmc && (psel == 0x38 || psel == 0x3a)) + alt[nalt++] = ((event ^ 0x2) & ~PM_PMC_MSKS) | + ((pmc > 2? pmc - 2: pmc + 2) << PM_PMC_SH); + } + + if (flags & PPMU_ONLY_COUNT_RUN) { + /* + * We're only counting in RUN state, + * so PM_CYC is equivalent to PM_RUN_CYC, + * PM_INST_CMPL === PM_RUN_INST_CMPL, PM_PURR === PM_RUN_PURR. + * This doesn't include alternatives that don't provide + * any extra flexibility in assigning PMCs (e.g. + * 0x10000a for PM_RUN_CYC vs. 0x1e for PM_CYC). + * Note that even with these additional alternatives + * we never end up with more than 4 alternatives for any event. + */ + j = nalt; + for (i = 0; i < nalt; ++i) { + switch (alt[i]) { + case 0x1e: /* PM_CYC */ + alt[j++] = 0x600005; /* PM_RUN_CYC */ + ++nlim; + break; + case 0x10000a: /* PM_RUN_CYC */ + alt[j++] = 0x1e; /* PM_CYC */ + break; + case 2: /* PM_INST_CMPL */ + alt[j++] = 0x500009; /* PM_RUN_INST_CMPL */ + ++nlim; + break; + case 0x500009: /* PM_RUN_INST_CMPL */ + alt[j++] = 2; /* PM_INST_CMPL */ + break; + case 0x10000e: /* PM_PURR */ + alt[j++] = 0x4000f4; /* PM_RUN_PURR */ + break; + case 0x4000f4: /* PM_RUN_PURR */ + alt[j++] = 0x10000e; /* PM_PURR */ + break; + } + } + nalt = j; + } + + if (!(flags & PPMU_LIMITED_PMC_OK) && nlim) { + /* remove the limited PMC events */ + j = 0; + for (i = 0; i < nalt; ++i) { + if (!p6_limited_pmc_event(alt[i])) { + alt[j] = alt[i]; + ++j; + } + } + nalt = j; + } else if ((flags & PPMU_LIMITED_PMC_REQD) && nlim < nalt) { + /* remove all but the limited PMC events */ + j = 0; + for (i = 0; i < nalt; ++i) { + if (p6_limited_pmc_event(alt[i])) { + alt[j] = alt[i]; + ++j; + } + } + nalt = j; + } + + return nalt; +} + +static void p6_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +{ + /* Set PMCxSEL to 0 to disable PMCx */ + if (pmc <= 3) + mmcr[1] &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); +} + +static int power6_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 0x1e, + [PERF_COUNT_HW_INSTRUCTIONS] = 2, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x280030, /* LD_REF_L1 */ + [PERF_COUNT_HW_CACHE_MISSES] = 0x30000c, /* LD_MISS_L1 */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x410a0, /* BR_PRED */ + [PERF_COUNT_HW_BRANCH_MISSES] = 0x400052, /* BR_MPRED */ +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + * The "DTLB" and "ITLB" events relate to the DERAT and IERAT. + */ +static int power6_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x280030, 0x80080 }, + [C(OP_WRITE)] = { 0x180032, 0x80088 }, + [C(OP_PREFETCH)] = { 0x810a4, 0 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x100056 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0x4008c, 0 }, + }, + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x150730, 0x250532 }, + [C(OP_WRITE)] = { 0x250432, 0x150432 }, + [C(OP_PREFETCH)] = { 0x810a6, 0 }, + }, + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x20000e }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x420ce }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x430e6, 0x400052 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +static struct power_pmu power6_pmu = { + .name = "POWER6", + .n_counter = 6, + .max_alternatives = MAX_ALT, + .add_fields = 0x1555, + .test_adder = 0x3000, + .compute_mmcr = p6_compute_mmcr, + .get_constraint = p6_get_constraint, + .get_alternatives = p6_get_alternatives, + .disable_pmc = p6_disable_pmc, + .limited_pmc_event = p6_limited_pmc_event, + .flags = PPMU_LIMITED_PMC5_6 | PPMU_ALT_SIPR, + .n_generic = ARRAY_SIZE(power6_generic_events), + .generic_events = power6_generic_events, + .cache_events = &power6_cache_events, +}; + +static int __init init_power6_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type || + strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power6")) + return -ENODEV; + + return register_power_pmu(&power6_pmu); +} + +early_initcall(init_power6_pmu); diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c new file mode 100644 index 0000000..1251e4d --- /dev/null +++ b/arch/powerpc/perf/power7-pmu.c @@ -0,0 +1,379 @@ +/* + * Performance counter support for POWER7 processors. + * + * Copyright 2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include +#include + +/* + * Bits in event code for POWER7 + */ +#define PM_PMC_SH 16 /* PMC number (1-based) for direct events */ +#define PM_PMC_MSK 0xf +#define PM_PMC_MSKS (PM_PMC_MSK << PM_PMC_SH) +#define PM_UNIT_SH 12 /* TTMMUX number and setting - unit select */ +#define PM_UNIT_MSK 0xf +#define PM_COMBINE_SH 11 /* Combined event bit */ +#define PM_COMBINE_MSK 1 +#define PM_COMBINE_MSKS 0x800 +#define PM_L2SEL_SH 8 /* L2 event select */ +#define PM_L2SEL_MSK 7 +#define PM_PMCSEL_MSK 0xff + +/* + * Bits in MMCR1 for POWER7 + */ +#define MMCR1_TTM0SEL_SH 60 +#define MMCR1_TTM1SEL_SH 56 +#define MMCR1_TTM2SEL_SH 52 +#define MMCR1_TTM3SEL_SH 48 +#define MMCR1_TTMSEL_MSK 0xf +#define MMCR1_L2SEL_SH 45 +#define MMCR1_L2SEL_MSK 7 +#define MMCR1_PMC1_COMBINE_SH 35 +#define MMCR1_PMC2_COMBINE_SH 34 +#define MMCR1_PMC3_COMBINE_SH 33 +#define MMCR1_PMC4_COMBINE_SH 32 +#define MMCR1_PMC1SEL_SH 24 +#define MMCR1_PMC2SEL_SH 16 +#define MMCR1_PMC3SEL_SH 8 +#define MMCR1_PMC4SEL_SH 0 +#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) +#define MMCR1_PMCSEL_MSK 0xff + +/* + * Layout of constraint bits: + * 6666555555555544444444443333333333222222222211111111110000000000 + * 3210987654321098765432109876543210987654321098765432109876543210 + * [ ><><><><><><> + * NC P6P5P4P3P2P1 + * + * NC - number of counters + * 15: NC error 0x8000 + * 12-14: number of events needing PMC1-4 0x7000 + * + * P6 + * 11: P6 error 0x800 + * 10-11: Count of events needing PMC6 + * + * P1..P5 + * 0-9: Count of events needing PMC1..PMC5 + */ + +static int power7_get_constraint(u64 event, unsigned long *maskp, + unsigned long *valp) +{ + int pmc, sh; + unsigned long mask = 0, value = 0; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 6) + return -1; + sh = (pmc - 1) * 2; + mask |= 2 << sh; + value |= 1 << sh; + if (pmc >= 5 && !(event == 0x500fa || event == 0x600f4)) + return -1; + } + if (pmc < 5) { + /* need a counter from PMC1-4 set */ + mask |= 0x8000; + value |= 0x1000; + } + *maskp = mask; + *valp = value; + return 0; +} + +#define MAX_ALT 2 /* at most 2 alternatives for any event */ + +static const unsigned int event_alternatives[][MAX_ALT] = { + { 0x200f2, 0x300f2 }, /* PM_INST_DISP */ + { 0x200f4, 0x600f4 }, /* PM_RUN_CYC */ + { 0x400fa, 0x500fa }, /* PM_RUN_INST_CMPL */ +}; + +/* + * Scan the alternatives table for a match and return the + * index into the alternatives table if found, else -1. + */ +static int find_alternative(u64 event) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(event_alternatives); ++i) { + if (event < event_alternatives[i][0]) + break; + for (j = 0; j < MAX_ALT && event_alternatives[i][j]; ++j) + if (event == event_alternatives[i][j]) + return i; + } + return -1; +} + +static s64 find_alternative_decode(u64 event) +{ + int pmc, psel; + + /* this only handles the 4x decode events */ + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + psel = event & PM_PMCSEL_MSK; + if ((pmc == 2 || pmc == 4) && (psel & ~7) == 0x40) + return event - (1 << PM_PMC_SH) + 8; + if ((pmc == 1 || pmc == 3) && (psel & ~7) == 0x48) + return event + (1 << PM_PMC_SH) - 8; + return -1; +} + +static int power7_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + int i, j, nalt = 1; + s64 ae; + + alt[0] = event; + nalt = 1; + i = find_alternative(event); + if (i >= 0) { + for (j = 0; j < MAX_ALT; ++j) { + ae = event_alternatives[i][j]; + if (ae && ae != event) + alt[nalt++] = ae; + } + } else { + ae = find_alternative_decode(event); + if (ae > 0) + alt[nalt++] = ae; + } + + if (flags & PPMU_ONLY_COUNT_RUN) { + /* + * We're only counting in RUN state, + * so PM_CYC is equivalent to PM_RUN_CYC + * and PM_INST_CMPL === PM_RUN_INST_CMPL. + * This doesn't include alternatives that don't provide + * any extra flexibility in assigning PMCs. + */ + j = nalt; + for (i = 0; i < nalt; ++i) { + switch (alt[i]) { + case 0x1e: /* PM_CYC */ + alt[j++] = 0x600f4; /* PM_RUN_CYC */ + break; + case 0x600f4: /* PM_RUN_CYC */ + alt[j++] = 0x1e; + break; + case 0x2: /* PM_PPC_CMPL */ + alt[j++] = 0x500fa; /* PM_RUN_INST_CMPL */ + break; + case 0x500fa: /* PM_RUN_INST_CMPL */ + alt[j++] = 0x2; /* PM_PPC_CMPL */ + break; + } + } + nalt = j; + } + + return nalt; +} + +/* + * Returns 1 if event counts things relating to marked instructions + * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. + */ +static int power7_marked_instr_event(u64 event) +{ + int pmc, psel; + int unit; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + psel = event & PM_PMCSEL_MSK & ~1; /* trim off edge/level bit */ + if (pmc >= 5) + return 0; + + switch (psel >> 4) { + case 2: + return pmc == 2 || pmc == 4; + case 3: + if (psel == 0x3c) + return pmc == 1; + if (psel == 0x3e) + return pmc != 2; + return 1; + case 4: + case 5: + return unit == 0xd; + case 6: + if (psel == 0x64) + return pmc >= 3; + case 8: + return unit == 0xd; + } + return 0; +} + +static int power7_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]) +{ + unsigned long mmcr1 = 0; + unsigned long mmcra = MMCRA_SDAR_DCACHE_MISS | MMCRA_SDAR_ERAT_MISS; + unsigned int pmc, unit, combine, l2sel, psel; + unsigned int pmc_inuse = 0; + int i; + + /* First pass to count resource use */ + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 6) + return -1; + if (pmc_inuse & (1 << (pmc - 1))) + return -1; + pmc_inuse |= 1 << (pmc - 1); + } + } + + /* Second pass: assign PMCs, set all MMCR1 fields */ + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + combine = (event[i] >> PM_COMBINE_SH) & PM_COMBINE_MSK; + l2sel = (event[i] >> PM_L2SEL_SH) & PM_L2SEL_MSK; + psel = event[i] & PM_PMCSEL_MSK; + if (!pmc) { + /* Bus event or any-PMC direct event */ + for (pmc = 0; pmc < 4; ++pmc) { + if (!(pmc_inuse & (1 << pmc))) + break; + } + if (pmc >= 4) + return -1; + pmc_inuse |= 1 << pmc; + } else { + /* Direct or decoded event */ + --pmc; + } + if (pmc <= 3) { + mmcr1 |= (unsigned long) unit + << (MMCR1_TTM0SEL_SH - 4 * pmc); + mmcr1 |= (unsigned long) combine + << (MMCR1_PMC1_COMBINE_SH - pmc); + mmcr1 |= psel << MMCR1_PMCSEL_SH(pmc); + if (unit == 6) /* L2 events */ + mmcr1 |= (unsigned long) l2sel + << MMCR1_L2SEL_SH; + } + if (power7_marked_instr_event(event[i])) + mmcra |= MMCRA_SAMPLE_ENABLE; + hwc[i] = pmc; + } + + /* Return MMCRx values */ + mmcr[0] = 0; + if (pmc_inuse & 1) + mmcr[0] = MMCR0_PMC1CE; + if (pmc_inuse & 0x3e) + mmcr[0] |= MMCR0_PMCjCE; + mmcr[1] = mmcr1; + mmcr[2] = mmcra; + return 0; +} + +static void power7_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +{ + if (pmc <= 3) + mmcr[1] &= ~(0xffUL << MMCR1_PMCSEL_SH(pmc)); +} + +static int power7_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 0x1e, + [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x100f8, /* GCT_NOSLOT_CYC */ + [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x4000a, /* CMPLU_STALL */ + [PERF_COUNT_HW_INSTRUCTIONS] = 2, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0xc880, /* LD_REF_L1_LSU*/ + [PERF_COUNT_HW_CACHE_MISSES] = 0x400f0, /* LD_MISS_L1 */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x10068, /* BRU_FIN */ + [PERF_COUNT_HW_BRANCH_MISSES] = 0x400f6, /* BR_MPRED */ +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0xc880, 0x400f0 }, + [C(OP_WRITE)] = { 0, 0x300f0 }, + [C(OP_PREFETCH)] = { 0xd8b8, 0 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x200fc }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0x408a, 0 }, + }, + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x16080, 0x26080 }, + [C(OP_WRITE)] = { 0x16082, 0x26082 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x300fc }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x400fc }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x10068, 0x400f6 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +static struct power_pmu power7_pmu = { + .name = "POWER7", + .n_counter = 6, + .max_alternatives = MAX_ALT + 1, + .add_fields = 0x1555ul, + .test_adder = 0x3000ul, + .compute_mmcr = power7_compute_mmcr, + .get_constraint = power7_get_constraint, + .get_alternatives = power7_get_alternatives, + .disable_pmc = power7_disable_pmc, + .flags = PPMU_ALT_SIPR, + .n_generic = ARRAY_SIZE(power7_generic_events), + .generic_events = power7_generic_events, + .cache_events = &power7_cache_events, +}; + +static int __init init_power7_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type || + strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power7")) + return -ENODEV; + + return register_power_pmu(&power7_pmu); +} + +early_initcall(init_power7_pmu); diff --git a/arch/powerpc/perf/ppc970-pmu.c b/arch/powerpc/perf/ppc970-pmu.c new file mode 100644 index 0000000..111eb25 --- /dev/null +++ b/arch/powerpc/perf/ppc970-pmu.c @@ -0,0 +1,502 @@ +/* + * Performance counter support for PPC970-family processors. + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * + * 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 +#include +#include +#include + +/* + * Bits in event code for PPC970 + */ +#define PM_PMC_SH 12 /* PMC number (1-based) for direct events */ +#define PM_PMC_MSK 0xf +#define PM_UNIT_SH 8 /* TTMMUX number and setting - unit select */ +#define PM_UNIT_MSK 0xf +#define PM_SPCSEL_SH 6 +#define PM_SPCSEL_MSK 3 +#define PM_BYTE_SH 4 /* Byte number of event bus to use */ +#define PM_BYTE_MSK 3 +#define PM_PMCSEL_MSK 0xf + +/* Values in PM_UNIT field */ +#define PM_NONE 0 +#define PM_FPU 1 +#define PM_VPU 2 +#define PM_ISU 3 +#define PM_IFU 4 +#define PM_IDU 5 +#define PM_STS 6 +#define PM_LSU0 7 +#define PM_LSU1U 8 +#define PM_LSU1L 9 +#define PM_LASTUNIT 9 + +/* + * Bits in MMCR0 for PPC970 + */ +#define MMCR0_PMC1SEL_SH 8 +#define MMCR0_PMC2SEL_SH 1 +#define MMCR_PMCSEL_MSK 0x1f + +/* + * Bits in MMCR1 for PPC970 + */ +#define MMCR1_TTM0SEL_SH 62 +#define MMCR1_TTM1SEL_SH 59 +#define MMCR1_TTM3SEL_SH 53 +#define MMCR1_TTMSEL_MSK 3 +#define MMCR1_TD_CP_DBG0SEL_SH 50 +#define MMCR1_TD_CP_DBG1SEL_SH 48 +#define MMCR1_TD_CP_DBG2SEL_SH 46 +#define MMCR1_TD_CP_DBG3SEL_SH 44 +#define MMCR1_PMC1_ADDER_SEL_SH 39 +#define MMCR1_PMC2_ADDER_SEL_SH 38 +#define MMCR1_PMC6_ADDER_SEL_SH 37 +#define MMCR1_PMC5_ADDER_SEL_SH 36 +#define MMCR1_PMC8_ADDER_SEL_SH 35 +#define MMCR1_PMC7_ADDER_SEL_SH 34 +#define MMCR1_PMC3_ADDER_SEL_SH 33 +#define MMCR1_PMC4_ADDER_SEL_SH 32 +#define MMCR1_PMC3SEL_SH 27 +#define MMCR1_PMC4SEL_SH 22 +#define MMCR1_PMC5SEL_SH 17 +#define MMCR1_PMC6SEL_SH 12 +#define MMCR1_PMC7SEL_SH 7 +#define MMCR1_PMC8SEL_SH 2 + +static short mmcr1_adder_bits[8] = { + MMCR1_PMC1_ADDER_SEL_SH, + MMCR1_PMC2_ADDER_SEL_SH, + MMCR1_PMC3_ADDER_SEL_SH, + MMCR1_PMC4_ADDER_SEL_SH, + MMCR1_PMC5_ADDER_SEL_SH, + MMCR1_PMC6_ADDER_SEL_SH, + MMCR1_PMC7_ADDER_SEL_SH, + MMCR1_PMC8_ADDER_SEL_SH +}; + +/* + * Layout of constraint bits: + * 6666555555555544444444443333333333222222222211111111110000000000 + * 3210987654321098765432109876543210987654321098765432109876543210 + * <><><>[ >[ >[ >< >< >< >< ><><><><><><><><> + * SPT0T1 UC PS1 PS2 B0 B1 B2 B3 P1P2P3P4P5P6P7P8 + * + * SP - SPCSEL constraint + * 48-49: SPCSEL value 0x3_0000_0000_0000 + * + * T0 - TTM0 constraint + * 46-47: TTM0SEL value (0=FPU, 2=IFU, 3=VPU) 0xC000_0000_0000 + * + * T1 - TTM1 constraint + * 44-45: TTM1SEL value (0=IDU, 3=STS) 0x3000_0000_0000 + * + * UC - unit constraint: can't have all three of FPU|IFU|VPU, ISU, IDU|STS + * 43: UC3 error 0x0800_0000_0000 + * 42: FPU|IFU|VPU events needed 0x0400_0000_0000 + * 41: ISU events needed 0x0200_0000_0000 + * 40: IDU|STS events needed 0x0100_0000_0000 + * + * PS1 + * 39: PS1 error 0x0080_0000_0000 + * 36-38: count of events needing PMC1/2/5/6 0x0070_0000_0000 + * + * PS2 + * 35: PS2 error 0x0008_0000_0000 + * 32-34: count of events needing PMC3/4/7/8 0x0007_0000_0000 + * + * B0 + * 28-31: Byte 0 event source 0xf000_0000 + * Encoding as for the event code + * + * B1, B2, B3 + * 24-27, 20-23, 16-19: Byte 1, 2, 3 event sources + * + * P1 + * 15: P1 error 0x8000 + * 14-15: Count of events needing PMC1 + * + * P2..P8 + * 0-13: Count of events needing PMC2..PMC8 + */ + +static unsigned char direct_marked_event[8] = { + (1<<2) | (1<<3), /* PMC1: PM_MRK_GRP_DISP, PM_MRK_ST_CMPL */ + (1<<3) | (1<<5), /* PMC2: PM_THRESH_TIMEO, PM_MRK_BRU_FIN */ + (1<<3) | (1<<5), /* PMC3: PM_MRK_ST_CMPL_INT, PM_MRK_VMX_FIN */ + (1<<4) | (1<<5), /* PMC4: PM_MRK_GRP_CMPL, PM_MRK_CRU_FIN */ + (1<<4) | (1<<5), /* PMC5: PM_GRP_MRK, PM_MRK_GRP_TIMEO */ + (1<<3) | (1<<4) | (1<<5), + /* PMC6: PM_MRK_ST_STS, PM_MRK_FXU_FIN, PM_MRK_GRP_ISSUED */ + (1<<4) | (1<<5), /* PMC7: PM_MRK_FPU_FIN, PM_MRK_INST_FIN */ + (1<<4) /* PMC8: PM_MRK_LSU_FIN */ +}; + +/* + * Returns 1 if event counts things relating to marked instructions + * and thus needs the MMCRA_SAMPLE_ENABLE bit set, or 0 if not. + */ +static int p970_marked_instr_event(u64 event) +{ + int pmc, psel, unit, byte, bit; + unsigned int mask; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + psel = event & PM_PMCSEL_MSK; + if (pmc) { + if (direct_marked_event[pmc - 1] & (1 << psel)) + return 1; + if (psel == 0) /* add events */ + bit = (pmc <= 4)? pmc - 1: 8 - pmc; + else if (psel == 7 || psel == 13) /* decode events */ + bit = 4; + else + return 0; + } else + bit = psel; + + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + mask = 0; + switch (unit) { + case PM_VPU: + mask = 0x4c; /* byte 0 bits 2,3,6 */ + break; + case PM_LSU0: + /* byte 2 bits 0,2,3,4,6; all of byte 1 */ + mask = 0x085dff00; + break; + case PM_LSU1L: + mask = 0x50 << 24; /* byte 3 bits 4,6 */ + break; + } + return (mask >> (byte * 8 + bit)) & 1; +} + +/* Masks and values for using events from the various units */ +static unsigned long unit_cons[PM_LASTUNIT+1][2] = { + [PM_FPU] = { 0xc80000000000ull, 0x040000000000ull }, + [PM_VPU] = { 0xc80000000000ull, 0xc40000000000ull }, + [PM_ISU] = { 0x080000000000ull, 0x020000000000ull }, + [PM_IFU] = { 0xc80000000000ull, 0x840000000000ull }, + [PM_IDU] = { 0x380000000000ull, 0x010000000000ull }, + [PM_STS] = { 0x380000000000ull, 0x310000000000ull }, +}; + +static int p970_get_constraint(u64 event, unsigned long *maskp, + unsigned long *valp) +{ + int pmc, byte, unit, sh, spcsel; + unsigned long mask = 0, value = 0; + int grp = -1; + + pmc = (event >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc > 8) + return -1; + sh = (pmc - 1) * 2; + mask |= 2 << sh; + value |= 1 << sh; + grp = ((pmc - 1) >> 1) & 1; + } + unit = (event >> PM_UNIT_SH) & PM_UNIT_MSK; + if (unit) { + if (unit > PM_LASTUNIT) + return -1; + mask |= unit_cons[unit][0]; + value |= unit_cons[unit][1]; + byte = (event >> PM_BYTE_SH) & PM_BYTE_MSK; + /* + * Bus events on bytes 0 and 2 can be counted + * on PMC1/2/5/6; bytes 1 and 3 on PMC3/4/7/8. + */ + if (!pmc) + grp = byte & 1; + /* Set byte lane select field */ + mask |= 0xfULL << (28 - 4 * byte); + value |= (unsigned long)unit << (28 - 4 * byte); + } + if (grp == 0) { + /* increment PMC1/2/5/6 field */ + mask |= 0x8000000000ull; + value |= 0x1000000000ull; + } else if (grp == 1) { + /* increment PMC3/4/7/8 field */ + mask |= 0x800000000ull; + value |= 0x100000000ull; + } + spcsel = (event >> PM_SPCSEL_SH) & PM_SPCSEL_MSK; + if (spcsel) { + mask |= 3ull << 48; + value |= (unsigned long)spcsel << 48; + } + *maskp = mask; + *valp = value; + return 0; +} + +static int p970_get_alternatives(u64 event, unsigned int flags, u64 alt[]) +{ + alt[0] = event; + + /* 2 alternatives for LSU empty */ + if (event == 0x2002 || event == 0x3002) { + alt[1] = event ^ 0x1000; + return 2; + } + + return 1; +} + +static int p970_compute_mmcr(u64 event[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]) +{ + unsigned long mmcr0 = 0, mmcr1 = 0, mmcra = 0; + unsigned int pmc, unit, byte, psel; + unsigned int ttm, grp; + unsigned int pmc_inuse = 0; + unsigned int pmc_grp_use[2]; + unsigned char busbyte[4]; + unsigned char unituse[16]; + unsigned char unitmap[] = { 0, 0<<3, 3<<3, 1<<3, 2<<3, 0|4, 3|4 }; + unsigned char ttmuse[2]; + unsigned char pmcsel[8]; + int i; + int spcsel; + + if (n_ev > 8) + return -1; + + /* First pass to count resource use */ + pmc_grp_use[0] = pmc_grp_use[1] = 0; + memset(busbyte, 0, sizeof(busbyte)); + memset(unituse, 0, sizeof(unituse)); + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + if (pmc) { + if (pmc_inuse & (1 << (pmc - 1))) + return -1; + pmc_inuse |= 1 << (pmc - 1); + /* count 1/2/5/6 vs 3/4/7/8 use */ + ++pmc_grp_use[((pmc - 1) >> 1) & 1]; + } + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + if (unit) { + if (unit > PM_LASTUNIT) + return -1; + if (!pmc) + ++pmc_grp_use[byte & 1]; + if (busbyte[byte] && busbyte[byte] != unit) + return -1; + busbyte[byte] = unit; + unituse[unit] = 1; + } + } + if (pmc_grp_use[0] > 4 || pmc_grp_use[1] > 4) + return -1; + + /* + * Assign resources and set multiplexer selects. + * + * PM_ISU can go either on TTM0 or TTM1, but that's the only + * choice we have to deal with. + */ + if (unituse[PM_ISU] & + (unituse[PM_FPU] | unituse[PM_IFU] | unituse[PM_VPU])) + unitmap[PM_ISU] = 2 | 4; /* move ISU to TTM1 */ + /* Set TTM[01]SEL fields. */ + ttmuse[0] = ttmuse[1] = 0; + for (i = PM_FPU; i <= PM_STS; ++i) { + if (!unituse[i]) + continue; + ttm = unitmap[i]; + ++ttmuse[(ttm >> 2) & 1]; + mmcr1 |= (unsigned long)(ttm & ~4) << MMCR1_TTM1SEL_SH; + } + /* Check only one unit per TTMx */ + if (ttmuse[0] > 1 || ttmuse[1] > 1) + return -1; + + /* Set byte lane select fields and TTM3SEL. */ + for (byte = 0; byte < 4; ++byte) { + unit = busbyte[byte]; + if (!unit) + continue; + if (unit <= PM_STS) + ttm = (unitmap[unit] >> 2) & 1; + else if (unit == PM_LSU0) + ttm = 2; + else { + ttm = 3; + if (unit == PM_LSU1L && byte >= 2) + mmcr1 |= 1ull << (MMCR1_TTM3SEL_SH + 3 - byte); + } + mmcr1 |= (unsigned long)ttm + << (MMCR1_TD_CP_DBG0SEL_SH - 2 * byte); + } + + /* Second pass: assign PMCs, set PMCxSEL and PMCx_ADDER_SEL fields */ + memset(pmcsel, 0x8, sizeof(pmcsel)); /* 8 means don't count */ + for (i = 0; i < n_ev; ++i) { + pmc = (event[i] >> PM_PMC_SH) & PM_PMC_MSK; + unit = (event[i] >> PM_UNIT_SH) & PM_UNIT_MSK; + byte = (event[i] >> PM_BYTE_SH) & PM_BYTE_MSK; + psel = event[i] & PM_PMCSEL_MSK; + if (!pmc) { + /* Bus event or any-PMC direct event */ + if (unit) + psel |= 0x10 | ((byte & 2) << 2); + else + psel |= 8; + for (pmc = 0; pmc < 8; ++pmc) { + if (pmc_inuse & (1 << pmc)) + continue; + grp = (pmc >> 1) & 1; + if (unit) { + if (grp == (byte & 1)) + break; + } else if (pmc_grp_use[grp] < 4) { + ++pmc_grp_use[grp]; + break; + } + } + pmc_inuse |= 1 << pmc; + } else { + /* Direct event */ + --pmc; + if (psel == 0 && (byte & 2)) + /* add events on higher-numbered bus */ + mmcr1 |= 1ull << mmcr1_adder_bits[pmc]; + } + pmcsel[pmc] = psel; + hwc[i] = pmc; + spcsel = (event[i] >> PM_SPCSEL_SH) & PM_SPCSEL_MSK; + mmcr1 |= spcsel; + if (p970_marked_instr_event(event[i])) + mmcra |= MMCRA_SAMPLE_ENABLE; + } + for (pmc = 0; pmc < 2; ++pmc) + mmcr0 |= pmcsel[pmc] << (MMCR0_PMC1SEL_SH - 7 * pmc); + for (; pmc < 8; ++pmc) + mmcr1 |= (unsigned long)pmcsel[pmc] + << (MMCR1_PMC3SEL_SH - 5 * (pmc - 2)); + if (pmc_inuse & 1) + mmcr0 |= MMCR0_PMC1CE; + if (pmc_inuse & 0xfe) + mmcr0 |= MMCR0_PMCjCE; + + mmcra |= 0x2000; /* mark only one IOP per PPC instruction */ + + /* Return MMCRx values */ + mmcr[0] = mmcr0; + mmcr[1] = mmcr1; + mmcr[2] = mmcra; + return 0; +} + +static void p970_disable_pmc(unsigned int pmc, unsigned long mmcr[]) +{ + int shift, i; + + if (pmc <= 1) { + shift = MMCR0_PMC1SEL_SH - 7 * pmc; + i = 0; + } else { + shift = MMCR1_PMC3SEL_SH - 5 * (pmc - 2); + i = 1; + } + /* + * Setting the PMCxSEL field to 0x08 disables PMC x. + */ + mmcr[i] = (mmcr[i] & ~(0x1fUL << shift)) | (0x08UL << shift); +} + +static int ppc970_generic_events[] = { + [PERF_COUNT_HW_CPU_CYCLES] = 7, + [PERF_COUNT_HW_INSTRUCTIONS] = 1, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x8810, /* PM_LD_REF_L1 */ + [PERF_COUNT_HW_CACHE_MISSES] = 0x3810, /* PM_LD_MISS_L1 */ + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x431, /* PM_BR_ISSUED */ + [PERF_COUNT_HW_BRANCH_MISSES] = 0x327, /* PM_GRP_BR_MPRED */ +}; + +#define C(x) PERF_COUNT_HW_CACHE_##x + +/* + * Table of generalized cache-related events. + * 0 means not supported, -1 means nonsensical, other values + * are event codes. + */ +static int ppc970_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { + [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x8810, 0x3810 }, + [C(OP_WRITE)] = { 0x7810, 0x813 }, + [C(OP_PREFETCH)] = { 0x731, 0 }, + }, + [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { 0, 0 }, + }, + [C(LL)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0 }, + [C(OP_WRITE)] = { 0, 0 }, + [C(OP_PREFETCH)] = { 0x733, 0 }, + }, + [C(DTLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x704 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(ITLB)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0, 0x700 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(BPU)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { 0x431, 0x327 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, + [C(NODE)] = { /* RESULT_ACCESS RESULT_MISS */ + [C(OP_READ)] = { -1, -1 }, + [C(OP_WRITE)] = { -1, -1 }, + [C(OP_PREFETCH)] = { -1, -1 }, + }, +}; + +static struct power_pmu ppc970_pmu = { + .name = "PPC970/FX/MP", + .n_counter = 8, + .max_alternatives = 2, + .add_fields = 0x001100005555ull, + .test_adder = 0x013300000000ull, + .compute_mmcr = p970_compute_mmcr, + .get_constraint = p970_get_constraint, + .get_alternatives = p970_get_alternatives, + .disable_pmc = p970_disable_pmc, + .n_generic = ARRAY_SIZE(ppc970_generic_events), + .generic_events = ppc970_generic_events, + .cache_events = &ppc970_cache_events, +}; + +static int __init init_ppc970_pmu(void) +{ + if (!cur_cpu_spec->oprofile_cpu_type || + (strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970") + && strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/970MP"))) + return -ENODEV; + + return register_power_pmu(&ppc970_pmu); +} + +early_initcall(init_ppc970_pmu); -- cgit v0.10.2 From fe83364f0bf11fcb20cecbe944a95834632ffffb Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 22 Feb 2012 13:50:13 +0000 Subject: powerpc/mpic: Fix allocation of reverse-map for multi-ISU mpics When using a multi-ISU MPIC, we can interrupts up to isu_size * MPIC_MAX_ISU, not just isu_size, so allocate the right size reverse map. Without this, the code will constantly fallback to a linear search. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index b9b989d..16eb743 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1331,9 +1331,13 @@ struct mpic * __init mpic_alloc(struct device_node *node, /* * By default, the last source number comes from the MPIC, but the * device-tree and board support code can override it on buggy hw. + * If we get passed an isu_size (multi-isu MPIC) then we use that + * as a default instead of the value read from the HW. */ last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) - >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; + >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; + if (isu_size) + last_irq = isu_size * MPIC_MAX_ISU - 1; of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq); if (irq_count) last_irq = irq_count - 1; @@ -1352,7 +1356,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->isu_mask = (1 << mpic->isu_shift) - 1; mpic->irqhost = irq_alloc_host(mpic->node, IRQ_HOST_MAP_LINEAR, - mpic->isu_size, &mpic_host_ops, + last_irq + 1, &mpic_host_ops, intvec_top + 1); /* -- cgit v0.10.2 From 3d066d77cf464ea1e47808f95243301fd2175a7f Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 22 Feb 2012 14:10:12 +0000 Subject: powerpc: remove CONFIG_PPC_ISERIES from the architecture Kconfig files After this, we can remove the legacy iSeries code more easily. Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c713111..613eacf 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -614,7 +614,7 @@ endmenu config ISA_DMA_API bool - default !PPC_ISERIES || PCI + default PCI menu "Bus options" diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 4ccb2a0..72d55db 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -196,13 +196,6 @@ config PPC_EARLY_DEBUG_MAPLE help Select this to enable early debugging for Maple. -config PPC_EARLY_DEBUG_ISERIES - bool "iSeries HV Console" - depends on PPC_ISERIES - help - Select this to enable early debugging for legacy iSeries. You need - to hit "Ctrl-x Ctrl-x" to see the messages on the console. - config PPC_EARLY_DEBUG_PAS_REALMODE bool "PA Semi real mode" depends on PPC_PASEMI diff --git a/arch/powerpc/configs/iseries_defconfig b/arch/powerpc/configs/iseries_defconfig deleted file mode 100644 index 27c46d6..0000000 --- a/arch/powerpc/configs/iseries_defconfig +++ /dev/null @@ -1,236 +0,0 @@ -CONFIG_PPC64=y -CONFIG_SMP=y -CONFIG_EXPERIMENTAL=y -CONFIG_SYSVIPC=y -CONFIG_POSIX_MQUEUE=y -CONFIG_AUDIT=y -CONFIG_AUDITSYSCALL=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_BLK_DEV_INITRD=y -# CONFIG_COMPAT_BRK is not set -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_MODULE_SRCVERSION_ALL=y -# CONFIG_PPC_PSERIES is not set -CONFIG_LPARCFG=y -CONFIG_PPC_ISERIES=y -CONFIG_VIODASD=y -CONFIG_VIOCD=m -CONFIG_VIOTAPE=m -# CONFIG_PPC_PMAC is not set -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_IRQ_ALL_CPUS=y -# CONFIG_MIGRATION is not set -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_XFRM_USER=m -CONFIG_XFRM_SUB_POLICY=y -CONFIG_NET_KEY=m -CONFIG_INET=y -CONFIG_IP_MULTICAST=y -CONFIG_NET_IPIP=y -CONFIG_SYN_COOKIES=y -CONFIG_INET_AH=m -CONFIG_INET_ESP=m -CONFIG_INET_IPCOMP=m -CONFIG_INET_XFRM_MODE_BEET=m -# CONFIG_INET_LRO is not set -# CONFIG_IPV6 is not set -CONFIG_NETFILTER=y -CONFIG_NETFILTER_NETLINK_QUEUE=m -CONFIG_NETFILTER_NETLINK_LOG=m -CONFIG_NF_CONNTRACK=m -CONFIG_NF_CONNTRACK_EVENTS=y -# CONFIG_NF_CT_PROTO_SCTP is not set -CONFIG_NF_CONNTRACK_FTP=m -CONFIG_NF_CONNTRACK_IRC=m -CONFIG_NF_CONNTRACK_TFTP=m -CONFIG_NF_CT_NETLINK=m -CONFIG_NETFILTER_TPROXY=m -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m -CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -CONFIG_NETFILTER_XT_TARGET_DSCP=m -CONFIG_NETFILTER_XT_TARGET_MARK=m -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m -CONFIG_NETFILTER_XT_TARGET_TPROXY=m -CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m -CONFIG_NETFILTER_XT_MATCH_COMMENT=m -CONFIG_NETFILTER_XT_MATCH_CONNMARK=m -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m -CONFIG_NETFILTER_XT_MATCH_DSCP=m -CONFIG_NETFILTER_XT_MATCH_IPRANGE=m -CONFIG_NETFILTER_XT_MATCH_LENGTH=m -CONFIG_NETFILTER_XT_MATCH_LIMIT=m -CONFIG_NETFILTER_XT_MATCH_MAC=m -CONFIG_NETFILTER_XT_MATCH_MARK=m -CONFIG_NETFILTER_XT_MATCH_OWNER=m -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m -CONFIG_NETFILTER_XT_MATCH_RATEEST=m -CONFIG_NETFILTER_XT_MATCH_REALM=m -CONFIG_NETFILTER_XT_MATCH_RECENT=m -CONFIG_NETFILTER_XT_MATCH_STRING=m -CONFIG_NETFILTER_XT_MATCH_TCPMSS=m -CONFIG_NETFILTER_XT_MATCH_TIME=m -CONFIG_NF_CONNTRACK_IPV4=m -CONFIG_IP_NF_QUEUE=m -CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_ADDRTYPE=m -CONFIG_IP_NF_MATCH_ECN=m -CONFIG_IP_NF_MATCH_TTL=m -CONFIG_IP_NF_FILTER=m -CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_LOG=m -CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_NETMAP=m -CONFIG_IP_NF_TARGET_REDIRECT=m -CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m -CONFIG_IP_NF_TARGET_ECN=m -CONFIG_IP_NF_TARGET_TTL=m -CONFIG_IP_NF_RAW=m -CONFIG_IP_NF_ARPTABLES=m -CONFIG_IP_NF_ARPFILTER=m -CONFIG_IP_NF_ARP_MANGLE=m -CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_PROC_DEVICETREE=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_NBD=m -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=65536 -CONFIG_SCSI=y -CONFIG_BLK_DEV_SD=y -CONFIG_CHR_DEV_ST=y -CONFIG_BLK_DEV_SR=y -CONFIG_BLK_DEV_SR_VENDOR=y -CONFIG_CHR_DEV_SG=y -CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_CONSTANTS=y -CONFIG_SCSI_SPI_ATTRS=y -CONFIG_SCSI_FC_ATTRS=y -CONFIG_SCSI_SAS_LIBSAS=m -CONFIG_SCSI_IBMVSCSI=m -CONFIG_MD=y -CONFIG_BLK_DEV_MD=y -CONFIG_MD_LINEAR=y -CONFIG_MD_RAID0=y -CONFIG_MD_RAID1=y -CONFIG_MD_RAID10=m -CONFIG_MD_MULTIPATH=m -CONFIG_MD_FAULTY=m -CONFIG_BLK_DEV_DM=y -CONFIG_DM_CRYPT=m -CONFIG_DM_SNAPSHOT=m -CONFIG_DM_MIRROR=m -CONFIG_DM_ZERO=m -CONFIG_NETDEVICES=y -CONFIG_DUMMY=m -CONFIG_BONDING=m -CONFIG_TUN=m -CONFIG_NET_ETHERNET=y -CONFIG_NET_PCI=y -CONFIG_PCNET32=y -CONFIG_E100=y -CONFIG_ACENIC=m -CONFIG_E1000=m -CONFIG_ISERIES_VETH=y -CONFIG_PPP=m -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_PPP_BSDCOMP=m -CONFIG_PPPOE=m -CONFIG_NETCONSOLE=y -CONFIG_NETPOLL_TRAP=y -# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_SERIO is not set -CONFIG_SERIAL_ICOM=m -# CONFIG_HW_RANDOM is not set -CONFIG_GEN_RTC=y -CONFIG_RAW_DRIVER=y -# CONFIG_HWMON is not set -# CONFIG_HID_SUPPORT is not set -# CONFIG_USB_SUPPORT is not set -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT2_FS_POSIX_ACL=y -CONFIG_EXT2_FS_SECURITY=y -CONFIG_EXT2_FS_XIP=y -CONFIG_EXT3_FS=y -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_EXT3_FS_SECURITY=y -CONFIG_EXT4_FS=y -CONFIG_REISERFS_FS=y -CONFIG_REISERFS_FS_XATTR=y -CONFIG_REISERFS_FS_POSIX_ACL=y -CONFIG_REISERFS_FS_SECURITY=y -CONFIG_JFS_FS=m -CONFIG_JFS_POSIX_ACL=y -CONFIG_JFS_SECURITY=y -CONFIG_XFS_FS=m -CONFIG_XFS_POSIX_ACL=y -CONFIG_GFS2_FS=m -CONFIG_AUTOFS_FS=m -CONFIG_ISO9660_FS=y -CONFIG_JOLIET=y -CONFIG_ZISOFS=y -CONFIG_UDF_FS=m -CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y -CONFIG_PROC_KCORE=y -CONFIG_TMPFS=y -CONFIG_TMPFS_POSIX_ACL=y -CONFIG_CRAMFS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_NFS_V3_ACL=y -CONFIG_NFS_V4=y -CONFIG_NFSD=m -CONFIG_NFSD_V3_ACL=y -CONFIG_NFSD_V4=y -CONFIG_RPCSEC_GSS_SPKM3=m -CONFIG_CIFS=m -CONFIG_CIFS_XATTR=y -CONFIG_CIFS_POSIX=y -CONFIG_NLS_CODEPAGE_437=y -CONFIG_NLS_ASCII=y -CONFIG_NLS_ISO8859_1=y -CONFIG_DLM=m -CONFIG_CRC_T10DIF=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set -CONFIG_LATENCYTOP=y -CONFIG_SYSCTL_SYSCALL_CHECK=y -CONFIG_DEBUG_STACKOVERFLOW=y -CONFIG_DEBUG_STACK_USAGE=y -CONFIG_CRYPTO_NULL=m -CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_ECB=m -CONFIG_CRYPTO_PCBC=m -CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MICHAEL_MIC=m -CONFIG_CRYPTO_SHA256=m -CONFIG_CRYPTO_SHA512=m -CONFIG_CRYPTO_TGR192=m -CONFIG_CRYPTO_WP512=m -CONFIG_CRYPTO_AES=m -CONFIG_CRYPTO_ANUBIS=m -CONFIG_CRYPTO_ARC4=m -CONFIG_CRYPTO_BLOWFISH=m -CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_KHAZAD=m -CONFIG_CRYPTO_SEED=m -CONFIG_CRYPTO_SERPENT=m -CONFIG_CRYPTO_TEA=m -CONFIG_CRYPTO_TWOFISH=m -# CONFIG_CRYPTO_ANSI_CPRNG is not set -# CONFIG_CRYPTO_HW is not set diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 0cfb46d..236ab67 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -2,7 +2,6 @@ menu "Platform support" source "arch/powerpc/platforms/powernv/Kconfig" source "arch/powerpc/platforms/pseries/Kconfig" -source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" source "arch/powerpc/platforms/512x/Kconfig" source "arch/powerpc/platforms/52xx/Kconfig" @@ -138,7 +137,7 @@ config MPIC_BROKEN_REGREAD of the register contents in software. config IBMVIO - depends on PPC_PSERIES || PPC_ISERIES + depends on PPC_PSERIES bool default y diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig deleted file mode 100644 index b57cda3..0000000 --- a/arch/powerpc/platforms/iseries/Kconfig +++ /dev/null @@ -1,38 +0,0 @@ -config PPC_ISERIES - bool "IBM Legacy iSeries" - depends on PPC64 && PPC_BOOK3S - select PPC_SMP_MUXED_IPI - select PPC_INDIRECT_PIO - select PPC_INDIRECT_MMIO - select PPC_PCI_CHOICE if EXPERT - -menu "iSeries device drivers" - depends on PPC_ISERIES - -config VIODASD - tristate "iSeries Virtual I/O disk support" - depends on BLOCK - select VIOPATH - help - If you are running on an iSeries system and you want to use - virtual disks created and managed by OS/400, say Y. - -config VIOCD - tristate "iSeries Virtual I/O CD support" - depends on BLOCK - select VIOPATH - help - If you are running Linux on an IBM iSeries system and you want to - read a CD drive owned by OS/400, say Y here. - -config VIOTAPE - tristate "iSeries Virtual Tape Support" - select VIOPATH - help - If you are running Linux on an iSeries system and you want Linux - to read and/or write a tape drive owned by OS/400, say Y here. - -endmenu - -config VIOPATH - bool diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 31f22c1..9c28847 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -72,7 +72,7 @@ config IO_EVENT_IRQ config LPARCFG bool "LPAR Configuration Data" - depends on PPC_PSERIES || PPC_ISERIES + depends on PPC_PSERIES help Provide system capacity information via human readable = pairs through a /proc/ppc64/lparcfg interface. -- cgit v0.10.2 From ed7e3d1ca7005fe4bbb260da3caea3ef2af5c7c5 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Thu, 16 Feb 2012 03:55:54 +0000 Subject: arch/powerpc/kvm/book3s_hv.c: included linux/sched.h twice arch/powerpc/kvm/book3s_hv.c: included 'linux/sched.h' twice, remove the duplicate. Signed-off-by: Danny Kukawka Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 336983d..a726716 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include -- cgit v0.10.2 From 0a167e0a5cacd7c995abbdcd109bf31ad8cc2adb Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Thu, 16 Feb 2012 03:56:03 +0000 Subject: arch/powerpc/platforms/powernv/setup.c: included asm/xics.h twice arch/powerpc/platforms/powernv/setup.c: included 'asm/xics.h' twice, remove the duplicate. Signed-off-by: Danny Kukawka Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 467bd4a..db1ad1c 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "powernv.h" -- cgit v0.10.2 From 6d45584fdc202fd30da655120412210153429104 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Mon, 23 Jan 2012 07:26:36 +0000 Subject: macintosh: Fix typo in mediabay.c Fix typo "unsuported" to "unsupported" in drivers/machintosh/mediabay.c Signed-off-by: Masanari Iida Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index 2fd435b..831d751 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c @@ -356,7 +356,7 @@ static void poll_media_bay(struct media_bay_info* bay) static char *mb_content_types[] = { "a floppy drive", "a floppy drive", - "an unsuported audio device", + "an unsupported audio device", "an ATA device", "an unsupported PCI device", "an unknown device", -- cgit v0.10.2 From 75ff85a81680e5779383aa6210a4f89ed76e40ec Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 26 Jan 2012 10:59:54 +0000 Subject: carma-fpga: fix lockdep warning Lockdep occasionally complains with the message: INFO: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected This is caused by calling videobuf_dma_unmap() under spin_lock_irq(). To fix the warning, we drop the lock before unmapping and freeing the buffer. Signed-off-by: Ira W. Snyder Cc: Benjamin Herrenschmidt Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 14e974b2..4fd896d 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -1079,6 +1079,7 @@ static ssize_t data_read(struct file *filp, char __user *ubuf, size_t count, struct fpga_reader *reader = filp->private_data; struct fpga_device *priv = reader->priv; struct list_head *used = &priv->used; + bool drop_buffer = false; struct data_buf *dbuf; size_t avail; void *data; @@ -1166,10 +1167,12 @@ have_buffer: * One of two things has happened, the device is disabled, or the * device has been reconfigured underneath us. In either case, we * should just throw away the buffer. + * + * Lockdep complains if this is done under the spinlock, so we + * handle it during the unlock path. */ if (!priv->enabled || dbuf->size != priv->bufsize) { - videobuf_dma_unmap(priv->dev, &dbuf->vb); - data_free_buffer(dbuf); + drop_buffer = true; goto out_unlock; } @@ -1178,6 +1181,12 @@ have_buffer: out_unlock: spin_unlock_irq(&priv->lock); + + if (drop_buffer) { + videobuf_dma_unmap(priv->dev, &dbuf->vb); + data_free_buffer(dbuf); + } + return count; } -- cgit v0.10.2 From 6c15d7afbb2f9e2d3114b513306dae736b56f535 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Thu, 26 Jan 2012 11:00:14 +0000 Subject: carma-fpga: fix race between data dumping and DMA callback When the system is under heavy load, we occasionally saw a problem where the system would get a legitimate interrupt when they should be disabled. This was caused by the data_dma_cb() DMA callback unconditionally re-enabling FPGA interrupts even when data dumping is disabled. When data dumping was re-enabled, the irq handler would fire while a DMA was in progress. The "BUG_ON(priv->inflight != NULL);" during the second invocation of the DMA callback caused the system to crash. To fix the issue, the priv->enabled boolean is moved under the protection of the priv->lock spinlock. The DMA callback checks the boolean to know whether to re-enable FPGA interrupts before it returns. Now that it is fixed, the driver keeps FPGA interrupts disabled when it expects that they are disabled, fixing the bug. Signed-off-by: Ira W. Snyder Cc: Benjamin Herrenschmidt Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 4fd896d..0cfc5bf 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -560,6 +560,9 @@ static void data_enable_interrupts(struct fpga_device *priv) /* flush the writes */ fpga_read_reg(priv, 0, MMAP_REG_STATUS); + fpga_read_reg(priv, 1, MMAP_REG_STATUS); + fpga_read_reg(priv, 2, MMAP_REG_STATUS); + fpga_read_reg(priv, 3, MMAP_REG_STATUS); /* switch back to the external interrupt source */ iowrite32be(0x3F, priv->regs + SYS_IRQ_SOURCE_CTL); @@ -591,8 +594,12 @@ static void data_dma_cb(void *data) list_move_tail(&priv->inflight->entry, &priv->used); priv->inflight = NULL; - /* clear the FPGA status and re-enable interrupts */ - data_enable_interrupts(priv); + /* + * If data dumping is still enabled, then clear the FPGA + * status registers and re-enable FPGA interrupts + */ + if (priv->enabled) + data_enable_interrupts(priv); spin_unlock_irqrestore(&priv->lock, flags); @@ -708,6 +715,15 @@ static irqreturn_t data_irq(int irq, void *dev_id) spin_lock(&priv->lock); + /* + * This is an error case that should never happen. + * + * If this driver has a bug and manages to re-enable interrupts while + * a DMA is in progress, then we will hit this statement and should + * start paying attention immediately. + */ + BUG_ON(priv->inflight != NULL); + /* hide the interrupt by switching the IRQ driver to GPIO */ data_disable_interrupts(priv); @@ -762,11 +778,15 @@ out: */ static int data_device_enable(struct fpga_device *priv) { + bool enabled; u32 val; int ret; /* multiple enables are safe: they do nothing */ - if (priv->enabled) + spin_lock_irq(&priv->lock); + enabled = priv->enabled; + spin_unlock_irq(&priv->lock); + if (enabled) return 0; /* check that the FPGAs are programmed */ @@ -797,6 +817,9 @@ static int data_device_enable(struct fpga_device *priv) goto out_error; } + /* prevent the FPGAs from generating interrupts */ + data_disable_interrupts(priv); + /* hookup the irq handler */ ret = request_irq(priv->irq, data_irq, IRQF_SHARED, drv_name, priv); if (ret) { @@ -804,11 +827,13 @@ static int data_device_enable(struct fpga_device *priv) goto out_error; } - /* switch to the external FPGA IRQ line */ - data_enable_interrupts(priv); - - /* success, we're enabled */ + /* allow the DMA callback to re-enable FPGA interrupts */ + spin_lock_irq(&priv->lock); priv->enabled = true; + spin_unlock_irq(&priv->lock); + + /* allow the FPGAs to generate interrupts */ + data_enable_interrupts(priv); return 0; out_error: @@ -834,41 +859,40 @@ out_error: */ static int data_device_disable(struct fpga_device *priv) { - int ret; + spin_lock_irq(&priv->lock); /* allow multiple disable */ - if (!priv->enabled) + if (!priv->enabled) { + spin_unlock_irq(&priv->lock); return 0; + } + + /* + * Mark the device disabled + * + * This stops DMA callbacks from re-enabling interrupts + */ + priv->enabled = false; - /* switch to the internal GPIO IRQ line */ + /* prevent the FPGAs from generating interrupts */ data_disable_interrupts(priv); + /* wait until all ongoing DMA has finished */ + while (priv->inflight != NULL) { + spin_unlock_irq(&priv->lock); + wait_event(priv->wait, priv->inflight == NULL); + spin_lock_irq(&priv->lock); + } + + spin_unlock_irq(&priv->lock); + /* unhook the irq handler */ free_irq(priv->irq, priv); - /* - * wait for all outstanding DMA to complete - * - * Device interrupts are disabled, therefore another buffer cannot - * be marked inflight. - */ - ret = wait_event_interruptible(priv->wait, priv->inflight == NULL); - if (ret) - return ret; - /* free the correlation table */ sg_free_table(&priv->corl_table); priv->corl_nents = 0; - /* - * We are taking the spinlock not to protect priv->enabled, but instead - * to make sure that there are no readers in the process of altering - * the free or used lists while we are setting this flag. - */ - spin_lock_irq(&priv->lock); - priv->enabled = false; - spin_unlock_irq(&priv->lock); - /* free all buffers: the free and used lists are not being changed */ data_free_buffers(priv); return 0; @@ -896,15 +920,6 @@ static unsigned int list_num_entries(struct list_head *list) static int data_debug_show(struct seq_file *f, void *offset) { struct fpga_device *priv = f->private; - int ret; - - /* - * Lock the mutex first, so that we get an accurate value for enable - * Lock the spinlock next, to get accurate list counts - */ - ret = mutex_lock_interruptible(&priv->mutex); - if (ret) - return ret; spin_lock_irq(&priv->lock); @@ -917,7 +932,6 @@ static int data_debug_show(struct seq_file *f, void *offset) seq_printf(f, "num_dropped: %d\n", priv->num_dropped); spin_unlock_irq(&priv->lock); - mutex_unlock(&priv->mutex); return 0; } @@ -970,7 +984,13 @@ static ssize_t data_en_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fpga_device *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", priv->enabled); + int ret; + + spin_lock_irq(&priv->lock); + ret = snprintf(buf, PAGE_SIZE, "%u\n", priv->enabled); + spin_unlock_irq(&priv->lock); + + return ret; } static ssize_t data_en_set(struct device *dev, struct device_attribute *attr, @@ -986,6 +1006,7 @@ static ssize_t data_en_set(struct device *dev, struct device_attribute *attr, return -EINVAL; } + /* protect against concurrent enable/disable */ ret = mutex_lock_interruptible(&priv->mutex); if (ret) return ret; -- cgit v0.10.2 From b1ada6010e234d6c53bfcd5a617610c9284846b8 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 29 Feb 2012 21:09:53 +0000 Subject: atomic: Allow atomic_inc_not_zero to be overridden We want to implement a ppc64 specific version of atomic_inc_not_zero so wrap it in an ifdef to allow it to be overridden. Signed-off-by: Anton Blanchard Acked-by: Mike Frysinger Signed-off-by: Benjamin Herrenschmidt diff --git a/include/linux/atomic.h b/include/linux/atomic.h index 42b77b5..70cfcb2 100644 --- a/include/linux/atomic.h +++ b/include/linux/atomic.h @@ -24,7 +24,9 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u) * Atomically increments @v by 1, so long as @v is non-zero. * Returns non-zero if @v was non-zero, and zero otherwise. */ +#ifndef atomic_inc_not_zero #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) +#endif /** * atomic_inc_not_zero_hint - increment if not null -- cgit v0.10.2 From a6cf7ed5119fb22f54584a9f867b638edd3c4384 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 29 Feb 2012 21:12:16 +0000 Subject: powerpc/atomic: Implement atomic*_inc_not_zero Implement atomic_inc_not_zero and atomic64_inc_not_zero. At the moment we use atomic*_add_unless which requires us to put 0 and 1 constants into registers. We can also avoid a subtract by saving the original value in a second temporary. This removes 3 instructions from fget: - c0000000001b63c0: 39 00 00 00 li r8,0 - c0000000001b63c4: 39 40 00 01 li r10,1 ... - c0000000001b63e8: 7c 0a 00 50 subf r0,r10,r0 Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/atomic.h b/arch/powerpc/include/asm/atomic.h index 02e41b5..14174e8 100644 --- a/arch/powerpc/include/asm/atomic.h +++ b/arch/powerpc/include/asm/atomic.h @@ -212,6 +212,36 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u) return t; } +/** + * atomic_inc_not_zero - increment unless the number is zero + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1, so long as @v is non-zero. + * Returns non-zero if @v was non-zero, and zero otherwise. + */ +static __inline__ int atomic_inc_not_zero(atomic_t *v) +{ + int t1, t2; + + __asm__ __volatile__ ( + PPC_ATOMIC_ENTRY_BARRIER +"1: lwarx %0,0,%2 # atomic_inc_not_zero\n\ + cmpwi 0,%0,0\n\ + beq- 2f\n\ + addic %1,%0,1\n" + PPC405_ERR77(0,%2) +" stwcx. %1,0,%2\n\ + bne- 1b\n" + PPC_ATOMIC_EXIT_BARRIER + "\n\ +2:" + : "=&r" (t1), "=&r" (t2) + : "r" (&v->counter) + : "cc", "xer", "memory"); + + return t1; +} +#define atomic_inc_not_zero(v) atomic_inc_not_zero((v)) #define atomic_sub_and_test(a, v) (atomic_sub_return((a), (v)) == 0) #define atomic_dec_and_test(v) (atomic_dec_return((v)) == 0) @@ -467,7 +497,34 @@ static __inline__ int atomic64_add_unless(atomic64_t *v, long a, long u) return t != u; } -#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0) +/** + * atomic_inc64_not_zero - increment unless the number is zero + * @v: pointer of type atomic64_t + * + * Atomically increments @v by 1, so long as @v is non-zero. + * Returns non-zero if @v was non-zero, and zero otherwise. + */ +static __inline__ long atomic64_inc_not_zero(atomic64_t *v) +{ + long t1, t2; + + __asm__ __volatile__ ( + PPC_ATOMIC_ENTRY_BARRIER +"1: ldarx %0,0,%2 # atomic64_inc_not_zero\n\ + cmpdi 0,%0,0\n\ + beq- 2f\n\ + addic %1,%0,1\n\ + stdcx. %1,0,%2\n\ + bne- 1b\n" + PPC_ATOMIC_EXIT_BARRIER + "\n\ +2:" + : "=&r" (t1), "=&r" (t2) + : "r" (&v->counter) + : "cc", "xer", "memory"); + + return t1; +} #endif /* __powerpc64__ */ -- cgit v0.10.2 From de801de139ba2a2f2c74393ea00a321477ecc0dc Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Tue, 28 Feb 2012 13:27:07 +0000 Subject: powerpc/icswx: Fix race condition with IPI setting ACOP There is a race where a thread causes a coprocessor type to be valid in its own ACOP _and_ in the current context, but it does not propagate to the ACOP register of other threads in time for them to use it. The original code tries to solve this by sending an IPI to all threads on the system, which is heavy handed, but unfortunately still provides a window where the icswx is issued by other threads and the ACOP is not up to date. This patch detects that the ACOP DSI fault was a "false positive" and syncs the ACOP and causes the icswx to be replayed. Signed-off-by: Jimi Xenidis Cc: Anton Blanchard Cc: Benjamin Herrenschmidt Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/mm/icswx.c b/arch/powerpc/mm/icswx.c index 5d9a59e..8cdbd86 100644 --- a/arch/powerpc/mm/icswx.c +++ b/arch/powerpc/mm/icswx.c @@ -163,7 +163,7 @@ EXPORT_SYMBOL_GPL(drop_cop); static int acop_use_cop(int ct) { - /* todo */ + /* There is no alternate policy, yet */ return -1; } @@ -227,11 +227,30 @@ int acop_handle_fault(struct pt_regs *regs, unsigned long address, ct = (ccw >> 16) & 0x3f; } + /* + * We could be here because another thread has enabled acop + * but the ACOP register has yet to be updated. + * + * This should have been taken care of by the IPI to sync all + * the threads (see smp_call_function(sync_cop, mm, 1)), but + * that could take forever if there are a significant amount + * of threads. + * + * Given the number of threads on some of these systems, + * perhaps this is the best way to sync ACOP rather than whack + * every thread with an IPI. + */ + if ((acop_copro_type_bit(ct) & current->active_mm->context.acop) != 0) { + sync_cop(current->active_mm); + return 0; + } + + /* check for alternate policy */ if (!acop_use_cop(ct)) return 0; /* at this point the CT is unknown to the system */ - pr_warn("%s[%d]: Coprocessor %d is unavailable", + pr_warn("%s[%d]: Coprocessor %d is unavailable\n", current->comm, current->pid, ct); /* get inst if we don't already have it */ diff --git a/arch/powerpc/mm/icswx.h b/arch/powerpc/mm/icswx.h index 42176bd..6dedc08 100644 --- a/arch/powerpc/mm/icswx.h +++ b/arch/powerpc/mm/icswx.h @@ -59,4 +59,10 @@ extern void free_cop_pid(int free_pid); extern int acop_handle_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code); + +static inline u64 acop_copro_type_bit(unsigned int type) +{ + return 1ULL << (63 - type); +} + #endif /* !_ARCH_POWERPC_MM_ICSWX_H_ */ -- cgit v0.10.2 From a2234b4baefe2a2742b5fa8839be1ab1aca39057 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 28 Feb 2012 08:49:34 +0000 Subject: powerpc: Use vsprintf extention %pf with builtin_return_address Emit the function name not the address when possible. builtin_return_address() gives an address. When building a kernel with CONFIG_KALLSYMS, emit the actual function name not the address. Signed-off-by: Joe Perches Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index 51f8795..0907f92 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -207,7 +207,7 @@ __ioremap_caller(phys_addr_t addr, unsigned long size, unsigned long flags, */ if (mem_init_done && (p < virt_to_phys(high_memory)) && !(__allow_ioremap_reserved && memblock_is_region_reserved(p, size))) { - printk("__ioremap(): phys addr 0x%llx is RAM lr %p\n", + printk("__ioremap(): phys addr 0x%llx is RAM lr %pf\n", (unsigned long long)p, __builtin_return_address(0)); return NULL; } -- cgit v0.10.2 From a2007ce8447c9a71d9d694ddcdf64f9dbbf022ff Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 14 Feb 2012 01:40:59 +0000 Subject: powerpc: Use set_current_blocked() and block_sigmask() As described in e6fa16ab ("signal: sigprocmask() should do retarget_shared_pending()") the modification of current->blocked is incorrect as we need to check whether the signal we're about to block is pending in the shared queue. Also, use the new helper function introduced in commit 5e6292c0f28f ("signal: add block_sigmask() for adding sigmask to current->blocked") which centralises the code for updating current->blocked after successfully delivering a signal and reduces the amount of duplicate code across architectures. In the past some architectures got this code wrong, so using this helper function should stop that from happening again. Cc: Oleg Nesterov Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Matt Fleming Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index ac6e437..7006b7f 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -57,10 +57,7 @@ void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, void restore_sigmask(sigset_t *set) { sigdelsetmask(set, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - current->blocked = *set; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + set_current_blocked(set); } static void check_syscall_restart(struct pt_regs *regs, struct k_sigaction *ka, @@ -169,13 +166,7 @@ static int do_signal(struct pt_regs *regs) regs->trap = 0; if (ret) { - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, - &ka.sa.sa_mask); - if (!(ka.sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, signr); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + block_sigmask(&ka, signr); /* * A signal was successfully delivered; the saved sigmask is in diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 836a5a1..e061ef5 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -242,12 +242,13 @@ static inline int restore_general_regs(struct pt_regs *regs, */ long sys_sigsuspend(old_sigset_t mask) { - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); + sigset_t blocked; + current->saved_sigmask = current->blocked; - siginitset(¤t->blocked, mask); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + + mask &= _BLOCKABLE; + siginitset(&blocked, mask); + set_current_blocked(&blocked); current->state = TASK_INTERRUPTIBLE; schedule(); -- cgit v0.10.2 From e9daf2ad7f603f173d7cd7ee3673b326414f82f4 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Mon, 27 Feb 2012 08:55:15 +0000 Subject: powerpc/prom: Remove limit on maximum size of properties On a 16TB system (using AMS/CMO), I get: WARNING: ignoring large property [/ibm,dynamic-reconfiguration-memory] ibm,dynamic-memory length 0x000000000017ffec and significantly less memory is thus shown to the partition. As far as I can tell, the constant used is arbitrary. Ben Herrenschmidt provided additional background that > The limit was originally set because of Apple machines carrying ROM > images in the device-tree, at a time where we were much more memory > constrained than we are now. and that it is likely not very useful any longer. Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index eca626e..e2d5990 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -48,14 +48,6 @@ #include /* - * Properties whose value is longer than this get excluded from our - * copy of the device tree. This value does need to be big enough to - * ensure that we don't lose things like the interrupt-map property - * on a PCI-PCI bridge. - */ -#define MAX_PROPERTY_LENGTH (1UL * 1024 * 1024) - -/* * Eventually bump that one up */ #define DEVTREE_CHUNK_SIZE 0x100000 @@ -2273,13 +2265,6 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, /* sanity checks */ if (l == PROM_ERROR) continue; - if (l > MAX_PROPERTY_LENGTH) { - prom_printf("WARNING: ignoring large property "); - /* It seems OF doesn't null-terminate the path :-( */ - prom_printf("[%s] ", path); - prom_printf("%s length 0x%x\n", RELOC(pname), l); - continue; - } /* push property head */ dt_push_token(OF_DT_PROP, mem_start, mem_end); -- cgit v0.10.2 From ad5b7f1350c263eef0c99c20f8659d0ed363cb32 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 30 Jan 2012 08:02:19 +0000 Subject: powerpc: Make SPARSE_IRQ required All IRQs on powerpc are managed via irq_domain anyway, there isn't really any advantage to turning SPARSE_IRQ off, and it's the direction we want to take the kernel design anyway. This patch makes powerpc always use SPARSE_IRQ. On pseries_defconfig, SPARSE_IRQ adds only about 0x300 bytes to the .text sections, and removes about 0x20000 from the data section for the static irq_desc table. Signed-off-by: Grant Likely Cc: Rob Herring Cc: Ben Herrenschmidt Cc: Thomas Gleixner Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 613eacf..4eecaaa 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -134,6 +134,7 @@ config PPC select HAVE_HW_BREAKPOINT if PERF_EVENTS && PPC_BOOK3S_64 select HAVE_GENERIC_HARDIRQS select HAVE_SPARSE_IRQ + select SPARSE_IRQ select IRQ_PER_CPU select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW_LEVEL diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 01e2877..9b6e806 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -93,10 +93,6 @@ extern int tau_interrupts(int); #ifdef CONFIG_PPC64 -#ifndef CONFIG_SPARSE_IRQ -EXPORT_SYMBOL(irq_desc); -#endif - int distribute_irqs = 1; static inline notrace unsigned long get_hard_enabled(void) -- cgit v0.10.2 From 2d4b971287cbce16585acda4b76308faa8da0950 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 27 Jan 2012 04:24:48 +0000 Subject: powerpc/pmac: Use string library in nvram code - Use memchr_inv to check if the data contains all 0xFF bytes. It is faster than looping for each byte. - Use memcmp to compare memory areas Signed-off-by: Akinobu Mita Cc: Benjamin Herrenschmidt Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/powermac/nvram.c b/arch/powerpc/platforms/powermac/nvram.c index 54d2271..da18b26 100644 --- a/arch/powerpc/platforms/powermac/nvram.c +++ b/arch/powerpc/platforms/powermac/nvram.c @@ -279,7 +279,7 @@ static u32 core99_check(u8* datas) static int sm_erase_bank(int bank) { - int stat, i; + int stat; unsigned long timeout; u8 __iomem *base = (u8 __iomem *)nvram_data + core99_bank*NVRAM_SIZE; @@ -301,11 +301,10 @@ static int sm_erase_bank(int bank) out_8(base, SM_FLASH_CMD_CLEAR_STATUS); out_8(base, SM_FLASH_CMD_RESET); - for (i=0; i Date: Wed, 7 Mar 2012 17:02:07 +0000 Subject: powerpc: Remove the main legacy iSerie platform code Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 2635a22..879b4a4 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_FSL_SOC_BOOKE) += 85xx/ obj-$(CONFIG_PPC_86xx) += 86xx/ obj-$(CONFIG_PPC_POWERNV) += powernv/ obj-$(CONFIG_PPC_PSERIES) += pseries/ -obj-$(CONFIG_PPC_ISERIES) += iseries/ obj-$(CONFIG_PPC_MAPLE) += maple/ obj-$(CONFIG_PPC_PASEMI) += pasemi/ obj-$(CONFIG_PPC_CELL) += cell/ diff --git a/arch/powerpc/platforms/iseries/Makefile b/arch/powerpc/platforms/iseries/Makefile deleted file mode 100644 index a7602b1..0000000 --- a/arch/powerpc/platforms/iseries/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -ccflags-y := -mno-minimal-toc - -obj-y += exception.o -obj-y += hvlog.o hvlpconfig.o lpardata.o setup.o dt.o mf.o lpevents.o \ - hvcall.o proc.o htab.o iommu.o misc.o irq.o -obj-$(CONFIG_PCI) += pci.o -obj-$(CONFIG_SMP) += smp.o -obj-$(CONFIG_VIOPATH) += viopath.o vio.o -obj-$(CONFIG_MODULES) += ksyms.o diff --git a/arch/powerpc/platforms/iseries/call_hpt.h b/arch/powerpc/platforms/iseries/call_hpt.h deleted file mode 100644 index 8d95fe4b..0000000 --- a/arch/powerpc/platforms/iseries/call_hpt.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _PLATFORMS_ISERIES_CALL_HPT_H -#define _PLATFORMS_ISERIES_CALL_HPT_H - -/* - * This file contains the "hypervisor call" interface which is used to - * drive the hypervisor from the OS. - */ - -#include -#include -#include - -#define HvCallHptGetHptAddress HvCallHpt + 0 -#define HvCallHptGetHptPages HvCallHpt + 1 -#define HvCallHptSetPp HvCallHpt + 5 -#define HvCallHptSetSwBits HvCallHpt + 6 -#define HvCallHptUpdate HvCallHpt + 7 -#define HvCallHptInvalidateNoSyncICache HvCallHpt + 8 -#define HvCallHptGet HvCallHpt + 11 -#define HvCallHptFindNextValid HvCallHpt + 12 -#define HvCallHptFindValid HvCallHpt + 13 -#define HvCallHptAddValidate HvCallHpt + 16 -#define HvCallHptInvalidateSetSwBitsGet HvCallHpt + 18 - - -static inline u64 HvCallHpt_getHptAddress(void) -{ - return HvCall0(HvCallHptGetHptAddress); -} - -static inline u64 HvCallHpt_getHptPages(void) -{ - return HvCall0(HvCallHptGetHptPages); -} - -static inline void HvCallHpt_setPp(u32 hpteIndex, u8 value) -{ - HvCall2(HvCallHptSetPp, hpteIndex, value); -} - -static inline void HvCallHpt_setSwBits(u32 hpteIndex, u8 bitson, u8 bitsoff) -{ - HvCall3(HvCallHptSetSwBits, hpteIndex, bitson, bitsoff); -} - -static inline void HvCallHpt_invalidateNoSyncICache(u32 hpteIndex) -{ - HvCall1(HvCallHptInvalidateNoSyncICache, hpteIndex); -} - -static inline u64 HvCallHpt_invalidateSetSwBitsGet(u32 hpteIndex, u8 bitson, - u8 bitsoff) -{ - u64 compressedStatus; - - compressedStatus = HvCall4(HvCallHptInvalidateSetSwBitsGet, - hpteIndex, bitson, bitsoff, 1); - HvCall1(HvCallHptInvalidateNoSyncICache, hpteIndex); - return compressedStatus; -} - -static inline u64 HvCallHpt_findValid(struct hash_pte *hpte, u64 vpn) -{ - return HvCall3Ret16(HvCallHptFindValid, hpte, vpn, 0, 0); -} - -static inline u64 HvCallHpt_findNextValid(struct hash_pte *hpte, u32 hpteIndex, - u8 bitson, u8 bitsoff) -{ - return HvCall3Ret16(HvCallHptFindNextValid, hpte, hpteIndex, - bitson, bitsoff); -} - -static inline void HvCallHpt_get(struct hash_pte *hpte, u32 hpteIndex) -{ - HvCall2Ret16(HvCallHptGet, hpte, hpteIndex, 0); -} - -static inline void HvCallHpt_addValidate(u32 hpteIndex, u32 hBit, - struct hash_pte *hpte) -{ - HvCall4(HvCallHptAddValidate, hpteIndex, hBit, hpte->v, hpte->r); -} - -#endif /* _PLATFORMS_ISERIES_CALL_HPT_H */ diff --git a/arch/powerpc/platforms/iseries/call_pci.h b/arch/powerpc/platforms/iseries/call_pci.h deleted file mode 100644 index dbdf698..0000000 --- a/arch/powerpc/platforms/iseries/call_pci.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Provides the Hypervisor PCI calls for iSeries Linux Parition. - * Copyright (C) 2001 - * - * 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. - * - * 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 - * - * Change Activity: - * Created, Jan 9, 2001 - */ - -#ifndef _PLATFORMS_ISERIES_CALL_PCI_H -#define _PLATFORMS_ISERIES_CALL_PCI_H - -#include -#include - -/* - * DSA == Direct Select Address - * this struct must be 64 bits in total - */ -struct HvCallPci_DsaAddr { - u16 busNumber; /* PHB index? */ - u8 subBusNumber; /* PCI bus number? */ - u8 deviceId; /* device and function? */ - u8 barNumber; - u8 reserved[3]; -}; - -union HvDsaMap { - u64 DsaAddr; - struct HvCallPci_DsaAddr Dsa; -}; - -struct HvCallPci_LoadReturn { - u64 rc; - u64 value; -}; - -enum HvCallPci_DeviceType { - HvCallPci_NodeDevice = 1, - HvCallPci_SpDevice = 2, - HvCallPci_IopDevice = 3, - HvCallPci_BridgeDevice = 4, - HvCallPci_MultiFunctionDevice = 5, - HvCallPci_IoaDevice = 6 -}; - - -struct HvCallPci_DeviceInfo { - u32 deviceType; /* See DeviceType enum for values */ -}; - -struct HvCallPci_BusUnitInfo { - u32 sizeReturned; /* length of data returned */ - u32 deviceType; /* see DeviceType enum for values */ -}; - -struct HvCallPci_BridgeInfo { - struct HvCallPci_BusUnitInfo busUnitInfo; /* Generic bus unit info */ - u8 subBusNumber; /* Bus number of secondary bus */ - u8 maxAgents; /* Max idsels on secondary bus */ - u8 maxSubBusNumber; /* Max Sub Bus */ - u8 logicalSlotNumber; /* Logical Slot Number for IOA */ -}; - - -/* - * Maximum BusUnitInfo buffer size. Provided for clients so - * they can allocate a buffer big enough for any type of bus - * unit. Increase as needed. - */ -enum {HvCallPci_MaxBusUnitInfoSize = 128}; - -struct HvCallPci_BarParms { - u64 vaddr; - u64 raddr; - u64 size; - u64 protectStart; - u64 protectEnd; - u64 relocationOffset; - u64 pciAddress; - u64 reserved[3]; -}; - -enum HvCallPci_VpdType { - HvCallPci_BusVpd = 1, - HvCallPci_BusAdapterVpd = 2 -}; - -#define HvCallPciConfigLoad8 HvCallPci + 0 -#define HvCallPciConfigLoad16 HvCallPci + 1 -#define HvCallPciConfigLoad32 HvCallPci + 2 -#define HvCallPciConfigStore8 HvCallPci + 3 -#define HvCallPciConfigStore16 HvCallPci + 4 -#define HvCallPciConfigStore32 HvCallPci + 5 -#define HvCallPciEoi HvCallPci + 16 -#define HvCallPciGetBarParms HvCallPci + 18 -#define HvCallPciMaskFisr HvCallPci + 20 -#define HvCallPciUnmaskFisr HvCallPci + 21 -#define HvCallPciSetSlotReset HvCallPci + 25 -#define HvCallPciGetDeviceInfo HvCallPci + 27 -#define HvCallPciGetCardVpd HvCallPci + 28 -#define HvCallPciBarLoad8 HvCallPci + 40 -#define HvCallPciBarLoad16 HvCallPci + 41 -#define HvCallPciBarLoad32 HvCallPci + 42 -#define HvCallPciBarLoad64 HvCallPci + 43 -#define HvCallPciBarStore8 HvCallPci + 44 -#define HvCallPciBarStore16 HvCallPci + 45 -#define HvCallPciBarStore32 HvCallPci + 46 -#define HvCallPciBarStore64 HvCallPci + 47 -#define HvCallPciMaskInterrupts HvCallPci + 48 -#define HvCallPciUnmaskInterrupts HvCallPci + 49 -#define HvCallPciGetBusUnitInfo HvCallPci + 50 - -static inline u64 HvCallPci_configLoad16(u16 busNumber, u8 subBusNumber, - u8 deviceId, u32 offset, u16 *value) -{ - struct HvCallPci_DsaAddr dsa; - struct HvCallPci_LoadReturn retVal; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumber; - dsa.subBusNumber = subBusNumber; - dsa.deviceId = deviceId; - - HvCall3Ret16(HvCallPciConfigLoad16, &retVal, *(u64 *)&dsa, offset, 0); - - *value = retVal.value; - - return retVal.rc; -} - -static inline u64 HvCallPci_configLoad32(u16 busNumber, u8 subBusNumber, - u8 deviceId, u32 offset, u32 *value) -{ - struct HvCallPci_DsaAddr dsa; - struct HvCallPci_LoadReturn retVal; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumber; - dsa.subBusNumber = subBusNumber; - dsa.deviceId = deviceId; - - HvCall3Ret16(HvCallPciConfigLoad32, &retVal, *(u64 *)&dsa, offset, 0); - - *value = retVal.value; - - return retVal.rc; -} - -static inline u64 HvCallPci_configStore8(u16 busNumber, u8 subBusNumber, - u8 deviceId, u32 offset, u8 value) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumber; - dsa.subBusNumber = subBusNumber; - dsa.deviceId = deviceId; - - return HvCall4(HvCallPciConfigStore8, *(u64 *)&dsa, offset, value, 0); -} - -static inline u64 HvCallPci_eoi(u16 busNumberParm, u8 subBusParm, - u8 deviceIdParm) -{ - struct HvCallPci_DsaAddr dsa; - struct HvCallPci_LoadReturn retVal; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceIdParm; - - HvCall1Ret16(HvCallPciEoi, &retVal, *(u64*)&dsa); - - return retVal.rc; -} - -static inline u64 HvCallPci_getBarParms(u16 busNumberParm, u8 subBusParm, - u8 deviceIdParm, u8 barNumberParm, u64 parms, u32 sizeofParms) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceIdParm; - dsa.barNumber = barNumberParm; - - return HvCall3(HvCallPciGetBarParms, *(u64*)&dsa, parms, sizeofParms); -} - -static inline u64 HvCallPci_maskFisr(u16 busNumberParm, u8 subBusParm, - u8 deviceIdParm, u64 fisrMask) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceIdParm; - - return HvCall2(HvCallPciMaskFisr, *(u64*)&dsa, fisrMask); -} - -static inline u64 HvCallPci_unmaskFisr(u16 busNumberParm, u8 subBusParm, - u8 deviceIdParm, u64 fisrMask) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceIdParm; - - return HvCall2(HvCallPciUnmaskFisr, *(u64*)&dsa, fisrMask); -} - -static inline u64 HvCallPci_getDeviceInfo(u16 busNumberParm, u8 subBusParm, - u8 deviceNumberParm, u64 parms, u32 sizeofParms) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceNumberParm << 4; - - return HvCall3(HvCallPciGetDeviceInfo, *(u64*)&dsa, parms, sizeofParms); -} - -static inline u64 HvCallPci_maskInterrupts(u16 busNumberParm, u8 subBusParm, - u8 deviceIdParm, u64 interruptMask) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceIdParm; - - return HvCall2(HvCallPciMaskInterrupts, *(u64*)&dsa, interruptMask); -} - -static inline u64 HvCallPci_unmaskInterrupts(u16 busNumberParm, u8 subBusParm, - u8 deviceIdParm, u64 interruptMask) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceIdParm; - - return HvCall2(HvCallPciUnmaskInterrupts, *(u64*)&dsa, interruptMask); -} - -static inline u64 HvCallPci_getBusUnitInfo(u16 busNumberParm, u8 subBusParm, - u8 deviceIdParm, u64 parms, u32 sizeofParms) -{ - struct HvCallPci_DsaAddr dsa; - - *((u64*)&dsa) = 0; - - dsa.busNumber = busNumberParm; - dsa.subBusNumber = subBusParm; - dsa.deviceId = deviceIdParm; - - return HvCall3(HvCallPciGetBusUnitInfo, *(u64*)&dsa, parms, - sizeofParms); -} - -static inline int HvCallPci_getBusVpd(u16 busNumParm, u64 destParm, - u16 sizeParm) -{ - u64 xRc = HvCall4(HvCallPciGetCardVpd, busNumParm, destParm, - sizeParm, HvCallPci_BusVpd); - if (xRc == -1) - return -1; - else - return xRc & 0xFFFF; -} - -#endif /* _PLATFORMS_ISERIES_CALL_PCI_H */ diff --git a/arch/powerpc/platforms/iseries/call_sm.h b/arch/powerpc/platforms/iseries/call_sm.h deleted file mode 100644 index c7e2516..0000000 --- a/arch/powerpc/platforms/iseries/call_sm.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ISERIES_CALL_SM_H -#define _ISERIES_CALL_SM_H - -/* - * This file contains the "hypervisor call" interface which is used to - * drive the hypervisor from the OS. - */ - -#include -#include - -#define HvCallSmGet64BitsOfAccessMap HvCallSm + 11 - -static inline u64 HvCallSm_get64BitsOfAccessMap(HvLpIndex lpIndex, - u64 indexIntoBitMap) -{ - return HvCall2(HvCallSmGet64BitsOfAccessMap, lpIndex, indexIntoBitMap); -} - -#endif /* _ISERIES_CALL_SM_H */ diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c deleted file mode 100644 index f0491cc..0000000 --- a/arch/powerpc/platforms/iseries/dt.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright (C) 2005-2006 Michael Ellerman, IBM Corporation - * Copyright (C) 2000-2004, IBM Corporation - * - * Description: - * This file contains all the routines to build a flattened device - * tree for a legacy iSeries machine. - * - * 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. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* ETH_ALEN */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "processor_vpd.h" -#include "call_hpt.h" -#include "call_pci.h" -#include "pci.h" -#include "it_exp_vpd_panel.h" -#include "naca.h" - -#ifdef DEBUG -#define DBG(fmt...) udbg_printf(fmt) -#else -#define DBG(fmt...) -#endif - -/* - * These are created by the linker script at the start and end - * of the section containing all the strings marked with the DS macro. - */ -extern char __dt_strings_start[]; -extern char __dt_strings_end[]; - -#define DS(s) ({ \ - static const char __s[] __attribute__((section(".dt_strings"))) = s; \ - __s; \ -}) - -struct iseries_flat_dt { - struct boot_param_header header; - u64 reserve_map[2]; -}; - -static void * __initdata dt_data; - -/* - * Putting these strings here keeps them out of the .dt_strings section - * that we capture for the strings blob of the flattened device tree. - */ -static char __initdata device_type_cpu[] = "cpu"; -static char __initdata device_type_memory[] = "memory"; -static char __initdata device_type_serial[] = "serial"; -static char __initdata device_type_network[] = "network"; -static char __initdata device_type_pci[] = "pci"; -static char __initdata device_type_vdevice[] = "vdevice"; -static char __initdata device_type_vscsi[] = "vscsi"; - - -/* EBCDIC to ASCII conversion routines */ - -static unsigned char __init e2a(unsigned char x) -{ - switch (x) { - case 0x81 ... 0x89: - return x - 0x81 + 'a'; - case 0x91 ... 0x99: - return x - 0x91 + 'j'; - case 0xA2 ... 0xA9: - return x - 0xA2 + 's'; - case 0xC1 ... 0xC9: - return x - 0xC1 + 'A'; - case 0xD1 ... 0xD9: - return x - 0xD1 + 'J'; - case 0xE2 ... 0xE9: - return x - 0xE2 + 'S'; - case 0xF0 ... 0xF9: - return x - 0xF0 + '0'; - } - return ' '; -} - -static unsigned char * __init strne2a(unsigned char *dest, - const unsigned char *src, size_t n) -{ - int i; - - n = strnlen(src, n); - - for (i = 0; i < n; i++) - dest[i] = e2a(src[i]); - - return dest; -} - -static struct iseries_flat_dt * __init dt_init(void) -{ - struct iseries_flat_dt *dt; - unsigned long str_len; - - str_len = __dt_strings_end - __dt_strings_start; - dt = (struct iseries_flat_dt *)ALIGN(klimit, 8); - dt->header.off_mem_rsvmap = - offsetof(struct iseries_flat_dt, reserve_map); - dt->header.off_dt_strings = ALIGN(sizeof(*dt), 8); - dt->header.off_dt_struct = dt->header.off_dt_strings - + ALIGN(str_len, 8); - dt_data = (void *)((unsigned long)dt + dt->header.off_dt_struct); - dt->header.dt_strings_size = str_len; - - /* There is no notion of hardware cpu id on iSeries */ - dt->header.boot_cpuid_phys = smp_processor_id(); - - memcpy((char *)dt + dt->header.off_dt_strings, __dt_strings_start, - str_len); - - dt->header.magic = OF_DT_HEADER; - dt->header.version = 0x10; - dt->header.last_comp_version = 0x10; - - dt->reserve_map[0] = 0; - dt->reserve_map[1] = 0; - - return dt; -} - -static void __init dt_push_u32(struct iseries_flat_dt *dt, u32 value) -{ - *((u32 *)dt_data) = value; - dt_data += sizeof(u32); -} - -#ifdef notyet -static void __init dt_push_u64(struct iseries_flat_dt *dt, u64 value) -{ - *((u64 *)dt_data) = value; - dt_data += sizeof(u64); -} -#endif - -static void __init dt_push_bytes(struct iseries_flat_dt *dt, const char *data, - int len) -{ - memcpy(dt_data, data, len); - dt_data += ALIGN(len, 4); -} - -static void __init dt_start_node(struct iseries_flat_dt *dt, const char *name) -{ - dt_push_u32(dt, OF_DT_BEGIN_NODE); - dt_push_bytes(dt, name, strlen(name) + 1); -} - -#define dt_end_node(dt) dt_push_u32(dt, OF_DT_END_NODE) - -static void __init __dt_prop(struct iseries_flat_dt *dt, const char *name, - const void *data, int len) -{ - unsigned long offset; - - dt_push_u32(dt, OF_DT_PROP); - - /* Length of the data */ - dt_push_u32(dt, len); - - offset = name - __dt_strings_start; - - /* The offset of the properties name in the string blob. */ - dt_push_u32(dt, (u32)offset); - - /* The actual data. */ - dt_push_bytes(dt, data, len); -} -#define dt_prop(dt, name, data, len) __dt_prop((dt), DS(name), (data), (len)) - -#define dt_prop_str(dt, name, data) \ - dt_prop((dt), name, (data), strlen((data)) + 1); /* + 1 for NULL */ - -static void __init __dt_prop_u32(struct iseries_flat_dt *dt, const char *name, - u32 data) -{ - __dt_prop(dt, name, &data, sizeof(u32)); -} -#define dt_prop_u32(dt, name, data) __dt_prop_u32((dt), DS(name), (data)) - -static void __init __maybe_unused __dt_prop_u64(struct iseries_flat_dt *dt, - const char *name, u64 data) -{ - __dt_prop(dt, name, &data, sizeof(u64)); -} -#define dt_prop_u64(dt, name, data) __dt_prop_u64((dt), DS(name), (data)) - -#define dt_prop_u64_list(dt, name, data, n) \ - dt_prop((dt), name, (data), sizeof(u64) * (n)) - -#define dt_prop_u32_list(dt, name, data, n) \ - dt_prop((dt), name, (data), sizeof(u32) * (n)) - -#define dt_prop_empty(dt, name) dt_prop((dt), name, NULL, 0) - -static void __init dt_cpus(struct iseries_flat_dt *dt) -{ - unsigned char buf[32]; - unsigned char *p; - unsigned int i, index; - struct IoHriProcessorVpd *d; - u32 pft_size[2]; - - /* yuck */ - snprintf(buf, 32, "PowerPC,%s", cur_cpu_spec->cpu_name); - p = strchr(buf, ' '); - if (!p) p = buf + strlen(buf); - - dt_start_node(dt, "cpus"); - dt_prop_u32(dt, "#address-cells", 1); - dt_prop_u32(dt, "#size-cells", 0); - - pft_size[0] = 0; /* NUMA CEC cookie, 0 for non NUMA */ - pft_size[1] = __ilog2(HvCallHpt_getHptPages() * HW_PAGE_SIZE); - - for (i = 0; i < NR_LPPACAS; i++) { - if (lppaca[i].dyn_proc_status >= 2) - continue; - - snprintf(p, 32 - (p - buf), "@%d", i); - dt_start_node(dt, buf); - - dt_prop_str(dt, "device_type", device_type_cpu); - - index = lppaca[i].dyn_hv_phys_proc_index; - d = &xIoHriProcessorVpd[index]; - - dt_prop_u32(dt, "i-cache-size", d->xInstCacheSize * 1024); - dt_prop_u32(dt, "i-cache-line-size", d->xInstCacheOperandSize); - - dt_prop_u32(dt, "d-cache-size", d->xDataL1CacheSizeKB * 1024); - dt_prop_u32(dt, "d-cache-line-size", d->xDataCacheOperandSize); - - /* magic conversions to Hz copied from old code */ - dt_prop_u32(dt, "clock-frequency", - ((1UL << 34) * 1000000) / d->xProcFreq); - dt_prop_u32(dt, "timebase-frequency", - ((1UL << 32) * 1000000) / d->xTimeBaseFreq); - - dt_prop_u32(dt, "reg", i); - - dt_prop_u32_list(dt, "ibm,pft-size", pft_size, 2); - - dt_end_node(dt); - } - - dt_end_node(dt); -} - -static void __init dt_model(struct iseries_flat_dt *dt) -{ - char buf[16] = "IBM,"; - - /* N.B. lparcfg.c knows about the "IBM," prefixes ... */ - /* "IBM," + mfgId[2:3] + systemSerial[1:5] */ - strne2a(buf + 4, xItExtVpdPanel.mfgID + 2, 2); - strne2a(buf + 6, xItExtVpdPanel.systemSerial + 1, 5); - buf[11] = '\0'; - dt_prop_str(dt, "system-id", buf); - - /* "IBM," + machineType[0:4] */ - strne2a(buf + 4, xItExtVpdPanel.machineType, 4); - buf[8] = '\0'; - dt_prop_str(dt, "model", buf); - - dt_prop_str(dt, "compatible", "IBM,iSeries"); - dt_prop_u32(dt, "ibm,partition-no", HvLpConfig_getLpIndex()); -} - -static void __init dt_initrd(struct iseries_flat_dt *dt) -{ -#ifdef CONFIG_BLK_DEV_INITRD - if (naca.xRamDisk) { - dt_prop_u64(dt, "linux,initrd-start", (u64)naca.xRamDisk); - dt_prop_u64(dt, "linux,initrd-end", - (u64)naca.xRamDisk + naca.xRamDiskSize * HW_PAGE_SIZE); - } -#endif -} - -static void __init dt_do_vdevice(struct iseries_flat_dt *dt, - const char *name, u32 reg, int unit, - const char *type, const char *compat, int end) -{ - char buf[32]; - - snprintf(buf, 32, "%s@%08x", name, reg + ((unit >= 0) ? unit : 0)); - dt_start_node(dt, buf); - dt_prop_str(dt, "device_type", type); - if (compat) - dt_prop_str(dt, "compatible", compat); - dt_prop_u32(dt, "reg", reg + ((unit >= 0) ? unit : 0)); - if (unit >= 0) - dt_prop_u32(dt, "linux,unit_address", unit); - if (end) - dt_end_node(dt); -} - -static void __init dt_vdevices(struct iseries_flat_dt *dt) -{ - u32 reg = 0; - HvLpIndexMap vlan_map; - int i; - - dt_start_node(dt, "vdevice"); - dt_prop_str(dt, "device_type", device_type_vdevice); - dt_prop_str(dt, "compatible", "IBM,iSeries-vdevice"); - dt_prop_u32(dt, "#address-cells", 1); - dt_prop_u32(dt, "#size-cells", 0); - - dt_do_vdevice(dt, "vty", reg, -1, device_type_serial, - "IBM,iSeries-vty", 1); - reg++; - - dt_do_vdevice(dt, "v-scsi", reg, -1, device_type_vscsi, - "IBM,v-scsi", 1); - reg++; - - vlan_map = HvLpConfig_getVirtualLanIndexMap(); - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { - unsigned char mac_addr[ETH_ALEN]; - - if ((vlan_map & (0x8000 >> i)) == 0) - continue; - dt_do_vdevice(dt, "l-lan", reg, i, device_type_network, - "IBM,iSeries-l-lan", 0); - mac_addr[0] = 0x02; - mac_addr[1] = 0x01; - mac_addr[2] = 0xff; - mac_addr[3] = i; - mac_addr[4] = 0xff; - mac_addr[5] = HvLpConfig_getLpIndex_outline(); - dt_prop(dt, "local-mac-address", (char *)mac_addr, ETH_ALEN); - dt_prop(dt, "mac-address", (char *)mac_addr, ETH_ALEN); - dt_prop_u32(dt, "max-frame-size", 9000); - dt_prop_u32(dt, "address-bits", 48); - - dt_end_node(dt); - } - - dt_end_node(dt); -} - -struct pci_class_name { - u16 code; - const char *name; - const char *type; -}; - -static struct pci_class_name __initdata pci_class_name[] = { - { PCI_CLASS_NETWORK_ETHERNET, "ethernet", device_type_network }, -}; - -static struct pci_class_name * __init dt_find_pci_class_name(u16 class_code) -{ - struct pci_class_name *cp; - - for (cp = pci_class_name; - cp < &pci_class_name[ARRAY_SIZE(pci_class_name)]; cp++) - if (cp->code == class_code) - return cp; - return NULL; -} - -/* - * This assumes that the node slot is always on the primary bus! - */ -static void __init scan_bridge_slot(struct iseries_flat_dt *dt, - HvBusNumber bus, struct HvCallPci_BridgeInfo *bridge_info) -{ - HvSubBusNumber sub_bus = bridge_info->subBusNumber; - u16 vendor_id; - u16 device_id; - u32 class_id; - int err; - char buf[32]; - u32 reg[5]; - int id_sel = ISERIES_GET_DEVICE_FROM_SUBBUS(sub_bus); - int function = ISERIES_GET_FUNCTION_FROM_SUBBUS(sub_bus); - HvAgentId eads_id_sel = ISERIES_PCI_AGENTID(id_sel, function); - u8 devfn; - struct pci_class_name *cp; - - /* - * Connect all functions of any device found. - */ - for (id_sel = 1; id_sel <= bridge_info->maxAgents; id_sel++) { - for (function = 0; function < 8; function++) { - HvAgentId agent_id = ISERIES_PCI_AGENTID(id_sel, - function); - err = HvCallXm_connectBusUnit(bus, sub_bus, - agent_id, 0); - if (err) { - if (err != 0x302) - DBG("connectBusUnit(%x, %x, %x) %x\n", - bus, sub_bus, agent_id, err); - continue; - } - - err = HvCallPci_configLoad16(bus, sub_bus, agent_id, - PCI_VENDOR_ID, &vendor_id); - if (err) { - DBG("ReadVendor(%x, %x, %x) %x\n", - bus, sub_bus, agent_id, err); - continue; - } - err = HvCallPci_configLoad16(bus, sub_bus, agent_id, - PCI_DEVICE_ID, &device_id); - if (err) { - DBG("ReadDevice(%x, %x, %x) %x\n", - bus, sub_bus, agent_id, err); - continue; - } - err = HvCallPci_configLoad32(bus, sub_bus, agent_id, - PCI_CLASS_REVISION , &class_id); - if (err) { - DBG("ReadClass(%x, %x, %x) %x\n", - bus, sub_bus, agent_id, err); - continue; - } - - devfn = PCI_DEVFN(ISERIES_ENCODE_DEVICE(eads_id_sel), - function); - cp = dt_find_pci_class_name(class_id >> 16); - if (cp && cp->name) - strncpy(buf, cp->name, sizeof(buf) - 1); - else - snprintf(buf, sizeof(buf), "pci%x,%x", - vendor_id, device_id); - buf[sizeof(buf) - 1] = '\0'; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "@%x", PCI_SLOT(devfn)); - buf[sizeof(buf) - 1] = '\0'; - if (function != 0) - snprintf(buf + strlen(buf), - sizeof(buf) - strlen(buf), - ",%x", function); - dt_start_node(dt, buf); - reg[0] = (bus << 16) | (devfn << 8); - reg[1] = 0; - reg[2] = 0; - reg[3] = 0; - reg[4] = 0; - dt_prop_u32_list(dt, "reg", reg, 5); - if (cp && (cp->type || cp->name)) - dt_prop_str(dt, "device_type", - cp->type ? cp->type : cp->name); - dt_prop_u32(dt, "vendor-id", vendor_id); - dt_prop_u32(dt, "device-id", device_id); - dt_prop_u32(dt, "class-code", class_id >> 8); - dt_prop_u32(dt, "revision-id", class_id & 0xff); - dt_prop_u32(dt, "linux,subbus", sub_bus); - dt_prop_u32(dt, "linux,agent-id", agent_id); - dt_prop_u32(dt, "linux,logical-slot-number", - bridge_info->logicalSlotNumber); - dt_end_node(dt); - - } - } -} - -static void __init scan_bridge(struct iseries_flat_dt *dt, HvBusNumber bus, - HvSubBusNumber sub_bus, int id_sel) -{ - struct HvCallPci_BridgeInfo bridge_info; - HvAgentId agent_id; - int function; - int ret; - - /* Note: hvSubBus and irq is always be 0 at this level! */ - for (function = 0; function < 8; ++function) { - agent_id = ISERIES_PCI_AGENTID(id_sel, function); - ret = HvCallXm_connectBusUnit(bus, sub_bus, agent_id, 0); - if (ret != 0) { - if (ret != 0xb) - DBG("connectBusUnit(%x, %x, %x) %x\n", - bus, sub_bus, agent_id, ret); - continue; - } - DBG("found device at bus %d idsel %d func %d (AgentId %x)\n", - bus, id_sel, function, agent_id); - ret = HvCallPci_getBusUnitInfo(bus, sub_bus, agent_id, - iseries_hv_addr(&bridge_info), - sizeof(struct HvCallPci_BridgeInfo)); - if (ret != 0) - continue; - DBG("bridge info: type %x subbus %x " - "maxAgents %x maxsubbus %x logslot %x\n", - bridge_info.busUnitInfo.deviceType, - bridge_info.subBusNumber, - bridge_info.maxAgents, - bridge_info.maxSubBusNumber, - bridge_info.logicalSlotNumber); - if (bridge_info.busUnitInfo.deviceType == - HvCallPci_BridgeDevice) - scan_bridge_slot(dt, bus, &bridge_info); - else - DBG("PCI: Invalid Bridge Configuration(0x%02X)", - bridge_info.busUnitInfo.deviceType); - } -} - -static void __init scan_phb(struct iseries_flat_dt *dt, HvBusNumber bus) -{ - struct HvCallPci_DeviceInfo dev_info; - const HvSubBusNumber sub_bus = 0; /* EADs is always 0. */ - int err; - int id_sel; - const int max_agents = 8; - - /* - * Probe for EADs Bridges - */ - for (id_sel = 1; id_sel < max_agents; ++id_sel) { - err = HvCallPci_getDeviceInfo(bus, sub_bus, id_sel, - iseries_hv_addr(&dev_info), - sizeof(struct HvCallPci_DeviceInfo)); - if (err) { - if (err != 0x302) - DBG("getDeviceInfo(%x, %x, %x) %x\n", - bus, sub_bus, id_sel, err); - continue; - } - if (dev_info.deviceType != HvCallPci_NodeDevice) { - DBG("PCI: Invalid System Configuration" - "(0x%02X) for bus 0x%02x id 0x%02x.\n", - dev_info.deviceType, bus, id_sel); - continue; - } - scan_bridge(dt, bus, sub_bus, id_sel); - } -} - -static void __init dt_pci_devices(struct iseries_flat_dt *dt) -{ - HvBusNumber bus; - char buf[32]; - u32 buses[2]; - int phb_num = 0; - - /* Check all possible buses. */ - for (bus = 0; bus < 256; bus++) { - int err = HvCallXm_testBus(bus); - - if (err) { - /* - * Check for Unexpected Return code, a clue that - * something has gone wrong. - */ - if (err != 0x0301) - DBG("Unexpected Return on Probe(0x%02X) " - "0x%04X\n", bus, err); - continue; - } - DBG("bus %d appears to exist\n", bus); - snprintf(buf, 32, "pci@%d", phb_num); - dt_start_node(dt, buf); - dt_prop_str(dt, "device_type", device_type_pci); - dt_prop_str(dt, "compatible", "IBM,iSeries-Logical-PHB"); - dt_prop_u32(dt, "#address-cells", 3); - dt_prop_u32(dt, "#size-cells", 2); - buses[0] = buses[1] = bus; - dt_prop_u32_list(dt, "bus-range", buses, 2); - scan_phb(dt, bus); - dt_end_node(dt); - phb_num++; - } -} - -static void dt_finish(struct iseries_flat_dt *dt) -{ - dt_push_u32(dt, OF_DT_END); - dt->header.totalsize = (unsigned long)dt_data - (unsigned long)dt; - klimit = ALIGN((unsigned long)dt_data, 8); -} - -void * __init build_flat_dt(unsigned long phys_mem_size) -{ - struct iseries_flat_dt *iseries_dt; - u64 tmp[2]; - - iseries_dt = dt_init(); - - dt_start_node(iseries_dt, ""); - - dt_prop_u32(iseries_dt, "#address-cells", 2); - dt_prop_u32(iseries_dt, "#size-cells", 2); - dt_model(iseries_dt); - - /* /memory */ - dt_start_node(iseries_dt, "memory@0"); - dt_prop_str(iseries_dt, "device_type", device_type_memory); - tmp[0] = 0; - tmp[1] = phys_mem_size; - dt_prop_u64_list(iseries_dt, "reg", tmp, 2); - dt_end_node(iseries_dt); - - /* /chosen */ - dt_start_node(iseries_dt, "chosen"); - dt_prop_str(iseries_dt, "bootargs", cmd_line); - dt_initrd(iseries_dt); - dt_end_node(iseries_dt); - - dt_cpus(iseries_dt); - - dt_vdevices(iseries_dt); - dt_pci_devices(iseries_dt); - - dt_end_node(iseries_dt); - - dt_finish(iseries_dt); - - return iseries_dt; -} diff --git a/arch/powerpc/platforms/iseries/exception.S b/arch/powerpc/platforms/iseries/exception.S deleted file mode 100644 index f519ee1..0000000 --- a/arch/powerpc/platforms/iseries/exception.S +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Low level routines for legacy iSeries support. - * - * Extracted from head_64.S - * - * PowerPC version - * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) - * - * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP - * Copyright (C) 1996 Cort Dougan - * Adapted for Power Macintosh by Paul Mackerras. - * Low-level exception handlers and MMU support - * rewritten by Paul Mackerras. - * Copyright (C) 1996 Paul Mackerras. - * - * Adapted for 64bit PowerPC by Dave Engebretsen, Peter Bergner, and - * Mike Corrigan {engebret|bergner|mikejc}@us.ibm.com - * - * This file contains the low-level support and setup for the - * PowerPC-64 platform, including trap and interrupt dispatch. - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "exception.h" - - .text - - .globl system_reset_iSeries -system_reset_iSeries: - bl .relative_toc - mfspr r13,SPRN_SPRG3 /* Get alpaca address */ - LOAD_REG_ADDR(r23, alpaca) - li r0,ALPACA_SIZE - sub r23,r13,r23 - divdu r24,r23,r0 /* r24 has cpu number */ - cmpwi 0,r24,0 /* Are we processor 0? */ - bne 1f - LOAD_REG_ADDR(r13, boot_paca) - mtspr SPRN_SPRG_PACA,r13 /* Save it away for the future */ - mfmsr r23 - ori r23,r23,MSR_RI - mtmsrd r23 /* RI on */ - b .__start_initialization_iSeries /* Start up the first processor */ -1: mfspr r4,SPRN_CTRLF - li r5,CTRL_RUNLATCH /* Turn off the run light */ - andc r4,r4,r5 - mtspr SPRN_CTRLT,r4 - -/* Spin on __secondary_hold_spinloop until it is updated by the boot cpu. */ -/* In the UP case we'll yield() later, and we will not access the paca anyway */ -#ifdef CONFIG_SMP -iSeries_secondary_wait_paca: - HMT_LOW - LOAD_REG_ADDR(r23, __secondary_hold_spinloop) - ld r23,0(r23) - - cmpdi 0,r23,0 - bne 2f /* go on when the master is ready */ - - /* Keep poking the Hypervisor until we're released */ - /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ - lis r3,0x8002 - rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ - li r0,-1 /* r0=-1 indicates a Hypervisor call */ - sc /* Invoke the hypervisor via a system call */ - b iSeries_secondary_wait_paca - -2: - HMT_MEDIUM - sync - - LOAD_REG_ADDR(r3, nr_cpu_ids) /* get number of pacas allocated */ - lwz r3,0(r3) /* nr_cpus= or NR_CPUS can limit */ - cmpld 0,r24,r3 /* is our cpu number allocated? */ - bge iSeries_secondary_yield /* no, yield forever */ - - /* Load our paca now that it's been allocated */ - LOAD_REG_ADDR(r13, paca) - ld r13,0(r13) - mulli r0,r24,PACA_SIZE - add r13,r13,r0 - mtspr SPRN_SPRG_PACA,r13 /* Save it away for the future */ - mfmsr r23 - ori r23,r23,MSR_RI - mtmsrd r23 /* RI on */ - -iSeries_secondary_smp_loop: - lbz r23,PACAPROCSTART(r13) /* Test if this processor - * should start */ - cmpwi 0,r23,0 - bne 3f /* go on when we are told */ - - HMT_LOW - /* Let the Hypervisor know we are alive */ - /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ - lis r3,0x8002 - rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ - li r0,-1 /* r0=-1 indicates a Hypervisor call */ - sc /* Invoke the hypervisor via a system call */ - mfspr r13,SPRN_SPRG_PACA /* Put r13 back ???? */ - b iSeries_secondary_smp_loop /* wait for signal to start */ - -3: - HMT_MEDIUM - sync - LOAD_REG_ADDR(r3,current_set) - sldi r28,r24,3 /* get current_set[cpu#] */ - ldx r3,r3,r28 - addi r1,r3,THREAD_SIZE - subi r1,r1,STACK_FRAME_OVERHEAD - - b __secondary_start /* Loop until told to go */ -#endif /* CONFIG_SMP */ - -iSeries_secondary_yield: - /* Yield the processor. This is required for non-SMP kernels - which are running on multi-threaded machines. */ - HMT_LOW - lis r3,0x8000 - rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */ - addi r3,r3,18 /* r3 = 0x8000000000000012 which is "yield" */ - li r4,0 /* "yield timed" */ - li r5,-1 /* "yield forever" */ - li r0,-1 /* r0=-1 indicates a Hypervisor call */ - sc /* Invoke the hypervisor via a system call */ - mfspr r13,SPRN_SPRG_PACA /* Put r13 back ???? */ - b iSeries_secondary_yield /* If SMP not configured, secondaries - * loop forever */ - -/*** ISeries-LPAR interrupt handlers ***/ - - STD_EXCEPTION_ISERIES(machine_check, PACA_EXMC) - - .globl data_access_iSeries -data_access_iSeries: - mtspr SPRN_SPRG_SCRATCH0,r13 -BEGIN_FTR_SECTION - mfspr r13,SPRN_SPRG_PACA - std r9,PACA_EXSLB+EX_R9(r13) - std r10,PACA_EXSLB+EX_R10(r13) - mfspr r10,SPRN_DAR - mfspr r9,SPRN_DSISR - srdi r10,r10,60 - rlwimi r10,r9,16,0x20 - mfcr r9 - cmpwi r10,0x2c - beq .do_stab_bolted_iSeries - ld r10,PACA_EXSLB+EX_R10(r13) - std r11,PACA_EXGEN+EX_R11(r13) - ld r11,PACA_EXSLB+EX_R9(r13) - std r12,PACA_EXGEN+EX_R12(r13) - mfspr r12,SPRN_SPRG_SCRATCH0 - std r10,PACA_EXGEN+EX_R10(r13) - std r11,PACA_EXGEN+EX_R9(r13) - std r12,PACA_EXGEN+EX_R13(r13) - EXCEPTION_PROLOG_ISERIES_1 -FTR_SECTION_ELSE - EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, 0) - EXCEPTION_PROLOG_ISERIES_1 -ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_SLB) - b data_access_common - -.do_stab_bolted_iSeries: - std r11,PACA_EXSLB+EX_R11(r13) - std r12,PACA_EXSLB+EX_R12(r13) - mfspr r10,SPRN_SPRG_SCRATCH0 - std r10,PACA_EXSLB+EX_R13(r13) - EXCEPTION_PROLOG_ISERIES_1 - b .do_stab_bolted - - .globl data_access_slb_iSeries -data_access_slb_iSeries: - mtspr SPRN_SPRG_SCRATCH0,r13 /* save r13 */ - mfspr r13,SPRN_SPRG_PACA /* get paca address into r13 */ - std r3,PACA_EXSLB+EX_R3(r13) - mfspr r3,SPRN_DAR - std r9,PACA_EXSLB+EX_R9(r13) - mfcr r9 -#ifdef __DISABLED__ - cmpdi r3,0 - bge slb_miss_user_iseries -#endif - std r10,PACA_EXSLB+EX_R10(r13) - std r11,PACA_EXSLB+EX_R11(r13) - std r12,PACA_EXSLB+EX_R12(r13) - mfspr r10,SPRN_SPRG_SCRATCH0 - std r10,PACA_EXSLB+EX_R13(r13) - ld r12,PACALPPACAPTR(r13) - ld r12,LPPACASRR1(r12) - b .slb_miss_realmode - - STD_EXCEPTION_ISERIES(instruction_access, PACA_EXGEN) - - .globl instruction_access_slb_iSeries -instruction_access_slb_iSeries: - mtspr SPRN_SPRG_SCRATCH0,r13 /* save r13 */ - mfspr r13,SPRN_SPRG_PACA /* get paca address into r13 */ - std r3,PACA_EXSLB+EX_R3(r13) - ld r3,PACALPPACAPTR(r13) - ld r3,LPPACASRR0(r3) /* get SRR0 value */ - std r9,PACA_EXSLB+EX_R9(r13) - mfcr r9 -#ifdef __DISABLED__ - cmpdi r3,0 - bge slb_miss_user_iseries -#endif - std r10,PACA_EXSLB+EX_R10(r13) - std r11,PACA_EXSLB+EX_R11(r13) - std r12,PACA_EXSLB+EX_R12(r13) - mfspr r10,SPRN_SPRG_SCRATCH0 - std r10,PACA_EXSLB+EX_R13(r13) - ld r12,PACALPPACAPTR(r13) - ld r12,LPPACASRR1(r12) - b .slb_miss_realmode - -#ifdef __DISABLED__ -slb_miss_user_iseries: - std r10,PACA_EXGEN+EX_R10(r13) - std r11,PACA_EXGEN+EX_R11(r13) - std r12,PACA_EXGEN+EX_R12(r13) - mfspr r10,SPRG_SCRATCH0 - ld r11,PACA_EXSLB+EX_R9(r13) - ld r12,PACA_EXSLB+EX_R3(r13) - std r10,PACA_EXGEN+EX_R13(r13) - std r11,PACA_EXGEN+EX_R9(r13) - std r12,PACA_EXGEN+EX_R3(r13) - EXCEPTION_PROLOG_ISERIES_1 - b slb_miss_user_common -#endif - - MASKABLE_EXCEPTION_ISERIES(hardware_interrupt) - STD_EXCEPTION_ISERIES(alignment, PACA_EXGEN) - STD_EXCEPTION_ISERIES(program_check, PACA_EXGEN) - STD_EXCEPTION_ISERIES(fp_unavailable, PACA_EXGEN) - MASKABLE_EXCEPTION_ISERIES(decrementer) - STD_EXCEPTION_ISERIES(trap_0a, PACA_EXGEN) - STD_EXCEPTION_ISERIES(trap_0b, PACA_EXGEN) - - .globl system_call_iSeries -system_call_iSeries: - mr r9,r13 - mfspr r13,SPRN_SPRG_PACA - EXCEPTION_PROLOG_ISERIES_1 - b system_call_common - - STD_EXCEPTION_ISERIES(single_step, PACA_EXGEN) - STD_EXCEPTION_ISERIES(trap_0e, PACA_EXGEN) - STD_EXCEPTION_ISERIES(performance_monitor, PACA_EXGEN) - -decrementer_iSeries_masked: - /* We may not have a valid TOC pointer in here. */ - li r11,1 - ld r12,PACALPPACAPTR(r13) - stb r11,LPPACADECRINT(r12) - li r12,-1 - clrldi r12,r12,33 /* set DEC to 0x7fffffff */ - mtspr SPRN_DEC,r12 - /* fall through */ - -hardware_interrupt_iSeries_masked: - mtcrf 0x80,r9 /* Restore regs */ - ld r12,PACALPPACAPTR(r13) - ld r11,LPPACASRR0(r12) - ld r12,LPPACASRR1(r12) - mtspr SPRN_SRR0,r11 - mtspr SPRN_SRR1,r12 - ld r9,PACA_EXGEN+EX_R9(r13) - ld r10,PACA_EXGEN+EX_R10(r13) - ld r11,PACA_EXGEN+EX_R11(r13) - ld r12,PACA_EXGEN+EX_R12(r13) - ld r13,PACA_EXGEN+EX_R13(r13) - rfid - b . /* prevent speculative execution */ - -_INIT_STATIC(__start_initialization_iSeries) - /* Clear out the BSS */ - LOAD_REG_ADDR(r11,__bss_stop) - LOAD_REG_ADDR(r8,__bss_start) - sub r11,r11,r8 /* bss size */ - addi r11,r11,7 /* round up to an even double word */ - rldicl. r11,r11,61,3 /* shift right by 3 */ - beq 4f - addi r8,r8,-8 - li r0,0 - mtctr r11 /* zero this many doublewords */ -3: stdu r0,8(r8) - bdnz 3b -4: - LOAD_REG_ADDR(r1,init_thread_union) - addi r1,r1,THREAD_SIZE - li r0,0 - stdu r0,-STACK_FRAME_OVERHEAD(r1) - - bl .iSeries_early_setup - bl .early_setup - - /* relocation is on at this point */ - - b .start_here_common diff --git a/arch/powerpc/platforms/iseries/exception.h b/arch/powerpc/platforms/iseries/exception.h deleted file mode 100644 index 50271b5..0000000 --- a/arch/powerpc/platforms/iseries/exception.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _ASM_POWERPC_ISERIES_EXCEPTION_H -#define _ASM_POWERPC_ISERIES_EXCEPTION_H -/* - * Extracted from head_64.S - * - * PowerPC version - * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) - * - * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP - * Copyright (C) 1996 Cort Dougan - * Adapted for Power Macintosh by Paul Mackerras. - * Low-level exception handlers and MMU support - * rewritten by Paul Mackerras. - * Copyright (C) 1996 Paul Mackerras. - * - * Adapted for 64bit PowerPC by Dave Engebretsen, Peter Bergner, and - * Mike Corrigan {engebret|bergner|mikejc}@us.ibm.com - * - * This file contains the low-level support and setup for the - * PowerPC-64 platform, including trap and interrupt dispatch. - * - * 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 - -#define EXCEPTION_PROLOG_ISERIES_1 \ - mfmsr r10; \ - ld r12,PACALPPACAPTR(r13); \ - ld r11,LPPACASRR0(r12); \ - ld r12,LPPACASRR1(r12); \ - ori r10,r10,MSR_RI; \ - mtmsrd r10,1 - -#define STD_EXCEPTION_ISERIES(label, area) \ - .globl label##_iSeries; \ -label##_iSeries: \ - HMT_MEDIUM; \ - mtspr SPRN_SPRG_SCRATCH0,r13; /* save r13 */ \ - EXCEPTION_PROLOG_1(area, NOTEST, 0); \ - EXCEPTION_PROLOG_ISERIES_1; \ - b label##_common - -#define MASKABLE_EXCEPTION_ISERIES(label) \ - .globl label##_iSeries; \ -label##_iSeries: \ - HMT_MEDIUM; \ - mtspr SPRN_SPRG_SCRATCH0,r13; /* save r13 */ \ - EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, 0); \ - lbz r10,PACASOFTIRQEN(r13); \ - cmpwi 0,r10,0; \ - beq- label##_iSeries_masked; \ - EXCEPTION_PROLOG_ISERIES_1; \ - b label##_common; \ - -#endif /* _ASM_POWERPC_ISERIES_EXCEPTION_H */ diff --git a/arch/powerpc/platforms/iseries/htab.c b/arch/powerpc/platforms/iseries/htab.c deleted file mode 100644 index 3ae66ab..0000000 --- a/arch/powerpc/platforms/iseries/htab.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * iSeries hashtable management. - * Derived from pSeries_htab.c - * - * SMP scalability work: - * Copyright (C) 2001 Anton Blanchard , IBM - * - * 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 -#include -#include -#include -#include -#include - -#include "call_hpt.h" - -static spinlock_t iSeries_hlocks[64] __cacheline_aligned_in_smp; - -/* - * Very primitive algorithm for picking up a lock - */ -static inline void iSeries_hlock(unsigned long slot) -{ - if (slot & 0x8) - slot = ~slot; - spin_lock(&iSeries_hlocks[(slot >> 4) & 0x3f]); -} - -static inline void iSeries_hunlock(unsigned long slot) -{ - if (slot & 0x8) - slot = ~slot; - spin_unlock(&iSeries_hlocks[(slot >> 4) & 0x3f]); -} - -static long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va, - unsigned long pa, unsigned long rflags, - unsigned long vflags, int psize, int ssize) -{ - long slot; - struct hash_pte lhpte; - int secondary = 0; - - BUG_ON(psize != MMU_PAGE_4K); - - /* - * The hypervisor tries both primary and secondary. - * If we are being called to insert in the secondary, - * it means we have already tried both primary and secondary, - * so we return failure immediately. - */ - if (vflags & HPTE_V_SECONDARY) - return -1; - - iSeries_hlock(hpte_group); - - slot = HvCallHpt_findValid(&lhpte, va >> HW_PAGE_SHIFT); - if (unlikely(lhpte.v & HPTE_V_VALID)) { - if (vflags & HPTE_V_BOLTED) { - HvCallHpt_setSwBits(slot, 0x10, 0); - HvCallHpt_setPp(slot, PP_RWXX); - iSeries_hunlock(hpte_group); - if (slot < 0) - return 0x8 | (slot & 7); - else - return slot & 7; - } - BUG(); - } - - if (slot == -1) { /* No available entry found in either group */ - iSeries_hunlock(hpte_group); - return -1; - } - - if (slot < 0) { /* MSB set means secondary group */ - vflags |= HPTE_V_SECONDARY; - secondary = 1; - slot &= 0x7fffffffffffffff; - } - - - lhpte.v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M) | - vflags | HPTE_V_VALID; - lhpte.r = hpte_encode_r(phys_to_abs(pa), MMU_PAGE_4K) | rflags; - - /* Now fill in the actual HPTE */ - HvCallHpt_addValidate(slot, secondary, &lhpte); - - iSeries_hunlock(hpte_group); - - return (secondary << 3) | (slot & 7); -} - -static unsigned long iSeries_hpte_getword0(unsigned long slot) -{ - struct hash_pte hpte; - - HvCallHpt_get(&hpte, slot); - return hpte.v; -} - -static long iSeries_hpte_remove(unsigned long hpte_group) -{ - unsigned long slot_offset; - int i; - unsigned long hpte_v; - - /* Pick a random slot to start at */ - slot_offset = mftb() & 0x7; - - iSeries_hlock(hpte_group); - - for (i = 0; i < HPTES_PER_GROUP; i++) { - hpte_v = iSeries_hpte_getword0(hpte_group + slot_offset); - - if (! (hpte_v & HPTE_V_BOLTED)) { - HvCallHpt_invalidateSetSwBitsGet(hpte_group + - slot_offset, 0, 0); - iSeries_hunlock(hpte_group); - return i; - } - - slot_offset++; - slot_offset &= 0x7; - } - - iSeries_hunlock(hpte_group); - - return -1; -} - -/* - * The HyperVisor expects the "flags" argument in this form: - * bits 0..59 : reserved - * bit 60 : N - * bits 61..63 : PP2,PP1,PP0 - */ -static long iSeries_hpte_updatepp(unsigned long slot, unsigned long newpp, - unsigned long va, int psize, int ssize, int local) -{ - struct hash_pte hpte; - unsigned long want_v; - - iSeries_hlock(slot); - - HvCallHpt_get(&hpte, slot); - want_v = hpte_encode_v(va, MMU_PAGE_4K, MMU_SEGSIZE_256M); - - if (HPTE_V_COMPARE(hpte.v, want_v) && (hpte.v & HPTE_V_VALID)) { - /* - * Hypervisor expects bits as NPPP, which is - * different from how they are mapped in our PP. - */ - HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1)); - iSeries_hunlock(slot); - return 0; - } - iSeries_hunlock(slot); - - return -1; -} - -/* - * Functions used to find the PTE for a particular virtual address. - * Only used during boot when bolting pages. - * - * Input : vpn : virtual page number - * Output: PTE index within the page table of the entry - * -1 on failure - */ -static long iSeries_hpte_find(unsigned long vpn) -{ - struct hash_pte hpte; - long slot; - - /* - * The HvCallHpt_findValid interface is as follows: - * 0xffffffffffffffff : No entry found. - * 0x00000000xxxxxxxx : Entry found in primary group, slot x - * 0x80000000xxxxxxxx : Entry found in secondary group, slot x - */ - slot = HvCallHpt_findValid(&hpte, vpn); - if (hpte.v & HPTE_V_VALID) { - if (slot < 0) { - slot &= 0x7fffffffffffffff; - slot = -slot; - } - } else - slot = -1; - return slot; -} - -/* - * Update the page protection bits. Intended to be used to create - * guard pages for kernel data structures on pages which are bolted - * in the HPT. Assumes pages being operated on will not be stolen. - * Does not work on large pages. - * - * No need to lock here because we should be the only user. - */ -static void iSeries_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, - int psize, int ssize) -{ - unsigned long vsid,va,vpn; - long slot; - - BUG_ON(psize != MMU_PAGE_4K); - - vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M); - va = (vsid << 28) | (ea & 0x0fffffff); - vpn = va >> HW_PAGE_SHIFT; - slot = iSeries_hpte_find(vpn); - if (slot == -1) - panic("updateboltedpp: Could not find page to bolt\n"); - HvCallHpt_setPp(slot, newpp); -} - -static void iSeries_hpte_invalidate(unsigned long slot, unsigned long va, - int psize, int ssize, int local) -{ - unsigned long hpte_v; - unsigned long avpn = va >> 23; - unsigned long flags; - - local_irq_save(flags); - - iSeries_hlock(slot); - - hpte_v = iSeries_hpte_getword0(slot); - - if ((HPTE_V_AVPN_VAL(hpte_v) == avpn) && (hpte_v & HPTE_V_VALID)) - HvCallHpt_invalidateSetSwBitsGet(slot, 0, 0); - - iSeries_hunlock(slot); - - local_irq_restore(flags); -} - -void __init hpte_init_iSeries(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(iSeries_hlocks); i++) - spin_lock_init(&iSeries_hlocks[i]); - - ppc_md.hpte_invalidate = iSeries_hpte_invalidate; - ppc_md.hpte_updatepp = iSeries_hpte_updatepp; - ppc_md.hpte_updateboltedpp = iSeries_hpte_updateboltedpp; - ppc_md.hpte_insert = iSeries_hpte_insert; - ppc_md.hpte_remove = iSeries_hpte_remove; -} diff --git a/arch/powerpc/platforms/iseries/hvcall.S b/arch/powerpc/platforms/iseries/hvcall.S deleted file mode 100644 index 07ae6ad..0000000 --- a/arch/powerpc/platforms/iseries/hvcall.S +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file contains the code to perform calls to the - * iSeries LPAR hypervisor - * - * 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 -#include -#include /* XXX for STACK_FRAME_OVERHEAD */ - - .text - -/* - * Hypervisor call - * - * Invoke the iSeries hypervisor via the System Call instruction - * Parameters are passed to this routine in registers r3 - r10 - * - * r3 contains the HV function to be called - * r4-r10 contain the operands to the hypervisor function - * - */ - -_GLOBAL(HvCall) -_GLOBAL(HvCall0) -_GLOBAL(HvCall1) -_GLOBAL(HvCall2) -_GLOBAL(HvCall3) -_GLOBAL(HvCall4) -_GLOBAL(HvCall5) -_GLOBAL(HvCall6) -_GLOBAL(HvCall7) - - - mfcr r0 - std r0,-8(r1) - stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) - - /* r0 = 0xffffffffffffffff indicates a hypervisor call */ - - li r0,-1 - - /* Invoke the hypervisor */ - - sc - - ld r1,0(r1) - ld r0,-8(r1) - mtcrf 0xff,r0 - - /* return to caller, return value in r3 */ - - blr - -_GLOBAL(HvCall0Ret16) -_GLOBAL(HvCall1Ret16) -_GLOBAL(HvCall2Ret16) -_GLOBAL(HvCall3Ret16) -_GLOBAL(HvCall4Ret16) -_GLOBAL(HvCall5Ret16) -_GLOBAL(HvCall6Ret16) -_GLOBAL(HvCall7Ret16) - - mfcr r0 - std r0,-8(r1) - std r31,-16(r1) - stdu r1,-(STACK_FRAME_OVERHEAD+32)(r1) - - mr r31,r4 - li r0,-1 - mr r4,r5 - mr r5,r6 - mr r6,r7 - mr r7,r8 - mr r8,r9 - mr r9,r10 - - sc - - std r3,0(r31) - std r4,8(r31) - - mr r3,r5 - - ld r1,0(r1) - ld r0,-8(r1) - mtcrf 0xff,r0 - ld r31,-16(r1) - - blr diff --git a/arch/powerpc/platforms/iseries/hvlog.c b/arch/powerpc/platforms/iseries/hvlog.c deleted file mode 100644 index f476d71..0000000 --- a/arch/powerpc/platforms/iseries/hvlog.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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 -#include -#include -#include -#include - - -void HvCall_writeLogBuffer(const void *buffer, u64 len) -{ - struct HvLpBufferList hv_buf; - u64 left_this_page; - u64 cur = virt_to_abs(buffer); - - while (len) { - hv_buf.addr = cur; - left_this_page = ((cur & HW_PAGE_MASK) + HW_PAGE_SIZE) - cur; - if (left_this_page > len) - left_this_page = len; - hv_buf.len = left_this_page; - len -= left_this_page; - HvCall2(HvCallBaseWriteLogBuffer, - virt_to_abs(&hv_buf), - left_this_page); - cur = (cur & HW_PAGE_MASK) + HW_PAGE_SIZE; - } -} diff --git a/arch/powerpc/platforms/iseries/hvlpconfig.c b/arch/powerpc/platforms/iseries/hvlpconfig.c deleted file mode 100644 index f62a0c5..0000000 --- a/arch/powerpc/platforms/iseries/hvlpconfig.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2001 Kyle A. Lucke, IBM Corporation - * - * 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. - * - * 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 -#include -#include "it_lp_naca.h" - -HvLpIndex HvLpConfig_getLpIndex_outline(void) -{ - return HvLpConfig_getLpIndex(); -} -EXPORT_SYMBOL(HvLpConfig_getLpIndex_outline); - -HvLpIndex HvLpConfig_getLpIndex(void) -{ - return itLpNaca.xLpIndex; -} -EXPORT_SYMBOL(HvLpConfig_getLpIndex); - -HvLpIndex HvLpConfig_getPrimaryLpIndex(void) -{ - return itLpNaca.xPrimaryLpIndex; -} -EXPORT_SYMBOL_GPL(HvLpConfig_getPrimaryLpIndex); diff --git a/arch/powerpc/platforms/iseries/iommu.c b/arch/powerpc/platforms/iseries/iommu.c deleted file mode 100644 index 2f3d911..0000000 --- a/arch/powerpc/platforms/iseries/iommu.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation - * - * Rewrite, cleanup: - * - * Copyright (C) 2004 Olof Johansson , IBM Corporation - * Copyright (C) 2006 Olof Johansson - * - * Dynamic DMA mapping support, iSeries-specific parts. - * - * - * 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. - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int tce_build_iSeries(struct iommu_table *tbl, long index, long npages, - unsigned long uaddr, enum dma_data_direction direction, - struct dma_attrs *attrs) -{ - u64 rc; - u64 tce, rpn; - - while (npages--) { - rpn = virt_to_abs(uaddr) >> TCE_SHIFT; - tce = (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; - - if (tbl->it_type == TCE_VB) { - /* Virtual Bus */ - tce |= TCE_VALID|TCE_ALLIO; - if (direction != DMA_TO_DEVICE) - tce |= TCE_VB_WRITE; - } else { - /* PCI Bus */ - tce |= TCE_PCI_READ; /* Read allowed */ - if (direction != DMA_TO_DEVICE) - tce |= TCE_PCI_WRITE; - } - - rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, tce); - if (rc) - panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%llx\n", - rc); - index++; - uaddr += TCE_PAGE_SIZE; - } - return 0; -} - -static void tce_free_iSeries(struct iommu_table *tbl, long index, long npages) -{ - u64 rc; - - while (npages--) { - rc = HvCallXm_setTce((u64)tbl->it_index, (u64)index, 0); - if (rc) - panic("PCI_DMA: HvCallXm_setTce failed, Rc: 0x%llx\n", - rc); - index++; - } -} - -/* - * Structure passed to HvCallXm_getTceTableParms - */ -struct iommu_table_cb { - unsigned long itc_busno; /* Bus number for this tce table */ - unsigned long itc_start; /* Will be NULL for secondary */ - unsigned long itc_totalsize; /* Size (in pages) of whole table */ - unsigned long itc_offset; /* Index into real tce table of the - start of our section */ - unsigned long itc_size; /* Size (in pages) of our section */ - unsigned long itc_index; /* Index of this tce table */ - unsigned short itc_maxtables; /* Max num of tables for partition */ - unsigned char itc_virtbus; /* Flag to indicate virtual bus */ - unsigned char itc_slotno; /* IOA Tce Slot Index */ - unsigned char itc_rsvd[4]; -}; - -/* - * Call Hv with the architected data structure to get TCE table info. - * info. Put the returned data into the Linux representation of the - * TCE table data. - * The Hardware Tce table comes in three flavors. - * 1. TCE table shared between Buses. - * 2. TCE table per Bus. - * 3. TCE Table per IOA. - */ -void iommu_table_getparms_iSeries(unsigned long busno, - unsigned char slotno, - unsigned char virtbus, - struct iommu_table* tbl) -{ - struct iommu_table_cb *parms; - - parms = kzalloc(sizeof(*parms), GFP_KERNEL); - if (parms == NULL) - panic("PCI_DMA: TCE Table Allocation failed."); - - parms->itc_busno = busno; - parms->itc_slotno = slotno; - parms->itc_virtbus = virtbus; - - HvCallXm_getTceTableParms(iseries_hv_addr(parms)); - - if (parms->itc_size == 0) - panic("PCI_DMA: parms->size is zero, parms is 0x%p", parms); - - /* itc_size is in pages worth of table, it_size is in # of entries */ - tbl->it_size = (parms->itc_size * TCE_PAGE_SIZE) / TCE_ENTRY_SIZE; - tbl->it_busno = parms->itc_busno; - tbl->it_offset = parms->itc_offset; - tbl->it_index = parms->itc_index; - tbl->it_blocksize = 1; - tbl->it_type = virtbus ? TCE_VB : TCE_PCI; - - kfree(parms); -} - - -#ifdef CONFIG_PCI -/* - * This function compares the known tables to find an iommu_table - * that has already been built for hardware TCEs. - */ -static struct iommu_table *iommu_table_find(struct iommu_table * tbl) -{ - struct device_node *node; - - for (node = NULL; (node = of_find_all_nodes(node)); ) { - struct pci_dn *pdn = PCI_DN(node); - struct iommu_table *it; - - if (pdn == NULL) - continue; - it = pdn->iommu_table; - if ((it != NULL) && - (it->it_type == TCE_PCI) && - (it->it_offset == tbl->it_offset) && - (it->it_index == tbl->it_index) && - (it->it_size == tbl->it_size)) { - of_node_put(node); - return it; - } - } - return NULL; -} - - -static void pci_dma_dev_setup_iseries(struct pci_dev *pdev) -{ - struct iommu_table *tbl; - struct device_node *dn = pci_device_to_OF_node(pdev); - struct pci_dn *pdn = PCI_DN(dn); - const u32 *lsn = of_get_property(dn, "linux,logical-slot-number", NULL); - - BUG_ON(lsn == NULL); - - tbl = kzalloc(sizeof(struct iommu_table), GFP_KERNEL); - - iommu_table_getparms_iSeries(pdn->busno, *lsn, 0, tbl); - - /* Look for existing tce table */ - pdn->iommu_table = iommu_table_find(tbl); - if (pdn->iommu_table == NULL) - pdn->iommu_table = iommu_init_table(tbl, -1); - else - kfree(tbl); - set_iommu_table_base(&pdev->dev, pdn->iommu_table); -} -#else -#define pci_dma_dev_setup_iseries NULL -#endif - -static struct iommu_table veth_iommu_table; -static struct iommu_table vio_iommu_table; - -void *iseries_hv_alloc(size_t size, dma_addr_t *dma_handle, gfp_t flag) -{ - return iommu_alloc_coherent(NULL, &vio_iommu_table, size, dma_handle, - DMA_BIT_MASK(32), flag, -1); -} -EXPORT_SYMBOL_GPL(iseries_hv_alloc); - -void iseries_hv_free(size_t size, void *vaddr, dma_addr_t dma_handle) -{ - iommu_free_coherent(&vio_iommu_table, size, vaddr, dma_handle); -} -EXPORT_SYMBOL_GPL(iseries_hv_free); - -dma_addr_t iseries_hv_map(void *vaddr, size_t size, - enum dma_data_direction direction) -{ - return iommu_map_page(NULL, &vio_iommu_table, virt_to_page(vaddr), - (unsigned long)vaddr % PAGE_SIZE, size, - DMA_BIT_MASK(32), direction, NULL); -} - -void iseries_hv_unmap(dma_addr_t dma_handle, size_t size, - enum dma_data_direction direction) -{ - iommu_unmap_page(&vio_iommu_table, dma_handle, size, direction, NULL); -} - -void __init iommu_vio_init(void) -{ - iommu_table_getparms_iSeries(255, 0, 0xff, &veth_iommu_table); - veth_iommu_table.it_size /= 2; - vio_iommu_table = veth_iommu_table; - vio_iommu_table.it_offset += veth_iommu_table.it_size; - - if (!iommu_init_table(&veth_iommu_table, -1)) - printk("Virtual Bus VETH TCE table failed.\n"); - if (!iommu_init_table(&vio_iommu_table, -1)) - printk("Virtual Bus VIO TCE table failed.\n"); -} - -struct iommu_table *vio_build_iommu_table_iseries(struct vio_dev *dev) -{ - if (strcmp(dev->type, "network") == 0) - return &veth_iommu_table; - return &vio_iommu_table; -} - -void iommu_init_early_iSeries(void) -{ - ppc_md.tce_build = tce_build_iSeries; - ppc_md.tce_free = tce_free_iSeries; - - ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_iseries; - set_pci_dma_ops(&dma_iommu_ops); -} diff --git a/arch/powerpc/platforms/iseries/ipl_parms.h b/arch/powerpc/platforms/iseries/ipl_parms.h deleted file mode 100644 index 83e4ca4..0000000 --- a/arch/powerpc/platforms/iseries/ipl_parms.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ISERIES_IPL_PARMS_H -#define _ISERIES_IPL_PARMS_H - -/* - * This struct maps the IPL Parameters DMA'd from the SP. - * - * Warning: - * This data must map in exactly 64 bytes and match the architecture for - * the IPL parms - */ - -#include - -struct ItIplParmsReal { - u8 xFormat; // Defines format of IplParms x00-x00 - u8 xRsvd01:6; // Reserved x01-x01 - u8 xAlternateSearch:1; // Alternate search indicator ... - u8 xUaSupplied:1; // UA Supplied on programmed IPL... - u8 xLsUaFormat; // Format byte for UA x02-x02 - u8 xRsvd02; // Reserved x03-x03 - u32 xLsUa; // LS UA x04-x07 - u32 xUnusedLsLid; // First OS LID to load x08-x0B - u16 xLsBusNumber; // LS Bus Number x0C-x0D - u8 xLsCardAdr; // LS Card Address x0E-x0E - u8 xLsBoardAdr; // LS Board Address x0F-x0F - u32 xRsvd03; // Reserved x10-x13 - u8 xSpcnPresent:1; // SPCN present x14-x14 - u8 xCpmPresent:1; // CPM present ... - u8 xRsvd04:6; // Reserved ... - u8 xRsvd05:4; // Reserved x15-x15 - u8 xKeyLock:4; // Keylock setting ... - u8 xRsvd06:6; // Reserved x16-x16 - u8 xIplMode:2; // Ipl mode (A|B|C|D) ... - u8 xHwIplType; // Fast v slow v slow EC HW IPL x17-x17 - u16 xCpmEnabledIpl:1; // CPM in effect when IPL initiatedx18-x19 - u16 xPowerOnResetIpl:1; // Indicate POR condition ... - u16 xMainStorePreserved:1; // Main Storage is preserved ... - u16 xRsvd07:13; // Reserved ... - u16 xIplSource:16; // Ipl source x1A-x1B - u8 xIplReason:8; // Reason for this IPL x1C-x1C - u8 xRsvd08; // Reserved x1D-x1D - u16 xRsvd09; // Reserved x1E-x1F - u16 xSysBoxType; // System Box Type x20-x21 - u16 xSysProcType; // System Processor Type x22-x23 - u32 xRsvd10; // Reserved x24-x27 - u64 xRsvd11; // Reserved x28-x2F - u64 xRsvd12; // Reserved x30-x37 - u64 xRsvd13; // Reserved x38-x3F -}; - -#endif /* _ISERIES_IPL_PARMS_H */ diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c deleted file mode 100644 index b210345..0000000 --- a/arch/powerpc/platforms/iseries/irq.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * This module supports the iSeries PCI bus interrupt handling - * Copyright (C) 20yy - * Copyright (C) 2004-2005 IBM Corporation - * - * 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. - * - * 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 - * - * Change Activity: - * Created, December 13, 2000 by Wayne Holm - * End Change Activity - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "irq.h" -#include "pci.h" -#include "call_pci.h" - -#ifdef CONFIG_PCI - -enum pci_event_type { - pe_bus_created = 0, /* PHB has been created */ - pe_bus_error = 1, /* PHB has failed */ - pe_bus_failed = 2, /* Msg to Secondary, Primary failed bus */ - pe_node_failed = 4, /* Multi-adapter bridge has failed */ - pe_node_recovered = 5, /* Multi-adapter bridge has recovered */ - pe_bus_recovered = 12, /* PHB has been recovered */ - pe_unquiese_bus = 18, /* Secondary bus unqiescing */ - pe_bridge_error = 21, /* Bridge Error */ - pe_slot_interrupt = 22 /* Slot interrupt */ -}; - -struct pci_event { - struct HvLpEvent event; - union { - u64 __align; /* Align on an 8-byte boundary */ - struct { - u32 fisr; - HvBusNumber bus_number; - HvSubBusNumber sub_bus_number; - HvAgentId dev_id; - } slot; - struct { - HvBusNumber bus_number; - HvSubBusNumber sub_bus_number; - } bus; - struct { - HvBusNumber bus_number; - HvSubBusNumber sub_bus_number; - HvAgentId dev_id; - } node; - } data; -}; - -static DEFINE_SPINLOCK(pending_irqs_lock); -static int num_pending_irqs; -static int pending_irqs[NR_IRQS]; - -static void int_received(struct pci_event *event) -{ - int irq; - - switch (event->event.xSubtype) { - case pe_slot_interrupt: - irq = event->event.xCorrelationToken; - if (irq < NR_IRQS) { - spin_lock(&pending_irqs_lock); - pending_irqs[irq]++; - num_pending_irqs++; - spin_unlock(&pending_irqs_lock); - } else { - printk(KERN_WARNING "int_received: bad irq number %d\n", - irq); - HvCallPci_eoi(event->data.slot.bus_number, - event->data.slot.sub_bus_number, - event->data.slot.dev_id); - } - break; - /* Ignore error recovery events for now */ - case pe_bus_created: - printk(KERN_INFO "int_received: system bus %d created\n", - event->data.bus.bus_number); - break; - case pe_bus_error: - case pe_bus_failed: - printk(KERN_INFO "int_received: system bus %d failed\n", - event->data.bus.bus_number); - break; - case pe_bus_recovered: - case pe_unquiese_bus: - printk(KERN_INFO "int_received: system bus %d recovered\n", - event->data.bus.bus_number); - break; - case pe_node_failed: - case pe_bridge_error: - printk(KERN_INFO - "int_received: multi-adapter bridge %d/%d/%d failed\n", - event->data.node.bus_number, - event->data.node.sub_bus_number, - event->data.node.dev_id); - break; - case pe_node_recovered: - printk(KERN_INFO - "int_received: multi-adapter bridge %d/%d/%d recovered\n", - event->data.node.bus_number, - event->data.node.sub_bus_number, - event->data.node.dev_id); - break; - default: - printk(KERN_ERR - "int_received: unrecognized event subtype 0x%x\n", - event->event.xSubtype); - break; - } -} - -static void pci_event_handler(struct HvLpEvent *event) -{ - if (event && (event->xType == HvLpEvent_Type_PciIo)) { - if (hvlpevent_is_int(event)) - int_received((struct pci_event *)event); - else - printk(KERN_ERR - "pci_event_handler: unexpected ack received\n"); - } else if (event) - printk(KERN_ERR - "pci_event_handler: Unrecognized PCI event type 0x%x\n", - (int)event->xType); - else - printk(KERN_ERR "pci_event_handler: NULL event received\n"); -} - -#define REAL_IRQ_TO_SUBBUS(irq) (((irq) >> 14) & 0xff) -#define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1) -#define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1) -#define REAL_IRQ_TO_FUNC(irq) ((irq) & 7) - -/* - * This will be called by device drivers (via enable_IRQ) - * to enable INTA in the bridge interrupt status register. - */ -static void iseries_enable_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - /* The IRQ has already been locked by the caller */ - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Unmask secondary INTA */ - mask = 0x80000000; - HvCallPci_unmaskInterrupts(bus, sub_bus, dev_id, mask); -} - -/* This is called by iseries_activate_IRQs */ -static unsigned int iseries_startup_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Link the IRQ number to the bridge */ - HvCallXm_connectBusUnit(bus, sub_bus, dev_id, d->irq); - - /* Unmask bridge interrupts in the FISR */ - mask = 0x01010000 << function; - HvCallPci_unmaskFisr(bus, sub_bus, dev_id, mask); - iseries_enable_IRQ(d); - return 0; -} - -/* - * This is called out of iSeries_fixup to activate interrupt - * generation for usable slots - */ -void __init iSeries_activate_IRQs() -{ - int irq; - unsigned long flags; - - for_each_irq (irq) { - struct irq_desc *desc = irq_to_desc(irq); - struct irq_chip *chip; - - if (!desc) - continue; - - chip = irq_desc_get_chip(desc); - if (chip && chip->irq_startup) { - raw_spin_lock_irqsave(&desc->lock, flags); - chip->irq_startup(&desc->irq_data); - raw_spin_unlock_irqrestore(&desc->lock, flags); - } - } -} - -/* this is not called anywhere currently */ -static void iseries_shutdown_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - /* irq should be locked by the caller */ - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Invalidate the IRQ number in the bridge */ - HvCallXm_connectBusUnit(bus, sub_bus, dev_id, 0); - - /* Mask bridge interrupts in the FISR */ - mask = 0x01010000 << function; - HvCallPci_maskFisr(bus, sub_bus, dev_id, mask); -} - -/* - * This will be called by device drivers (via disable_IRQ) - * to disable INTA in the bridge interrupt status register. - */ -static void iseries_disable_IRQ(struct irq_data *d) -{ - u32 bus, dev_id, function, mask; - const u32 sub_bus = 0; - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - /* The IRQ has already been locked by the caller */ - bus = REAL_IRQ_TO_BUS(rirq); - function = REAL_IRQ_TO_FUNC(rirq); - dev_id = (REAL_IRQ_TO_IDSEL(rirq) << 4) + function; - - /* Mask secondary INTA */ - mask = 0x80000000; - HvCallPci_maskInterrupts(bus, sub_bus, dev_id, mask); -} - -static void iseries_end_IRQ(struct irq_data *d) -{ - unsigned int rirq = (unsigned int)irqd_to_hwirq(d); - - HvCallPci_eoi(REAL_IRQ_TO_BUS(rirq), REAL_IRQ_TO_SUBBUS(rirq), - (REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq)); -} - -static struct irq_chip iseries_pic = { - .name = "iSeries", - .irq_startup = iseries_startup_IRQ, - .irq_shutdown = iseries_shutdown_IRQ, - .irq_unmask = iseries_enable_IRQ, - .irq_mask = iseries_disable_IRQ, - .irq_eoi = iseries_end_IRQ -}; - -/* - * This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot - * It calculates the irq value for the slot. - * Note that sub_bus is always 0 (at the moment at least). - */ -int __init iSeries_allocate_IRQ(HvBusNumber bus, - HvSubBusNumber sub_bus, u32 bsubbus) -{ - unsigned int realirq; - u8 idsel = ISERIES_GET_DEVICE_FROM_SUBBUS(bsubbus); - u8 function = ISERIES_GET_FUNCTION_FROM_SUBBUS(bsubbus); - - realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3) - + function; - - return irq_create_mapping(NULL, realirq); -} - -#endif /* CONFIG_PCI */ - -/* - * Get the next pending IRQ. - */ -unsigned int iSeries_get_irq(void) -{ - int irq = NO_IRQ_IGNORE; - -#ifdef CONFIG_SMP - if (get_lppaca()->int_dword.fields.ipi_cnt) { - get_lppaca()->int_dword.fields.ipi_cnt = 0; - smp_ipi_demux(); - } -#endif /* CONFIG_SMP */ - if (hvlpevent_is_pending()) - process_hvlpevents(); - -#ifdef CONFIG_PCI - if (num_pending_irqs) { - spin_lock(&pending_irqs_lock); - for (irq = 0; irq < NR_IRQS; irq++) { - if (pending_irqs[irq]) { - pending_irqs[irq]--; - num_pending_irqs--; - break; - } - } - spin_unlock(&pending_irqs_lock); - if (irq >= NR_IRQS) - irq = NO_IRQ_IGNORE; - } -#endif - - return irq; -} - -#ifdef CONFIG_PCI - -static int iseries_irq_host_map(struct irq_host *h, unsigned int virq, - irq_hw_number_t hw) -{ - irq_set_chip_and_handler(virq, &iseries_pic, handle_fasteoi_irq); - - return 0; -} - -static int iseries_irq_host_match(struct irq_host *h, struct device_node *np) -{ - /* Match all */ - return 1; -} - -static struct irq_host_ops iseries_irq_host_ops = { - .map = iseries_irq_host_map, - .match = iseries_irq_host_match, -}; - -/* - * This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c - * It must be called before the bus walk. - */ -void __init iSeries_init_IRQ(void) -{ - /* Register PCI event handler and open an event path */ - struct irq_host *host; - int ret; - - /* - * The Hypervisor only allows us up to 256 interrupt - * sources (the irq number is passed in a u8). - */ - irq_set_virq_count(256); - - /* Create irq host. No need for a revmap since HV will give us - * back our virtual irq number - */ - host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0, - &iseries_irq_host_ops, 0); - BUG_ON(host == NULL); - irq_set_default_host(host); - - ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, - &pci_event_handler); - if (ret == 0) { - ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); - if (ret != 0) - printk(KERN_ERR "iseries_init_IRQ: open event path " - "failed with rc 0x%x\n", ret); - } else - printk(KERN_ERR "iseries_init_IRQ: register handler " - "failed with rc 0x%x\n", ret); -} - -#endif /* CONFIG_PCI */ diff --git a/arch/powerpc/platforms/iseries/irq.h b/arch/powerpc/platforms/iseries/irq.h deleted file mode 100644 index a1c2360..0000000 --- a/arch/powerpc/platforms/iseries/irq.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _ISERIES_IRQ_H -#define _ISERIES_IRQ_H - -#ifdef CONFIG_PCI -extern void iSeries_init_IRQ(void); -extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, u32); -extern void iSeries_activate_IRQs(void); -#else -#define iSeries_init_IRQ NULL -#endif -extern unsigned int iSeries_get_irq(void); - -#endif /* _ISERIES_IRQ_H */ diff --git a/arch/powerpc/platforms/iseries/it_exp_vpd_panel.h b/arch/powerpc/platforms/iseries/it_exp_vpd_panel.h deleted file mode 100644 index 6de9097..0000000 --- a/arch/powerpc/platforms/iseries/it_exp_vpd_panel.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2002 Dave Boutcher IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _PLATFORMS_ISERIES_IT_EXT_VPD_PANEL_H -#define _PLATFORMS_ISERIES_IT_EXT_VPD_PANEL_H - -/* - * This struct maps the panel information - * - * Warning: - * This data must match the architecture for the panel information - */ - -#include - -struct ItExtVpdPanel { - /* Definition of the Extended Vpd On Panel Data Area */ - char systemSerial[8]; - char mfgID[4]; - char reserved1[24]; - char machineType[4]; - char systemID[6]; - char somUniqueCnt[4]; - char serialNumberCount; - char reserved2[7]; - u16 bbu3; - u16 bbu2; - u16 bbu1; - char xLocationLabel[8]; - u8 xRsvd1[6]; - u16 xFrameId; - u8 xRsvd2[48]; -}; - -extern struct ItExtVpdPanel xItExtVpdPanel; - -#endif /* _PLATFORMS_ISERIES_IT_EXT_VPD_PANEL_H */ diff --git a/arch/powerpc/platforms/iseries/it_lp_naca.h b/arch/powerpc/platforms/iseries/it_lp_naca.h deleted file mode 100644 index cf6dcf6..0000000 --- a/arch/powerpc/platforms/iseries/it_lp_naca.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _PLATFORMS_ISERIES_IT_LP_NACA_H -#define _PLATFORMS_ISERIES_IT_LP_NACA_H - -#include - -/* - * This control block contains the data that is shared between the - * hypervisor (PLIC) and the OS. - */ - -struct ItLpNaca { -// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data - u32 xDesc; // Eye catcher x00-x03 - u16 xSize; // Size of this class x04-x05 - u16 xIntHdlrOffset; // Offset to IntHdlr array x06-x07 - u8 xMaxIntHdlrEntries; // Number of entries in array x08-x08 - u8 xPrimaryLpIndex; // LP Index of Primary x09-x09 - u8 xServiceLpIndex; // LP Ind of Service Focal Pointx0A-x0A - u8 xLpIndex; // LP Index x0B-x0B - u16 xMaxLpQueues; // Number of allocated queues x0C-x0D - u16 xLpQueueOffset; // Offset to start of LP queues x0E-x0F - u8 xPirEnvironMode; // Piranha or hardware x10-x10 - u8 xPirConsoleMode; // Piranha console indicator x11-x11 - u8 xPirDasdMode; // Piranha dasd indicator x12-x12 - u8 xRsvd1_0[5]; // Reserved for Piranha related x13-x17 - u8 flags; // flags, see below x18-x1F - u8 xSpVpdFormat; // VPD areas are in CSP format ... - u8 xIntProcRatio; // Ratio of int procs to procs ... - u8 xRsvd1_2[5]; // Reserved ... - u16 xRsvd1_3; // Reserved x20-x21 - u16 xPlicVrmIndex; // VRM index of PLIC x22-x23 - u16 xMinSupportedSlicVrmInd;// Min supported OS VRM index x24-x25 - u16 xMinCompatableSlicVrmInd;// Min compatible OS VRM index x26-x27 - u64 xLoadAreaAddr; // ER address of load area x28-x2F - u32 xLoadAreaChunks; // Chunks for the load area x30-x33 - u32 xPaseSysCallCRMask; // Mask used to test CR before x34-x37 - // doing an ASR switch on PASE - // system call. - u64 xSlicSegmentTablePtr; // Pointer to Slic seg table. x38-x3f - u8 xRsvd1_4[64]; // x40-x7F - -// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data - u8 xRsvd2_0[128]; // Reserved x00-x7F - -// CACHE_LINE_3-6 0x0100 - 0x02FF Contains LP Queue indicators -// NB: Padding required to keep xInterruptHdlr at x300 which is required -// for v4r4 PLIC. - u8 xOldLpQueue[128]; // LP Queue needed for v4r4 100-17F - u8 xRsvd3_0[384]; // Reserved 180-2FF - -// CACHE_LINE_7-8 0x0300 - 0x03FF Contains the address of the OS interrupt -// handlers - u64 xInterruptHdlr[32]; // Interrupt handlers 300-x3FF -}; - -extern struct ItLpNaca itLpNaca; - -#define ITLPNACA_LPAR 0x80 /* Is LPAR installed on the system */ -#define ITLPNACA_PARTITIONED 0x40 /* Is the system partitioned */ -#define ITLPNACA_HWSYNCEDTBS 0x20 /* Hardware synced TBs */ -#define ITLPNACA_HMTINT 0x10 /* Utilize MHT for interrupts */ - -#endif /* _PLATFORMS_ISERIES_IT_LP_NACA_H */ diff --git a/arch/powerpc/platforms/iseries/ksyms.c b/arch/powerpc/platforms/iseries/ksyms.c deleted file mode 100644 index 997e234..0000000 --- a/arch/powerpc/platforms/iseries/ksyms.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * (C) 2001-2005 PPC 64 Team, IBM Corp - * - * 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 - -#include -#include - -EXPORT_SYMBOL(HvCall0); -EXPORT_SYMBOL(HvCall1); -EXPORT_SYMBOL(HvCall2); -EXPORT_SYMBOL(HvCall3); -EXPORT_SYMBOL(HvCall4); -EXPORT_SYMBOL(HvCall5); -EXPORT_SYMBOL(HvCall6); -EXPORT_SYMBOL(HvCall7); diff --git a/arch/powerpc/platforms/iseries/lpardata.c b/arch/powerpc/platforms/iseries/lpardata.c deleted file mode 100644 index 00e0ec8..0000000 --- a/arch/powerpc/platforms/iseries/lpardata.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2001 Mike Corrigan, IBM Corp - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "naca.h" -#include "vpd_areas.h" -#include "spcomm_area.h" -#include "ipl_parms.h" -#include "processor_vpd.h" -#include "release_data.h" -#include "it_exp_vpd_panel.h" -#include "it_lp_naca.h" - -/* The HvReleaseData is the root of the information shared between - * the hypervisor and Linux. - */ -const struct HvReleaseData hvReleaseData = { - .xDesc = 0xc8a5d9c4, /* "HvRD" ebcdic */ - .xSize = sizeof(struct HvReleaseData), - .xVpdAreasPtrOffset = offsetof(struct naca_struct, xItVpdAreas), - .xSlicNacaAddr = &naca, /* 64-bit Naca address */ - .xMsNucDataOffset = LPARMAP_PHYS, - .xFlags = HVREL_TAGSINACTIVE /* tags inactive */ - /* 64 bit */ - /* shared processors */ - /* HMT allowed */ - | 6, /* TEMP: This allows non-GA driver */ - .xVrmIndex = 4, /* We are v5r2m0 */ - .xMinSupportedPlicVrmIndex = 3, /* v5r1m0 */ - .xMinCompatablePlicVrmIndex = 3, /* v5r1m0 */ - .xVrmName = { 0xd3, 0x89, 0x95, 0xa4, /* "Linux 2.4.64" ebcdic */ - 0xa7, 0x40, 0xf2, 0x4b, - 0xf4, 0x4b, 0xf6, 0xf4 }, -}; - -/* - * The NACA. The first dword of the naca is required by the iSeries - * hypervisor to point to itVpdAreas. The hypervisor finds the NACA - * through the pointer in hvReleaseData. - */ -struct naca_struct naca = { - .xItVpdAreas = &itVpdAreas, - .xRamDisk = 0, - .xRamDiskSize = 0, -}; - -struct ItLpRegSave { - u32 xDesc; // Eye catcher "LpRS" ebcdic 000-003 - u16 xSize; // Size of this class 004-005 - u8 xInUse; // Area is live 006-007 - u8 xRsvd1[9]; // Reserved 007-00F - - u8 xFixedRegSave[352]; // Fixed Register Save Area 010-16F - u32 xCTRL; // Control Register 170-173 - u32 xDEC; // Decrementer 174-177 - u32 xFPSCR; // FP Status and Control Reg 178-17B - u32 xPVR; // Processor Version Number 17C-17F - - u64 xMMCR0; // Monitor Mode Control Reg 0 180-187 - u32 xPMC1; // Perf Monitor Counter 1 188-18B - u32 xPMC2; // Perf Monitor Counter 2 18C-18F - u32 xPMC3; // Perf Monitor Counter 3 190-193 - u32 xPMC4; // Perf Monitor Counter 4 194-197 - u32 xPIR; // Processor ID Reg 198-19B - - u32 xMMCR1; // Monitor Mode Control Reg 1 19C-19F - u32 xMMCRA; // Monitor Mode Control Reg A 1A0-1A3 - u32 xPMC5; // Perf Monitor Counter 5 1A4-1A7 - u32 xPMC6; // Perf Monitor Counter 6 1A8-1AB - u32 xPMC7; // Perf Monitor Counter 7 1AC-1AF - u32 xPMC8; // Perf Monitor Counter 8 1B0-1B3 - u32 xTSC; // Thread Switch Control 1B4-1B7 - u32 xTST; // Thread Switch Timeout 1B8-1BB - u32 xRsvd; // Reserved 1BC-1BF - - u64 xACCR; // Address Compare Control Reg 1C0-1C7 - u64 xIMR; // Instruction Match Register 1C8-1CF - u64 xSDR1; // Storage Description Reg 1 1D0-1D7 - u64 xSPRG0; // Special Purpose Reg General0 1D8-1DF - u64 xSPRG1; // Special Purpose Reg General1 1E0-1E7 - u64 xSPRG2; // Special Purpose Reg General2 1E8-1EF - u64 xSPRG3; // Special Purpose Reg General3 1F0-1F7 - u64 xTB; // Time Base Register 1F8-1FF - - u64 xFPR[32]; // Floating Point Registers 200-2FF - - u64 xMSR; // Machine State Register 300-307 - u64 xNIA; // Next Instruction Address 308-30F - - u64 xDABR; // Data Address Breakpoint Reg 310-317 - u64 xIABR; // Inst Address Breakpoint Reg 318-31F - - u64 xHID0; // HW Implementation Dependent0 320-327 - - u64 xHID4; // HW Implementation Dependent4 328-32F - u64 xSCOMd; // SCON Data Reg (SPRG4) 330-337 - u64 xSCOMc; // SCON Command Reg (SPRG5) 338-33F - u64 xSDAR; // Sample Data Address Register 340-347 - u64 xSIAR; // Sample Inst Address Register 348-34F - - u8 xRsvd3[176]; // Reserved 350-3FF -}; - -extern void system_reset_iSeries(void); -extern void machine_check_iSeries(void); -extern void data_access_iSeries(void); -extern void instruction_access_iSeries(void); -extern void hardware_interrupt_iSeries(void); -extern void alignment_iSeries(void); -extern void program_check_iSeries(void); -extern void fp_unavailable_iSeries(void); -extern void decrementer_iSeries(void); -extern void trap_0a_iSeries(void); -extern void trap_0b_iSeries(void); -extern void system_call_iSeries(void); -extern void single_step_iSeries(void); -extern void trap_0e_iSeries(void); -extern void performance_monitor_iSeries(void); -extern void data_access_slb_iSeries(void); -extern void instruction_access_slb_iSeries(void); - -struct ItLpNaca itLpNaca = { - .xDesc = 0xd397d581, /* "LpNa" ebcdic */ - .xSize = 0x0400, /* size of ItLpNaca */ - .xIntHdlrOffset = 0x0300, /* offset to int array */ - .xMaxIntHdlrEntries = 19, /* # ents */ - .xPrimaryLpIndex = 0, /* Part # of primary */ - .xServiceLpIndex = 0, /* Part # of serv */ - .xLpIndex = 0, /* Part # of me */ - .xMaxLpQueues = 0, /* # of LP queues */ - .xLpQueueOffset = 0x100, /* offset of start of LP queues */ - .xPirEnvironMode = 0, /* Piranha stuff */ - .xPirConsoleMode = 0, - .xPirDasdMode = 0, - .flags = 0, - .xSpVpdFormat = 0, - .xIntProcRatio = 0, - .xPlicVrmIndex = 0, /* VRM index of PLIC */ - .xMinSupportedSlicVrmInd = 0, /* min supported SLIC */ - .xMinCompatableSlicVrmInd = 0, /* min compat SLIC */ - .xLoadAreaAddr = 0, /* 64-bit addr of load area */ - .xLoadAreaChunks = 0, /* chunks for load area */ - .xPaseSysCallCRMask = 0, /* PASE mask */ - .xSlicSegmentTablePtr = 0, /* seg table */ - .xOldLpQueue = { 0 }, /* Old LP Queue */ - .xInterruptHdlr = { - (u64)system_reset_iSeries, /* 0x100 System Reset */ - (u64)machine_check_iSeries, /* 0x200 Machine Check */ - (u64)data_access_iSeries, /* 0x300 Data Access */ - (u64)instruction_access_iSeries, /* 0x400 Instruction Access */ - (u64)hardware_interrupt_iSeries, /* 0x500 External */ - (u64)alignment_iSeries, /* 0x600 Alignment */ - (u64)program_check_iSeries, /* 0x700 Program Check */ - (u64)fp_unavailable_iSeries, /* 0x800 FP Unavailable */ - (u64)decrementer_iSeries, /* 0x900 Decrementer */ - (u64)trap_0a_iSeries, /* 0xa00 Trap 0A */ - (u64)trap_0b_iSeries, /* 0xb00 Trap 0B */ - (u64)system_call_iSeries, /* 0xc00 System Call */ - (u64)single_step_iSeries, /* 0xd00 Single Step */ - (u64)trap_0e_iSeries, /* 0xe00 Trap 0E */ - (u64)performance_monitor_iSeries,/* 0xf00 Performance Monitor */ - 0, /* int 0x1000 */ - 0, /* int 0x1010 */ - 0, /* int 0x1020 CPU ctls */ - (u64)hardware_interrupt_iSeries, /* SC Ret Hdlr */ - (u64)data_access_slb_iSeries, /* 0x380 D-SLB */ - (u64)instruction_access_slb_iSeries /* 0x480 I-SLB */ - } -}; - -/* May be filled in by the hypervisor so cannot end up in the BSS */ -static struct ItIplParmsReal xItIplParmsReal __attribute__((__section__(".data"))); - -/* May be filled in by the hypervisor so cannot end up in the BSS */ -struct ItExtVpdPanel xItExtVpdPanel __attribute__((__section__(".data"))); - -#define maxPhysicalProcessors 32 - -struct IoHriProcessorVpd xIoHriProcessorVpd[maxPhysicalProcessors] = { - { - .xInstCacheOperandSize = 32, - .xDataCacheOperandSize = 32, - .xProcFreq = 50000000, - .xTimeBaseFreq = 50000000, - .xPVR = 0x3600 - } -}; - -/* Space for Main Store Vpd 27,200 bytes */ -/* May be filled in by the hypervisor so cannot end up in the BSS */ -u64 xMsVpd[3400] __attribute__((__section__(".data"))); - -/* Space for Recovery Log Buffer */ -/* May be filled in by the hypervisor so cannot end up in the BSS */ -static u64 xRecoveryLogBuffer[32] __attribute__((__section__(".data"))); - -static const struct SpCommArea xSpCommArea = { - .xDesc = 0xE2D7C3C2, - .xFormat = 1, -}; - -static const struct ItLpRegSave iseries_reg_save[] = { - [0 ... (NR_CPUS-1)] = { - .xDesc = 0xd397d9e2, /* "LpRS" */ - .xSize = sizeof(struct ItLpRegSave), - }, -}; - -#define ALPACA_INIT(number) \ -{ \ - .lppaca_ptr = &lppaca[number], \ - .reg_save_ptr = &iseries_reg_save[number], \ -} - -const struct alpaca alpaca[] = { - ALPACA_INIT( 0), -#if NR_CPUS > 1 - ALPACA_INIT( 1), ALPACA_INIT( 2), ALPACA_INIT( 3), -#if NR_CPUS > 4 - ALPACA_INIT( 4), ALPACA_INIT( 5), ALPACA_INIT( 6), ALPACA_INIT( 7), -#if NR_CPUS > 8 - ALPACA_INIT( 8), ALPACA_INIT( 9), ALPACA_INIT(10), ALPACA_INIT(11), - ALPACA_INIT(12), ALPACA_INIT(13), ALPACA_INIT(14), ALPACA_INIT(15), - ALPACA_INIT(16), ALPACA_INIT(17), ALPACA_INIT(18), ALPACA_INIT(19), - ALPACA_INIT(20), ALPACA_INIT(21), ALPACA_INIT(22), ALPACA_INIT(23), - ALPACA_INIT(24), ALPACA_INIT(25), ALPACA_INIT(26), ALPACA_INIT(27), - ALPACA_INIT(28), ALPACA_INIT(29), ALPACA_INIT(30), ALPACA_INIT(31), -#if NR_CPUS > 32 - ALPACA_INIT(32), ALPACA_INIT(33), ALPACA_INIT(34), ALPACA_INIT(35), - ALPACA_INIT(36), ALPACA_INIT(37), ALPACA_INIT(38), ALPACA_INIT(39), - ALPACA_INIT(40), ALPACA_INIT(41), ALPACA_INIT(42), ALPACA_INIT(43), - ALPACA_INIT(44), ALPACA_INIT(45), ALPACA_INIT(46), ALPACA_INIT(47), - ALPACA_INIT(48), ALPACA_INIT(49), ALPACA_INIT(50), ALPACA_INIT(51), - ALPACA_INIT(52), ALPACA_INIT(53), ALPACA_INIT(54), ALPACA_INIT(55), - ALPACA_INIT(56), ALPACA_INIT(57), ALPACA_INIT(58), ALPACA_INIT(59), - ALPACA_INIT(60), ALPACA_INIT(61), ALPACA_INIT(62), ALPACA_INIT(63), -#endif -#endif -#endif -#endif -}; - -/* The LparMap data is now located at offset 0x6000 in head.S - * It was put there so that the HvReleaseData could address it - * with a 32-bit offset as required by the iSeries hypervisor - * - * The Naca has a pointer to the ItVpdAreas. The hypervisor finds - * the Naca via the HvReleaseData area. The HvReleaseData has the - * offset into the Naca of the pointer to the ItVpdAreas. - */ -const struct ItVpdAreas itVpdAreas = { - .xSlicDesc = 0xc9a3e5c1, /* "ItVA" */ - .xSlicSize = sizeof(struct ItVpdAreas), - .xSlicVpdEntries = ItVpdMaxEntries, /* # VPD array entries */ - .xSlicDmaEntries = ItDmaMaxEntries, /* # DMA array entries */ - .xSlicMaxLogicalProcs = NR_CPUS * 2, /* Max logical procs */ - .xSlicMaxPhysicalProcs = maxPhysicalProcessors, /* Max physical procs */ - .xSlicDmaToksOffset = offsetof(struct ItVpdAreas, xPlicDmaToks), - .xSlicVpdAdrsOffset = offsetof(struct ItVpdAreas, xSlicVpdAdrs), - .xSlicDmaLensOffset = offsetof(struct ItVpdAreas, xPlicDmaLens), - .xSlicVpdLensOffset = offsetof(struct ItVpdAreas, xSlicVpdLens), - .xSlicMaxSlotLabels = 0, /* max slot labels */ - .xSlicMaxLpQueues = 1, /* max LP queues */ - .xPlicDmaLens = { 0 }, /* DMA lengths */ - .xPlicDmaToks = { 0 }, /* DMA tokens */ - .xSlicVpdLens = { /* VPD lengths */ - 0,0,0, /* 0 - 2 */ - sizeof(xItExtVpdPanel), /* 3 Extended VPD */ - sizeof(struct alpaca), /* 4 length of (fake) Paca */ - 0, /* 5 */ - sizeof(struct ItIplParmsReal),/* 6 length of IPL parms */ - 26992, /* 7 length of MS VPD */ - 0, /* 8 */ - sizeof(struct ItLpNaca),/* 9 length of LP Naca */ - 0, /* 10 */ - 256, /* 11 length of Recovery Log Buf */ - sizeof(struct SpCommArea), /* 12 length of SP Comm Area */ - 0,0,0, /* 13 - 15 */ - sizeof(struct IoHriProcessorVpd),/* 16 length of Proc Vpd */ - 0,0,0,0,0,0, /* 17 - 22 */ - sizeof(struct hvlpevent_queue), /* 23 length of Lp Queue */ - 0,0 /* 24 - 25 */ - }, - .xSlicVpdAdrs = { /* VPD addresses */ - 0,0,0, /* 0 - 2 */ - &xItExtVpdPanel, /* 3 Extended VPD */ - &alpaca[0], /* 4 first (fake) Paca */ - 0, /* 5 */ - &xItIplParmsReal, /* 6 IPL parms */ - &xMsVpd, /* 7 MS Vpd */ - 0, /* 8 */ - &itLpNaca, /* 9 LpNaca */ - 0, /* 10 */ - &xRecoveryLogBuffer, /* 11 Recovery Log Buffer */ - &xSpCommArea, /* 12 SP Comm Area */ - 0,0,0, /* 13 - 15 */ - &xIoHriProcessorVpd, /* 16 Proc Vpd */ - 0,0,0,0,0,0, /* 17 - 22 */ - &hvlpevent_queue, /* 23 Lp Queue */ - 0,0 - } -}; diff --git a/arch/powerpc/platforms/iseries/lpevents.c b/arch/powerpc/platforms/iseries/lpevents.c deleted file mode 100644 index 202e227..0000000 --- a/arch/powerpc/platforms/iseries/lpevents.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "it_lp_naca.h" - -/* - * The LpQueue is used to pass event data from the hypervisor to - * the partition. This is where I/O interrupt events are communicated. - * - * It is written to by the hypervisor so cannot end up in the BSS. - */ -struct hvlpevent_queue hvlpevent_queue __attribute__((__section__(".data"))); - -DEFINE_PER_CPU(unsigned long[HvLpEvent_Type_NumTypes], hvlpevent_counts); - -static char *event_types[HvLpEvent_Type_NumTypes] = { - "Hypervisor", - "Machine Facilities", - "Session Manager", - "SPD I/O", - "Virtual Bus", - "PCI I/O", - "RIO I/O", - "Virtual Lan", - "Virtual I/O" -}; - -/* Array of LpEvent handler functions */ -static LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; -static unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes]; - -static struct HvLpEvent * get_next_hvlpevent(void) -{ - struct HvLpEvent * event; - event = (struct HvLpEvent *)hvlpevent_queue.hq_current_event; - - if (hvlpevent_is_valid(event)) { - /* rmb() needed only for weakly consistent machines (regatta) */ - rmb(); - /* Set pointer to next potential event */ - hvlpevent_queue.hq_current_event += ((event->xSizeMinus1 + - IT_LP_EVENT_ALIGN) / IT_LP_EVENT_ALIGN) * - IT_LP_EVENT_ALIGN; - - /* Wrap to beginning if no room at end */ - if (hvlpevent_queue.hq_current_event > - hvlpevent_queue.hq_last_event) { - hvlpevent_queue.hq_current_event = - hvlpevent_queue.hq_event_stack; - } - } else { - event = NULL; - } - - return event; -} - -static unsigned long spread_lpevents = NR_CPUS; - -int hvlpevent_is_pending(void) -{ - struct HvLpEvent *next_event; - - if (smp_processor_id() >= spread_lpevents) - return 0; - - next_event = (struct HvLpEvent *)hvlpevent_queue.hq_current_event; - - return hvlpevent_is_valid(next_event) || - hvlpevent_queue.hq_overflow_pending; -} - -static void hvlpevent_clear_valid(struct HvLpEvent * event) -{ - /* Tell the Hypervisor that we're done with this event. - * Also clear bits within this event that might look like valid bits. - * ie. on 64-byte boundaries. - */ - struct HvLpEvent *tmp; - unsigned extra = ((event->xSizeMinus1 + IT_LP_EVENT_ALIGN) / - IT_LP_EVENT_ALIGN) - 1; - - switch (extra) { - case 3: - tmp = (struct HvLpEvent*)((char*)event + 3 * IT_LP_EVENT_ALIGN); - hvlpevent_invalidate(tmp); - case 2: - tmp = (struct HvLpEvent*)((char*)event + 2 * IT_LP_EVENT_ALIGN); - hvlpevent_invalidate(tmp); - case 1: - tmp = (struct HvLpEvent*)((char*)event + 1 * IT_LP_EVENT_ALIGN); - hvlpevent_invalidate(tmp); - } - - mb(); - - hvlpevent_invalidate(event); -} - -void process_hvlpevents(void) -{ - struct HvLpEvent * event; - - restart: - /* If we have recursed, just return */ - if (!spin_trylock(&hvlpevent_queue.hq_lock)) - return; - - for (;;) { - event = get_next_hvlpevent(); - if (event) { - /* Call appropriate handler here, passing - * a pointer to the LpEvent. The handler - * must make a copy of the LpEvent if it - * needs it in a bottom half. (perhaps for - * an ACK) - * - * Handlers are responsible for ACK processing - * - * The Hypervisor guarantees that LpEvents will - * only be delivered with types that we have - * registered for, so no type check is necessary - * here! - */ - if (event->xType < HvLpEvent_Type_NumTypes) - __get_cpu_var(hvlpevent_counts)[event->xType]++; - if (event->xType < HvLpEvent_Type_NumTypes && - lpEventHandler[event->xType]) - lpEventHandler[event->xType](event); - else { - u8 type = event->xType; - - /* - * Don't printk in the spinlock as printk - * may require ack events form the HV to send - * any characters there. - */ - hvlpevent_clear_valid(event); - spin_unlock(&hvlpevent_queue.hq_lock); - printk(KERN_INFO - "Unexpected Lp Event type=%d\n", type); - goto restart; - } - - hvlpevent_clear_valid(event); - } else if (hvlpevent_queue.hq_overflow_pending) - /* - * No more valid events. If overflow events are - * pending process them - */ - HvCallEvent_getOverflowLpEvents(hvlpevent_queue.hq_index); - else - break; - } - - spin_unlock(&hvlpevent_queue.hq_lock); -} - -static int set_spread_lpevents(char *str) -{ - unsigned long val = simple_strtoul(str, NULL, 0); - - /* - * The parameter is the number of processors to share in processing - * lp events. - */ - if (( val > 0) && (val <= NR_CPUS)) { - spread_lpevents = val; - printk("lpevent processing spread over %ld processors\n", val); - } else { - printk("invalid spread_lpevents %ld\n", val); - } - - return 1; -} -__setup("spread_lpevents=", set_spread_lpevents); - -void __init setup_hvlpevent_queue(void) -{ - void *eventStack; - - spin_lock_init(&hvlpevent_queue.hq_lock); - - /* Allocate a page for the Event Stack. */ - eventStack = alloc_bootmem_pages(IT_LP_EVENT_STACK_SIZE); - memset(eventStack, 0, IT_LP_EVENT_STACK_SIZE); - - /* Invoke the hypervisor to initialize the event stack */ - HvCallEvent_setLpEventStack(0, eventStack, IT_LP_EVENT_STACK_SIZE); - - hvlpevent_queue.hq_event_stack = eventStack; - hvlpevent_queue.hq_current_event = eventStack; - hvlpevent_queue.hq_last_event = (char *)eventStack + - (IT_LP_EVENT_STACK_SIZE - IT_LP_EVENT_MAX_SIZE); - hvlpevent_queue.hq_index = 0; -} - -/* Register a handler for an LpEvent type */ -int HvLpEvent_registerHandler(HvLpEvent_Type eventType, LpEventHandler handler) -{ - if (eventType < HvLpEvent_Type_NumTypes) { - lpEventHandler[eventType] = handler; - return 0; - } - return 1; -} -EXPORT_SYMBOL(HvLpEvent_registerHandler); - -int HvLpEvent_unregisterHandler(HvLpEvent_Type eventType) -{ - might_sleep(); - - if (eventType < HvLpEvent_Type_NumTypes) { - if (!lpEventHandlerPaths[eventType]) { - lpEventHandler[eventType] = NULL; - /* - * We now sleep until all other CPUs have scheduled. - * This ensures that the deletion is seen by all - * other CPUs, and that the deleted handler isn't - * still running on another CPU when we return. - */ - synchronize_sched(); - return 0; - } - } - return 1; -} -EXPORT_SYMBOL(HvLpEvent_unregisterHandler); - -/* - * lpIndex is the partition index of the target partition. - * needed only for VirtualIo, VirtualLan and SessionMgr. Zero - * indicates to use our partition index - for the other types. - */ -int HvLpEvent_openPath(HvLpEvent_Type eventType, HvLpIndex lpIndex) -{ - if ((eventType < HvLpEvent_Type_NumTypes) && - lpEventHandler[eventType]) { - if (lpIndex == 0) - lpIndex = itLpNaca.xLpIndex; - HvCallEvent_openLpEventPath(lpIndex, eventType); - ++lpEventHandlerPaths[eventType]; - return 0; - } - return 1; -} - -int HvLpEvent_closePath(HvLpEvent_Type eventType, HvLpIndex lpIndex) -{ - if ((eventType < HvLpEvent_Type_NumTypes) && - lpEventHandler[eventType] && - lpEventHandlerPaths[eventType]) { - if (lpIndex == 0) - lpIndex = itLpNaca.xLpIndex; - HvCallEvent_closeLpEventPath(lpIndex, eventType); - --lpEventHandlerPaths[eventType]; - return 0; - } - return 1; -} - -static int proc_lpevents_show(struct seq_file *m, void *v) -{ - int cpu, i; - unsigned long sum; - static unsigned long cpu_totals[NR_CPUS]; - - /* FIXME: do we care that there's no locking here? */ - sum = 0; - for_each_online_cpu(cpu) { - cpu_totals[cpu] = 0; - for (i = 0; i < HvLpEvent_Type_NumTypes; i++) { - cpu_totals[cpu] += per_cpu(hvlpevent_counts, cpu)[i]; - } - sum += cpu_totals[cpu]; - } - - seq_printf(m, "LpEventQueue 0\n"); - seq_printf(m, " events processed:\t%lu\n", sum); - - for (i = 0; i < HvLpEvent_Type_NumTypes; ++i) { - sum = 0; - for_each_online_cpu(cpu) { - sum += per_cpu(hvlpevent_counts, cpu)[i]; - } - - seq_printf(m, " %-20s %10lu\n", event_types[i], sum); - } - - seq_printf(m, "\n events processed by processor:\n"); - - for_each_online_cpu(cpu) { - seq_printf(m, " CPU%02d %10lu\n", cpu, cpu_totals[cpu]); - } - - return 0; -} - -static int proc_lpevents_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_lpevents_show, NULL); -} - -static const struct file_operations proc_lpevents_operations = { - .open = proc_lpevents_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init proc_lpevents_init(void) -{ - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - - proc_create("iSeries/lpevents", S_IFREG|S_IRUGO, NULL, - &proc_lpevents_operations); - return 0; -} -__initcall(proc_lpevents_init); - diff --git a/arch/powerpc/platforms/iseries/main_store.h b/arch/powerpc/platforms/iseries/main_store.h deleted file mode 100644 index 1a7a3f5..0000000 --- a/arch/powerpc/platforms/iseries/main_store.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ - -#ifndef _ISERIES_MAIN_STORE_H -#define _ISERIES_MAIN_STORE_H - -/* Main Store Vpd for Condor,iStar,sStar */ -struct IoHriMainStoreSegment4 { - u8 msArea0Exists:1; - u8 msArea1Exists:1; - u8 msArea2Exists:1; - u8 msArea3Exists:1; - u8 reserved1:4; - u8 reserved2; - - u8 msArea0Functional:1; - u8 msArea1Functional:1; - u8 msArea2Functional:1; - u8 msArea3Functional:1; - u8 reserved3:4; - u8 reserved4; - - u32 totalMainStore; - - u64 msArea0Ptr; - u64 msArea1Ptr; - u64 msArea2Ptr; - u64 msArea3Ptr; - - u32 cardProductionLevel; - - u32 msAdrHole; - - u8 msArea0HasRiserVpd:1; - u8 msArea1HasRiserVpd:1; - u8 msArea2HasRiserVpd:1; - u8 msArea3HasRiserVpd:1; - u8 reserved5:4; - u8 reserved6; - u16 reserved7; - - u8 reserved8[28]; - - u64 nonInterleavedBlocksStartAdr; - u64 nonInterleavedBlocksEndAdr; -}; - -/* Main Store VPD for Power4 */ -struct __attribute((packed)) IoHriMainStoreChipInfo1 { - u32 chipMfgID; - char chipECLevel[4]; -}; - -struct IoHriMainStoreVpdIdData { - char typeNumber[4]; - char modelNumber[4]; - char partNumber[12]; - char serialNumber[12]; -}; - -struct __attribute((packed)) IoHriMainStoreVpdFruData { - char fruLabel[8]; - u8 numberOfSlots; - u8 pluggingType; - u16 slotMapIndex; -}; - -struct __attribute((packed)) IoHriMainStoreAdrRangeBlock { - void *blockStart; - void *blockEnd; - u32 blockProcChipId; -}; - -#define MaxAreaAdrRangeBlocks 4 - -struct __attribute((packed)) IoHriMainStoreArea4 { - u32 msVpdFormat; - u8 containedVpdType; - u8 reserved1; - u16 reserved2; - - u64 msExists; - u64 msFunctional; - - u32 memorySize; - u32 procNodeId; - - u32 numAdrRangeBlocks; - struct IoHriMainStoreAdrRangeBlock xAdrRangeBlock[MaxAreaAdrRangeBlocks]; - - struct IoHriMainStoreChipInfo1 chipInfo0; - struct IoHriMainStoreChipInfo1 chipInfo1; - struct IoHriMainStoreChipInfo1 chipInfo2; - struct IoHriMainStoreChipInfo1 chipInfo3; - struct IoHriMainStoreChipInfo1 chipInfo4; - struct IoHriMainStoreChipInfo1 chipInfo5; - struct IoHriMainStoreChipInfo1 chipInfo6; - struct IoHriMainStoreChipInfo1 chipInfo7; - - void *msRamAreaArray; - u32 msRamAreaArrayNumEntries; - u32 msRamAreaArrayEntrySize; - - u32 numaDimmExists; - u32 numaDimmFunctional; - void *numaDimmArray; - u32 numaDimmArrayNumEntries; - u32 numaDimmArrayEntrySize; - - struct IoHriMainStoreVpdIdData idData; - - u64 powerData; - u64 cardAssemblyPartNum; - u64 chipSerialNum; - - u64 reserved3; - char reserved4[16]; - - struct IoHriMainStoreVpdFruData fruData; - - u8 vpdPortNum; - u8 reserved5; - u8 frameId; - u8 rackUnit; - char asciiKeywordVpd[256]; - u32 reserved6; -}; - - -struct IoHriMainStoreSegment5 { - u16 reserved1; - u8 reserved2; - u8 msVpdFormat; - - u32 totalMainStore; - u64 maxConfiguredMsAdr; - - struct IoHriMainStoreArea4 *msAreaArray; - u32 msAreaArrayNumEntries; - u32 msAreaArrayEntrySize; - - u32 msAreaExists; - u32 msAreaFunctional; - - u64 reserved3; -}; - -extern u64 xMsVpd[]; - -#endif /* _ISERIES_MAIN_STORE_H */ diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c deleted file mode 100644 index 254c1fc..0000000 --- a/arch/powerpc/platforms/iseries/mf.c +++ /dev/null @@ -1,1275 +0,0 @@ -/* - * Copyright (C) 2001 Troy D. Armstrong IBM Corporation - * Copyright (C) 2004-2005 Stephen Rothwell IBM Corporation - * - * This modules exists as an interface between a Linux secondary partition - * running on an iSeries and the primary partition's Virtual Service - * Processor (VSP) object. The VSP has final authority over powering on/off - * all partitions in the iSeries. It also provides miscellaneous low-level - * machine facility type operations. - * - * - * 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. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "setup.h" - -static int mf_initialized; - -/* - * This is the structure layout for the Machine Facilities LPAR event - * flows. - */ -struct vsp_cmd_data { - u64 token; - u16 cmd; - HvLpIndex lp_index; - u8 result_code; - u32 reserved; - union { - u64 state; /* GetStateOut */ - u64 ipl_type; /* GetIplTypeOut, Function02SelectIplTypeIn */ - u64 ipl_mode; /* GetIplModeOut, Function02SelectIplModeIn */ - u64 page[4]; /* GetSrcHistoryIn */ - u64 flag; /* GetAutoIplWhenPrimaryIplsOut, - SetAutoIplWhenPrimaryIplsIn, - WhiteButtonPowerOffIn, - Function08FastPowerOffIn, - IsSpcnRackPowerIncompleteOut */ - struct { - u64 token; - u64 address_type; - u64 side; - u32 length; - u32 offset; - } kern; /* SetKernelImageIn, GetKernelImageIn, - SetKernelCmdLineIn, GetKernelCmdLineIn */ - u32 length_out; /* GetKernelImageOut, GetKernelCmdLineOut */ - u8 reserved[80]; - } sub_data; -}; - -struct vsp_rsp_data { - struct completion com; - struct vsp_cmd_data *response; -}; - -struct alloc_data { - u16 size; - u16 type; - u32 count; - u16 reserved1; - u8 reserved2; - HvLpIndex target_lp; -}; - -struct ce_msg_data; - -typedef void (*ce_msg_comp_hdlr)(void *token, struct ce_msg_data *vsp_cmd_rsp); - -struct ce_msg_comp_data { - ce_msg_comp_hdlr handler; - void *token; -}; - -struct ce_msg_data { - u8 ce_msg[12]; - char reserved[4]; - struct ce_msg_comp_data *completion; -}; - -struct io_mf_lp_event { - struct HvLpEvent hp_lp_event; - u16 subtype_result_code; - u16 reserved1; - u32 reserved2; - union { - struct alloc_data alloc; - struct ce_msg_data ce_msg; - struct vsp_cmd_data vsp_cmd; - } data; -}; - -#define subtype_data(a, b, c, d) \ - (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) - -/* - * All outgoing event traffic is kept on a FIFO queue. The first - * pointer points to the one that is outstanding, and all new - * requests get stuck on the end. Also, we keep a certain number of - * preallocated pending events so that we can operate very early in - * the boot up sequence (before kmalloc is ready). - */ -struct pending_event { - struct pending_event *next; - struct io_mf_lp_event event; - MFCompleteHandler hdlr; - char dma_data[72]; - unsigned dma_data_length; - unsigned remote_address; -}; -static spinlock_t pending_event_spinlock; -static struct pending_event *pending_event_head; -static struct pending_event *pending_event_tail; -static struct pending_event *pending_event_avail; -#define PENDING_EVENT_PREALLOC_LEN 16 -static struct pending_event pending_event_prealloc[PENDING_EVENT_PREALLOC_LEN]; - -/* - * Put a pending event onto the available queue, so it can get reused. - * Attention! You must have the pending_event_spinlock before calling! - */ -static void free_pending_event(struct pending_event *ev) -{ - if (ev != NULL) { - ev->next = pending_event_avail; - pending_event_avail = ev; - } -} - -/* - * Enqueue the outbound event onto the stack. If the queue was - * empty to begin with, we must also issue it via the Hypervisor - * interface. There is a section of code below that will touch - * the first stack pointer without the protection of the pending_event_spinlock. - * This is OK, because we know that nobody else will be modifying - * the first pointer when we do this. - */ -static int signal_event(struct pending_event *ev) -{ - int rc = 0; - unsigned long flags; - int go = 1; - struct pending_event *ev1; - HvLpEvent_Rc hv_rc; - - /* enqueue the event */ - if (ev != NULL) { - ev->next = NULL; - spin_lock_irqsave(&pending_event_spinlock, flags); - if (pending_event_head == NULL) - pending_event_head = ev; - else { - go = 0; - pending_event_tail->next = ev; - } - pending_event_tail = ev; - spin_unlock_irqrestore(&pending_event_spinlock, flags); - } - - /* send the event */ - while (go) { - go = 0; - - /* any DMA data to send beforehand? */ - if (pending_event_head->dma_data_length > 0) - HvCallEvent_dmaToSp(pending_event_head->dma_data, - pending_event_head->remote_address, - pending_event_head->dma_data_length, - HvLpDma_Direction_LocalToRemote); - - hv_rc = HvCallEvent_signalLpEvent( - &pending_event_head->event.hp_lp_event); - if (hv_rc != HvLpEvent_Rc_Good) { - printk(KERN_ERR "mf.c: HvCallEvent_signalLpEvent() " - "failed with %d\n", (int)hv_rc); - - spin_lock_irqsave(&pending_event_spinlock, flags); - ev1 = pending_event_head; - pending_event_head = pending_event_head->next; - if (pending_event_head != NULL) - go = 1; - spin_unlock_irqrestore(&pending_event_spinlock, flags); - - if (ev1 == ev) - rc = -EIO; - else if (ev1->hdlr != NULL) - (*ev1->hdlr)((void *)ev1->event.hp_lp_event.xCorrelationToken, -EIO); - - spin_lock_irqsave(&pending_event_spinlock, flags); - free_pending_event(ev1); - spin_unlock_irqrestore(&pending_event_spinlock, flags); - } - } - - return rc; -} - -/* - * Allocate a new pending_event structure, and initialize it. - */ -static struct pending_event *new_pending_event(void) -{ - struct pending_event *ev = NULL; - HvLpIndex primary_lp = HvLpConfig_getPrimaryLpIndex(); - unsigned long flags; - struct HvLpEvent *hev; - - spin_lock_irqsave(&pending_event_spinlock, flags); - if (pending_event_avail != NULL) { - ev = pending_event_avail; - pending_event_avail = pending_event_avail->next; - } - spin_unlock_irqrestore(&pending_event_spinlock, flags); - if (ev == NULL) { - ev = kmalloc(sizeof(struct pending_event), GFP_ATOMIC); - if (ev == NULL) { - printk(KERN_ERR "mf.c: unable to kmalloc %ld bytes\n", - sizeof(struct pending_event)); - return NULL; - } - } - memset(ev, 0, sizeof(struct pending_event)); - hev = &ev->event.hp_lp_event; - hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DO_ACK | HV_LP_EVENT_INT; - hev->xType = HvLpEvent_Type_MachineFac; - hev->xSourceLp = HvLpConfig_getLpIndex(); - hev->xTargetLp = primary_lp; - hev->xSizeMinus1 = sizeof(ev->event) - 1; - hev->xRc = HvLpEvent_Rc_Good; - hev->xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primary_lp, - HvLpEvent_Type_MachineFac); - hev->xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primary_lp, - HvLpEvent_Type_MachineFac); - - return ev; -} - -static int __maybe_unused -signal_vsp_instruction(struct vsp_cmd_data *vsp_cmd) -{ - struct pending_event *ev = new_pending_event(); - int rc; - struct vsp_rsp_data response; - - if (ev == NULL) - return -ENOMEM; - - init_completion(&response.com); - response.response = vsp_cmd; - ev->event.hp_lp_event.xSubtype = 6; - ev->event.hp_lp_event.x.xSubtypeData = - subtype_data('M', 'F', 'V', 'I'); - ev->event.data.vsp_cmd.token = (u64)&response; - ev->event.data.vsp_cmd.cmd = vsp_cmd->cmd; - ev->event.data.vsp_cmd.lp_index = HvLpConfig_getLpIndex(); - ev->event.data.vsp_cmd.result_code = 0xFF; - ev->event.data.vsp_cmd.reserved = 0; - memcpy(&(ev->event.data.vsp_cmd.sub_data), - &(vsp_cmd->sub_data), sizeof(vsp_cmd->sub_data)); - mb(); - - rc = signal_event(ev); - if (rc == 0) - wait_for_completion(&response.com); - return rc; -} - - -/* - * Send a 12-byte CE message to the primary partition VSP object - */ -static int signal_ce_msg(char *ce_msg, struct ce_msg_comp_data *completion) -{ - struct pending_event *ev = new_pending_event(); - - if (ev == NULL) - return -ENOMEM; - - ev->event.hp_lp_event.xSubtype = 0; - ev->event.hp_lp_event.x.xSubtypeData = - subtype_data('M', 'F', 'C', 'E'); - memcpy(ev->event.data.ce_msg.ce_msg, ce_msg, 12); - ev->event.data.ce_msg.completion = completion; - return signal_event(ev); -} - -/* - * Send a 12-byte CE message (with no data) to the primary partition VSP object - */ -static int signal_ce_msg_simple(u8 ce_op, struct ce_msg_comp_data *completion) -{ - u8 ce_msg[12]; - - memset(ce_msg, 0, sizeof(ce_msg)); - ce_msg[3] = ce_op; - return signal_ce_msg(ce_msg, completion); -} - -/* - * Send a 12-byte CE message and DMA data to the primary partition VSP object - */ -static int dma_and_signal_ce_msg(char *ce_msg, - struct ce_msg_comp_data *completion, void *dma_data, - unsigned dma_data_length, unsigned remote_address) -{ - struct pending_event *ev = new_pending_event(); - - if (ev == NULL) - return -ENOMEM; - - ev->event.hp_lp_event.xSubtype = 0; - ev->event.hp_lp_event.x.xSubtypeData = - subtype_data('M', 'F', 'C', 'E'); - memcpy(ev->event.data.ce_msg.ce_msg, ce_msg, 12); - ev->event.data.ce_msg.completion = completion; - memcpy(ev->dma_data, dma_data, dma_data_length); - ev->dma_data_length = dma_data_length; - ev->remote_address = remote_address; - return signal_event(ev); -} - -/* - * Initiate a nice (hopefully) shutdown of Linux. We simply are - * going to try and send the init process a SIGINT signal. If - * this fails (why?), we'll simply force it off in a not-so-nice - * manner. - */ -static int shutdown(void) -{ - int rc = kill_cad_pid(SIGINT, 1); - - if (rc) { - printk(KERN_ALERT "mf.c: SIGINT to init failed (%d), " - "hard shutdown commencing\n", rc); - mf_power_off(); - } else - printk(KERN_INFO "mf.c: init has been successfully notified " - "to proceed with shutdown\n"); - return rc; -} - -/* - * The primary partition VSP object is sending us a new - * event flow. Handle it... - */ -static void handle_int(struct io_mf_lp_event *event) -{ - struct ce_msg_data *ce_msg_data; - struct ce_msg_data *pce_msg_data; - unsigned long flags; - struct pending_event *pev; - - /* ack the interrupt */ - event->hp_lp_event.xRc = HvLpEvent_Rc_Good; - HvCallEvent_ackLpEvent(&event->hp_lp_event); - - /* process interrupt */ - switch (event->hp_lp_event.xSubtype) { - case 0: /* CE message */ - ce_msg_data = &event->data.ce_msg; - switch (ce_msg_data->ce_msg[3]) { - case 0x5B: /* power control notification */ - if ((ce_msg_data->ce_msg[5] & 0x20) != 0) { - printk(KERN_INFO "mf.c: Commencing partition shutdown\n"); - if (shutdown() == 0) - signal_ce_msg_simple(0xDB, NULL); - } - break; - case 0xC0: /* get time */ - spin_lock_irqsave(&pending_event_spinlock, flags); - pev = pending_event_head; - if (pev != NULL) - pending_event_head = pending_event_head->next; - spin_unlock_irqrestore(&pending_event_spinlock, flags); - if (pev == NULL) - break; - pce_msg_data = &pev->event.data.ce_msg; - if (pce_msg_data->ce_msg[3] != 0x40) - break; - if (pce_msg_data->completion != NULL) { - ce_msg_comp_hdlr handler = - pce_msg_data->completion->handler; - void *token = pce_msg_data->completion->token; - - if (handler != NULL) - (*handler)(token, ce_msg_data); - } - spin_lock_irqsave(&pending_event_spinlock, flags); - free_pending_event(pev); - spin_unlock_irqrestore(&pending_event_spinlock, flags); - /* send next waiting event */ - if (pending_event_head != NULL) - signal_event(NULL); - break; - } - break; - case 1: /* IT sys shutdown */ - printk(KERN_INFO "mf.c: Commencing system shutdown\n"); - shutdown(); - break; - } -} - -/* - * The primary partition VSP object is acknowledging the receipt - * of a flow we sent to them. If there are other flows queued - * up, we must send another one now... - */ -static void handle_ack(struct io_mf_lp_event *event) -{ - unsigned long flags; - struct pending_event *two = NULL; - unsigned long free_it = 0; - struct ce_msg_data *ce_msg_data; - struct ce_msg_data *pce_msg_data; - struct vsp_rsp_data *rsp; - - /* handle current event */ - if (pending_event_head == NULL) { - printk(KERN_ERR "mf.c: stack empty for receiving ack\n"); - return; - } - - switch (event->hp_lp_event.xSubtype) { - case 0: /* CE msg */ - ce_msg_data = &event->data.ce_msg; - if (ce_msg_data->ce_msg[3] != 0x40) { - free_it = 1; - break; - } - if (ce_msg_data->ce_msg[2] == 0) - break; - free_it = 1; - pce_msg_data = &pending_event_head->event.data.ce_msg; - if (pce_msg_data->completion != NULL) { - ce_msg_comp_hdlr handler = - pce_msg_data->completion->handler; - void *token = pce_msg_data->completion->token; - - if (handler != NULL) - (*handler)(token, ce_msg_data); - } - break; - case 4: /* allocate */ - case 5: /* deallocate */ - if (pending_event_head->hdlr != NULL) - (*pending_event_head->hdlr)((void *)event->hp_lp_event.xCorrelationToken, event->data.alloc.count); - free_it = 1; - break; - case 6: - free_it = 1; - rsp = (struct vsp_rsp_data *)event->data.vsp_cmd.token; - if (rsp == NULL) { - printk(KERN_ERR "mf.c: no rsp\n"); - break; - } - if (rsp->response != NULL) - memcpy(rsp->response, &event->data.vsp_cmd, - sizeof(event->data.vsp_cmd)); - complete(&rsp->com); - break; - } - - /* remove from queue */ - spin_lock_irqsave(&pending_event_spinlock, flags); - if ((pending_event_head != NULL) && (free_it == 1)) { - struct pending_event *oldHead = pending_event_head; - - pending_event_head = pending_event_head->next; - two = pending_event_head; - free_pending_event(oldHead); - } - spin_unlock_irqrestore(&pending_event_spinlock, flags); - - /* send next waiting event */ - if (two != NULL) - signal_event(NULL); -} - -/* - * This is the generic event handler we are registering with - * the Hypervisor. Ensure the flows are for us, and then - * parse it enough to know if it is an interrupt or an - * acknowledge. - */ -static void hv_handler(struct HvLpEvent *event) -{ - if ((event != NULL) && (event->xType == HvLpEvent_Type_MachineFac)) { - if (hvlpevent_is_ack(event)) - handle_ack((struct io_mf_lp_event *)event); - else - handle_int((struct io_mf_lp_event *)event); - } else - printk(KERN_ERR "mf.c: alien event received\n"); -} - -/* - * Global kernel interface to allocate and seed events into the - * Hypervisor. - */ -void mf_allocate_lp_events(HvLpIndex target_lp, HvLpEvent_Type type, - unsigned size, unsigned count, MFCompleteHandler hdlr, - void *user_token) -{ - struct pending_event *ev = new_pending_event(); - int rc; - - if (ev == NULL) { - rc = -ENOMEM; - } else { - ev->event.hp_lp_event.xSubtype = 4; - ev->event.hp_lp_event.xCorrelationToken = (u64)user_token; - ev->event.hp_lp_event.x.xSubtypeData = - subtype_data('M', 'F', 'M', 'A'); - ev->event.data.alloc.target_lp = target_lp; - ev->event.data.alloc.type = type; - ev->event.data.alloc.size = size; - ev->event.data.alloc.count = count; - ev->hdlr = hdlr; - rc = signal_event(ev); - } - if ((rc != 0) && (hdlr != NULL)) - (*hdlr)(user_token, rc); -} -EXPORT_SYMBOL(mf_allocate_lp_events); - -/* - * Global kernel interface to unseed and deallocate events already in - * Hypervisor. - */ -void mf_deallocate_lp_events(HvLpIndex target_lp, HvLpEvent_Type type, - unsigned count, MFCompleteHandler hdlr, void *user_token) -{ - struct pending_event *ev = new_pending_event(); - int rc; - - if (ev == NULL) - rc = -ENOMEM; - else { - ev->event.hp_lp_event.xSubtype = 5; - ev->event.hp_lp_event.xCorrelationToken = (u64)user_token; - ev->event.hp_lp_event.x.xSubtypeData = - subtype_data('M', 'F', 'M', 'D'); - ev->event.data.alloc.target_lp = target_lp; - ev->event.data.alloc.type = type; - ev->event.data.alloc.count = count; - ev->hdlr = hdlr; - rc = signal_event(ev); - } - if ((rc != 0) && (hdlr != NULL)) - (*hdlr)(user_token, rc); -} -EXPORT_SYMBOL(mf_deallocate_lp_events); - -/* - * Global kernel interface to tell the VSP object in the primary - * partition to power this partition off. - */ -void mf_power_off(void) -{ - printk(KERN_INFO "mf.c: Down it goes...\n"); - signal_ce_msg_simple(0x4d, NULL); - for (;;) - ; -} - -/* - * Global kernel interface to tell the VSP object in the primary - * partition to reboot this partition. - */ -void mf_reboot(char *cmd) -{ - printk(KERN_INFO "mf.c: Preparing to bounce...\n"); - signal_ce_msg_simple(0x4e, NULL); - for (;;) - ; -} - -/* - * Display a single word SRC onto the VSP control panel. - */ -void mf_display_src(u32 word) -{ - u8 ce[12]; - - memset(ce, 0, sizeof(ce)); - ce[3] = 0x4a; - ce[7] = 0x01; - ce[8] = word >> 24; - ce[9] = word >> 16; - ce[10] = word >> 8; - ce[11] = word; - signal_ce_msg(ce, NULL); -} - -/* - * Display a single word SRC of the form "PROGXXXX" on the VSP control panel. - */ -static __init void mf_display_progress_src(u16 value) -{ - u8 ce[12]; - u8 src[72]; - - memcpy(ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12); - memcpy(src, "\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00PROGxxxx ", - 72); - src[6] = value >> 8; - src[7] = value & 255; - src[44] = "0123456789ABCDEF"[(value >> 12) & 15]; - src[45] = "0123456789ABCDEF"[(value >> 8) & 15]; - src[46] = "0123456789ABCDEF"[(value >> 4) & 15]; - src[47] = "0123456789ABCDEF"[value & 15]; - dma_and_signal_ce_msg(ce, NULL, src, sizeof(src), 9 * 64 * 1024); -} - -/* - * Clear the VSP control panel. Used to "erase" an SRC that was - * previously displayed. - */ -static void mf_clear_src(void) -{ - signal_ce_msg_simple(0x4b, NULL); -} - -void __init mf_display_progress(u16 value) -{ - if (!mf_initialized) - return; - - if (0xFFFF == value) - mf_clear_src(); - else - mf_display_progress_src(value); -} - -/* - * Initialization code here. - */ -void __init mf_init(void) -{ - int i; - - spin_lock_init(&pending_event_spinlock); - - for (i = 0; i < PENDING_EVENT_PREALLOC_LEN; i++) - free_pending_event(&pending_event_prealloc[i]); - - HvLpEvent_registerHandler(HvLpEvent_Type_MachineFac, &hv_handler); - - /* virtual continue ack */ - signal_ce_msg_simple(0x57, NULL); - - mf_initialized = 1; - mb(); - - printk(KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities " - "initialized\n"); -} - -struct rtc_time_data { - struct completion com; - struct ce_msg_data ce_msg; - int rc; -}; - -static void get_rtc_time_complete(void *token, struct ce_msg_data *ce_msg) -{ - struct rtc_time_data *rtc = token; - - memcpy(&rtc->ce_msg, ce_msg, sizeof(rtc->ce_msg)); - rtc->rc = 0; - complete(&rtc->com); -} - -static int mf_set_rtc(struct rtc_time *tm) -{ - char ce_time[12]; - u8 day, mon, hour, min, sec, y1, y2; - unsigned year; - - year = 1900 + tm->tm_year; - y1 = year / 100; - y2 = year % 100; - - sec = tm->tm_sec; - min = tm->tm_min; - hour = tm->tm_hour; - day = tm->tm_mday; - mon = tm->tm_mon + 1; - - sec = bin2bcd(sec); - min = bin2bcd(min); - hour = bin2bcd(hour); - mon = bin2bcd(mon); - day = bin2bcd(day); - y1 = bin2bcd(y1); - y2 = bin2bcd(y2); - - memset(ce_time, 0, sizeof(ce_time)); - ce_time[3] = 0x41; - ce_time[4] = y1; - ce_time[5] = y2; - ce_time[6] = sec; - ce_time[7] = min; - ce_time[8] = hour; - ce_time[10] = day; - ce_time[11] = mon; - - return signal_ce_msg(ce_time, NULL); -} - -static int rtc_set_tm(int rc, u8 *ce_msg, struct rtc_time *tm) -{ - tm->tm_wday = 0; - tm->tm_yday = 0; - tm->tm_isdst = 0; - if (rc) { - tm->tm_sec = 0; - tm->tm_min = 0; - tm->tm_hour = 0; - tm->tm_mday = 15; - tm->tm_mon = 5; - tm->tm_year = 52; - return rc; - } - - if ((ce_msg[2] == 0xa9) || - (ce_msg[2] == 0xaf)) { - /* TOD clock is not set */ - tm->tm_sec = 1; - tm->tm_min = 1; - tm->tm_hour = 1; - tm->tm_mday = 10; - tm->tm_mon = 8; - tm->tm_year = 71; - mf_set_rtc(tm); - } - { - u8 year = ce_msg[5]; - u8 sec = ce_msg[6]; - u8 min = ce_msg[7]; - u8 hour = ce_msg[8]; - u8 day = ce_msg[10]; - u8 mon = ce_msg[11]; - - sec = bcd2bin(sec); - min = bcd2bin(min); - hour = bcd2bin(hour); - day = bcd2bin(day); - mon = bcd2bin(mon); - year = bcd2bin(year); - - if (year <= 69) - year += 100; - - tm->tm_sec = sec; - tm->tm_min = min; - tm->tm_hour = hour; - tm->tm_mday = day; - tm->tm_mon = mon; - tm->tm_year = year; - } - - return 0; -} - -static int mf_get_rtc(struct rtc_time *tm) -{ - struct ce_msg_comp_data ce_complete; - struct rtc_time_data rtc_data; - int rc; - - memset(&ce_complete, 0, sizeof(ce_complete)); - memset(&rtc_data, 0, sizeof(rtc_data)); - init_completion(&rtc_data.com); - ce_complete.handler = &get_rtc_time_complete; - ce_complete.token = &rtc_data; - rc = signal_ce_msg_simple(0x40, &ce_complete); - if (rc) - return rc; - wait_for_completion(&rtc_data.com); - return rtc_set_tm(rtc_data.rc, rtc_data.ce_msg.ce_msg, tm); -} - -struct boot_rtc_time_data { - int busy; - struct ce_msg_data ce_msg; - int rc; -}; - -static void get_boot_rtc_time_complete(void *token, struct ce_msg_data *ce_msg) -{ - struct boot_rtc_time_data *rtc = token; - - memcpy(&rtc->ce_msg, ce_msg, sizeof(rtc->ce_msg)); - rtc->rc = 0; - rtc->busy = 0; -} - -static int mf_get_boot_rtc(struct rtc_time *tm) -{ - struct ce_msg_comp_data ce_complete; - struct boot_rtc_time_data rtc_data; - int rc; - - memset(&ce_complete, 0, sizeof(ce_complete)); - memset(&rtc_data, 0, sizeof(rtc_data)); - rtc_data.busy = 1; - ce_complete.handler = &get_boot_rtc_time_complete; - ce_complete.token = &rtc_data; - rc = signal_ce_msg_simple(0x40, &ce_complete); - if (rc) - return rc; - /* We need to poll here as we are not yet taking interrupts */ - while (rtc_data.busy) { - if (hvlpevent_is_pending()) - process_hvlpevents(); - } - return rtc_set_tm(rtc_data.rc, rtc_data.ce_msg.ce_msg, tm); -} - -#ifdef CONFIG_PROC_FS -static int mf_cmdline_proc_show(struct seq_file *m, void *v) -{ - char *page, *p; - struct vsp_cmd_data vsp_cmd; - int rc; - dma_addr_t dma_addr; - - /* The HV appears to return no more than 256 bytes of command line */ - page = kmalloc(256, GFP_KERNEL); - if (!page) - return -ENOMEM; - - dma_addr = iseries_hv_map(page, 256, DMA_FROM_DEVICE); - if (dma_addr == DMA_ERROR_CODE) { - kfree(page); - return -ENOMEM; - } - memset(page, 0, 256); - memset(&vsp_cmd, 0, sizeof(vsp_cmd)); - vsp_cmd.cmd = 33; - vsp_cmd.sub_data.kern.token = dma_addr; - vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex; - vsp_cmd.sub_data.kern.side = (u64)m->private; - vsp_cmd.sub_data.kern.length = 256; - mb(); - rc = signal_vsp_instruction(&vsp_cmd); - iseries_hv_unmap(dma_addr, 256, DMA_FROM_DEVICE); - if (rc) { - kfree(page); - return rc; - } - if (vsp_cmd.result_code != 0) { - kfree(page); - return -ENOMEM; - } - p = page; - while (p - page < 256) { - if (*p == '\0' || *p == '\n') { - *p = '\n'; - break; - } - p++; - - } - seq_write(m, page, p - page); - kfree(page); - return 0; -} - -static int mf_cmdline_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, mf_cmdline_proc_show, PDE(inode)->data); -} - -#if 0 -static int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side) -{ - struct vsp_cmd_data vsp_cmd; - int rc; - int len = *size; - dma_addr_t dma_addr; - - dma_addr = iseries_hv_map(buffer, len, DMA_FROM_DEVICE); - memset(buffer, 0, len); - memset(&vsp_cmd, 0, sizeof(vsp_cmd)); - vsp_cmd.cmd = 32; - vsp_cmd.sub_data.kern.token = dma_addr; - vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex; - vsp_cmd.sub_data.kern.side = side; - vsp_cmd.sub_data.kern.offset = offset; - vsp_cmd.sub_data.kern.length = len; - mb(); - rc = signal_vsp_instruction(&vsp_cmd); - if (rc == 0) { - if (vsp_cmd.result_code == 0) - *size = vsp_cmd.sub_data.length_out; - else - rc = -ENOMEM; - } - - iseries_hv_unmap(dma_addr, len, DMA_FROM_DEVICE); - - return rc; -} - -static int proc_mf_dump_vmlinux(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int sizeToGet = count; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (mf_getVmlinuxChunk(page, &sizeToGet, off, (u64)data) == 0) { - if (sizeToGet != 0) { - *start = page + off; - return sizeToGet; - } - *eof = 1; - return 0; - } - *eof = 1; - return 0; -} -#endif - -static int mf_side_proc_show(struct seq_file *m, void *v) -{ - char mf_current_side = ' '; - struct vsp_cmd_data vsp_cmd; - - memset(&vsp_cmd, 0, sizeof(vsp_cmd)); - vsp_cmd.cmd = 2; - vsp_cmd.sub_data.ipl_type = 0; - mb(); - - if (signal_vsp_instruction(&vsp_cmd) == 0) { - if (vsp_cmd.result_code == 0) { - switch (vsp_cmd.sub_data.ipl_type) { - case 0: mf_current_side = 'A'; - break; - case 1: mf_current_side = 'B'; - break; - case 2: mf_current_side = 'C'; - break; - default: mf_current_side = 'D'; - break; - } - } - } - - seq_printf(m, "%c\n", mf_current_side); - return 0; -} - -static int mf_side_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, mf_side_proc_show, NULL); -} - -static ssize_t mf_side_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - char side; - u64 newSide; - struct vsp_cmd_data vsp_cmd; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if (count == 0) - return 0; - - if (get_user(side, buffer)) - return -EFAULT; - - switch (side) { - case 'A': newSide = 0; - break; - case 'B': newSide = 1; - break; - case 'C': newSide = 2; - break; - case 'D': newSide = 3; - break; - default: - printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n"); - return -EINVAL; - } - - memset(&vsp_cmd, 0, sizeof(vsp_cmd)); - vsp_cmd.sub_data.ipl_type = newSide; - vsp_cmd.cmd = 10; - - (void)signal_vsp_instruction(&vsp_cmd); - - return count; -} - -static const struct file_operations mf_side_proc_fops = { - .owner = THIS_MODULE, - .open = mf_side_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = mf_side_proc_write, -}; - -static int mf_src_proc_show(struct seq_file *m, void *v) -{ - return 0; -} - -static int mf_src_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, mf_src_proc_show, NULL); -} - -static ssize_t mf_src_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - char stkbuf[10]; - - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - - if ((count < 4) && (count != 1)) { - printk(KERN_ERR "mf_proc: invalid src\n"); - return -EINVAL; - } - - if (count > (sizeof(stkbuf) - 1)) - count = sizeof(stkbuf) - 1; - if (copy_from_user(stkbuf, buffer, count)) - return -EFAULT; - - if ((count == 1) && (*stkbuf == '\0')) - mf_clear_src(); - else - mf_display_src(*(u32 *)stkbuf); - - return count; -} - -static const struct file_operations mf_src_proc_fops = { - .owner = THIS_MODULE, - .open = mf_src_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = mf_src_proc_write, -}; - -static ssize_t mf_cmdline_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - void *data = PDE(file->f_path.dentry->d_inode)->data; - struct vsp_cmd_data vsp_cmd; - dma_addr_t dma_addr; - char *page; - int ret = -EACCES; - - if (!capable(CAP_SYS_ADMIN)) - goto out; - - dma_addr = 0; - page = iseries_hv_alloc(count, &dma_addr, GFP_ATOMIC); - ret = -ENOMEM; - if (page == NULL) - goto out; - - ret = -EFAULT; - if (copy_from_user(page, buffer, count)) - goto out_free; - - memset(&vsp_cmd, 0, sizeof(vsp_cmd)); - vsp_cmd.cmd = 31; - vsp_cmd.sub_data.kern.token = dma_addr; - vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex; - vsp_cmd.sub_data.kern.side = (u64)data; - vsp_cmd.sub_data.kern.length = count; - mb(); - (void)signal_vsp_instruction(&vsp_cmd); - ret = count; - -out_free: - iseries_hv_free(count, page, dma_addr); -out: - return ret; -} - -static const struct file_operations mf_cmdline_proc_fops = { - .owner = THIS_MODULE, - .open = mf_cmdline_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = mf_cmdline_proc_write, -}; - -static ssize_t proc_mf_change_vmlinux(struct file *file, - const char __user *buf, - size_t count, loff_t *ppos) -{ - struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); - ssize_t rc; - dma_addr_t dma_addr; - char *page; - struct vsp_cmd_data vsp_cmd; - - rc = -EACCES; - if (!capable(CAP_SYS_ADMIN)) - goto out; - - dma_addr = 0; - page = iseries_hv_alloc(count, &dma_addr, GFP_ATOMIC); - rc = -ENOMEM; - if (page == NULL) { - printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n"); - goto out; - } - rc = -EFAULT; - if (copy_from_user(page, buf, count)) - goto out_free; - - memset(&vsp_cmd, 0, sizeof(vsp_cmd)); - vsp_cmd.cmd = 30; - vsp_cmd.sub_data.kern.token = dma_addr; - vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex; - vsp_cmd.sub_data.kern.side = (u64)dp->data; - vsp_cmd.sub_data.kern.offset = *ppos; - vsp_cmd.sub_data.kern.length = count; - mb(); - rc = signal_vsp_instruction(&vsp_cmd); - if (rc) - goto out_free; - rc = -ENOMEM; - if (vsp_cmd.result_code != 0) - goto out_free; - - *ppos += count; - rc = count; -out_free: - iseries_hv_free(count, page, dma_addr); -out: - return rc; -} - -static const struct file_operations proc_vmlinux_operations = { - .write = proc_mf_change_vmlinux, - .llseek = default_llseek, -}; - -static int __init mf_proc_init(void) -{ - struct proc_dir_entry *mf_proc_root; - struct proc_dir_entry *ent; - struct proc_dir_entry *mf; - char name[2]; - int i; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - - mf_proc_root = proc_mkdir("iSeries/mf", NULL); - if (!mf_proc_root) - return 1; - - name[1] = '\0'; - for (i = 0; i < 4; i++) { - name[0] = 'A' + i; - mf = proc_mkdir(name, mf_proc_root); - if (!mf) - return 1; - - ent = proc_create_data("cmdline", S_IRUSR|S_IWUSR, mf, - &mf_cmdline_proc_fops, (void *)(long)i); - if (!ent) - return 1; - - if (i == 3) /* no vmlinux entry for 'D' */ - continue; - - ent = proc_create_data("vmlinux", S_IFREG|S_IWUSR, mf, - &proc_vmlinux_operations, - (void *)(long)i); - if (!ent) - return 1; - } - - ent = proc_create("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root, - &mf_side_proc_fops); - if (!ent) - return 1; - - ent = proc_create("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root, - &mf_src_proc_fops); - if (!ent) - return 1; - - return 0; -} - -__initcall(mf_proc_init); - -#endif /* CONFIG_PROC_FS */ - -/* - * Get the RTC from the virtual service processor - * This requires flowing LpEvents to the primary partition - */ -void iSeries_get_rtc_time(struct rtc_time *rtc_tm) -{ - mf_get_rtc(rtc_tm); - rtc_tm->tm_mon--; -} - -/* - * Set the RTC in the virtual service processor - * This requires flowing LpEvents to the primary partition - */ -int iSeries_set_rtc_time(struct rtc_time *tm) -{ - mf_set_rtc(tm); - return 0; -} - -unsigned long iSeries_get_boot_time(void) -{ - struct rtc_time tm; - - mf_get_boot_rtc(&tm); - return mktime(tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); -} diff --git a/arch/powerpc/platforms/iseries/misc.S b/arch/powerpc/platforms/iseries/misc.S deleted file mode 100644 index 2c6ff0f..0000000 --- a/arch/powerpc/platforms/iseries/misc.S +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This file contains miscellaneous low-level functions. - * Copyright (C) 1995-2005 IBM Corp - * - * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) - * and Paul Mackerras. - * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) - * PPC64 updates by Dave Engebretsen (engebret@us.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 -#include -#include - - .text - -/* Handle pending interrupts in interrupt context */ -_GLOBAL(iseries_handle_interrupts) - li r0,0x5555 - sc - blr diff --git a/arch/powerpc/platforms/iseries/naca.h b/arch/powerpc/platforms/iseries/naca.h deleted file mode 100644 index f01708e..0000000 --- a/arch/powerpc/platforms/iseries/naca.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _PLATFORMS_ISERIES_NACA_H -#define _PLATFORMS_ISERIES_NACA_H - -/* - * c 2001 PPC 64 Team, IBM Corp - * - * 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 - -struct naca_struct { - /* Kernel only data - undefined for user space */ - const void *xItVpdAreas; /* VPD Data 0x00 */ - void *xRamDisk; /* iSeries ramdisk 0x08 */ - u64 xRamDiskSize; /* In pages 0x10 */ -}; - -extern struct naca_struct naca; - -#endif /* _PLATFORMS_ISERIES_NACA_H */ diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c deleted file mode 100644 index c754128..0000000 --- a/arch/powerpc/platforms/iseries/pci.c +++ /dev/null @@ -1,919 +0,0 @@ -/* - * Copyright (C) 2001 Allan Trautman, IBM Corporation - * Copyright (C) 2005,2007 Stephen Rothwell, IBM Corp - * - * iSeries specific routines for PCI. - * - * Based on code from pci.c and iSeries_pci.c 32bit - * - * 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. - * - * 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 - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "irq.h" -#include "pci.h" -#include "call_pci.h" - -#define PCI_RETRY_MAX 3 -static int limit_pci_retries = 1; /* Set Retry Error on. */ - -/* - * Table defines - * Each Entry size is 4 MB * 1024 Entries = 4GB I/O address space. - */ -#define IOMM_TABLE_MAX_ENTRIES 1024 -#define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL -#define BASE_IO_MEMORY 0xE000000000000000UL -#define END_IO_MEMORY 0xEFFFFFFFFFFFFFFFUL - -static unsigned long max_io_memory = BASE_IO_MEMORY; -static long current_iomm_table_entry; - -/* - * Lookup Tables. - */ -static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES]; -static u64 ds_addr_table[IOMM_TABLE_MAX_ENTRIES]; - -static DEFINE_SPINLOCK(iomm_table_lock); - -/* - * Generate a Direct Select Address for the Hypervisor - */ -static inline u64 iseries_ds_addr(struct device_node *node) -{ - struct pci_dn *pdn = PCI_DN(node); - const u32 *sbp = of_get_property(node, "linux,subbus", NULL); - - return ((u64)pdn->busno << 48) + ((u64)(sbp ? *sbp : 0) << 40) - + ((u64)0x10 << 32); -} - -/* - * Size of Bus VPD data - */ -#define BUS_VPDSIZE 1024 - -/* - * Bus Vpd Tags - */ -#define VPD_END_OF_AREA 0x79 -#define VPD_ID_STRING 0x82 -#define VPD_VENDOR_AREA 0x84 - -/* - * Mfg Area Tags - */ -#define VPD_FRU_FRAME_ID 0x4649 /* "FI" */ -#define VPD_SLOT_MAP_FORMAT 0x4D46 /* "MF" */ -#define VPD_SLOT_MAP 0x534D /* "SM" */ - -/* - * Structures of the areas - */ -struct mfg_vpd_area { - u16 tag; - u8 length; - u8 data1; - u8 data2; -}; -#define MFG_ENTRY_SIZE 3 - -struct slot_map { - u8 agent; - u8 secondary_agent; - u8 phb; - char card_location[3]; - char parms[8]; - char reserved[2]; -}; -#define SLOT_ENTRY_SIZE 16 - -/* - * Parse the Slot Area - */ -static void __init iseries_parse_slot_area(struct slot_map *map, int len, - HvAgentId agent, u8 *phb, char card[4]) -{ - /* - * Parse Slot label until we find the one requested - */ - while (len > 0) { - if (map->agent == agent) { - /* - * If Phb wasn't found, grab the entry first one found. - */ - if (*phb == 0xff) - *phb = map->phb; - /* Found it, extract the data. */ - if (map->phb == *phb) { - memcpy(card, &map->card_location, 3); - card[3] = 0; - break; - } - } - /* Point to the next Slot */ - map = (struct slot_map *)((char *)map + SLOT_ENTRY_SIZE); - len -= SLOT_ENTRY_SIZE; - } -} - -/* - * Parse the Mfg Area - */ -static void __init iseries_parse_mfg_area(struct mfg_vpd_area *area, int len, - HvAgentId agent, u8 *phb, u8 *frame, char card[4]) -{ - u16 slot_map_fmt = 0; - - /* Parse Mfg Data */ - while (len > 0) { - int mfg_tag_len = area->length; - /* Frame ID (FI 4649020310 ) */ - if (area->tag == VPD_FRU_FRAME_ID) - *frame = area->data1; - /* Slot Map Format (MF 4D46020004 ) */ - else if (area->tag == VPD_SLOT_MAP_FORMAT) - slot_map_fmt = (area->data1 * 256) - + area->data2; - /* Slot Map (SM 534D90 */ - else if (area->tag == VPD_SLOT_MAP) { - struct slot_map *slot_map; - - if (slot_map_fmt == 0x1004) - slot_map = (struct slot_map *)((char *)area - + MFG_ENTRY_SIZE + 1); - else - slot_map = (struct slot_map *)((char *)area - + MFG_ENTRY_SIZE); - iseries_parse_slot_area(slot_map, mfg_tag_len, - agent, phb, card); - } - /* - * Point to the next Mfg Area - * Use defined size, sizeof give wrong answer - */ - area = (struct mfg_vpd_area *)((char *)area + mfg_tag_len - + MFG_ENTRY_SIZE); - len -= (mfg_tag_len + MFG_ENTRY_SIZE); - } -} - -/* - * Look for "BUS".. Data is not Null terminated. - * PHBID of 0xFF indicates PHB was not found in VPD Data. - */ -static u8 __init iseries_parse_phbid(u8 *area, int len) -{ - while (len > 0) { - if ((*area == 'B') && (*(area + 1) == 'U') - && (*(area + 2) == 'S')) { - area += 3; - while (*area == ' ') - area++; - return *area & 0x0F; - } - area++; - len--; - } - return 0xff; -} - -/* - * Parse out the VPD Areas - */ -static void __init iseries_parse_vpd(u8 *data, int data_len, - HvAgentId agent, u8 *frame, char card[4]) -{ - u8 phb = 0xff; - - while (data_len > 0) { - int len; - u8 tag = *data; - - if (tag == VPD_END_OF_AREA) - break; - len = *(data + 1) + (*(data + 2) * 256); - data += 3; - data_len -= 3; - if (tag == VPD_ID_STRING) - phb = iseries_parse_phbid(data, len); - else if (tag == VPD_VENDOR_AREA) - iseries_parse_mfg_area((struct mfg_vpd_area *)data, len, - agent, &phb, frame, card); - /* Point to next Area. */ - data += len; - data_len -= len; - } -} - -static int __init iseries_get_location_code(u16 bus, HvAgentId agent, - u8 *frame, char card[4]) -{ - int status = 0; - int bus_vpd_len = 0; - u8 *bus_vpd = kmalloc(BUS_VPDSIZE, GFP_KERNEL); - - if (bus_vpd == NULL) { - printk("PCI: Bus VPD Buffer allocation failure.\n"); - return 0; - } - bus_vpd_len = HvCallPci_getBusVpd(bus, iseries_hv_addr(bus_vpd), - BUS_VPDSIZE); - if (bus_vpd_len == 0) { - printk("PCI: Bus VPD Buffer zero length.\n"); - goto out_free; - } - /* printk("PCI: bus_vpd: %p, %d\n",bus_vpd, bus_vpd_len); */ - /* Make sure this is what I think it is */ - if (*bus_vpd != VPD_ID_STRING) { - printk("PCI: Bus VPD Buffer missing starting tag.\n"); - goto out_free; - } - iseries_parse_vpd(bus_vpd, bus_vpd_len, agent, frame, card); - status = 1; -out_free: - kfree(bus_vpd); - return status; -} - -/* - * Prints the device information. - * - Pass in pci_dev* pointer to the device. - * - Pass in the device count - * - * Format: - * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet - * controller - */ -static void __init iseries_device_information(struct pci_dev *pdev, - u16 bus, HvSubBusNumber subbus) -{ - u8 frame = 0; - char card[4]; - HvAgentId agent; - - agent = ISERIES_PCI_AGENTID(ISERIES_GET_DEVICE_FROM_SUBBUS(subbus), - ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus)); - - if (iseries_get_location_code(bus, agent, &frame, card)) { - printk(KERN_INFO "PCI: %s, Vendor %04X Frame%3d, " - "Card %4s 0x%04X\n", pci_name(pdev), pdev->vendor, - frame, card, (int)(pdev->class >> 8)); - } -} - -/* - * iomm_table_allocate_entry - * - * Adds pci_dev entry in address translation table - * - * - Allocates the number of entries required in table base on BAR - * size. - * - Allocates starting at BASE_IO_MEMORY and increases. - * - The size is round up to be a multiple of entry size. - * - CurrentIndex is incremented to keep track of the last entry. - * - Builds the resource entry for allocated BARs. - */ -static void __init iomm_table_allocate_entry(struct pci_dev *dev, int bar_num) -{ - struct resource *bar_res = &dev->resource[bar_num]; - long bar_size = pci_resource_len(dev, bar_num); - struct device_node *dn = pci_device_to_OF_node(dev); - - /* - * No space to allocate, quick exit, skip Allocation. - */ - if (bar_size == 0) - return; - /* - * Set Resource values. - */ - spin_lock(&iomm_table_lock); - bar_res->start = BASE_IO_MEMORY + - IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; - bar_res->end = bar_res->start + bar_size - 1; - /* - * Allocate the number of table entries needed for BAR. - */ - while (bar_size > 0 ) { - iomm_table[current_iomm_table_entry] = dn; - ds_addr_table[current_iomm_table_entry] = - iseries_ds_addr(dn) | (bar_num << 24); - bar_size -= IOMM_TABLE_ENTRY_SIZE; - ++current_iomm_table_entry; - } - max_io_memory = BASE_IO_MEMORY + - IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; - spin_unlock(&iomm_table_lock); -} - -/* - * allocate_device_bars - * - * - Allocates ALL pci_dev BAR's and updates the resources with the - * BAR value. BARS with zero length will have the resources - * The HvCallPci_getBarParms is used to get the size of the BAR - * space. It calls iomm_table_allocate_entry to allocate - * each entry. - * - Loops through The Bar resources(0 - 5) including the ROM - * is resource(6). - */ -static void __init allocate_device_bars(struct pci_dev *dev) -{ - int bar_num; - - for (bar_num = 0; bar_num <= PCI_ROM_RESOURCE; ++bar_num) - iomm_table_allocate_entry(dev, bar_num); -} - -/* - * Log error information to system console. - * Filter out the device not there errors. - * PCI: EADs Connect Failed 0x18.58.10 Rc: 0x00xx - * PCI: Read Vendor Failed 0x18.58.10 Rc: 0x00xx - * PCI: Connect Bus Unit Failed 0x18.58.10 Rc: 0x00xx - */ -static void pci_log_error(char *error, int bus, int subbus, - int agent, int hv_res) -{ - if (hv_res == 0x0302) - return; - printk(KERN_ERR "PCI: %s Failed: 0x%02X.%02X.%02X Rc: 0x%04X", - error, bus, subbus, agent, hv_res); -} - -/* - * Look down the chain to find the matching Device Device - */ -static struct device_node *find_device_node(int bus, int devfn) -{ - struct device_node *node; - - for (node = NULL; (node = of_find_all_nodes(node)); ) { - struct pci_dn *pdn = PCI_DN(node); - - if (pdn && (bus == pdn->busno) && (devfn == pdn->devfn)) - return node; - } - return NULL; -} - -/* - * iSeries_pcibios_fixup_resources - * - * Fixes up all resources for devices - */ -void __init iSeries_pcibios_fixup_resources(struct pci_dev *pdev) -{ - const u32 *agent; - const u32 *sub_bus; - unsigned char bus = pdev->bus->number; - struct device_node *node; - int i; - - node = pci_device_to_OF_node(pdev); - pr_debug("PCI: iSeries %s, pdev %p, node %p\n", - pci_name(pdev), pdev, node); - if (!node) { - printk("PCI: %s disabled, device tree entry not found !\n", - pci_name(pdev)); - for (i = 0; i <= PCI_ROM_RESOURCE; i++) - pdev->resource[i].flags = 0; - return; - } - sub_bus = of_get_property(node, "linux,subbus", NULL); - agent = of_get_property(node, "linux,agent-id", NULL); - if (agent && sub_bus) { - u8 irq = iSeries_allocate_IRQ(bus, 0, *sub_bus); - int err; - - err = HvCallXm_connectBusUnit(bus, *sub_bus, *agent, irq); - if (err) - pci_log_error("Connect Bus Unit", - bus, *sub_bus, *agent, err); - else { - err = HvCallPci_configStore8(bus, *sub_bus, - *agent, PCI_INTERRUPT_LINE, irq); - if (err) - pci_log_error("PciCfgStore Irq Failed!", - bus, *sub_bus, *agent, err); - else - pdev->irq = irq; - } - } - - allocate_device_bars(pdev); - if (likely(sub_bus)) - iseries_device_information(pdev, bus, *sub_bus); - else - printk(KERN_ERR "PCI: Device node %s has missing or invalid " - "linux,subbus property\n", node->full_name); -} - -/* - * iSeries_pci_final_fixup(void) - */ -void __init iSeries_pci_final_fixup(void) -{ - /* Fix up at the device node and pci_dev relationship */ - mf_display_src(0xC9000100); - iSeries_activate_IRQs(); - mf_display_src(0xC9000200); -} - -/* - * Config space read and write functions. - * For now at least, we look for the device node for the bus and devfn - * that we are asked to access. It may be possible to translate the devfn - * to a subbus and deviceid more directly. - */ -static u64 hv_cfg_read_func[4] = { - HvCallPciConfigLoad8, HvCallPciConfigLoad16, - HvCallPciConfigLoad32, HvCallPciConfigLoad32 -}; - -static u64 hv_cfg_write_func[4] = { - HvCallPciConfigStore8, HvCallPciConfigStore16, - HvCallPciConfigStore32, HvCallPciConfigStore32 -}; - -/* - * Read PCI config space - */ -static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn, - int offset, int size, u32 *val) -{ - struct device_node *node = find_device_node(bus->number, devfn); - u64 fn; - struct HvCallPci_LoadReturn ret; - - if (node == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - if (offset > 255) { - *val = ~0; - return PCIBIOS_BAD_REGISTER_NUMBER; - } - - fn = hv_cfg_read_func[(size - 1) & 3]; - HvCall3Ret16(fn, &ret, iseries_ds_addr(node), offset, 0); - - if (ret.rc != 0) { - *val = ~0; - return PCIBIOS_DEVICE_NOT_FOUND; /* or something */ - } - - *val = ret.value; - return 0; -} - -/* - * Write PCI config space - */ - -static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn, - int offset, int size, u32 val) -{ - struct device_node *node = find_device_node(bus->number, devfn); - u64 fn; - u64 ret; - - if (node == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - if (offset > 255) - return PCIBIOS_BAD_REGISTER_NUMBER; - - fn = hv_cfg_write_func[(size - 1) & 3]; - ret = HvCall4(fn, iseries_ds_addr(node), offset, val, 0); - - if (ret != 0) - return PCIBIOS_DEVICE_NOT_FOUND; - - return 0; -} - -static struct pci_ops iSeries_pci_ops = { - .read = iSeries_pci_read_config, - .write = iSeries_pci_write_config -}; - -/* - * Check Return Code - * -> On Failure, print and log information. - * Increment Retry Count, if exceeds max, panic partition. - * - * PCI: Device 23.90 ReadL I/O Error( 0): 0x1234 - * PCI: Device 23.90 ReadL Retry( 1) - * PCI: Device 23.90 ReadL Retry Successful(1) - */ -static int check_return_code(char *type, struct device_node *dn, - int *retry, u64 ret) -{ - if (ret != 0) { - struct pci_dn *pdn = PCI_DN(dn); - - (*retry)++; - printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n", - type, pdn->busno, pdn->devfn, - *retry, (int)ret); - /* - * Bump the retry and check for retry count exceeded. - * If, Exceeded, panic the system. - */ - if (((*retry) > PCI_RETRY_MAX) && - (limit_pci_retries > 0)) { - mf_display_src(0xB6000103); - panic_timeout = 0; - panic("PCI: Hardware I/O Error, SRC B6000103, " - "Automatic Reboot Disabled.\n"); - } - return -1; /* Retry Try */ - } - return 0; -} - -/* - * Translate the I/O Address into a device node, bar, and bar offset. - * Note: Make sure the passed variable end up on the stack to avoid - * the exposure of being device global. - */ -static inline struct device_node *xlate_iomm_address( - const volatile void __iomem *addr, - u64 *dsaptr, u64 *bar_offset, const char *func) -{ - unsigned long orig_addr; - unsigned long base_addr; - unsigned long ind; - struct device_node *dn; - - orig_addr = (unsigned long __force)addr; - if ((orig_addr < BASE_IO_MEMORY) || (orig_addr >= max_io_memory)) { - static DEFINE_RATELIMIT_STATE(ratelimit, 60 * HZ, 10); - - if (__ratelimit(&ratelimit)) - printk(KERN_ERR - "iSeries_%s: invalid access at IO address %p\n", - func, addr); - return NULL; - } - base_addr = orig_addr - BASE_IO_MEMORY; - ind = base_addr / IOMM_TABLE_ENTRY_SIZE; - dn = iomm_table[ind]; - - if (dn != NULL) { - *dsaptr = ds_addr_table[ind]; - *bar_offset = base_addr % IOMM_TABLE_ENTRY_SIZE; - } else - panic("PCI: Invalid PCI IO address detected!\n"); - return dn; -} - -/* - * Read MM I/O Instructions for the iSeries - * On MM I/O error, all ones are returned and iSeries_pci_IoError is cal - * else, data is returned in Big Endian format. - */ -static u8 iseries_readb(const volatile void __iomem *addr) -{ - u64 bar_offset; - u64 dsa; - int retry = 0; - struct HvCallPci_LoadReturn ret; - struct device_node *dn = - xlate_iomm_address(addr, &dsa, &bar_offset, "read_byte"); - - if (dn == NULL) - return 0xff; - do { - HvCall3Ret16(HvCallPciBarLoad8, &ret, dsa, bar_offset, 0); - } while (check_return_code("RDB", dn, &retry, ret.rc) != 0); - - return ret.value; -} - -static u16 iseries_readw_be(const volatile void __iomem *addr) -{ - u64 bar_offset; - u64 dsa; - int retry = 0; - struct HvCallPci_LoadReturn ret; - struct device_node *dn = - xlate_iomm_address(addr, &dsa, &bar_offset, "read_word"); - - if (dn == NULL) - return 0xffff; - do { - HvCall3Ret16(HvCallPciBarLoad16, &ret, dsa, - bar_offset, 0); - } while (check_return_code("RDW", dn, &retry, ret.rc) != 0); - - return ret.value; -} - -static u32 iseries_readl_be(const volatile void __iomem *addr) -{ - u64 bar_offset; - u64 dsa; - int retry = 0; - struct HvCallPci_LoadReturn ret; - struct device_node *dn = - xlate_iomm_address(addr, &dsa, &bar_offset, "read_long"); - - if (dn == NULL) - return 0xffffffff; - do { - HvCall3Ret16(HvCallPciBarLoad32, &ret, dsa, - bar_offset, 0); - } while (check_return_code("RDL", dn, &retry, ret.rc) != 0); - - return ret.value; -} - -/* - * Write MM I/O Instructions for the iSeries - * - */ -static void iseries_writeb(u8 data, volatile void __iomem *addr) -{ - u64 bar_offset; - u64 dsa; - int retry = 0; - u64 rc; - struct device_node *dn = - xlate_iomm_address(addr, &dsa, &bar_offset, "write_byte"); - - if (dn == NULL) - return; - do { - rc = HvCall4(HvCallPciBarStore8, dsa, bar_offset, data, 0); - } while (check_return_code("WWB", dn, &retry, rc) != 0); -} - -static void iseries_writew_be(u16 data, volatile void __iomem *addr) -{ - u64 bar_offset; - u64 dsa; - int retry = 0; - u64 rc; - struct device_node *dn = - xlate_iomm_address(addr, &dsa, &bar_offset, "write_word"); - - if (dn == NULL) - return; - do { - rc = HvCall4(HvCallPciBarStore16, dsa, bar_offset, data, 0); - } while (check_return_code("WWW", dn, &retry, rc) != 0); -} - -static void iseries_writel_be(u32 data, volatile void __iomem *addr) -{ - u64 bar_offset; - u64 dsa; - int retry = 0; - u64 rc; - struct device_node *dn = - xlate_iomm_address(addr, &dsa, &bar_offset, "write_long"); - - if (dn == NULL) - return; - do { - rc = HvCall4(HvCallPciBarStore32, dsa, bar_offset, data, 0); - } while (check_return_code("WWL", dn, &retry, rc) != 0); -} - -static u16 iseries_readw(const volatile void __iomem *addr) -{ - return le16_to_cpu(iseries_readw_be(addr)); -} - -static u32 iseries_readl(const volatile void __iomem *addr) -{ - return le32_to_cpu(iseries_readl_be(addr)); -} - -static void iseries_writew(u16 data, volatile void __iomem *addr) -{ - iseries_writew_be(cpu_to_le16(data), addr); -} - -static void iseries_writel(u32 data, volatile void __iomem *addr) -{ - iseries_writel(cpu_to_le32(data), addr); -} - -static void iseries_readsb(const volatile void __iomem *addr, void *buf, - unsigned long count) -{ - u8 *dst = buf; - while(count-- > 0) - *(dst++) = iseries_readb(addr); -} - -static void iseries_readsw(const volatile void __iomem *addr, void *buf, - unsigned long count) -{ - u16 *dst = buf; - while(count-- > 0) - *(dst++) = iseries_readw_be(addr); -} - -static void iseries_readsl(const volatile void __iomem *addr, void *buf, - unsigned long count) -{ - u32 *dst = buf; - while(count-- > 0) - *(dst++) = iseries_readl_be(addr); -} - -static void iseries_writesb(volatile void __iomem *addr, const void *buf, - unsigned long count) -{ - const u8 *src = buf; - while(count-- > 0) - iseries_writeb(*(src++), addr); -} - -static void iseries_writesw(volatile void __iomem *addr, const void *buf, - unsigned long count) -{ - const u16 *src = buf; - while(count-- > 0) - iseries_writew_be(*(src++), addr); -} - -static void iseries_writesl(volatile void __iomem *addr, const void *buf, - unsigned long count) -{ - const u32 *src = buf; - while(count-- > 0) - iseries_writel_be(*(src++), addr); -} - -static void iseries_memset_io(volatile void __iomem *addr, int c, - unsigned long n) -{ - volatile char __iomem *d = addr; - - while (n-- > 0) - iseries_writeb(c, d++); -} - -static void iseries_memcpy_fromio(void *dest, const volatile void __iomem *src, - unsigned long n) -{ - char *d = dest; - const volatile char __iomem *s = src; - - while (n-- > 0) - *d++ = iseries_readb(s++); -} - -static void iseries_memcpy_toio(volatile void __iomem *dest, const void *src, - unsigned long n) -{ - const char *s = src; - volatile char __iomem *d = dest; - - while (n-- > 0) - iseries_writeb(*s++, d++); -} - -/* We only set MMIO ops. The default PIO ops will be default - * to the MMIO ops + pci_io_base which is 0 on iSeries as - * expected so both should work. - * - * Note that we don't implement the readq/writeq versions as - * I don't know of an HV call for doing so. Thus, the default - * operation will be used instead, which will fault a the value - * return by iSeries for MMIO addresses always hits a non mapped - * area. This is as good as the BUG() we used to have there. - */ -static struct ppc_pci_io __initdata iseries_pci_io = { - .readb = iseries_readb, - .readw = iseries_readw, - .readl = iseries_readl, - .readw_be = iseries_readw_be, - .readl_be = iseries_readl_be, - .writeb = iseries_writeb, - .writew = iseries_writew, - .writel = iseries_writel, - .writew_be = iseries_writew_be, - .writel_be = iseries_writel_be, - .readsb = iseries_readsb, - .readsw = iseries_readsw, - .readsl = iseries_readsl, - .writesb = iseries_writesb, - .writesw = iseries_writesw, - .writesl = iseries_writesl, - .memset_io = iseries_memset_io, - .memcpy_fromio = iseries_memcpy_fromio, - .memcpy_toio = iseries_memcpy_toio, -}; - -/* - * iSeries_pcibios_init - * - * Description: - * This function checks for all possible system PCI host bridges that connect - * PCI buses. The system hypervisor is queried as to the guest partition - * ownership status. A pci_controller is built for any bus which is partially - * owned or fully owned by this guest partition. - */ -void __init iSeries_pcibios_init(void) -{ - struct pci_controller *phb; - struct device_node *root = of_find_node_by_path("/"); - struct device_node *node = NULL; - - /* Install IO hooks */ - ppc_pci_io = iseries_pci_io; - - pci_probe_only = 1; - - /* iSeries has no IO space in the common sense, it needs to set - * the IO base to 0 - */ - pci_io_base = 0; - - if (root == NULL) { - printk(KERN_CRIT "iSeries_pcibios_init: can't find root " - "of device tree\n"); - return; - } - while ((node = of_get_next_child(root, node)) != NULL) { - HvBusNumber bus; - const u32 *busp; - - if ((node->type == NULL) || (strcmp(node->type, "pci") != 0)) - continue; - - busp = of_get_property(node, "bus-range", NULL); - if (busp == NULL) - continue; - bus = *busp; - printk("bus %d appears to exist\n", bus); - phb = pcibios_alloc_controller(node); - if (phb == NULL) - continue; - /* All legacy iSeries PHBs are in domain zero */ - phb->global_number = 0; - - phb->first_busno = bus; - phb->last_busno = bus; - phb->ops = &iSeries_pci_ops; - phb->io_base_virt = (void __iomem *)_IO_BASE; - phb->io_resource.flags = IORESOURCE_IO; - phb->io_resource.start = BASE_IO_MEMORY; - phb->io_resource.end = END_IO_MEMORY; - phb->io_resource.name = "iSeries PCI IO"; - phb->mem_resources[0].flags = IORESOURCE_MEM; - phb->mem_resources[0].start = BASE_IO_MEMORY; - phb->mem_resources[0].end = END_IO_MEMORY; - phb->mem_resources[0].name = "Series PCI MEM"; - } - - of_node_put(root); - - pci_devs_phb_init(); -} - diff --git a/arch/powerpc/platforms/iseries/pci.h b/arch/powerpc/platforms/iseries/pci.h deleted file mode 100644 index d9cf974..0000000 --- a/arch/powerpc/platforms/iseries/pci.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _PLATFORMS_ISERIES_PCI_H -#define _PLATFORMS_ISERIES_PCI_H - -/* - * Created by Allan Trautman on Tue Feb 20, 2001. - * - * Define some useful macros for the iSeries pci routines. - * Copyright (C) 2001 Allan H Trautman, IBM Corporation - * - * 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. - * - * 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 - * - * Change Activity: - * Created Feb 20, 2001 - * Added device reset, March 22, 2001 - * Ported to ppc64, May 25, 2001 - * End Change Activity - */ - -/* - * Decodes Linux DevFn to iSeries DevFn, bridge device, or function. - * For Linux, see PCI_SLOT and PCI_FUNC in include/linux/pci.h - */ - -#define ISERIES_PCI_AGENTID(idsel, func) \ - (((idsel & 0x0F) << 4) | (func & 0x07)) -#define ISERIES_ENCODE_DEVICE(agentid) \ - ((0x10) | ((agentid & 0x20) >> 2) | (agentid & 0x07)) - -#define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7) -#define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7) - -struct pci_dev; - -#ifdef CONFIG_PCI -extern void iSeries_pcibios_init(void); -extern void iSeries_pci_final_fixup(void); -extern void iSeries_pcibios_fixup_resources(struct pci_dev *dev); -#else -static inline void iSeries_pcibios_init(void) { } -static inline void iSeries_pci_final_fixup(void) { } -static inline void iSeries_pcibios_fixup_resources(struct pci_dev *dev) {} -#endif - -#endif /* _PLATFORMS_ISERIES_PCI_H */ diff --git a/arch/powerpc/platforms/iseries/proc.c b/arch/powerpc/platforms/iseries/proc.c deleted file mode 100644 index 0676368..0000000 --- a/arch/powerpc/platforms/iseries/proc.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2001 Kyle A. Lucke IBM Corporation - * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen IBM Corporation - * - * 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. - * - * 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 -#include -#include -#include /* for HZ */ -#include -#include -#include -#include -#include -#include - -#include "processor_vpd.h" -#include "main_store.h" - -static int __init iseries_proc_create(void) -{ - struct proc_dir_entry *e; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - - e = proc_mkdir("iSeries", 0); - if (!e) - return 1; - - return 0; -} -core_initcall(iseries_proc_create); - -static unsigned long startTitan = 0; -static unsigned long startTb = 0; - -static int proc_titantod_show(struct seq_file *m, void *v) -{ - unsigned long tb0, titan_tod; - - tb0 = get_tb(); - titan_tod = HvCallXm_loadTod(); - - seq_printf(m, "Titan\n" ); - seq_printf(m, " time base = %016lx\n", tb0); - seq_printf(m, " titan tod = %016lx\n", titan_tod); - seq_printf(m, " xProcFreq = %016x\n", - xIoHriProcessorVpd[0].xProcFreq); - seq_printf(m, " xTimeBaseFreq = %016x\n", - xIoHriProcessorVpd[0].xTimeBaseFreq); - seq_printf(m, " tb_ticks_per_jiffy = %lu\n", tb_ticks_per_jiffy); - seq_printf(m, " tb_ticks_per_usec = %lu\n", tb_ticks_per_usec); - - if (!startTitan) { - startTitan = titan_tod; - startTb = tb0; - } else { - unsigned long titan_usec = (titan_tod - startTitan) >> 12; - unsigned long tb_ticks = (tb0 - startTb); - unsigned long titan_jiffies = titan_usec / (1000000/HZ); - unsigned long titan_jiff_usec = titan_jiffies * (1000000/HZ); - unsigned long titan_jiff_rem_usec = - titan_usec - titan_jiff_usec; - unsigned long tb_jiffies = tb_ticks / tb_ticks_per_jiffy; - unsigned long tb_jiff_ticks = tb_jiffies * tb_ticks_per_jiffy; - unsigned long tb_jiff_rem_ticks = tb_ticks - tb_jiff_ticks; - unsigned long tb_jiff_rem_usec = - tb_jiff_rem_ticks / tb_ticks_per_usec; - unsigned long new_tb_ticks_per_jiffy = - (tb_ticks * (1000000/HZ))/titan_usec; - - seq_printf(m, " titan elapsed = %lu uSec\n", titan_usec); - seq_printf(m, " tb elapsed = %lu ticks\n", tb_ticks); - seq_printf(m, " titan jiffies = %lu.%04lu\n", titan_jiffies, - titan_jiff_rem_usec); - seq_printf(m, " tb jiffies = %lu.%04lu\n", tb_jiffies, - tb_jiff_rem_usec); - seq_printf(m, " new tb_ticks_per_jiffy = %lu\n", - new_tb_ticks_per_jiffy); - } - - return 0; -} - -static int proc_titantod_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_titantod_show, NULL); -} - -static const struct file_operations proc_titantod_operations = { - .open = proc_titantod_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init iseries_proc_init(void) -{ - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - - proc_create("iSeries/titanTod", S_IFREG|S_IRUGO, NULL, - &proc_titantod_operations); - return 0; -} -__initcall(iseries_proc_init); diff --git a/arch/powerpc/platforms/iseries/processor_vpd.h b/arch/powerpc/platforms/iseries/processor_vpd.h deleted file mode 100644 index 7ac5d0d..0000000 --- a/arch/powerpc/platforms/iseries/processor_vpd.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ISERIES_PROCESSOR_VPD_H -#define _ISERIES_PROCESSOR_VPD_H - -#include - -/* - * This struct maps Processor Vpd that is DMAd to SLIC by CSP - */ -struct IoHriProcessorVpd { - u8 xFormat; // VPD format indicator x00-x00 - u8 xProcStatus:8; // Processor State x01-x01 - u8 xSecondaryThreadCount; // Secondary thread cnt x02-x02 - u8 xSrcType:1; // Src Type x03-x03 - u8 xSrcSoft:1; // Src stay soft ... - u8 xSrcParable:1; // Src parable ... - u8 xRsvd1:5; // Reserved ... - u16 xHvPhysicalProcIndex; // Hypervisor physical proc index04-x05 - u16 xRsvd2; // Reserved x06-x07 - u32 xHwNodeId; // Hardware node id x08-x0B - u32 xHwProcId; // Hardware processor id x0C-x0F - - u32 xTypeNum; // Card Type/CCIN number x10-x13 - u32 xModelNum; // Model/Feature number x14-x17 - u64 xSerialNum; // Serial number x18-x1F - char xPartNum[12]; // Book Part or FPU number x20-x2B - char xMfgID[4]; // Manufacturing ID x2C-x2F - - u32 xProcFreq; // Processor Frequency x30-x33 - u32 xTimeBaseFreq; // Time Base Frequency x34-x37 - - u32 xChipEcLevel; // Chip EC Levels x38-x3B - u32 xProcIdReg; // PIR SPR value x3C-x3F - u32 xPVR; // PVR value x40-x43 - u8 xRsvd3[12]; // Reserved x44-x4F - - u32 xInstCacheSize; // Instruction cache size in KB x50-x53 - u32 xInstBlockSize; // Instruction cache block size x54-x57 - u32 xDataCacheOperandSize; // Data cache operand size x58-x5B - u32 xInstCacheOperandSize; // Inst cache operand size x5C-x5F - - u32 xDataL1CacheSizeKB; // L1 data cache size in KB x60-x63 - u32 xDataL1CacheLineSize; // L1 data cache block size x64-x67 - u64 xRsvd4; // Reserved x68-x6F - - u32 xDataL2CacheSizeKB; // L2 data cache size in KB x70-x73 - u32 xDataL2CacheLineSize; // L2 data cache block size x74-x77 - u64 xRsvd5; // Reserved x78-x7F - - u32 xDataL3CacheSizeKB; // L3 data cache size in KB x80-x83 - u32 xDataL3CacheLineSize; // L3 data cache block size x84-x87 - u64 xRsvd6; // Reserved x88-x8F - - u64 xFruLabel; // Card Location Label x90-x97 - u8 xSlotsOnCard; // Slots on card (0=no slots) x98-x98 - u8 xPartLocFlag; // Location flag (0-pluggable 1-imbedded) x99-x99 - u16 xSlotMapIndex; // Index in slot map table x9A-x9B - u8 xSmartCardPortNo; // Smart card port number x9C-x9C - u8 xRsvd7; // Reserved x9D-x9D - u16 xFrameIdAndRackUnit; // Frame ID and rack unit adr x9E-x9F - - u8 xRsvd8[24]; // Reserved xA0-xB7 - - char xProcSrc[72]; // CSP format SRC xB8-xFF -}; - -extern struct IoHriProcessorVpd xIoHriProcessorVpd[]; - -#endif /* _ISERIES_PROCESSOR_VPD_H */ diff --git a/arch/powerpc/platforms/iseries/release_data.h b/arch/powerpc/platforms/iseries/release_data.h deleted file mode 100644 index 6ad7d84..0000000 --- a/arch/powerpc/platforms/iseries/release_data.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ISERIES_RELEASE_DATA_H -#define _ISERIES_RELEASE_DATA_H - -/* - * This control block contains the critical information about the - * release so that it can be changed in the future (ie, the virtual - * address of the OS's NACA). - */ -#include -#include "naca.h" - -/* - * When we IPL a secondary partition, we will check if if the - * secondary xMinPlicVrmIndex > the primary xVrmIndex. - * If it is then this tells PLIC that this secondary is not - * supported running on this "old" of a level of PLIC. - * - * Likewise, we will compare the primary xMinSlicVrmIndex to - * the secondary xVrmIndex. - * If the primary xMinSlicVrmDelta > secondary xVrmDelta then we - * know that this PLIC does not support running an OS "that old". - */ - -#define HVREL_TAGSINACTIVE 0x8000 -#define HVREL_32BIT 0x4000 -#define HVREL_NOSHAREDPROCS 0x2000 -#define HVREL_NOHMT 0x1000 - -struct HvReleaseData { - u32 xDesc; /* Descriptor "HvRD" ebcdic x00-x03 */ - u16 xSize; /* Size of this control block x04-x05 */ - u16 xVpdAreasPtrOffset; /* Offset in NACA of ItVpdAreas x06-x07 */ - struct naca_struct *xSlicNacaAddr; /* Virt addr of SLIC NACA x08-x0F */ - u32 xMsNucDataOffset; /* Offset of Linux Mapping Data x10-x13 */ - u32 xRsvd1; /* Reserved x14-x17 */ - u16 xFlags; - u16 xVrmIndex; /* VRM Index of OS image x1A-x1B */ - u16 xMinSupportedPlicVrmIndex; /* Min PLIC level (soft) x1C-x1D */ - u16 xMinCompatablePlicVrmIndex; /* Min PLIC levelP (hard) x1E-x1F */ - char xVrmName[12]; /* Displayable name x20-x2B */ - char xRsvd3[20]; /* Reserved x2C-x3F */ -}; - -extern const struct HvReleaseData hvReleaseData; - -#endif /* _ISERIES_RELEASE_DATA_H */ diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c deleted file mode 100644 index 8fc6258..0000000 --- a/arch/powerpc/platforms/iseries/setup.c +++ /dev/null @@ -1,722 +0,0 @@ -/* - * Copyright (c) 2000 Mike Corrigan - * Copyright (c) 1999-2000 Grant Erickson - * - * Description: - * Architecture- / platform-specific boot-time initialization code for - * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - * 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. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "naca.h" -#include "setup.h" -#include "irq.h" -#include "vpd_areas.h" -#include "processor_vpd.h" -#include "it_lp_naca.h" -#include "main_store.h" -#include "call_sm.h" -#include "call_hpt.h" -#include "pci.h" - -#ifdef DEBUG -#define DBG(fmt...) udbg_printf(fmt) -#else -#define DBG(fmt...) -#endif - -/* Function Prototypes */ -static unsigned long build_iSeries_Memory_Map(void); -static void iseries_shared_idle(void); -static void iseries_dedicated_idle(void); - - -struct MemoryBlock { - unsigned long absStart; - unsigned long absEnd; - unsigned long logicalStart; - unsigned long logicalEnd; -}; - -/* - * Process the main store vpd to determine where the holes in memory are - * and return the number of physical blocks and fill in the array of - * block data. - */ -static unsigned long iSeries_process_Condor_mainstore_vpd( - struct MemoryBlock *mb_array, unsigned long max_entries) -{ - unsigned long holeFirstChunk, holeSizeChunks; - unsigned long numMemoryBlocks = 1; - struct IoHriMainStoreSegment4 *msVpd = - (struct IoHriMainStoreSegment4 *)xMsVpd; - unsigned long holeStart = msVpd->nonInterleavedBlocksStartAdr; - unsigned long holeEnd = msVpd->nonInterleavedBlocksEndAdr; - unsigned long holeSize = holeEnd - holeStart; - - printk("Mainstore_VPD: Condor\n"); - /* - * Determine if absolute memory has any - * holes so that we can interpret the - * access map we get back from the hypervisor - * correctly. - */ - mb_array[0].logicalStart = 0; - mb_array[0].logicalEnd = 0x100000000UL; - mb_array[0].absStart = 0; - mb_array[0].absEnd = 0x100000000UL; - - if (holeSize) { - numMemoryBlocks = 2; - holeStart = holeStart & 0x000fffffffffffffUL; - holeStart = addr_to_chunk(holeStart); - holeFirstChunk = holeStart; - holeSize = addr_to_chunk(holeSize); - holeSizeChunks = holeSize; - printk( "Main store hole: start chunk = %0lx, size = %0lx chunks\n", - holeFirstChunk, holeSizeChunks ); - mb_array[0].logicalEnd = holeFirstChunk; - mb_array[0].absEnd = holeFirstChunk; - mb_array[1].logicalStart = holeFirstChunk; - mb_array[1].logicalEnd = 0x100000000UL - holeSizeChunks; - mb_array[1].absStart = holeFirstChunk + holeSizeChunks; - mb_array[1].absEnd = 0x100000000UL; - } - return numMemoryBlocks; -} - -#define MaxSegmentAreas 32 -#define MaxSegmentAdrRangeBlocks 128 -#define MaxAreaRangeBlocks 4 - -static unsigned long iSeries_process_Regatta_mainstore_vpd( - struct MemoryBlock *mb_array, unsigned long max_entries) -{ - struct IoHriMainStoreSegment5 *msVpdP = - (struct IoHriMainStoreSegment5 *)xMsVpd; - unsigned long numSegmentBlocks = 0; - u32 existsBits = msVpdP->msAreaExists; - unsigned long area_num; - - printk("Mainstore_VPD: Regatta\n"); - - for (area_num = 0; area_num < MaxSegmentAreas; ++area_num ) { - unsigned long numAreaBlocks; - struct IoHriMainStoreArea4 *currentArea; - - if (existsBits & 0x80000000) { - unsigned long block_num; - - currentArea = &msVpdP->msAreaArray[area_num]; - numAreaBlocks = currentArea->numAdrRangeBlocks; - printk("ms_vpd: processing area %2ld blocks=%ld", - area_num, numAreaBlocks); - for (block_num = 0; block_num < numAreaBlocks; - ++block_num ) { - /* Process an address range block */ - struct MemoryBlock tempBlock; - unsigned long i; - - tempBlock.absStart = - (unsigned long)currentArea->xAdrRangeBlock[block_num].blockStart; - tempBlock.absEnd = - (unsigned long)currentArea->xAdrRangeBlock[block_num].blockEnd; - tempBlock.logicalStart = 0; - tempBlock.logicalEnd = 0; - printk("\n block %ld absStart=%016lx absEnd=%016lx", - block_num, tempBlock.absStart, - tempBlock.absEnd); - - for (i = 0; i < numSegmentBlocks; ++i) { - if (mb_array[i].absStart == - tempBlock.absStart) - break; - } - if (i == numSegmentBlocks) { - if (numSegmentBlocks == max_entries) - panic("iSeries_process_mainstore_vpd: too many memory blocks"); - mb_array[numSegmentBlocks] = tempBlock; - ++numSegmentBlocks; - } else - printk(" (duplicate)"); - } - printk("\n"); - } - existsBits <<= 1; - } - /* Now sort the blocks found into ascending sequence */ - if (numSegmentBlocks > 1) { - unsigned long m, n; - - for (m = 0; m < numSegmentBlocks - 1; ++m) { - for (n = numSegmentBlocks - 1; m < n; --n) { - if (mb_array[n].absStart < - mb_array[n-1].absStart) { - struct MemoryBlock tempBlock; - - tempBlock = mb_array[n]; - mb_array[n] = mb_array[n-1]; - mb_array[n-1] = tempBlock; - } - } - } - } - /* - * Assign "logical" addresses to each block. These - * addresses correspond to the hypervisor "bitmap" space. - * Convert all addresses into units of 256K chunks. - */ - { - unsigned long i, nextBitmapAddress; - - printk("ms_vpd: %ld sorted memory blocks\n", numSegmentBlocks); - nextBitmapAddress = 0; - for (i = 0; i < numSegmentBlocks; ++i) { - unsigned long length = mb_array[i].absEnd - - mb_array[i].absStart; - - mb_array[i].logicalStart = nextBitmapAddress; - mb_array[i].logicalEnd = nextBitmapAddress + length; - nextBitmapAddress += length; - printk(" Bitmap range: %016lx - %016lx\n" - " Absolute range: %016lx - %016lx\n", - mb_array[i].logicalStart, - mb_array[i].logicalEnd, - mb_array[i].absStart, mb_array[i].absEnd); - mb_array[i].absStart = addr_to_chunk(mb_array[i].absStart & - 0x000fffffffffffffUL); - mb_array[i].absEnd = addr_to_chunk(mb_array[i].absEnd & - 0x000fffffffffffffUL); - mb_array[i].logicalStart = - addr_to_chunk(mb_array[i].logicalStart); - mb_array[i].logicalEnd = addr_to_chunk(mb_array[i].logicalEnd); - } - } - - return numSegmentBlocks; -} - -static unsigned long iSeries_process_mainstore_vpd(struct MemoryBlock *mb_array, - unsigned long max_entries) -{ - unsigned long i; - unsigned long mem_blocks = 0; - - if (mmu_has_feature(MMU_FTR_SLB)) - mem_blocks = iSeries_process_Regatta_mainstore_vpd(mb_array, - max_entries); - else - mem_blocks = iSeries_process_Condor_mainstore_vpd(mb_array, - max_entries); - - printk("Mainstore_VPD: numMemoryBlocks = %ld\n", mem_blocks); - for (i = 0; i < mem_blocks; ++i) { - printk("Mainstore_VPD: block %3ld logical chunks %016lx - %016lx\n" - " abs chunks %016lx - %016lx\n", - i, mb_array[i].logicalStart, mb_array[i].logicalEnd, - mb_array[i].absStart, mb_array[i].absEnd); - } - return mem_blocks; -} - -static void __init iSeries_get_cmdline(void) -{ - char *p, *q; - - /* copy the command line parameter from the primary VSP */ - HvCallEvent_dmaToSp(cmd_line, 2 * 64* 1024, 256, - HvLpDma_Direction_RemoteToLocal); - - p = cmd_line; - q = cmd_line + 255; - while(p < q) { - if (!*p || *p == '\n') - break; - ++p; - } - *p = 0; -} - -static void __init iSeries_init_early(void) -{ - DBG(" -> iSeries_init_early()\n"); - - /* Snapshot the timebase, for use in later recalibration */ - iSeries_time_init_early(); - - /* - * Initialize the DMA/TCE management - */ - iommu_init_early_iSeries(); - - /* Initialize machine-dependency vectors */ -#ifdef CONFIG_SMP - smp_init_iSeries(); -#endif - - /* Associate Lp Event Queue 0 with processor 0 */ - HvCallEvent_setLpEventQueueInterruptProc(0, 0); - - mf_init(); - - DBG(" <- iSeries_init_early()\n"); -} - -struct mschunks_map mschunks_map = { - /* XXX We don't use these, but Piranha might need them. */ - .chunk_size = MSCHUNKS_CHUNK_SIZE, - .chunk_shift = MSCHUNKS_CHUNK_SHIFT, - .chunk_mask = MSCHUNKS_OFFSET_MASK, -}; -EXPORT_SYMBOL(mschunks_map); - -static void mschunks_alloc(unsigned long num_chunks) -{ - klimit = _ALIGN(klimit, sizeof(u32)); - mschunks_map.mapping = (u32 *)klimit; - klimit += num_chunks * sizeof(u32); - mschunks_map.num_chunks = num_chunks; -} - -/* - * The iSeries may have very large memories ( > 128 GB ) and a partition - * may get memory in "chunks" that may be anywhere in the 2**52 real - * address space. The chunks are 256K in size. To map this to the - * memory model Linux expects, the AS/400 specific code builds a - * translation table to translate what Linux thinks are "physical" - * addresses to the actual real addresses. This allows us to make - * it appear to Linux that we have contiguous memory starting at - * physical address zero while in fact this could be far from the truth. - * To avoid confusion, I'll let the words physical and/or real address - * apply to the Linux addresses while I'll use "absolute address" to - * refer to the actual hardware real address. - * - * build_iSeries_Memory_Map gets information from the Hypervisor and - * looks at the Main Store VPD to determine the absolute addresses - * of the memory that has been assigned to our partition and builds - * a table used to translate Linux's physical addresses to these - * absolute addresses. Absolute addresses are needed when - * communicating with the hypervisor (e.g. to build HPT entries) - * - * Returns the physical memory size - */ - -static unsigned long __init build_iSeries_Memory_Map(void) -{ - u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize; - u32 nextPhysChunk; - u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages; - u32 totalChunks,moreChunks; - u32 currChunk, thisChunk, absChunk; - u32 currDword; - u32 chunkBit; - u64 map; - struct MemoryBlock mb[32]; - unsigned long numMemoryBlocks, curBlock; - - /* Chunk size on iSeries is 256K bytes */ - totalChunks = (u32)HvLpConfig_getMsChunks(); - mschunks_alloc(totalChunks); - - /* - * Get absolute address of our load area - * and map it to physical address 0 - * This guarantees that the loadarea ends up at physical 0 - * otherwise, it might not be returned by PLIC as the first - * chunks - */ - - loadAreaFirstChunk = (u32)addr_to_chunk(itLpNaca.xLoadAreaAddr); - loadAreaSize = itLpNaca.xLoadAreaChunks; - - /* - * Only add the pages already mapped here. - * Otherwise we might add the hpt pages - * The rest of the pages of the load area - * aren't in the HPT yet and can still - * be assigned an arbitrary physical address - */ - if ((loadAreaSize * 64) > HvPagesToMap) - loadAreaSize = HvPagesToMap / 64; - - loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; - - /* - * TODO Do we need to do something if the HPT is in the 64MB load area? - * This would be required if the itLpNaca.xLoadAreaChunks includes - * the HPT size - */ - - printk("Mapping load area - physical addr = 0000000000000000\n" - " absolute addr = %016lx\n", - chunk_to_addr(loadAreaFirstChunk)); - printk("Load area size %dK\n", loadAreaSize * 256); - - for (nextPhysChunk = 0; nextPhysChunk < loadAreaSize; ++nextPhysChunk) - mschunks_map.mapping[nextPhysChunk] = - loadAreaFirstChunk + nextPhysChunk; - - /* - * Get absolute address of our HPT and remember it so - * we won't map it to any physical address - */ - hptFirstChunk = (u32)addr_to_chunk(HvCallHpt_getHptAddress()); - hptSizePages = (u32)HvCallHpt_getHptPages(); - hptSizeChunks = hptSizePages >> - (MSCHUNKS_CHUNK_SHIFT - HW_PAGE_SHIFT); - hptLastChunk = hptFirstChunk + hptSizeChunks - 1; - - printk("HPT absolute addr = %016lx, size = %dK\n", - chunk_to_addr(hptFirstChunk), hptSizeChunks * 256); - - /* - * Determine if absolute memory has any - * holes so that we can interpret the - * access map we get back from the hypervisor - * correctly. - */ - numMemoryBlocks = iSeries_process_mainstore_vpd(mb, 32); - - /* - * Process the main store access map from the hypervisor - * to build up our physical -> absolute translation table - */ - curBlock = 0; - currChunk = 0; - currDword = 0; - moreChunks = totalChunks; - - while (moreChunks) { - map = HvCallSm_get64BitsOfAccessMap(itLpNaca.xLpIndex, - currDword); - thisChunk = currChunk; - while (map) { - chunkBit = map >> 63; - map <<= 1; - if (chunkBit) { - --moreChunks; - while (thisChunk >= mb[curBlock].logicalEnd) { - ++curBlock; - if (curBlock >= numMemoryBlocks) - panic("out of memory blocks"); - } - if (thisChunk < mb[curBlock].logicalStart) - panic("memory block error"); - - absChunk = mb[curBlock].absStart + - (thisChunk - mb[curBlock].logicalStart); - if (((absChunk < hptFirstChunk) || - (absChunk > hptLastChunk)) && - ((absChunk < loadAreaFirstChunk) || - (absChunk > loadAreaLastChunk))) { - mschunks_map.mapping[nextPhysChunk] = - absChunk; - ++nextPhysChunk; - } - } - ++thisChunk; - } - ++currDword; - currChunk += 64; - } - - /* - * main store size (in chunks) is - * totalChunks - hptSizeChunks - * which should be equal to - * nextPhysChunk - */ - return chunk_to_addr(nextPhysChunk); -} - -/* - * Document me. - */ -static void __init iSeries_setup_arch(void) -{ - if (get_lppaca()->shared_proc) { - ppc_md.idle_loop = iseries_shared_idle; - printk(KERN_DEBUG "Using shared processor idle loop\n"); - } else { - ppc_md.idle_loop = iseries_dedicated_idle; - printk(KERN_DEBUG "Using dedicated idle loop\n"); - } - - /* Setup the Lp Event Queue */ - setup_hvlpevent_queue(); - - printk("Max logical processors = %d\n", - itVpdAreas.xSlicMaxLogicalProcs); - printk("Max physical processors = %d\n", - itVpdAreas.xSlicMaxPhysicalProcs); - - iSeries_pcibios_init(); -} - -static void iSeries_show_cpuinfo(struct seq_file *m) -{ - seq_printf(m, "machine\t\t: 64-bit iSeries Logical Partition\n"); -} - -static void __init iSeries_progress(char * st, unsigned short code) -{ - printk("Progress: [%04x] - %s\n", (unsigned)code, st); - mf_display_progress(code); -} - -static void __init iSeries_fixup_klimit(void) -{ - /* - * Change klimit to take into account any ram disk - * that may be included - */ - if (naca.xRamDisk) - klimit = KERNELBASE + (u64)naca.xRamDisk + - (naca.xRamDiskSize * HW_PAGE_SIZE); -} - -static int __init iSeries_src_init(void) -{ - /* clear the progress line */ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - ppc_md.progress(" ", 0xffff); - return 0; -} - -late_initcall(iSeries_src_init); - -static inline void process_iSeries_events(void) -{ - asm volatile ("li 0,0x5555; sc" : : : "r0", "r3"); -} - -static void yield_shared_processor(void) -{ - unsigned long tb; - - HvCall_setEnabledInterrupts(HvCall_MaskIPI | - HvCall_MaskLpEvent | - HvCall_MaskLpProd | - HvCall_MaskTimeout); - - tb = get_tb(); - /* Compute future tb value when yield should expire */ - HvCall_yieldProcessor(HvCall_YieldTimed, tb+tb_ticks_per_jiffy); - - /* - * The decrementer stops during the yield. Force a fake decrementer - * here and let the timer_interrupt code sort out the actual time. - */ - get_lppaca()->int_dword.fields.decr_int = 1; - ppc64_runlatch_on(); - process_iSeries_events(); -} - -static void iseries_shared_idle(void) -{ - while (1) { - tick_nohz_idle_enter(); - rcu_idle_enter(); - while (!need_resched() && !hvlpevent_is_pending()) { - local_irq_disable(); - ppc64_runlatch_off(); - - /* Recheck with irqs off */ - if (!need_resched() && !hvlpevent_is_pending()) - yield_shared_processor(); - - HMT_medium(); - local_irq_enable(); - } - - ppc64_runlatch_on(); - rcu_idle_exit(); - tick_nohz_idle_exit(); - - if (hvlpevent_is_pending()) - process_iSeries_events(); - - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } -} - -static void iseries_dedicated_idle(void) -{ - set_thread_flag(TIF_POLLING_NRFLAG); - - while (1) { - tick_nohz_idle_enter(); - rcu_idle_enter(); - if (!need_resched()) { - while (!need_resched()) { - ppc64_runlatch_off(); - HMT_low(); - - if (hvlpevent_is_pending()) { - HMT_medium(); - ppc64_runlatch_on(); - process_iSeries_events(); - } - } - - HMT_medium(); - } - - ppc64_runlatch_on(); - rcu_idle_exit(); - tick_nohz_idle_exit(); - preempt_enable_no_resched(); - schedule(); - preempt_disable(); - } -} - -static void __iomem *iseries_ioremap(phys_addr_t address, unsigned long size, - unsigned long flags, void *caller) -{ - return (void __iomem *)address; -} - -static void iseries_iounmap(volatile void __iomem *token) -{ -} - -static int __init iseries_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(root, "IBM,iSeries")) - return 0; - - hpte_init_iSeries(); - /* iSeries does not support 16M pages */ - cur_cpu_spec->mmu_features &= ~MMU_FTR_16M_PAGE; - - return 1; -} - -#ifdef CONFIG_KEXEC -static int iseries_kexec_prepare(struct kimage *image) -{ - return -ENOSYS; -} -#endif - -define_machine(iseries) { - .name = "iSeries", - .setup_arch = iSeries_setup_arch, - .show_cpuinfo = iSeries_show_cpuinfo, - .init_IRQ = iSeries_init_IRQ, - .get_irq = iSeries_get_irq, - .init_early = iSeries_init_early, - .pcibios_fixup = iSeries_pci_final_fixup, - .pcibios_fixup_resources= iSeries_pcibios_fixup_resources, - .restart = mf_reboot, - .power_off = mf_power_off, - .halt = mf_power_off, - .get_boot_time = iSeries_get_boot_time, - .set_rtc_time = iSeries_set_rtc_time, - .get_rtc_time = iSeries_get_rtc_time, - .calibrate_decr = generic_calibrate_decr, - .progress = iSeries_progress, - .probe = iseries_probe, - .ioremap = iseries_ioremap, - .iounmap = iseries_iounmap, -#ifdef CONFIG_KEXEC - .machine_kexec_prepare = iseries_kexec_prepare, -#endif - /* XXX Implement enable_pmcs for iSeries */ -}; - -void * __init iSeries_early_setup(void) -{ - unsigned long phys_mem_size; - - /* Identify CPU type. This is done again by the common code later - * on but calling this function multiple times is fine. - */ - identify_cpu(0, mfspr(SPRN_PVR)); - initialise_paca(&boot_paca, 0); - - powerpc_firmware_features |= FW_FEATURE_ISERIES; - powerpc_firmware_features |= FW_FEATURE_LPAR; - -#ifdef CONFIG_SMP - /* On iSeries we know we can never have more than 64 cpus */ - nr_cpu_ids = max(nr_cpu_ids, 64); -#endif - - iSeries_fixup_klimit(); - - /* - * Initialize the table which translate Linux physical addresses to - * AS/400 absolute addresses - */ - phys_mem_size = build_iSeries_Memory_Map(); - - iSeries_get_cmdline(); - - return (void *) __pa(build_flat_dt(phys_mem_size)); -} - -static void hvputc(char c) -{ - if (c == '\n') - hvputc('\r'); - - HvCall_writeLogBuffer(&c, 1); -} - -void __init udbg_init_iseries(void) -{ - udbg_putc = hvputc; -} diff --git a/arch/powerpc/platforms/iseries/setup.h b/arch/powerpc/platforms/iseries/setup.h deleted file mode 100644 index 729754b..0000000 --- a/arch/powerpc/platforms/iseries/setup.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2000 Mike Corrigan - * Copyright (c) 1999-2000 Grant Erickson - * - * Description: - * Architecture- / platform-specific boot-time initialization code for - * the IBM AS/400 LPAR. Adapted from original code by Grant Erickson and - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - * 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. - */ - -#ifndef __ISERIES_SETUP_H__ -#define __ISERIES_SETUP_H__ - -extern void *iSeries_early_setup(void); -extern unsigned long iSeries_get_boot_time(void); -extern int iSeries_set_rtc_time(struct rtc_time *tm); -extern void iSeries_get_rtc_time(struct rtc_time *tm); - -extern void *build_flat_dt(unsigned long phys_mem_size); - -#endif /* __ISERIES_SETUP_H__ */ diff --git a/arch/powerpc/platforms/iseries/smp.c b/arch/powerpc/platforms/iseries/smp.c deleted file mode 100644 index 02df49f..0000000 --- a/arch/powerpc/platforms/iseries/smp.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * SMP support for iSeries machines. - * - * Dave Engebretsen, Peter Bergner, and - * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com - * - * Plus various changes from other IBM teams... - * - * 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. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void smp_iSeries_cause_ipi(int cpu, unsigned long data) -{ - HvCall_sendIPI(&(paca[cpu])); -} - -static int smp_iSeries_probe(void) -{ - return cpumask_weight(cpu_possible_mask); -} - -static int smp_iSeries_kick_cpu(int nr) -{ - BUG_ON((nr < 0) || (nr >= NR_CPUS)); - - /* Verify that our partition has a processor nr */ - if (lppaca_of(nr).dyn_proc_status >= 2) - return -ENOENT; - - /* The processor is currently spinning, waiting - * for the cpu_start field to become non-zero - * After we set cpu_start, the processor will - * continue on to secondary_start in iSeries_head.S - */ - paca[nr].cpu_start = 1; - - return 0; -} - -static void __devinit smp_iSeries_setup_cpu(int nr) -{ -} - -static struct smp_ops_t iSeries_smp_ops = { - .message_pass = NULL, /* Use smp_muxed_ipi_message_pass */ - .cause_ipi = smp_iSeries_cause_ipi, - .probe = smp_iSeries_probe, - .kick_cpu = smp_iSeries_kick_cpu, - .setup_cpu = smp_iSeries_setup_cpu, -}; - -/* This is called very early. */ -void __init smp_init_iSeries(void) -{ - smp_ops = &iSeries_smp_ops; -} diff --git a/arch/powerpc/platforms/iseries/spcomm_area.h b/arch/powerpc/platforms/iseries/spcomm_area.h deleted file mode 100644 index 598b7c1..0000000 --- a/arch/powerpc/platforms/iseries/spcomm_area.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ - -#ifndef _ISERIES_SPCOMM_AREA_H -#define _ISERIES_SPCOMM_AREA_H - - -struct SpCommArea { - u32 xDesc; // Descriptor (only in new formats) 000-003 - u8 xFormat; // Format (only in new formats) 004-004 - u8 xRsvd1[11]; // Reserved 005-00F - u64 xRawTbAtIplStart; // Raw HW TB value when IPL is started 010-017 - u64 xRawTodAtIplStart; // Raw HW TOD value when IPL is started 018-01F - u64 xBcdTimeAtIplStart; // BCD time when IPL is started 020-027 - u64 xBcdTimeAtOsStart; // BCD time when OS passed control 028-02F - u8 xRsvd2[80]; // Reserved 030-07F -}; - -#endif /* _ISERIES_SPCOMM_AREA_H */ diff --git a/arch/powerpc/platforms/iseries/vio.c b/arch/powerpc/platforms/iseries/vio.c deleted file mode 100644 index 04be62d..0000000 --- a/arch/powerpc/platforms/iseries/vio.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Legacy iSeries specific vio initialisation - * that needs to be built in (not a module). - * - * © Copyright 2007 IBM Corporation - * Author: Stephen Rothwell - * Some parts collected from various other files - * - * 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. - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define FIRST_VTY 0 -#define NUM_VTYS 1 -#define FIRST_VSCSI (FIRST_VTY + NUM_VTYS) -#define NUM_VSCSIS 1 -#define FIRST_VLAN (FIRST_VSCSI + NUM_VSCSIS) -#define NUM_VLANS HVMAXARCHITECTEDVIRTUALLANS -#define FIRST_VIODASD (FIRST_VLAN + NUM_VLANS) -#define NUM_VIODASDS HVMAXARCHITECTEDVIRTUALDISKS -#define FIRST_VIOCD (FIRST_VIODASD + NUM_VIODASDS) -#define NUM_VIOCDS HVMAXARCHITECTEDVIRTUALCDROMS -#define FIRST_VIOTAPE (FIRST_VIOCD + NUM_VIOCDS) -#define NUM_VIOTAPES HVMAXARCHITECTEDVIRTUALTAPES - -struct vio_waitevent { - struct completion com; - int rc; - u16 sub_result; -}; - -struct vio_resource { - char rsrcname[10]; - char type[4]; - char model[3]; -}; - -static struct property *new_property(const char *name, int length, - const void *value) -{ - struct property *np = kzalloc(sizeof(*np) + strlen(name) + 1 + length, - GFP_KERNEL); - - if (!np) - return NULL; - np->name = (char *)(np + 1); - np->value = np->name + strlen(name) + 1; - strcpy(np->name, name); - memcpy(np->value, value, length); - np->length = length; - return np; -} - -static void free_property(struct property *np) -{ - kfree(np); -} - -static struct device_node *new_node(const char *path, - struct device_node *parent) -{ - struct device_node *np = kzalloc(sizeof(*np), GFP_KERNEL); - - if (!np) - return NULL; - np->full_name = kstrdup(path, GFP_KERNEL); - if (!np->full_name) { - kfree(np); - return NULL; - } - of_node_set_flag(np, OF_DYNAMIC); - kref_init(&np->kref); - np->parent = of_node_get(parent); - return np; -} - -static void free_node(struct device_node *np) -{ - struct property *next; - struct property *prop; - - next = np->properties; - while (next) { - prop = next; - next = prop->next; - free_property(prop); - } - of_node_put(np->parent); - kfree(np->full_name); - kfree(np); -} - -static int add_string_property(struct device_node *np, const char *name, - const char *value) -{ - struct property *nprop = new_property(name, strlen(value) + 1, value); - - if (!nprop) - return 0; - prom_add_property(np, nprop); - return 1; -} - -static int add_raw_property(struct device_node *np, const char *name, - int length, const void *value) -{ - struct property *nprop = new_property(name, length, value); - - if (!nprop) - return 0; - prom_add_property(np, nprop); - return 1; -} - -static struct device_node *do_device_node(struct device_node *parent, - const char *name, u32 reg, u32 unit, const char *type, - const char *compat, struct vio_resource *res) -{ - struct device_node *np; - char path[32]; - - snprintf(path, sizeof(path), "/vdevice/%s@%08x", name, reg); - np = new_node(path, parent); - if (!np) - return NULL; - if (!add_string_property(np, "name", name) || - !add_string_property(np, "device_type", type) || - !add_string_property(np, "compatible", compat) || - !add_raw_property(np, "reg", sizeof(reg), ®) || - !add_raw_property(np, "linux,unit_address", - sizeof(unit), &unit)) { - goto node_free; - } - if (res) { - if (!add_raw_property(np, "linux,vio_rsrcname", - sizeof(res->rsrcname), res->rsrcname) || - !add_raw_property(np, "linux,vio_type", - sizeof(res->type), res->type) || - !add_raw_property(np, "linux,vio_model", - sizeof(res->model), res->model)) - goto node_free; - } - np->name = of_get_property(np, "name", NULL); - np->type = of_get_property(np, "device_type", NULL); - of_attach_node(np); -#ifdef CONFIG_PROC_DEVICETREE - if (parent->pde) { - struct proc_dir_entry *ent; - - ent = proc_mkdir(strrchr(np->full_name, '/') + 1, parent->pde); - if (ent) - proc_device_tree_add_node(np, ent); - } -#endif - return np; - - node_free: - free_node(np); - return NULL; -} - -/* - * This is here so that we can dynamically add viodasd - * devices without exposing all the above infrastructure. - */ -struct vio_dev *vio_create_viodasd(u32 unit) -{ - struct device_node *vio_root; - struct device_node *np; - struct vio_dev *vdev = NULL; - - vio_root = of_find_node_by_path("/vdevice"); - if (!vio_root) - return NULL; - np = do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, - "block", "IBM,iSeries-viodasd", NULL); - of_node_put(vio_root); - if (np) { - vdev = vio_register_device_node(np); - if (!vdev) - free_node(np); - } - return vdev; -} -EXPORT_SYMBOL_GPL(vio_create_viodasd); - -static void __init handle_block_event(struct HvLpEvent *event) -{ - struct vioblocklpevent *bevent = (struct vioblocklpevent *)event; - struct vio_waitevent *pwe; - - if (event == NULL) - /* Notification that a partition went away! */ - return; - /* First, we should NEVER get an int here...only acks */ - if (hvlpevent_is_int(event)) { - printk(KERN_WARNING "handle_viod_request: " - "Yikes! got an int in viodasd event handler!\n"); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - return; - } - - switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { - case vioblockopen: - /* - * Handle a response to an open request. We get all the - * disk information in the response, so update it. The - * correlation token contains a pointer to a waitevent - * structure that has a completion in it. update the - * return code in the waitevent structure and post the - * completion to wake up the guy who sent the request - */ - pwe = (struct vio_waitevent *)event->xCorrelationToken; - pwe->rc = event->xRc; - pwe->sub_result = bevent->sub_result; - complete(&pwe->com); - break; - case vioblockclose: - break; - default: - printk(KERN_WARNING "handle_viod_request: unexpected subtype!"); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - -static void __init probe_disk(struct device_node *vio_root, u32 unit) -{ - HvLpEvent_Rc hvrc; - struct vio_waitevent we; - u16 flags = 0; - -retry: - init_completion(&we.com); - - /* Send the open event to OS/400 */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_blockio | vioblockopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)&we, VIOVERSION << 16, - ((u64)unit << 48) | ((u64)flags<< 32), - 0, 0, 0); - if (hvrc != 0) { - printk(KERN_WARNING "probe_disk: bad rc on HV open %d\n", - (int)hvrc); - return; - } - - wait_for_completion(&we.com); - - if (we.rc != 0) { - if (flags != 0) - return; - /* try again with read only flag set */ - flags = vioblockflags_ro; - goto retry; - } - - /* Send the close event to OS/400. We DON'T expect a response */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_blockio | vioblockclose, - HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - 0, VIOVERSION << 16, - ((u64)unit << 48) | ((u64)flags << 32), - 0, 0, 0); - if (hvrc != 0) { - printk(KERN_WARNING "probe_disk: " - "bad rc sending event to OS/400 %d\n", (int)hvrc); - return; - } - - do_device_node(vio_root, "viodasd", FIRST_VIODASD + unit, unit, - "block", "IBM,iSeries-viodasd", NULL); -} - -static void __init get_viodasd_info(struct device_node *vio_root) -{ - int rc; - u32 unit; - - rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, 2); - if (rc) { - printk(KERN_WARNING "get_viodasd_info: " - "error opening path to host partition %d\n", - viopath_hostLp); - return; - } - - /* Initialize our request handler */ - vio_setHandler(viomajorsubtype_blockio, handle_block_event); - - for (unit = 0; unit < HVMAXARCHITECTEDVIRTUALDISKS; unit++) - probe_disk(vio_root, unit); - - vio_clearHandler(viomajorsubtype_blockio); - viopath_close(viopath_hostLp, viomajorsubtype_blockio, 2); -} - -static void __init handle_cd_event(struct HvLpEvent *event) -{ - struct viocdlpevent *bevent; - struct vio_waitevent *pwe; - - if (!event) - /* Notification that a partition went away! */ - return; - - /* First, we should NEVER get an int here...only acks */ - if (hvlpevent_is_int(event)) { - printk(KERN_WARNING "handle_cd_event: got an unexpected int\n"); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - return; - } - - bevent = (struct viocdlpevent *)event; - - switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { - case viocdgetinfo: - pwe = (struct vio_waitevent *)event->xCorrelationToken; - pwe->rc = event->xRc; - pwe->sub_result = bevent->sub_result; - complete(&pwe->com); - break; - - default: - printk(KERN_WARNING "handle_cd_event: " - "message with unexpected subtype %0x04X!\n", - event->xSubtype & VIOMINOR_SUBTYPE_MASK); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - -static void __init get_viocd_info(struct device_node *vio_root) -{ - HvLpEvent_Rc hvrc; - u32 unit; - struct vio_waitevent we; - struct vio_resource *unitinfo; - dma_addr_t unitinfo_dmaaddr; - int ret; - - ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, 2); - if (ret) { - printk(KERN_WARNING - "get_viocd_info: error opening path to host partition %d\n", - viopath_hostLp); - return; - } - - /* Initialize our request handler */ - vio_setHandler(viomajorsubtype_cdio, handle_cd_event); - - unitinfo = iseries_hv_alloc( - sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, - &unitinfo_dmaaddr, GFP_ATOMIC); - if (!unitinfo) { - printk(KERN_WARNING - "get_viocd_info: error allocating unitinfo\n"); - goto clear_handler; - } - - memset(unitinfo, 0, sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS); - - init_completion(&we.com); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_cdio | viocdgetinfo, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)&we, VIOVERSION << 16, unitinfo_dmaaddr, 0, - sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(KERN_WARNING - "get_viocd_info: cdrom error sending event. rc %d\n", - (int)hvrc); - goto hv_free; - } - - wait_for_completion(&we.com); - - if (we.rc) { - printk(KERN_WARNING "get_viocd_info: bad rc %d:0x%04X\n", - we.rc, we.sub_result); - goto hv_free; - } - - for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALCDROMS) && - unitinfo[unit].rsrcname[0]; unit++) { - if (!do_device_node(vio_root, "viocd", FIRST_VIOCD + unit, unit, - "block", "IBM,iSeries-viocd", &unitinfo[unit])) - break; - } - - hv_free: - iseries_hv_free(sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALCDROMS, - unitinfo, unitinfo_dmaaddr); - clear_handler: - vio_clearHandler(viomajorsubtype_cdio); - viopath_close(viopath_hostLp, viomajorsubtype_cdio, 2); -} - -/* Handle interrupt events for tape */ -static void __init handle_tape_event(struct HvLpEvent *event) -{ - struct vio_waitevent *we; - struct viotapelpevent *tevent = (struct viotapelpevent *)event; - - if (event == NULL) - /* Notification that a partition went away! */ - return; - - we = (struct vio_waitevent *)event->xCorrelationToken; - switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { - case viotapegetinfo: - we->rc = tevent->sub_type_result; - complete(&we->com); - break; - default: - printk(KERN_WARNING "handle_tape_event: weird ack\n"); - } -} - -static void __init get_viotape_info(struct device_node *vio_root) -{ - HvLpEvent_Rc hvrc; - u32 unit; - struct vio_resource *unitinfo; - dma_addr_t unitinfo_dmaaddr; - size_t len = sizeof(*unitinfo) * HVMAXARCHITECTEDVIRTUALTAPES; - struct vio_waitevent we; - int ret; - - init_completion(&we.com); - - ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, 2); - if (ret) { - printk(KERN_WARNING "get_viotape_info: " - "error on viopath_open to hostlp %d\n", ret); - return; - } - - vio_setHandler(viomajorsubtype_tape, handle_tape_event); - - unitinfo = iseries_hv_alloc(len, &unitinfo_dmaaddr, GFP_ATOMIC); - if (!unitinfo) - goto clear_handler; - - memset(unitinfo, 0, len); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapegetinfo, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)&we, VIOVERSION << 16, - unitinfo_dmaaddr, len, 0, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(KERN_WARNING "get_viotape_info: hv error on op %d\n", - (int)hvrc); - goto hv_free; - } - - wait_for_completion(&we.com); - - for (unit = 0; (unit < HVMAXARCHITECTEDVIRTUALTAPES) && - unitinfo[unit].rsrcname[0]; unit++) { - if (!do_device_node(vio_root, "viotape", FIRST_VIOTAPE + unit, - unit, "byte", "IBM,iSeries-viotape", - &unitinfo[unit])) - break; - } - - hv_free: - iseries_hv_free(len, unitinfo, unitinfo_dmaaddr); - clear_handler: - vio_clearHandler(viomajorsubtype_tape); - viopath_close(viopath_hostLp, viomajorsubtype_tape, 2); -} - -static int __init iseries_vio_init(void) -{ - struct device_node *vio_root; - int ret = -ENODEV; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - goto out; - - iommu_vio_init(); - - vio_root = of_find_node_by_path("/vdevice"); - if (!vio_root) - goto out; - - if (viopath_hostLp == HvLpIndexInvalid) { - vio_set_hostlp(); - /* If we don't have a host, bail out */ - if (viopath_hostLp == HvLpIndexInvalid) - goto put_node; - } - - get_viodasd_info(vio_root); - get_viocd_info(vio_root); - get_viotape_info(vio_root); - - ret = 0; - - put_node: - of_node_put(vio_root); - out: - return ret; -} -arch_initcall(iseries_vio_init); diff --git a/arch/powerpc/platforms/iseries/viopath.c b/arch/powerpc/platforms/iseries/viopath.c deleted file mode 100644 index 40dad08..0000000 --- a/arch/powerpc/platforms/iseries/viopath.c +++ /dev/null @@ -1,677 +0,0 @@ -/* -*- linux-c -*- - * - * iSeries Virtual I/O Message Path code - * - * Authors: Dave Boutcher - * Ryan Arnold - * Colin Devilbiss - * - * (C) Copyright 2000-2005 IBM Corporation - * - * This code is used by the iSeries virtual disk, cd, - * tape, and console to communicate with OS/400 in another - * partition. - * - * 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) anyu later version. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Status of the path to each other partition in the system. - * This is overkill, since we will only ever establish connections - * to our hosting partition and the primary partition on the system. - * But this allows for other support in the future. - */ -static struct viopathStatus { - int isOpen; /* Did we open the path? */ - int isActive; /* Do we have a mon msg outstanding */ - int users[VIO_MAX_SUBTYPES]; - HvLpInstanceId mSourceInst; - HvLpInstanceId mTargetInst; - int numberAllocated; -} viopathStatus[HVMAXARCHITECTEDLPS]; - -static DEFINE_SPINLOCK(statuslock); - -/* - * For each kind of event we allocate a buffer that is - * guaranteed not to cross a page boundary - */ -static unsigned char event_buffer[VIO_MAX_SUBTYPES * 256] - __attribute__((__aligned__(4096))); -static atomic_t event_buffer_available[VIO_MAX_SUBTYPES]; -static int event_buffer_initialised; - -static void handleMonitorEvent(struct HvLpEvent *event); - -/* - * We use this structure to handle asynchronous responses. The caller - * blocks on the semaphore and the handler posts the semaphore. However, - * if system_state is not SYSTEM_RUNNING, then wait_atomic is used ... - */ -struct alloc_parms { - struct completion done; - int number; - atomic_t wait_atomic; - int used_wait_atomic; -}; - -/* Put a sequence number in each mon msg. The value is not - * important. Start at something other than 0 just for - * readability. wrapping this is ok. - */ -static u8 viomonseq = 22; - -/* Our hosting logical partition. We get this at startup - * time, and different modules access this variable directly. - */ -HvLpIndex viopath_hostLp = HvLpIndexInvalid; -EXPORT_SYMBOL(viopath_hostLp); -HvLpIndex viopath_ourLp = HvLpIndexInvalid; -EXPORT_SYMBOL(viopath_ourLp); - -/* For each kind of incoming event we set a pointer to a - * routine to call. - */ -static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES]; - -#define VIOPATH_KERN_WARN KERN_WARNING "viopath: " -#define VIOPATH_KERN_INFO KERN_INFO "viopath: " - -static int proc_viopath_show(struct seq_file *m, void *v) -{ - char *buf; - u16 vlanMap; - dma_addr_t handle; - HvLpEvent_Rc hvrc; - DECLARE_COMPLETION_ONSTACK(done); - struct device_node *node; - const char *sysid; - - buf = kzalloc(HW_PAGE_SIZE, GFP_KERNEL); - if (!buf) - return 0; - - handle = iseries_hv_map(buf, HW_PAGE_SIZE, DMA_FROM_DEVICE); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_config | vioconfigget, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)&done, VIOVERSION << 16, - ((u64)handle) << 32, HW_PAGE_SIZE, 0, 0); - - if (hvrc != HvLpEvent_Rc_Good) - printk(VIOPATH_KERN_WARN "hv error on op %d\n", (int)hvrc); - - wait_for_completion(&done); - - vlanMap = HvLpConfig_getVirtualLanIndexMap(); - - buf[HW_PAGE_SIZE-1] = '\0'; - seq_printf(m, "%s", buf); - - iseries_hv_unmap(handle, HW_PAGE_SIZE, DMA_FROM_DEVICE); - kfree(buf); - - seq_printf(m, "AVAILABLE_VETH=%x\n", vlanMap); - - node = of_find_node_by_path("/"); - sysid = NULL; - if (node != NULL) - sysid = of_get_property(node, "system-id", NULL); - - if (sysid == NULL) - seq_printf(m, "SRLNBR=\n"); - else - /* Skip "IBM," on front of serial number, see dt.c */ - seq_printf(m, "SRLNBR=%s\n", sysid + 4); - - of_node_put(node); - - return 0; -} - -static int proc_viopath_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_viopath_show, NULL); -} - -static const struct file_operations proc_viopath_operations = { - .open = proc_viopath_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init vio_proc_init(void) -{ - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; - - proc_create("iSeries/config", 0, NULL, &proc_viopath_operations); - return 0; -} -__initcall(vio_proc_init); - -/* See if a given LP is active. Allow for invalid lps to be passed in - * and just return invalid - */ -int viopath_isactive(HvLpIndex lp) -{ - if (lp == HvLpIndexInvalid) - return 0; - if (lp < HVMAXARCHITECTEDLPS) - return viopathStatus[lp].isActive; - else - return 0; -} -EXPORT_SYMBOL(viopath_isactive); - -/* - * We cache the source and target instance ids for each - * partition. - */ -HvLpInstanceId viopath_sourceinst(HvLpIndex lp) -{ - return viopathStatus[lp].mSourceInst; -} -EXPORT_SYMBOL(viopath_sourceinst); - -HvLpInstanceId viopath_targetinst(HvLpIndex lp) -{ - return viopathStatus[lp].mTargetInst; -} -EXPORT_SYMBOL(viopath_targetinst); - -/* - * Send a monitor message. This is a message with the acknowledge - * bit on that the other side will NOT explicitly acknowledge. When - * the other side goes down, the hypervisor will acknowledge any - * outstanding messages....so we will know when the other side dies. - */ -static void sendMonMsg(HvLpIndex remoteLp) -{ - HvLpEvent_Rc hvrc; - - viopathStatus[remoteLp].mSourceInst = - HvCallEvent_getSourceLpInstanceId(remoteLp, - HvLpEvent_Type_VirtualIo); - viopathStatus[remoteLp].mTargetInst = - HvCallEvent_getTargetLpInstanceId(remoteLp, - HvLpEvent_Type_VirtualIo); - - /* - * Deliberately ignore the return code here. if we call this - * more than once, we don't care. - */ - vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent); - - hvrc = HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo, - viomajorsubtype_monitor, HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_DeferredAck, - viopathStatus[remoteLp].mSourceInst, - viopathStatus[remoteLp].mTargetInst, - viomonseq++, 0, 0, 0, 0, 0); - - if (hvrc == HvLpEvent_Rc_Good) - viopathStatus[remoteLp].isActive = 1; - else { - printk(VIOPATH_KERN_WARN "could not connect to partition %d\n", - remoteLp); - viopathStatus[remoteLp].isActive = 0; - } -} - -static void handleMonitorEvent(struct HvLpEvent *event) -{ - HvLpIndex remoteLp; - int i; - - /* - * This handler is _also_ called as part of the loop - * at the end of this routine, so it must be able to - * ignore NULL events... - */ - if (!event) - return; - - /* - * First see if this is just a normal monitor message from the - * other partition - */ - if (hvlpevent_is_int(event)) { - remoteLp = event->xSourceLp; - if (!viopathStatus[remoteLp].isActive) - sendMonMsg(remoteLp); - return; - } - - /* - * This path is for an acknowledgement; the other partition - * died - */ - remoteLp = event->xTargetLp; - if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) || - (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) { - printk(VIOPATH_KERN_WARN "ignoring ack....mismatched instances\n"); - return; - } - - printk(VIOPATH_KERN_WARN "partition %d ended\n", remoteLp); - - viopathStatus[remoteLp].isActive = 0; - - /* - * For each active handler, pass them a NULL - * message to indicate that the other partition - * died - */ - for (i = 0; i < VIO_MAX_SUBTYPES; i++) { - if (vio_handler[i] != NULL) - (*vio_handler[i])(NULL); - } -} - -int vio_setHandler(int subtype, vio_event_handler_t *beh) -{ - subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; - if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) - return -EINVAL; - if (vio_handler[subtype] != NULL) - return -EBUSY; - vio_handler[subtype] = beh; - return 0; -} -EXPORT_SYMBOL(vio_setHandler); - -int vio_clearHandler(int subtype) -{ - subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; - if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) - return -EINVAL; - if (vio_handler[subtype] == NULL) - return -EAGAIN; - vio_handler[subtype] = NULL; - return 0; -} -EXPORT_SYMBOL(vio_clearHandler); - -static void handleConfig(struct HvLpEvent *event) -{ - if (!event) - return; - if (hvlpevent_is_int(event)) { - printk(VIOPATH_KERN_WARN - "unexpected config request from partition %d", - event->xSourceLp); - - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - return; - } - - complete((struct completion *)event->xCorrelationToken); -} - -/* - * Initialization of the hosting partition - */ -void vio_set_hostlp(void) -{ - /* - * If this has already been set then we DON'T want to either change - * it or re-register the proc file system - */ - if (viopath_hostLp != HvLpIndexInvalid) - return; - - /* - * Figure out our hosting partition. This isn't allowed to change - * while we're active - */ - viopath_ourLp = HvLpConfig_getLpIndex(); - viopath_hostLp = HvLpConfig_getHostingLpIndex(viopath_ourLp); - - if (viopath_hostLp != HvLpIndexInvalid) - vio_setHandler(viomajorsubtype_config, handleConfig); -} -EXPORT_SYMBOL(vio_set_hostlp); - -static void vio_handleEvent(struct HvLpEvent *event) -{ - HvLpIndex remoteLp; - int subtype = (event->xSubtype & VIOMAJOR_SUBTYPE_MASK) - >> VIOMAJOR_SUBTYPE_SHIFT; - - if (hvlpevent_is_int(event)) { - remoteLp = event->xSourceLp; - /* - * The isActive is checked because if the hosting partition - * went down and came back up it would not be active but it - * would have different source and target instances, in which - * case we'd want to reset them. This case really protects - * against an unauthorized active partition sending interrupts - * or acks to this linux partition. - */ - if (viopathStatus[remoteLp].isActive - && (event->xSourceInstanceId != - viopathStatus[remoteLp].mTargetInst)) { - printk(VIOPATH_KERN_WARN - "message from invalid partition. " - "int msg rcvd, source inst (%d) doesn't match (%d)\n", - viopathStatus[remoteLp].mTargetInst, - event->xSourceInstanceId); - return; - } - - if (viopathStatus[remoteLp].isActive - && (event->xTargetInstanceId != - viopathStatus[remoteLp].mSourceInst)) { - printk(VIOPATH_KERN_WARN - "message from invalid partition. " - "int msg rcvd, target inst (%d) doesn't match (%d)\n", - viopathStatus[remoteLp].mSourceInst, - event->xTargetInstanceId); - return; - } - } else { - remoteLp = event->xTargetLp; - if (event->xSourceInstanceId != - viopathStatus[remoteLp].mSourceInst) { - printk(VIOPATH_KERN_WARN - "message from invalid partition. " - "ack msg rcvd, source inst (%d) doesn't match (%d)\n", - viopathStatus[remoteLp].mSourceInst, - event->xSourceInstanceId); - return; - } - - if (event->xTargetInstanceId != - viopathStatus[remoteLp].mTargetInst) { - printk(VIOPATH_KERN_WARN - "message from invalid partition. " - "viopath: ack msg rcvd, target inst (%d) doesn't match (%d)\n", - viopathStatus[remoteLp].mTargetInst, - event->xTargetInstanceId); - return; - } - } - - if (vio_handler[subtype] == NULL) { - printk(VIOPATH_KERN_WARN - "unexpected virtual io event subtype %d from partition %d\n", - event->xSubtype, remoteLp); - /* No handler. Ack if necessary */ - if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - return; - } - - /* This innocuous little line is where all the real work happens */ - (*vio_handler[subtype])(event); -} - -static void viopath_donealloc(void *parm, int number) -{ - struct alloc_parms *parmsp = parm; - - parmsp->number = number; - if (parmsp->used_wait_atomic) - atomic_set(&parmsp->wait_atomic, 0); - else - complete(&parmsp->done); -} - -static int allocateEvents(HvLpIndex remoteLp, int numEvents) -{ - struct alloc_parms parms; - - if (system_state != SYSTEM_RUNNING) { - parms.used_wait_atomic = 1; - atomic_set(&parms.wait_atomic, 1); - } else { - parms.used_wait_atomic = 0; - init_completion(&parms.done); - } - mf_allocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */ - numEvents, &viopath_donealloc, &parms); - if (system_state != SYSTEM_RUNNING) { - while (atomic_read(&parms.wait_atomic)) - mb(); - } else - wait_for_completion(&parms.done); - return parms.number; -} - -int viopath_open(HvLpIndex remoteLp, int subtype, int numReq) -{ - int i; - unsigned long flags; - int tempNumAllocated; - - if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid)) - return -EINVAL; - - subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; - if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) - return -EINVAL; - - spin_lock_irqsave(&statuslock, flags); - - if (!event_buffer_initialised) { - for (i = 0; i < VIO_MAX_SUBTYPES; i++) - atomic_set(&event_buffer_available[i], 1); - event_buffer_initialised = 1; - } - - viopathStatus[remoteLp].users[subtype]++; - - if (!viopathStatus[remoteLp].isOpen) { - viopathStatus[remoteLp].isOpen = 1; - HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); - - /* - * Don't hold the spinlock during an operation that - * can sleep. - */ - spin_unlock_irqrestore(&statuslock, flags); - tempNumAllocated = allocateEvents(remoteLp, 1); - spin_lock_irqsave(&statuslock, flags); - - viopathStatus[remoteLp].numberAllocated += tempNumAllocated; - - if (viopathStatus[remoteLp].numberAllocated == 0) { - HvCallEvent_closeLpEventPath(remoteLp, - HvLpEvent_Type_VirtualIo); - - spin_unlock_irqrestore(&statuslock, flags); - return -ENOMEM; - } - - viopathStatus[remoteLp].mSourceInst = - HvCallEvent_getSourceLpInstanceId(remoteLp, - HvLpEvent_Type_VirtualIo); - viopathStatus[remoteLp].mTargetInst = - HvCallEvent_getTargetLpInstanceId(remoteLp, - HvLpEvent_Type_VirtualIo); - HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo, - &vio_handleEvent); - sendMonMsg(remoteLp); - printk(VIOPATH_KERN_INFO "opening connection to partition %d, " - "setting sinst %d, tinst %d\n", - remoteLp, viopathStatus[remoteLp].mSourceInst, - viopathStatus[remoteLp].mTargetInst); - } - - spin_unlock_irqrestore(&statuslock, flags); - tempNumAllocated = allocateEvents(remoteLp, numReq); - spin_lock_irqsave(&statuslock, flags); - viopathStatus[remoteLp].numberAllocated += tempNumAllocated; - spin_unlock_irqrestore(&statuslock, flags); - - return 0; -} -EXPORT_SYMBOL(viopath_open); - -int viopath_close(HvLpIndex remoteLp, int subtype, int numReq) -{ - unsigned long flags; - int i; - int numOpen; - struct alloc_parms parms; - - if ((remoteLp >= HVMAXARCHITECTEDLPS) || (remoteLp == HvLpIndexInvalid)) - return -EINVAL; - - subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; - if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) - return -EINVAL; - - spin_lock_irqsave(&statuslock, flags); - /* - * If the viopath_close somehow gets called before a - * viopath_open it could decrement to -1 which is a non - * recoverable state so we'll prevent this from - * happening. - */ - if (viopathStatus[remoteLp].users[subtype] > 0) - viopathStatus[remoteLp].users[subtype]--; - - spin_unlock_irqrestore(&statuslock, flags); - - parms.used_wait_atomic = 0; - init_completion(&parms.done); - mf_deallocate_lp_events(remoteLp, HvLpEvent_Type_VirtualIo, - numReq, &viopath_donealloc, &parms); - wait_for_completion(&parms.done); - - spin_lock_irqsave(&statuslock, flags); - for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++) - numOpen += viopathStatus[remoteLp].users[i]; - - if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) { - printk(VIOPATH_KERN_INFO "closing connection to partition %d\n", - remoteLp); - - HvCallEvent_closeLpEventPath(remoteLp, - HvLpEvent_Type_VirtualIo); - viopathStatus[remoteLp].isOpen = 0; - viopathStatus[remoteLp].isActive = 0; - - for (i = 0; i < VIO_MAX_SUBTYPES; i++) - atomic_set(&event_buffer_available[i], 0); - event_buffer_initialised = 0; - } - spin_unlock_irqrestore(&statuslock, flags); - return 0; -} -EXPORT_SYMBOL(viopath_close); - -void *vio_get_event_buffer(int subtype) -{ - subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; - if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) - return NULL; - - if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0) - return &event_buffer[subtype * 256]; - else - return NULL; -} -EXPORT_SYMBOL(vio_get_event_buffer); - -void vio_free_event_buffer(int subtype, void *buffer) -{ - subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; - if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) { - printk(VIOPATH_KERN_WARN - "unexpected subtype %d freeing event buffer\n", subtype); - return; - } - - if (atomic_read(&event_buffer_available[subtype]) != 0) { - printk(VIOPATH_KERN_WARN - "freeing unallocated event buffer, subtype %d\n", - subtype); - return; - } - - if (buffer != &event_buffer[subtype * 256]) { - printk(VIOPATH_KERN_WARN - "freeing invalid event buffer, subtype %d\n", subtype); - } - - atomic_set(&event_buffer_available[subtype], 1); -} -EXPORT_SYMBOL(vio_free_event_buffer); - -static const struct vio_error_entry vio_no_error = - { 0, 0, "Non-VIO Error" }; -static const struct vio_error_entry vio_unknown_error = - { 0, EIO, "Unknown Error" }; - -static const struct vio_error_entry vio_default_errors[] = { - {0x0001, EIO, "No Connection"}, - {0x0002, EIO, "No Receiver"}, - {0x0003, EIO, "No Buffer Available"}, - {0x0004, EBADRQC, "Invalid Message Type"}, - {0x0000, 0, NULL}, -}; - -const struct vio_error_entry *vio_lookup_rc( - const struct vio_error_entry *local_table, u16 rc) -{ - const struct vio_error_entry *cur; - - if (!rc) - return &vio_no_error; - if (local_table) - for (cur = local_table; cur->rc; ++cur) - if (cur->rc == rc) - return cur; - for (cur = vio_default_errors; cur->rc; ++cur) - if (cur->rc == rc) - return cur; - return &vio_unknown_error; -} -EXPORT_SYMBOL(vio_lookup_rc); diff --git a/arch/powerpc/platforms/iseries/vpd_areas.h b/arch/powerpc/platforms/iseries/vpd_areas.h deleted file mode 100644 index feb001f..0000000 --- a/arch/powerpc/platforms/iseries/vpd_areas.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ISERIES_VPD_AREAS_H -#define _ISERIES_VPD_AREAS_H - -/* - * This file defines the address and length of all of the VPD area passed to - * the OS from PLIC (most of which start from the SP). - */ - -#include - -/* VPD Entry index is carved in stone - cannot be changed (easily). */ -#define ItVpdCecVpd 0 -#define ItVpdDynamicSpace 1 -#define ItVpdExtVpd 2 -#define ItVpdExtVpdOnPanel 3 -#define ItVpdFirstPaca 4 -#define ItVpdIoVpd 5 -#define ItVpdIplParms 6 -#define ItVpdMsVpd 7 -#define ItVpdPanelVpd 8 -#define ItVpdLpNaca 9 -#define ItVpdBackplaneAndMaybeClockCardVpd 10 -#define ItVpdRecoveryLogBuffer 11 -#define ItVpdSpCommArea 12 -#define ItVpdSpLogBuffer 13 -#define ItVpdSpLogBufferSave 14 -#define ItVpdSpCardVpd 15 -#define ItVpdFirstProcVpd 16 -#define ItVpdApModelVpd 17 -#define ItVpdClockCardVpd 18 -#define ItVpdBusExtCardVpd 19 -#define ItVpdProcCapacityVpd 20 -#define ItVpdInteractiveCapacityVpd 21 -#define ItVpdFirstSlotLabel 22 -#define ItVpdFirstLpQueue 23 -#define ItVpdFirstL3CacheVpd 24 -#define ItVpdFirstProcFruVpd 25 - -#define ItVpdMaxEntries 26 - -#define ItDmaMaxEntries 10 - -#define ItVpdAreasMaxSlotLabels 192 - - -struct ItVpdAreas { - u32 xSlicDesc; // Descriptor 000-003 - u16 xSlicSize; // Size of this control block 004-005 - u16 xPlicAdjustVpdLens:1; // Flag to indicate new interface006-007 - u16 xRsvd1:15; // Reserved bits ... - u16 xSlicVpdEntries; // Number of VPD entries 008-009 - u16 xSlicDmaEntries; // Number of DMA entries 00A-00B - u16 xSlicMaxLogicalProcs; // Maximum logical processors 00C-00D - u16 xSlicMaxPhysicalProcs; // Maximum physical processors 00E-00F - u16 xSlicDmaToksOffset; // Offset into this of array 010-011 - u16 xSlicVpdAdrsOffset; // Offset into this of array 012-013 - u16 xSlicDmaLensOffset; // Offset into this of array 014-015 - u16 xSlicVpdLensOffset; // Offset into this of array 016-017 - u16 xSlicMaxSlotLabels; // Maximum number of slot labels018-019 - u16 xSlicMaxLpQueues; // Maximum number of LP Queues 01A-01B - u8 xRsvd2[4]; // Reserved 01C-01F - u64 xRsvd3[12]; // Reserved 020-07F - u32 xPlicDmaLens[ItDmaMaxEntries];// Array of DMA lengths 080-0A7 - u32 xPlicDmaToks[ItDmaMaxEntries];// Array of DMA tokens 0A8-0CF - u32 xSlicVpdLens[ItVpdMaxEntries];// Array of VPD lengths 0D0-12F - const void *xSlicVpdAdrs[ItVpdMaxEntries];// Array of VPD buffers 130-1EF -}; - -extern const struct ItVpdAreas itVpdAreas; - -#endif /* _ISERIES_VPD_AREAS_H */ -- cgit v0.10.2 From e92a665949f6fabd2d25708e00239866f91a644f Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 7 Mar 2012 18:33:53 +0000 Subject: net: powerpc: remove the legacy iSeries ethernet driver This driver is specific to the PowerPC legcay iSeries platform which is being removed. Cc: David Miller Cc: Signed-off-by: Stephen Rothwell Acked-by: David S. Miller Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig index 9e16f3f..b9773d2 100644 --- a/drivers/net/ethernet/ibm/Kconfig +++ b/drivers/net/ethernet/ibm/Kconfig @@ -29,10 +29,6 @@ config IBMVETH To compile this driver as a module, choose M here. The module will be called ibmveth. -config ISERIES_VETH - tristate "iSeries Virtual Ethernet driver support" - depends on PPC_ISERIES - source "drivers/net/ethernet/ibm/emac/Kconfig" config EHEA diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile index 5a7d4e9..2f04e71 100644 --- a/drivers/net/ethernet/ibm/Makefile +++ b/drivers/net/ethernet/ibm/Makefile @@ -3,6 +3,5 @@ # obj-$(CONFIG_IBMVETH) += ibmveth.o -obj-$(CONFIG_ISERIES_VETH) += iseries_veth.o obj-$(CONFIG_IBM_EMAC) += emac/ obj-$(CONFIG_EHEA) += ehea/ diff --git a/drivers/net/ethernet/ibm/iseries_veth.c b/drivers/net/ethernet/ibm/iseries_veth.c deleted file mode 100644 index acc31af..0000000 --- a/drivers/net/ethernet/ibm/iseries_veth.c +++ /dev/null @@ -1,1710 +0,0 @@ -/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */ -/* - * IBM eServer iSeries Virtual Ethernet Device Driver - * Copyright (C) 2001 Kyle A. Lucke (klucke@us.ibm.com), IBM Corp. - * Substantially cleaned up by: - * Copyright (C) 2003 David Gibson , IBM Corporation. - * Copyright (C) 2004-2005 Michael Ellerman, IBM Corporation. - * - * 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. - * - * 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 - * - * - * This module implements the virtual ethernet device for iSeries LPAR - * Linux. It uses hypervisor message passing to implement an - * ethernet-like network device communicating between partitions on - * the iSeries. - * - * The iSeries LPAR hypervisor currently allows for up to 16 different - * virtual ethernets. These are all dynamically configurable on - * OS/400 partitions, but dynamic configuration is not supported under - * Linux yet. An ethXX network device will be created for each - * virtual ethernet this partition is connected to. - * - * - This driver is responsible for routing packets to and from other - * partitions. The MAC addresses used by the virtual ethernets - * contains meaning and must not be modified. - * - * - Having 2 virtual ethernets to the same remote partition DOES NOT - * double the available bandwidth. The 2 devices will share the - * available hypervisor bandwidth. - * - * - If you send a packet to your own mac address, it will just be - * dropped, you won't get it on the receive side. - * - * - Multicast is implemented by sending the frame frame to every - * other partition. It is the responsibility of the receiving - * partition to filter the addresses desired. - * - * Tunable parameters: - * - * VETH_NUMBUFFERS: This compile time option defaults to 120. It - * controls how much memory Linux will allocate per remote partition - * it is communicating with. It can be thought of as the maximum - * number of packets outstanding to a remote partition at a time. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#undef DEBUG - -MODULE_AUTHOR("Kyle Lucke "); -MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); -MODULE_LICENSE("GPL"); - -#define VETH_EVENT_CAP (0) -#define VETH_EVENT_FRAMES (1) -#define VETH_EVENT_MONITOR (2) -#define VETH_EVENT_FRAMES_ACK (3) - -#define VETH_MAX_ACKS_PER_MSG (20) -#define VETH_MAX_FRAMES_PER_MSG (6) - -struct veth_frames_data { - u32 addr[VETH_MAX_FRAMES_PER_MSG]; - u16 len[VETH_MAX_FRAMES_PER_MSG]; - u32 eofmask; -}; -#define VETH_EOF_SHIFT (32-VETH_MAX_FRAMES_PER_MSG) - -struct veth_frames_ack_data { - u16 token[VETH_MAX_ACKS_PER_MSG]; -}; - -struct veth_cap_data { - u8 caps_version; - u8 rsvd1; - u16 num_buffers; - u16 ack_threshold; - u16 rsvd2; - u32 ack_timeout; - u32 rsvd3; - u64 rsvd4[3]; -}; - -struct veth_lpevent { - struct HvLpEvent base_event; - union { - struct veth_cap_data caps_data; - struct veth_frames_data frames_data; - struct veth_frames_ack_data frames_ack_data; - } u; - -}; - -#define DRV_NAME "iseries_veth" -#define DRV_VERSION "2.0" - -#define VETH_NUMBUFFERS (120) -#define VETH_ACKTIMEOUT (1000000) /* microseconds */ -#define VETH_MAX_MCAST (12) - -#define VETH_MAX_MTU (9000) - -#if VETH_NUMBUFFERS < 10 -#define ACK_THRESHOLD (1) -#elif VETH_NUMBUFFERS < 20 -#define ACK_THRESHOLD (4) -#elif VETH_NUMBUFFERS < 40 -#define ACK_THRESHOLD (10) -#else -#define ACK_THRESHOLD (20) -#endif - -#define VETH_STATE_SHUTDOWN (0x0001) -#define VETH_STATE_OPEN (0x0002) -#define VETH_STATE_RESET (0x0004) -#define VETH_STATE_SENTMON (0x0008) -#define VETH_STATE_SENTCAPS (0x0010) -#define VETH_STATE_GOTCAPACK (0x0020) -#define VETH_STATE_GOTCAPS (0x0040) -#define VETH_STATE_SENTCAPACK (0x0080) -#define VETH_STATE_READY (0x0100) - -struct veth_msg { - struct veth_msg *next; - struct veth_frames_data data; - int token; - int in_use; - struct sk_buff *skb; - struct device *dev; -}; - -struct veth_lpar_connection { - HvLpIndex remote_lp; - struct delayed_work statemachine_wq; - struct veth_msg *msgs; - int num_events; - struct veth_cap_data local_caps; - - struct kobject kobject; - struct timer_list ack_timer; - - struct timer_list reset_timer; - unsigned int reset_timeout; - unsigned long last_contact; - int outstanding_tx; - - spinlock_t lock; - unsigned long state; - HvLpInstanceId src_inst; - HvLpInstanceId dst_inst; - struct veth_lpevent cap_event, cap_ack_event; - u16 pending_acks[VETH_MAX_ACKS_PER_MSG]; - u32 num_pending_acks; - - int num_ack_events; - struct veth_cap_data remote_caps; - u32 ack_timeout; - - struct veth_msg *msg_stack_head; -}; - -struct veth_port { - struct device *dev; - u64 mac_addr; - HvLpIndexMap lpar_map; - - /* queue_lock protects the stopped_map and dev's queue. */ - spinlock_t queue_lock; - HvLpIndexMap stopped_map; - - /* mcast_gate protects promiscuous, num_mcast & mcast_addr. */ - rwlock_t mcast_gate; - int promiscuous; - int num_mcast; - u64 mcast_addr[VETH_MAX_MCAST]; - - struct kobject kobject; -}; - -static HvLpIndex this_lp; -static struct veth_lpar_connection *veth_cnx[HVMAXARCHITECTEDLPS]; /* = 0 */ -static struct net_device *veth_dev[HVMAXARCHITECTEDVIRTUALLANS]; /* = 0 */ - -static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); -static void veth_recycle_msg(struct veth_lpar_connection *, struct veth_msg *); -static void veth_wake_queues(struct veth_lpar_connection *cnx); -static void veth_stop_queues(struct veth_lpar_connection *cnx); -static void veth_receive(struct veth_lpar_connection *, struct veth_lpevent *); -static void veth_release_connection(struct kobject *kobject); -static void veth_timed_ack(unsigned long ptr); -static void veth_timed_reset(unsigned long ptr); - -/* - * Utility functions - */ - -#define veth_info(fmt, args...) \ - printk(KERN_INFO DRV_NAME ": " fmt, ## args) - -#define veth_error(fmt, args...) \ - printk(KERN_ERR DRV_NAME ": Error: " fmt, ## args) - -#ifdef DEBUG -#define veth_debug(fmt, args...) \ - printk(KERN_DEBUG DRV_NAME ": " fmt, ## args) -#else -#define veth_debug(fmt, args...) do {} while (0) -#endif - -/* You must hold the connection's lock when you call this function. */ -static inline void veth_stack_push(struct veth_lpar_connection *cnx, - struct veth_msg *msg) -{ - msg->next = cnx->msg_stack_head; - cnx->msg_stack_head = msg; -} - -/* You must hold the connection's lock when you call this function. */ -static inline struct veth_msg *veth_stack_pop(struct veth_lpar_connection *cnx) -{ - struct veth_msg *msg; - - msg = cnx->msg_stack_head; - if (msg) - cnx->msg_stack_head = cnx->msg_stack_head->next; - - return msg; -} - -/* You must hold the connection's lock when you call this function. */ -static inline int veth_stack_is_empty(struct veth_lpar_connection *cnx) -{ - return cnx->msg_stack_head == NULL; -} - -static inline HvLpEvent_Rc -veth_signalevent(struct veth_lpar_connection *cnx, u16 subtype, - HvLpEvent_AckInd ackind, HvLpEvent_AckType acktype, - u64 token, - u64 data1, u64 data2, u64 data3, u64 data4, u64 data5) -{ - return HvCallEvent_signalLpEventFast(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - subtype, ackind, acktype, - cnx->src_inst, - cnx->dst_inst, - token, data1, data2, data3, - data4, data5); -} - -static inline HvLpEvent_Rc veth_signaldata(struct veth_lpar_connection *cnx, - u16 subtype, u64 token, void *data) -{ - u64 *p = (u64 *) data; - - return veth_signalevent(cnx, subtype, HvLpEvent_AckInd_NoAck, - HvLpEvent_AckType_ImmediateAck, - token, p[0], p[1], p[2], p[3], p[4]); -} - -struct veth_allocation { - struct completion c; - int num; -}; - -static void veth_complete_allocation(void *parm, int number) -{ - struct veth_allocation *vc = (struct veth_allocation *)parm; - - vc->num = number; - complete(&vc->c); -} - -static int veth_allocate_events(HvLpIndex rlp, int number) -{ - struct veth_allocation vc = - { COMPLETION_INITIALIZER_ONSTACK(vc.c), 0 }; - - mf_allocate_lp_events(rlp, HvLpEvent_Type_VirtualLan, - sizeof(struct veth_lpevent), number, - &veth_complete_allocation, &vc); - wait_for_completion(&vc.c); - - return vc.num; -} - -/* - * sysfs support - */ - -struct veth_cnx_attribute { - struct attribute attr; - ssize_t (*show)(struct veth_lpar_connection *, char *buf); - ssize_t (*store)(struct veth_lpar_connection *, const char *buf); -}; - -static ssize_t veth_cnx_attribute_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct veth_cnx_attribute *cnx_attr; - struct veth_lpar_connection *cnx; - - cnx_attr = container_of(attr, struct veth_cnx_attribute, attr); - cnx = container_of(kobj, struct veth_lpar_connection, kobject); - - if (!cnx_attr->show) - return -EIO; - - return cnx_attr->show(cnx, buf); -} - -#define CUSTOM_CNX_ATTR(_name, _format, _expression) \ -static ssize_t _name##_show(struct veth_lpar_connection *cnx, char *buf)\ -{ \ - return sprintf(buf, _format, _expression); \ -} \ -struct veth_cnx_attribute veth_cnx_attr_##_name = __ATTR_RO(_name) - -#define SIMPLE_CNX_ATTR(_name) \ - CUSTOM_CNX_ATTR(_name, "%lu\n", (unsigned long)cnx->_name) - -SIMPLE_CNX_ATTR(outstanding_tx); -SIMPLE_CNX_ATTR(remote_lp); -SIMPLE_CNX_ATTR(num_events); -SIMPLE_CNX_ATTR(src_inst); -SIMPLE_CNX_ATTR(dst_inst); -SIMPLE_CNX_ATTR(num_pending_acks); -SIMPLE_CNX_ATTR(num_ack_events); -CUSTOM_CNX_ATTR(ack_timeout, "%d\n", jiffies_to_msecs(cnx->ack_timeout)); -CUSTOM_CNX_ATTR(reset_timeout, "%d\n", jiffies_to_msecs(cnx->reset_timeout)); -CUSTOM_CNX_ATTR(state, "0x%.4lX\n", cnx->state); -CUSTOM_CNX_ATTR(last_contact, "%d\n", cnx->last_contact ? - jiffies_to_msecs(jiffies - cnx->last_contact) : 0); - -#define GET_CNX_ATTR(_name) (&veth_cnx_attr_##_name.attr) - -static struct attribute *veth_cnx_default_attrs[] = { - GET_CNX_ATTR(outstanding_tx), - GET_CNX_ATTR(remote_lp), - GET_CNX_ATTR(num_events), - GET_CNX_ATTR(reset_timeout), - GET_CNX_ATTR(last_contact), - GET_CNX_ATTR(state), - GET_CNX_ATTR(src_inst), - GET_CNX_ATTR(dst_inst), - GET_CNX_ATTR(num_pending_acks), - GET_CNX_ATTR(num_ack_events), - GET_CNX_ATTR(ack_timeout), - NULL -}; - -static const struct sysfs_ops veth_cnx_sysfs_ops = { - .show = veth_cnx_attribute_show -}; - -static struct kobj_type veth_lpar_connection_ktype = { - .release = veth_release_connection, - .sysfs_ops = &veth_cnx_sysfs_ops, - .default_attrs = veth_cnx_default_attrs -}; - -struct veth_port_attribute { - struct attribute attr; - ssize_t (*show)(struct veth_port *, char *buf); - ssize_t (*store)(struct veth_port *, const char *buf); -}; - -static ssize_t veth_port_attribute_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct veth_port_attribute *port_attr; - struct veth_port *port; - - port_attr = container_of(attr, struct veth_port_attribute, attr); - port = container_of(kobj, struct veth_port, kobject); - - if (!port_attr->show) - return -EIO; - - return port_attr->show(port, buf); -} - -#define CUSTOM_PORT_ATTR(_name, _format, _expression) \ -static ssize_t _name##_show(struct veth_port *port, char *buf) \ -{ \ - return sprintf(buf, _format, _expression); \ -} \ -struct veth_port_attribute veth_port_attr_##_name = __ATTR_RO(_name) - -#define SIMPLE_PORT_ATTR(_name) \ - CUSTOM_PORT_ATTR(_name, "%lu\n", (unsigned long)port->_name) - -SIMPLE_PORT_ATTR(promiscuous); -SIMPLE_PORT_ATTR(num_mcast); -CUSTOM_PORT_ATTR(lpar_map, "0x%X\n", port->lpar_map); -CUSTOM_PORT_ATTR(stopped_map, "0x%X\n", port->stopped_map); -CUSTOM_PORT_ATTR(mac_addr, "0x%llX\n", port->mac_addr); - -#define GET_PORT_ATTR(_name) (&veth_port_attr_##_name.attr) -static struct attribute *veth_port_default_attrs[] = { - GET_PORT_ATTR(mac_addr), - GET_PORT_ATTR(lpar_map), - GET_PORT_ATTR(stopped_map), - GET_PORT_ATTR(promiscuous), - GET_PORT_ATTR(num_mcast), - NULL -}; - -static const struct sysfs_ops veth_port_sysfs_ops = { - .show = veth_port_attribute_show -}; - -static struct kobj_type veth_port_ktype = { - .sysfs_ops = &veth_port_sysfs_ops, - .default_attrs = veth_port_default_attrs -}; - -/* - * LPAR connection code - */ - -static inline void veth_kick_statemachine(struct veth_lpar_connection *cnx) -{ - schedule_delayed_work(&cnx->statemachine_wq, 0); -} - -static void veth_take_cap(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - /* Receiving caps may mean the other end has just come up, so - * we need to reload the instance ID of the far end */ - cnx->dst_inst = - HvCallEvent_getTargetLpInstanceId(cnx->remote_lp, - HvLpEvent_Type_VirtualLan); - - if (cnx->state & VETH_STATE_GOTCAPS) { - veth_error("Received a second capabilities from LPAR %d.\n", - cnx->remote_lp); - event->base_event.xRc = HvLpEvent_Rc_BufferNotAvailable; - HvCallEvent_ackLpEvent((struct HvLpEvent *) event); - } else { - memcpy(&cnx->cap_event, event, sizeof(cnx->cap_event)); - cnx->state |= VETH_STATE_GOTCAPS; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_take_cap_ack(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - if (cnx->state & VETH_STATE_GOTCAPACK) { - veth_error("Received a second capabilities ack from LPAR %d.\n", - cnx->remote_lp); - } else { - memcpy(&cnx->cap_ack_event, event, - sizeof(cnx->cap_ack_event)); - cnx->state |= VETH_STATE_GOTCAPACK; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_take_monitor_ack(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - unsigned long flags; - - spin_lock_irqsave(&cnx->lock, flags); - veth_debug("cnx %d: lost connection.\n", cnx->remote_lp); - - /* Avoid kicking the statemachine once we're shutdown. - * It's unnecessary and it could break veth_stop_connection(). */ - - if (! (cnx->state & VETH_STATE_SHUTDOWN)) { - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); - } - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_handle_ack(struct veth_lpevent *event) -{ - HvLpIndex rlp = event->base_event.xTargetLp; - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - - BUG_ON(! cnx); - - switch (event->base_event.xSubtype) { - case VETH_EVENT_CAP: - veth_take_cap_ack(cnx, event); - break; - case VETH_EVENT_MONITOR: - veth_take_monitor_ack(cnx, event); - break; - default: - veth_error("Unknown ack type %d from LPAR %d.\n", - event->base_event.xSubtype, rlp); - } -} - -static void veth_handle_int(struct veth_lpevent *event) -{ - HvLpIndex rlp = event->base_event.xSourceLp; - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - unsigned long flags; - int i, acked = 0; - - BUG_ON(! cnx); - - switch (event->base_event.xSubtype) { - case VETH_EVENT_CAP: - veth_take_cap(cnx, event); - break; - case VETH_EVENT_MONITOR: - /* do nothing... this'll hang out here til we're dead, - * and the hypervisor will return it for us. */ - break; - case VETH_EVENT_FRAMES_ACK: - spin_lock_irqsave(&cnx->lock, flags); - - for (i = 0; i < VETH_MAX_ACKS_PER_MSG; ++i) { - u16 msgnum = event->u.frames_ack_data.token[i]; - - if (msgnum < VETH_NUMBUFFERS) { - veth_recycle_msg(cnx, cnx->msgs + msgnum); - cnx->outstanding_tx--; - acked++; - } - } - - if (acked > 0) { - cnx->last_contact = jiffies; - veth_wake_queues(cnx); - } - - spin_unlock_irqrestore(&cnx->lock, flags); - break; - case VETH_EVENT_FRAMES: - veth_receive(cnx, event); - break; - default: - veth_error("Unknown interrupt type %d from LPAR %d.\n", - event->base_event.xSubtype, rlp); - } -} - -static void veth_handle_event(struct HvLpEvent *event) -{ - struct veth_lpevent *veth_event = (struct veth_lpevent *)event; - - if (hvlpevent_is_ack(event)) - veth_handle_ack(veth_event); - else - veth_handle_int(veth_event); -} - -static int veth_process_caps(struct veth_lpar_connection *cnx) -{ - struct veth_cap_data *remote_caps = &cnx->remote_caps; - int num_acks_needed; - - /* Convert timer to jiffies */ - cnx->ack_timeout = remote_caps->ack_timeout * HZ / 1000000; - - if ( (remote_caps->num_buffers == 0) || - (remote_caps->ack_threshold > VETH_MAX_ACKS_PER_MSG) || - (remote_caps->ack_threshold == 0) || - (cnx->ack_timeout == 0) ) { - veth_error("Received incompatible capabilities from LPAR %d.\n", - cnx->remote_lp); - return HvLpEvent_Rc_InvalidSubtypeData; - } - - num_acks_needed = (remote_caps->num_buffers - / remote_caps->ack_threshold) + 1; - - /* FIXME: locking on num_ack_events? */ - if (cnx->num_ack_events < num_acks_needed) { - int num; - - num = veth_allocate_events(cnx->remote_lp, - num_acks_needed-cnx->num_ack_events); - if (num > 0) - cnx->num_ack_events += num; - - if (cnx->num_ack_events < num_acks_needed) { - veth_error("Couldn't allocate enough ack events " - "for LPAR %d.\n", cnx->remote_lp); - - return HvLpEvent_Rc_BufferNotAvailable; - } - } - - - return HvLpEvent_Rc_Good; -} - -/* FIXME: The gotos here are a bit dubious */ -static void veth_statemachine(struct work_struct *work) -{ - struct veth_lpar_connection *cnx = - container_of(work, struct veth_lpar_connection, - statemachine_wq.work); - int rlp = cnx->remote_lp; - int rc; - - spin_lock_irq(&cnx->lock); - - restart: - if (cnx->state & VETH_STATE_RESET) { - if (cnx->state & VETH_STATE_OPEN) - HvCallEvent_closeLpEventPath(cnx->remote_lp, - HvLpEvent_Type_VirtualLan); - - /* - * Reset ack data. This prevents the ack_timer actually - * doing anything, even if it runs one more time when - * we drop the lock below. - */ - memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); - cnx->num_pending_acks = 0; - - cnx->state &= ~(VETH_STATE_RESET | VETH_STATE_SENTMON - | VETH_STATE_OPEN | VETH_STATE_SENTCAPS - | VETH_STATE_GOTCAPACK | VETH_STATE_GOTCAPS - | VETH_STATE_SENTCAPACK | VETH_STATE_READY); - - /* Clean up any leftover messages */ - if (cnx->msgs) { - int i; - for (i = 0; i < VETH_NUMBUFFERS; ++i) - veth_recycle_msg(cnx, cnx->msgs + i); - } - - cnx->outstanding_tx = 0; - veth_wake_queues(cnx); - - /* Drop the lock so we can do stuff that might sleep or - * take other locks. */ - spin_unlock_irq(&cnx->lock); - - del_timer_sync(&cnx->ack_timer); - del_timer_sync(&cnx->reset_timer); - - spin_lock_irq(&cnx->lock); - - if (cnx->state & VETH_STATE_RESET) - goto restart; - - /* Hack, wait for the other end to reset itself. */ - if (! (cnx->state & VETH_STATE_SHUTDOWN)) { - schedule_delayed_work(&cnx->statemachine_wq, 5 * HZ); - goto out; - } - } - - if (cnx->state & VETH_STATE_SHUTDOWN) - /* It's all over, do nothing */ - goto out; - - if ( !(cnx->state & VETH_STATE_OPEN) ) { - if (! cnx->msgs || (cnx->num_events < (2 + VETH_NUMBUFFERS)) ) - goto cant_cope; - - HvCallEvent_openLpEventPath(rlp, HvLpEvent_Type_VirtualLan); - cnx->src_inst = - HvCallEvent_getSourceLpInstanceId(rlp, - HvLpEvent_Type_VirtualLan); - cnx->dst_inst = - HvCallEvent_getTargetLpInstanceId(rlp, - HvLpEvent_Type_VirtualLan); - cnx->state |= VETH_STATE_OPEN; - } - - if ( (cnx->state & VETH_STATE_OPEN) && - !(cnx->state & VETH_STATE_SENTMON) ) { - rc = veth_signalevent(cnx, VETH_EVENT_MONITOR, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_DeferredAck, - 0, 0, 0, 0, 0, 0); - - if (rc == HvLpEvent_Rc_Good) { - cnx->state |= VETH_STATE_SENTMON; - } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) && - (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending monitor to LPAR %d, " - "rc = %d\n", rlp, rc); - - /* Oh well, hope we get a cap from the other - * end and do better when that kicks us */ - goto out; - } - } - - if ( (cnx->state & VETH_STATE_OPEN) && - !(cnx->state & VETH_STATE_SENTCAPS)) { - u64 *rawcap = (u64 *)&cnx->local_caps; - - rc = veth_signalevent(cnx, VETH_EVENT_CAP, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - 0, rawcap[0], rawcap[1], rawcap[2], - rawcap[3], rawcap[4]); - - if (rc == HvLpEvent_Rc_Good) { - cnx->state |= VETH_STATE_SENTCAPS; - } else { - if ( (rc != HvLpEvent_Rc_PartitionDead) && - (rc != HvLpEvent_Rc_PathClosed) ) - veth_error("Error sending caps to LPAR %d, " - "rc = %d\n", rlp, rc); - - /* Oh well, hope we get a cap from the other - * end and do better when that kicks us */ - goto out; - } - } - - if ((cnx->state & VETH_STATE_GOTCAPS) && - !(cnx->state & VETH_STATE_SENTCAPACK)) { - struct veth_cap_data *remote_caps = &cnx->remote_caps; - - memcpy(remote_caps, &cnx->cap_event.u.caps_data, - sizeof(*remote_caps)); - - spin_unlock_irq(&cnx->lock); - rc = veth_process_caps(cnx); - spin_lock_irq(&cnx->lock); - - /* We dropped the lock, so recheck for anything which - * might mess us up */ - if (cnx->state & (VETH_STATE_RESET|VETH_STATE_SHUTDOWN)) - goto restart; - - cnx->cap_event.base_event.xRc = rc; - HvCallEvent_ackLpEvent((struct HvLpEvent *)&cnx->cap_event); - if (rc == HvLpEvent_Rc_Good) - cnx->state |= VETH_STATE_SENTCAPACK; - else - goto cant_cope; - } - - if ((cnx->state & VETH_STATE_GOTCAPACK) && - (cnx->state & VETH_STATE_GOTCAPS) && - !(cnx->state & VETH_STATE_READY)) { - if (cnx->cap_ack_event.base_event.xRc == HvLpEvent_Rc_Good) { - /* Start the ACK timer */ - cnx->ack_timer.expires = jiffies + cnx->ack_timeout; - add_timer(&cnx->ack_timer); - cnx->state |= VETH_STATE_READY; - } else { - veth_error("Caps rejected by LPAR %d, rc = %d\n", - rlp, cnx->cap_ack_event.base_event.xRc); - goto cant_cope; - } - } - - out: - spin_unlock_irq(&cnx->lock); - return; - - cant_cope: - /* FIXME: we get here if something happens we really can't - * cope with. The link will never work once we get here, and - * all we can do is not lock the rest of the system up */ - veth_error("Unrecoverable error on connection to LPAR %d, shutting down" - " (state = 0x%04lx)\n", rlp, cnx->state); - cnx->state |= VETH_STATE_SHUTDOWN; - spin_unlock_irq(&cnx->lock); -} - -static int veth_init_connection(u8 rlp) -{ - struct veth_lpar_connection *cnx; - struct veth_msg *msgs; - int i; - - if ( (rlp == this_lp) || - ! HvLpConfig_doLpsCommunicateOnVirtualLan(this_lp, rlp) ) - return 0; - - cnx = kzalloc(sizeof(*cnx), GFP_KERNEL); - if (! cnx) - return -ENOMEM; - - cnx->remote_lp = rlp; - spin_lock_init(&cnx->lock); - INIT_DELAYED_WORK(&cnx->statemachine_wq, veth_statemachine); - - init_timer(&cnx->ack_timer); - cnx->ack_timer.function = veth_timed_ack; - cnx->ack_timer.data = (unsigned long) cnx; - - init_timer(&cnx->reset_timer); - cnx->reset_timer.function = veth_timed_reset; - cnx->reset_timer.data = (unsigned long) cnx; - cnx->reset_timeout = 5 * HZ * (VETH_ACKTIMEOUT / 1000000); - - memset(&cnx->pending_acks, 0xff, sizeof (cnx->pending_acks)); - - veth_cnx[rlp] = cnx; - - /* This gets us 1 reference, which is held on behalf of the driver - * infrastructure. It's released at module unload. */ - kobject_init(&cnx->kobject, &veth_lpar_connection_ktype); - - msgs = kcalloc(VETH_NUMBUFFERS, sizeof(struct veth_msg), GFP_KERNEL); - if (! msgs) { - veth_error("Can't allocate buffers for LPAR %d.\n", rlp); - return -ENOMEM; - } - - cnx->msgs = msgs; - - for (i = 0; i < VETH_NUMBUFFERS; i++) { - msgs[i].token = i; - veth_stack_push(cnx, msgs + i); - } - - cnx->num_events = veth_allocate_events(rlp, 2 + VETH_NUMBUFFERS); - - if (cnx->num_events < (2 + VETH_NUMBUFFERS)) { - veth_error("Can't allocate enough events for LPAR %d.\n", rlp); - return -ENOMEM; - } - - cnx->local_caps.num_buffers = VETH_NUMBUFFERS; - cnx->local_caps.ack_threshold = ACK_THRESHOLD; - cnx->local_caps.ack_timeout = VETH_ACKTIMEOUT; - - return 0; -} - -static void veth_stop_connection(struct veth_lpar_connection *cnx) -{ - if (!cnx) - return; - - spin_lock_irq(&cnx->lock); - cnx->state |= VETH_STATE_RESET | VETH_STATE_SHUTDOWN; - veth_kick_statemachine(cnx); - spin_unlock_irq(&cnx->lock); - - /* ensure the statemachine runs now and waits for its completion */ - flush_delayed_work_sync(&cnx->statemachine_wq); -} - -static void veth_destroy_connection(struct veth_lpar_connection *cnx) -{ - if (!cnx) - return; - - if (cnx->num_events > 0) - mf_deallocate_lp_events(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - cnx->num_events, - NULL, NULL); - if (cnx->num_ack_events > 0) - mf_deallocate_lp_events(cnx->remote_lp, - HvLpEvent_Type_VirtualLan, - cnx->num_ack_events, - NULL, NULL); - - kfree(cnx->msgs); - veth_cnx[cnx->remote_lp] = NULL; - kfree(cnx); -} - -static void veth_release_connection(struct kobject *kobj) -{ - struct veth_lpar_connection *cnx; - cnx = container_of(kobj, struct veth_lpar_connection, kobject); - veth_stop_connection(cnx); - veth_destroy_connection(cnx); -} - -/* - * net_device code - */ - -static int veth_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int veth_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int veth_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > VETH_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - -static void veth_set_multicast_list(struct net_device *dev) -{ - struct veth_port *port = netdev_priv(dev); - unsigned long flags; - - write_lock_irqsave(&port->mcast_gate, flags); - - if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || - (netdev_mc_count(dev) > VETH_MAX_MCAST)) { - port->promiscuous = 1; - } else { - struct netdev_hw_addr *ha; - - port->promiscuous = 0; - - /* Update table */ - port->num_mcast = 0; - - netdev_for_each_mc_addr(ha, dev) { - u8 *addr = ha->addr; - u64 xaddr = 0; - - memcpy(&xaddr, addr, ETH_ALEN); - port->mcast_addr[port->num_mcast] = xaddr; - port->num_mcast++; - } - } - - write_unlock_irqrestore(&port->mcast_gate, flags); -} - -static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); - info->driver[sizeof(info->driver) - 1] = '\0'; - strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); - info->version[sizeof(info->version) - 1] = '\0'; -} - -static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) -{ - ecmd->supported = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd->advertising = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_INTERNAL; - ecmd->phy_address = 0; - ecmd->speed = SPEED_1000; - ecmd->duplex = DUPLEX_FULL; - ecmd->autoneg = AUTONEG_ENABLE; - ecmd->maxtxpkt = 120; - ecmd->maxrxpkt = 120; - return 0; -} - -static const struct ethtool_ops ops = { - .get_drvinfo = veth_get_drvinfo, - .get_settings = veth_get_settings, - .get_link = ethtool_op_get_link, -}; - -static const struct net_device_ops veth_netdev_ops = { - .ndo_open = veth_open, - .ndo_stop = veth_close, - .ndo_start_xmit = veth_start_xmit, - .ndo_change_mtu = veth_change_mtu, - .ndo_set_rx_mode = veth_set_multicast_list, - .ndo_set_mac_address = NULL, - .ndo_validate_addr = eth_validate_addr, -}; - -static struct net_device *veth_probe_one(int vlan, - struct vio_dev *vio_dev) -{ - struct net_device *dev; - struct veth_port *port; - struct device *vdev = &vio_dev->dev; - int i, rc; - const unsigned char *mac_addr; - - mac_addr = vio_get_attribute(vio_dev, "local-mac-address", NULL); - if (mac_addr == NULL) - mac_addr = vio_get_attribute(vio_dev, "mac-address", NULL); - if (mac_addr == NULL) { - veth_error("Unable to fetch MAC address from device tree.\n"); - return NULL; - } - - dev = alloc_etherdev(sizeof (struct veth_port)); - if (! dev) { - veth_error("Unable to allocate net_device structure!\n"); - return NULL; - } - - port = netdev_priv(dev); - - spin_lock_init(&port->queue_lock); - rwlock_init(&port->mcast_gate); - port->stopped_map = 0; - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - HvLpVirtualLanIndexMap map; - - if (i == this_lp) - continue; - map = HvLpConfig_getVirtualLanIndexMapForLp(i); - if (map & (0x8000 >> vlan)) - port->lpar_map |= (1 << i); - } - port->dev = vdev; - - memcpy(dev->dev_addr, mac_addr, ETH_ALEN); - - dev->mtu = VETH_MAX_MTU; - - memcpy(&port->mac_addr, mac_addr, ETH_ALEN); - - dev->netdev_ops = &veth_netdev_ops; - SET_ETHTOOL_OPS(dev, &ops); - - SET_NETDEV_DEV(dev, vdev); - - rc = register_netdev(dev); - if (rc != 0) { - veth_error("Failed registering net device for vlan%d.\n", vlan); - free_netdev(dev); - return NULL; - } - - kobject_init(&port->kobject, &veth_port_ktype); - if (0 != kobject_add(&port->kobject, &dev->dev.kobj, "veth_port")) - veth_error("Failed adding port for %s to sysfs.\n", dev->name); - - veth_info("%s attached to iSeries vlan %d (LPAR map = 0x%.4X)\n", - dev->name, vlan, port->lpar_map); - - return dev; -} - -/* - * Tx path - */ - -static int veth_transmit_to_one(struct sk_buff *skb, HvLpIndex rlp, - struct net_device *dev) -{ - struct veth_lpar_connection *cnx = veth_cnx[rlp]; - struct veth_port *port = netdev_priv(dev); - HvLpEvent_Rc rc; - struct veth_msg *msg = NULL; - unsigned long flags; - - if (! cnx) - return 0; - - spin_lock_irqsave(&cnx->lock, flags); - - if (! (cnx->state & VETH_STATE_READY)) - goto no_error; - - if ((skb->len - ETH_HLEN) > VETH_MAX_MTU) - goto drop; - - msg = veth_stack_pop(cnx); - if (! msg) - goto drop; - - msg->in_use = 1; - msg->skb = skb_get(skb); - - msg->data.addr[0] = dma_map_single(port->dev, skb->data, - skb->len, DMA_TO_DEVICE); - - if (dma_mapping_error(port->dev, msg->data.addr[0])) - goto recycle_and_drop; - - msg->dev = port->dev; - msg->data.len[0] = skb->len; - msg->data.eofmask = 1 << VETH_EOF_SHIFT; - - rc = veth_signaldata(cnx, VETH_EVENT_FRAMES, msg->token, &msg->data); - - if (rc != HvLpEvent_Rc_Good) - goto recycle_and_drop; - - /* If the timer's not already running, start it now. */ - if (0 == cnx->outstanding_tx) - mod_timer(&cnx->reset_timer, jiffies + cnx->reset_timeout); - - cnx->last_contact = jiffies; - cnx->outstanding_tx++; - - if (veth_stack_is_empty(cnx)) - veth_stop_queues(cnx); - - no_error: - spin_unlock_irqrestore(&cnx->lock, flags); - return 0; - - recycle_and_drop: - veth_recycle_msg(cnx, msg); - drop: - spin_unlock_irqrestore(&cnx->lock, flags); - return 1; -} - -static void veth_transmit_to_many(struct sk_buff *skb, - HvLpIndexMap lpmask, - struct net_device *dev) -{ - int i, success, error; - - success = error = 0; - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - if ((lpmask & (1 << i)) == 0) - continue; - - if (veth_transmit_to_one(skb, i, dev)) - error = 1; - else - success = 1; - } - - if (error) - dev->stats.tx_errors++; - - if (success) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - } -} - -static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *frame = skb->data; - struct veth_port *port = netdev_priv(dev); - HvLpIndexMap lpmask; - - if (is_unicast_ether_addr(frame)) { - /* unicast packet */ - HvLpIndex rlp = frame[5]; - - if ( ! ((1 << rlp) & port->lpar_map) ) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - lpmask = 1 << rlp; - } else { - lpmask = port->lpar_map; - } - - veth_transmit_to_many(skb, lpmask, dev); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* You must hold the connection's lock when you call this function. */ -static void veth_recycle_msg(struct veth_lpar_connection *cnx, - struct veth_msg *msg) -{ - u32 dma_address, dma_length; - - if (msg->in_use) { - msg->in_use = 0; - dma_address = msg->data.addr[0]; - dma_length = msg->data.len[0]; - - if (!dma_mapping_error(msg->dev, dma_address)) - dma_unmap_single(msg->dev, dma_address, dma_length, - DMA_TO_DEVICE); - - if (msg->skb) { - dev_kfree_skb_any(msg->skb); - msg->skb = NULL; - } - - memset(&msg->data, 0, sizeof(msg->data)); - veth_stack_push(cnx, msg); - } else if (cnx->state & VETH_STATE_OPEN) { - veth_error("Non-pending frame (# %d) acked by LPAR %d.\n", - cnx->remote_lp, msg->token); - } -} - -static void veth_wake_queues(struct veth_lpar_connection *cnx) -{ - int i; - - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { - struct net_device *dev = veth_dev[i]; - struct veth_port *port; - unsigned long flags; - - if (! dev) - continue; - - port = netdev_priv(dev); - - if (! (port->lpar_map & (1<remote_lp))) - continue; - - spin_lock_irqsave(&port->queue_lock, flags); - - port->stopped_map &= ~(1 << cnx->remote_lp); - - if (0 == port->stopped_map && netif_queue_stopped(dev)) { - veth_debug("cnx %d: woke queue for %s.\n", - cnx->remote_lp, dev->name); - netif_wake_queue(dev); - } - spin_unlock_irqrestore(&port->queue_lock, flags); - } -} - -static void veth_stop_queues(struct veth_lpar_connection *cnx) -{ - int i; - - for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++) { - struct net_device *dev = veth_dev[i]; - struct veth_port *port; - - if (! dev) - continue; - - port = netdev_priv(dev); - - /* If this cnx is not on the vlan for this port, continue */ - if (! (port->lpar_map & (1 << cnx->remote_lp))) - continue; - - spin_lock(&port->queue_lock); - - netif_stop_queue(dev); - port->stopped_map |= (1 << cnx->remote_lp); - - veth_debug("cnx %d: stopped queue for %s, map = 0x%x.\n", - cnx->remote_lp, dev->name, port->stopped_map); - - spin_unlock(&port->queue_lock); - } -} - -static void veth_timed_reset(unsigned long ptr) -{ - struct veth_lpar_connection *cnx = (struct veth_lpar_connection *)ptr; - unsigned long trigger_time, flags; - - /* FIXME is it possible this fires after veth_stop_connection()? - * That would reschedule the statemachine for 5 seconds and probably - * execute it after the module's been unloaded. Hmm. */ - - spin_lock_irqsave(&cnx->lock, flags); - - if (cnx->outstanding_tx > 0) { - trigger_time = cnx->last_contact + cnx->reset_timeout; - - if (trigger_time < jiffies) { - cnx->state |= VETH_STATE_RESET; - veth_kick_statemachine(cnx); - veth_error("%d packets not acked by LPAR %d within %d " - "seconds, resetting.\n", - cnx->outstanding_tx, cnx->remote_lp, - cnx->reset_timeout / HZ); - } else { - /* Reschedule the timer */ - trigger_time = jiffies + cnx->reset_timeout; - mod_timer(&cnx->reset_timer, trigger_time); - } - } - - spin_unlock_irqrestore(&cnx->lock, flags); -} - -/* - * Rx path - */ - -static inline int veth_frame_wanted(struct veth_port *port, u64 mac_addr) -{ - int wanted = 0; - int i; - unsigned long flags; - - if ( (mac_addr == port->mac_addr) || (mac_addr == 0xffffffffffff0000) ) - return 1; - - read_lock_irqsave(&port->mcast_gate, flags); - - if (port->promiscuous) { - wanted = 1; - goto out; - } - - for (i = 0; i < port->num_mcast; ++i) { - if (port->mcast_addr[i] == mac_addr) { - wanted = 1; - break; - } - } - - out: - read_unlock_irqrestore(&port->mcast_gate, flags); - - return wanted; -} - -struct dma_chunk { - u64 addr; - u64 size; -}; - -#define VETH_MAX_PAGES_PER_FRAME ( (VETH_MAX_MTU+PAGE_SIZE-2)/PAGE_SIZE + 1 ) - -static inline void veth_build_dma_list(struct dma_chunk *list, - unsigned char *p, unsigned long length) -{ - unsigned long done; - int i = 1; - - /* FIXME: skbs are contiguous in real addresses. Do we - * really need to break it into PAGE_SIZE chunks, or can we do - * it just at the granularity of iSeries real->absolute - * mapping? Indeed, given the way the allocator works, can we - * count on them being absolutely contiguous? */ - list[0].addr = iseries_hv_addr(p); - list[0].size = min(length, - PAGE_SIZE - ((unsigned long)p & ~PAGE_MASK)); - - done = list[0].size; - while (done < length) { - list[i].addr = iseries_hv_addr(p + done); - list[i].size = min(length-done, PAGE_SIZE); - done += list[i].size; - i++; - } -} - -static void veth_flush_acks(struct veth_lpar_connection *cnx) -{ - HvLpEvent_Rc rc; - - rc = veth_signaldata(cnx, VETH_EVENT_FRAMES_ACK, - 0, &cnx->pending_acks); - - if (rc != HvLpEvent_Rc_Good) - veth_error("Failed acking frames from LPAR %d, rc = %d\n", - cnx->remote_lp, (int)rc); - - cnx->num_pending_acks = 0; - memset(&cnx->pending_acks, 0xff, sizeof(cnx->pending_acks)); -} - -static void veth_receive(struct veth_lpar_connection *cnx, - struct veth_lpevent *event) -{ - struct veth_frames_data *senddata = &event->u.frames_data; - int startchunk = 0; - int nchunks; - unsigned long flags; - HvLpDma_Rc rc; - - do { - u16 length = 0; - struct sk_buff *skb; - struct dma_chunk local_list[VETH_MAX_PAGES_PER_FRAME]; - struct dma_chunk remote_list[VETH_MAX_FRAMES_PER_MSG]; - u64 dest; - HvLpVirtualLanIndex vlan; - struct net_device *dev; - struct veth_port *port; - - /* FIXME: do we need this? */ - memset(local_list, 0, sizeof(local_list)); - memset(remote_list, 0, sizeof(remote_list)); - - /* a 0 address marks the end of the valid entries */ - if (senddata->addr[startchunk] == 0) - break; - - /* make sure that we have at least 1 EOF entry in the - * remaining entries */ - if (! (senddata->eofmask >> (startchunk + VETH_EOF_SHIFT))) { - veth_error("Missing EOF fragment in event " - "eofmask = 0x%x startchunk = %d\n", - (unsigned)senddata->eofmask, - startchunk); - break; - } - - /* build list of chunks in this frame */ - nchunks = 0; - do { - remote_list[nchunks].addr = - (u64) senddata->addr[startchunk+nchunks] << 32; - remote_list[nchunks].size = - senddata->len[startchunk+nchunks]; - length += remote_list[nchunks].size; - } while (! (senddata->eofmask & - (1 << (VETH_EOF_SHIFT + startchunk + nchunks++)))); - - /* length == total length of all chunks */ - /* nchunks == # of chunks in this frame */ - - if ((length - ETH_HLEN) > VETH_MAX_MTU) { - veth_error("Received oversize frame from LPAR %d " - "(length = %d)\n", - cnx->remote_lp, length); - continue; - } - - skb = alloc_skb(length, GFP_ATOMIC); - if (!skb) - continue; - - veth_build_dma_list(local_list, skb->data, length); - - rc = HvCallEvent_dmaBufList(HvLpEvent_Type_VirtualLan, - event->base_event.xSourceLp, - HvLpDma_Direction_RemoteToLocal, - cnx->src_inst, - cnx->dst_inst, - HvLpDma_AddressType_RealAddress, - HvLpDma_AddressType_TceIndex, - iseries_hv_addr(&local_list), - iseries_hv_addr(&remote_list), - length); - if (rc != HvLpDma_Rc_Good) { - dev_kfree_skb_irq(skb); - continue; - } - - vlan = skb->data[9]; - dev = veth_dev[vlan]; - if (! dev) { - /* - * Some earlier versions of the driver sent - * broadcasts down all connections, even to lpars - * that weren't on the relevant vlan. So ignore - * packets belonging to a vlan we're not on. - * We can also be here if we receive packets while - * the driver is going down, because then dev is NULL. - */ - dev_kfree_skb_irq(skb); - continue; - } - - port = netdev_priv(dev); - dest = *((u64 *) skb->data) & 0xFFFFFFFFFFFF0000; - - if ((vlan > HVMAXARCHITECTEDVIRTUALLANS) || !port) { - dev_kfree_skb_irq(skb); - continue; - } - if (! veth_frame_wanted(port, dest)) { - dev_kfree_skb_irq(skb); - continue; - } - - skb_put(skb, length); - skb->protocol = eth_type_trans(skb, dev); - skb_checksum_none_assert(skb); - netif_rx(skb); /* send it up */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += length; - } while (startchunk += nchunks, startchunk < VETH_MAX_FRAMES_PER_MSG); - - /* Ack it */ - spin_lock_irqsave(&cnx->lock, flags); - BUG_ON(cnx->num_pending_acks > VETH_MAX_ACKS_PER_MSG); - - cnx->pending_acks[cnx->num_pending_acks++] = - event->base_event.xCorrelationToken; - - if ( (cnx->num_pending_acks >= cnx->remote_caps.ack_threshold) || - (cnx->num_pending_acks >= VETH_MAX_ACKS_PER_MSG) ) - veth_flush_acks(cnx); - - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static void veth_timed_ack(unsigned long ptr) -{ - struct veth_lpar_connection *cnx = (struct veth_lpar_connection *) ptr; - unsigned long flags; - - /* Ack all the events */ - spin_lock_irqsave(&cnx->lock, flags); - if (cnx->num_pending_acks > 0) - veth_flush_acks(cnx); - - /* Reschedule the timer */ - cnx->ack_timer.expires = jiffies + cnx->ack_timeout; - add_timer(&cnx->ack_timer); - spin_unlock_irqrestore(&cnx->lock, flags); -} - -static int veth_remove(struct vio_dev *vdev) -{ - struct veth_lpar_connection *cnx; - struct net_device *dev; - struct veth_port *port; - int i; - - dev = veth_dev[vdev->unit_address]; - - if (! dev) - return 0; - - port = netdev_priv(dev); - - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - cnx = veth_cnx[i]; - - if (cnx && (port->lpar_map & (1 << i))) { - /* Drop our reference to connections on our VLAN */ - kobject_put(&cnx->kobject); - } - } - - veth_dev[vdev->unit_address] = NULL; - kobject_del(&port->kobject); - kobject_put(&port->kobject); - unregister_netdev(dev); - free_netdev(dev); - - return 0; -} - -static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) -{ - int i = vdev->unit_address; - struct net_device *dev; - struct veth_port *port; - - dev = veth_probe_one(i, vdev); - if (dev == NULL) { - veth_remove(vdev); - return 1; - } - veth_dev[i] = dev; - - port = netdev_priv(dev); - - /* Start the state machine on each connection on this vlan. If we're - * the first dev to do so this will commence link negotiation */ - for (i = 0; i < HVMAXARCHITECTEDLPS; i++) { - struct veth_lpar_connection *cnx; - - if (! (port->lpar_map & (1 << i))) - continue; - - cnx = veth_cnx[i]; - if (!cnx) - continue; - - kobject_get(&cnx->kobject); - veth_kick_statemachine(cnx); - } - - return 0; -} - -/** - * veth_device_table: Used by vio.c to match devices that we - * support. - */ -static struct vio_device_id veth_device_table[] __devinitdata = { - { "network", "IBM,iSeries-l-lan" }, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, veth_device_table); - -static struct vio_driver veth_driver = { - .id_table = veth_device_table, - .probe = veth_probe, - .remove = veth_remove, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - } -}; - -/* - * Module initialization/cleanup - */ - -static void __exit veth_module_cleanup(void) -{ - int i; - struct veth_lpar_connection *cnx; - - /* Disconnect our "irq" to stop events coming from the Hypervisor. */ - HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - cnx = veth_cnx[i]; - - if (!cnx) - continue; - - /* Cancel work queued from Hypervisor callbacks */ - cancel_delayed_work_sync(&cnx->statemachine_wq); - /* Remove the connection from sysfs */ - kobject_del(&cnx->kobject); - /* Drop the driver's reference to the connection */ - kobject_put(&cnx->kobject); - } - - /* Unregister the driver, which will close all the netdevs and stop - * the connections when they're no longer referenced. */ - vio_unregister_driver(&veth_driver); -} -module_exit(veth_module_cleanup); - -static int __init veth_module_init(void) -{ - int i; - int rc; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - - this_lp = HvLpConfig_getLpIndex_outline(); - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - rc = veth_init_connection(i); - if (rc != 0) - goto error; - } - - HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, - &veth_handle_event); - - rc = vio_register_driver(&veth_driver); - if (rc != 0) - goto error; - - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - struct kobject *kobj; - - if (!veth_cnx[i]) - continue; - - kobj = &veth_cnx[i]->kobject; - /* If the add failes, complain but otherwise continue */ - if (0 != driver_add_kobj(&veth_driver.driver, kobj, - "cnx%.2d", veth_cnx[i]->remote_lp)) - veth_error("cnx %d: Failed adding to sysfs.\n", i); - } - - return 0; - -error: - for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) { - veth_destroy_connection(veth_cnx[i]); - } - - return rc; -} -module_init(veth_module_init); -- cgit v0.10.2 From 7834799a6c8fed36d06d6b78bfd17831ecfa93c4 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 7 Mar 2012 18:35:38 +0000 Subject: powerpc: remove the legacy iSeries part of ibmvscsi The PowerPC legacy iSeries platform is being removed and this code is no longer selectable. There is more clean up that can be done, but this just gets the old code out of the way. Cc: "James E.J. Bottomley" Cc: Brian King Cc: linux-scsi@vger.kernel.org Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 16570aa..e38d843 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -967,9 +967,8 @@ config SCSI_IPS config SCSI_IBMVSCSI tristate "IBM Virtual SCSI support" - depends on PPC_PSERIES || PPC_ISERIES + depends on PPC_PSERIES select SCSI_SRP_ATTRS - select VIOPATH if PPC_ISERIES help This is the IBM POWER Virtual SCSI Client diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile index a423d96..ff5b5c5 100644 --- a/drivers/scsi/ibmvscsi/Makefile +++ b/drivers/scsi/ibmvscsi/Makefile @@ -1,7 +1,6 @@ obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic.o ibmvscsic-y += ibmvscsi.o -ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvstgt.o diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 3d391dc..e984951 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -55,13 +55,7 @@ * and sends a CRQ message back to inform the client that the request has * completed. * - * Note that some of the underlying infrastructure is different between - * machines conforming to the "RS/6000 Platform Architecture" (RPA) and - * the older iSeries hypervisor models. To support both, some low level - * routines have been broken out into rpa_vscsi.c and iseries_vscsi.c. - * The Makefile should pick one, not two, not zero, of these. - * - * TODO: This is currently pretty tied to the IBM i/pSeries hypervisor + * TODO: This is currently pretty tied to the IBM pSeries hypervisor * interfaces. It would be really nice to abstract this above an RDMA * layer. */ @@ -2085,9 +2079,7 @@ int __init ibmvscsi_module_init(void) driver_template.can_queue = max_requests; max_events = max_requests + 2; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - ibmvscsi_ops = &iseriesvscsi_ops; - else if (firmware_has_feature(FW_FEATURE_VIO)) + if (firmware_has_feature(FW_FEATURE_VIO)) ibmvscsi_ops = &rpavscsi_ops; else return -ENODEV; diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h index 02197a2..c503e17 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.h +++ b/drivers/scsi/ibmvscsi/ibmvscsi.h @@ -127,7 +127,6 @@ struct ibmvscsi_ops { int (*resume) (struct ibmvscsi_host_data *hostdata); }; -extern struct ibmvscsi_ops iseriesvscsi_ops; extern struct ibmvscsi_ops rpavscsi_ops; #endif /* IBMVSCSI_H */ diff --git a/drivers/scsi/ibmvscsi/iseries_vscsi.c b/drivers/scsi/ibmvscsi/iseries_vscsi.c deleted file mode 100644 index f477645..0000000 --- a/drivers/scsi/ibmvscsi/iseries_vscsi.c +++ /dev/null @@ -1,173 +0,0 @@ -/* ------------------------------------------------------------ - * iSeries_vscsi.c - * (C) Copyright IBM Corporation 1994, 2003 - * Authors: Colin DeVilbiss (devilbis@us.ibm.com) - * Santiago Leon (santil@us.ibm.com) - * Dave Boutcher (sleddog@us.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. - * - * 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 - * - * ------------------------------------------------------------ - * iSeries-specific functions of the SCSI host adapter for Virtual I/O devices - * - * This driver allows the Linux SCSI peripheral drivers to directly - * access devices in the hosting partition, either on an iSeries - * hypervisor system or a converged hypervisor system. - */ - -#include -#include -#include -#include -#include -#include -#include "ibmvscsi.h" - -/* global variables */ -static struct ibmvscsi_host_data *single_host_data; - -/* ------------------------------------------------------------ - * Routines for direct interpartition interaction - */ -struct srp_lp_event { - struct HvLpEvent lpevt; /* 0x00-0x17 */ - u32 reserved1; /* 0x18-0x1B; unused */ - u16 version; /* 0x1C-0x1D; unused */ - u16 subtype_rc; /* 0x1E-0x1F; unused */ - struct viosrp_crq crq; /* 0x20-0x3F */ -}; - -/** - * standard interface for handling logical partition events. - */ -static void iseriesvscsi_handle_event(struct HvLpEvent *lpevt) -{ - struct srp_lp_event *evt = (struct srp_lp_event *)lpevt; - - if (!evt) { - printk(KERN_ERR "ibmvscsi: received null event\n"); - return; - } - - if (single_host_data == NULL) { - printk(KERN_ERR - "ibmvscsi: received event, no adapter present\n"); - return; - } - - ibmvscsi_handle_crq(&evt->crq, single_host_data); -} - -/* ------------------------------------------------------------ - * Routines for driver initialization - */ -static int iseriesvscsi_init_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests) -{ - int rc; - - single_host_data = hostdata; - rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, max_requests); - if (rc < 0) { - printk("viopath_open failed with rc %d in open_event_path\n", - rc); - goto viopath_open_failed; - } - - rc = vio_setHandler(viomajorsubtype_scsi, iseriesvscsi_handle_event); - if (rc < 0) { - printk("vio_setHandler failed with rc %d in open_event_path\n", - rc); - goto vio_setHandler_failed; - } - return 0; - - vio_setHandler_failed: - viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); - viopath_open_failed: - return -1; -} - -static void iseriesvscsi_release_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata, - int max_requests) -{ - vio_clearHandler(viomajorsubtype_scsi); - viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); -} - -/** - * reset_crq_queue: - resets a crq after a failure - * @queue: crq_queue to initialize and register - * @hostdata: ibmvscsi_host_data of host - * - * no-op for iSeries - */ -static int iseriesvscsi_reset_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata) -{ - return 0; -} - -/** - * reenable_crq_queue: - reenables a crq after a failure - * @queue: crq_queue to initialize and register - * @hostdata: ibmvscsi_host_data of host - * - * no-op for iSeries - */ -static int iseriesvscsi_reenable_crq_queue(struct crq_queue *queue, - struct ibmvscsi_host_data *hostdata) -{ - return 0; -} - -/** - * iseriesvscsi_send_crq: - Send a CRQ - * @hostdata: the adapter - * @word1: the first 64 bits of the data - * @word2: the second 64 bits of the data - */ -static int iseriesvscsi_send_crq(struct ibmvscsi_host_data *hostdata, - u64 word1, u64 word2) -{ - single_host_data = hostdata; - return HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_scsi, - HvLpEvent_AckInd_NoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - 0, - VIOVERSION << 16, word1, word2, 0, - 0); -} - -static int iseriesvscsi_resume(struct ibmvscsi_host_data *hostdata) -{ - return 0; -} - -struct ibmvscsi_ops iseriesvscsi_ops = { - .init_crq_queue = iseriesvscsi_init_crq_queue, - .release_crq_queue = iseriesvscsi_release_crq_queue, - .reset_crq_queue = iseriesvscsi_reset_crq_queue, - .reenable_crq_queue = iseriesvscsi_reenable_crq_queue, - .send_crq = iseriesvscsi_send_crq, - .resume = iseriesvscsi_resume, -}; -- cgit v0.10.2 From b66808910dc76150acc81ebf443f401812442bbf Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 7 Mar 2012 18:37:40 +0000 Subject: tty: powerpc: remove hvc_iseries The PowerPC legacy iSeries platform is being removed, so this code is no longer needed. Cc: Greg Kroah-Hartman Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 4222035..8ea7f07 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -24,16 +24,6 @@ config HVC_OLD_HVSI depends on HVC_CONSOLE default n -config HVC_ISERIES - bool "iSeries Hypervisor Virtual Console support" - depends on PPC_ISERIES - default y - select HVC_DRIVER - select HVC_IRQ - select VIOPATH - help - iSeries machines support a hypervisor virtual console. - config HVC_OPAL bool "OPAL Console support" depends on PPC_POWERNV diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index 89abf40b..4ca3723 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile @@ -1,7 +1,6 @@ obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o obj-$(CONFIG_HVC_OPAL) += hvc_opal.o hvsi_lib.o obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o -obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o obj-$(CONFIG_HVC_TILE) += hvc_tile.o obj-$(CONFIG_HVC_DCC) += hvc_dcc.o diff --git a/drivers/tty/hvc/hvc_iseries.c b/drivers/tty/hvc/hvc_iseries.c deleted file mode 100644 index 3f4a897..0000000 --- a/drivers/tty/hvc/hvc_iseries.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * iSeries vio driver interface to hvc_console.c - * - * This code is based heavily on hvc_vio.c and viocons.c - * - * Copyright (C) 2006 Stephen Rothwell, IBM Corporation - * - * 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. - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hvc_console.h" - -#define VTTY_PORTS 10 - -static DEFINE_SPINLOCK(consolelock); -static DEFINE_SPINLOCK(consoleloglock); - -static const char hvc_driver_name[] = "hvc_console"; - -#define IN_BUF_SIZE 200 - -/* - * Our port information. - */ -static struct port_info { - HvLpIndex lp; - u64 seq; /* sequence number of last HV send */ - u64 ack; /* last ack from HV */ - struct hvc_struct *hp; - int in_start; - int in_end; - unsigned char in_buf[IN_BUF_SIZE]; -} port_info[VTTY_PORTS] = { - [ 0 ... VTTY_PORTS - 1 ] = { - .lp = HvLpIndexInvalid - } -}; - -#define viochar_is_console(pi) ((pi) == &port_info[0]) - -static struct vio_device_id hvc_driver_table[] __devinitdata = { - {"serial", "IBM,iSeries-vty"}, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, hvc_driver_table); - -static void hvlog(char *fmt, ...) -{ - int i; - unsigned long flags; - va_list args; - static char buf[256]; - - spin_lock_irqsave(&consoleloglock, flags); - va_start(args, fmt); - i = vscnprintf(buf, sizeof(buf) - 1, fmt, args); - va_end(args); - buf[i++] = '\r'; - HvCall_writeLogBuffer(buf, i); - spin_unlock_irqrestore(&consoleloglock, flags); -} - -/* - * Initialize the common fields in a charLpEvent - */ -static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp) -{ - struct HvLpEvent *hev = &viochar->event; - - memset(viochar, 0, sizeof(struct viocharlpevent)); - - hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK | - HV_LP_EVENT_INT; - hev->xType = HvLpEvent_Type_VirtualIo; - hev->xSubtype = viomajorsubtype_chario | viochardata; - hev->xSourceLp = HvLpConfig_getLpIndex(); - hev->xTargetLp = lp; - hev->xSizeMinus1 = sizeof(struct viocharlpevent); - hev->xSourceInstanceId = viopath_sourceinst(lp); - hev->xTargetInstanceId = viopath_targetinst(lp); -} - -static int get_chars(uint32_t vtermno, char *buf, int count) -{ - struct port_info *pi; - int n = 0; - unsigned long flags; - - if (vtermno >= VTTY_PORTS) - return -EINVAL; - if (count == 0) - return 0; - - pi = &port_info[vtermno]; - spin_lock_irqsave(&consolelock, flags); - - if (pi->in_end == 0) - goto done; - - n = pi->in_end - pi->in_start; - if (n > count) - n = count; - memcpy(buf, &pi->in_buf[pi->in_start], n); - pi->in_start += n; - if (pi->in_start == pi->in_end) { - pi->in_start = 0; - pi->in_end = 0; - } -done: - spin_unlock_irqrestore(&consolelock, flags); - return n; -} - -static int put_chars(uint32_t vtermno, const char *buf, int count) -{ - struct viocharlpevent *viochar; - struct port_info *pi; - HvLpEvent_Rc hvrc; - unsigned long flags; - int sent = 0; - - if (vtermno >= VTTY_PORTS) - return -EINVAL; - - pi = &port_info[vtermno]; - - spin_lock_irqsave(&consolelock, flags); - - if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) { - HvCall_writeLogBuffer(buf, count); - sent = count; - goto done; - } - - viochar = vio_get_event_buffer(viomajorsubtype_chario); - if (viochar == NULL) { - hvlog("\n\rviocons: Can't get viochar buffer."); - goto done; - } - - while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) { - int len; - - len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count; - - if (viochar_is_console(pi)) - HvCall_writeLogBuffer(buf, len); - - init_data_event(viochar, pi->lp); - - viochar->len = len; - viochar->event.xCorrelationToken = pi->seq++; - viochar->event.xSizeMinus1 = - offsetof(struct viocharlpevent, data) + len; - - memcpy(viochar->data, buf, len); - - hvrc = HvCallEvent_signalLpEvent(&viochar->event); - if (hvrc) - hvlog("\n\rerror sending event! return code %d\n\r", - (int)hvrc); - sent += len; - count -= len; - buf += len; - } - - vio_free_event_buffer(viomajorsubtype_chario, viochar); -done: - spin_unlock_irqrestore(&consolelock, flags); - return sent; -} - -static const struct hv_ops hvc_get_put_ops = { - .get_chars = get_chars, - .put_chars = put_chars, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int __devinit hvc_vio_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - struct hvc_struct *hp; - struct port_info *pi; - - /* probed with invalid parameters. */ - if (!vdev || !id) - return -EPERM; - - if (vdev->unit_address >= VTTY_PORTS) - return -ENODEV; - - pi = &port_info[vdev->unit_address]; - - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, - VIOCHAR_MAX_DATA); - if (IS_ERR(hp)) - return PTR_ERR(hp); - pi->hp = hp; - dev_set_drvdata(&vdev->dev, pi); - - return 0; -} - -static int __devexit hvc_vio_remove(struct vio_dev *vdev) -{ - struct port_info *pi = dev_get_drvdata(&vdev->dev); - struct hvc_struct *hp = pi->hp; - - return hvc_remove(hp); -} - -static struct vio_driver hvc_vio_driver = { - .id_table = hvc_driver_table, - .probe = hvc_vio_probe, - .remove = __devexit_p(hvc_vio_remove), - .driver = { - .name = hvc_driver_name, - .owner = THIS_MODULE, - } -}; - -static void hvc_open_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtual_device; - struct port_info *pi; - int reject = 0; - - if (hvlpevent_is_ack(event)) { - if (port >= VTTY_PORTS) - return; - - spin_lock_irqsave(&consolelock, flags); - - pi = &port_info[port]; - if (event->xRc == HvLpEvent_Rc_Good) { - pi->seq = pi->ack = 0; - /* - * This line allows connections from the primary - * partition but once one is connected from the - * primary partition nothing short of a reboot - * of linux will allow access from the hosting - * partition again without a required iSeries fix. - */ - pi->lp = event->xTargetLp; - } - - spin_unlock_irqrestore(&consolelock, flags); - if (event->xRc != HvLpEvent_Rc_Good) - printk(KERN_WARNING - "hvc: handle_open_event: event->xRc == (%d).\n", - event->xRc); - - if (event->xCorrelationToken != 0) { - atomic_t *aptr= (atomic_t *)event->xCorrelationToken; - atomic_set(aptr, 1); - } else - printk(KERN_WARNING - "hvc: weird...got open ack without atomic\n"); - return; - } - - /* This had better require an ack, otherwise complain */ - if (!hvlpevent_need_ack(event)) { - printk(KERN_WARNING "hvc: viocharopen without ack bit!\n"); - return; - } - - spin_lock_irqsave(&consolelock, flags); - - /* Make sure this is a good virtual tty */ - if (port >= VTTY_PORTS) { - event->xRc = HvLpEvent_Rc_SubtypeError; - cevent->subtype_result_code = viorc_openRejected; - /* - * Flag state here since we can't printk while holding - * the consolelock spinlock. - */ - reject = 1; - } else { - pi = &port_info[port]; - if ((pi->lp != HvLpIndexInvalid) && - (pi->lp != event->xSourceLp)) { - /* - * If this is tty is already connected to a different - * partition, fail. - */ - event->xRc = HvLpEvent_Rc_SubtypeError; - cevent->subtype_result_code = viorc_openRejected; - reject = 2; - } else { - pi->lp = event->xSourceLp; - event->xRc = HvLpEvent_Rc_Good; - cevent->subtype_result_code = viorc_good; - pi->seq = pi->ack = 0; - } - } - - spin_unlock_irqrestore(&consolelock, flags); - - if (reject == 1) - printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n"); - else if (reject == 2) - printk(KERN_WARNING "hvc: open rejected: console in exclusive " - "use by another partition.\n"); - - /* Return the acknowledgement */ - HvCallEvent_ackLpEvent(event); -} - -/* - * Handle a close charLpEvent. This should ONLY be an Interrupt because the - * virtual console should never actually issue a close event to the hypervisor - * because the virtual console never goes away. A close event coming from the - * hypervisor simply means that there are no client consoles connected to the - * virtual console. - */ -static void hvc_close_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtual_device; - - if (!hvlpevent_is_int(event)) { - printk(KERN_WARNING - "hvc: got unexpected close acknowledgement\n"); - return; - } - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING - "hvc: close message from invalid virtual device.\n"); - return; - } - - /* For closes, just mark the console partition invalid */ - spin_lock_irqsave(&consolelock, flags); - - if (port_info[port].lp == event->xSourceLp) - port_info[port].lp = HvLpIndexInvalid; - - spin_unlock_irqrestore(&consolelock, flags); -} - -static void hvc_data_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - struct port_info *pi; - int n; - u8 port = cevent->virtual_device; - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING "hvc: data on invalid virtual device %d\n", - port); - return; - } - if (cevent->len == 0) - return; - - /* - * Change 05/01/2003 - Ryan Arnold: If a partition other than - * the current exclusive partition tries to send us data - * events then just drop them on the floor because we don't - * want his stinking data. He isn't authorized to receive - * data because he wasn't the first one to get the console, - * therefore he shouldn't be allowed to send data either. - * This will work without an iSeries fix. - */ - pi = &port_info[port]; - if (pi->lp != event->xSourceLp) - return; - - spin_lock_irqsave(&consolelock, flags); - - n = IN_BUF_SIZE - pi->in_end; - if (n > cevent->len) - n = cevent->len; - if (n > 0) { - memcpy(&pi->in_buf[pi->in_end], cevent->data, n); - pi->in_end += n; - } - spin_unlock_irqrestore(&consolelock, flags); - if (n == 0) - printk(KERN_WARNING "hvc: input buffer overflow\n"); -} - -static void hvc_ack_event(struct HvLpEvent *event) -{ - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - unsigned long flags; - u8 port = cevent->virtual_device; - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING "hvc: data on invalid virtual device\n"); - return; - } - - spin_lock_irqsave(&consolelock, flags); - port_info[port].ack = event->xCorrelationToken; - spin_unlock_irqrestore(&consolelock, flags); -} - -static void hvc_config_event(struct HvLpEvent *event) -{ - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - - if (cevent->data[0] == 0x01) - printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n", - cevent->data[1], cevent->data[2], - cevent->data[3], cevent->data[4]); - else - printk(KERN_WARNING "hvc: unknown config event\n"); -} - -static void hvc_handle_event(struct HvLpEvent *event) -{ - int charminor; - - if (event == NULL) - return; - - charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; - switch (charminor) { - case viocharopen: - hvc_open_event(event); - break; - case viocharclose: - hvc_close_event(event); - break; - case viochardata: - hvc_data_event(event); - break; - case viocharack: - hvc_ack_event(event); - break; - case viocharconfig: - hvc_config_event(event); - break; - default: - if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - -static int __init send_open(HvLpIndex remoteLp, void *sem) -{ - return HvCallEvent_signalLpEventFast(remoteLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_chario | viocharopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(remoteLp), - viopath_targetinst(remoteLp), - (u64)(unsigned long)sem, VIOVERSION << 16, - 0, 0, 0, 0); -} - -static int __init hvc_vio_init(void) -{ - atomic_t wait_flag; - int rc; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - - /* +2 for fudge */ - rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), - viomajorsubtype_chario, VIOCHAR_WINDOW + 2); - if (rc) - printk(KERN_WARNING "hvc: error opening to primary %d\n", rc); - - if (viopath_hostLp == HvLpIndexInvalid) - vio_set_hostlp(); - - /* - * And if the primary is not the same as the hosting LP, open to the - * hosting lp - */ - if ((viopath_hostLp != HvLpIndexInvalid) && - (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { - printk(KERN_INFO "hvc: open path to hosting (%d)\n", - viopath_hostLp); - rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, - VIOCHAR_WINDOW + 2); /* +2 for fudge */ - if (rc) - printk(KERN_WARNING - "error opening to partition %d: %d\n", - viopath_hostLp, rc); - } - - if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0) - printk(KERN_WARNING - "hvc: error seting handler for console events!\n"); - - /* - * First, try to open the console to the hosting lp. - * Wait on a semaphore for the response. - */ - atomic_set(&wait_flag, 0); - if ((viopath_isactive(viopath_hostLp)) && - (send_open(viopath_hostLp, &wait_flag) == 0)) { - printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp); - while (atomic_read(&wait_flag) == 0) - mb(); - atomic_set(&wait_flag, 0); - } - - /* - * If we don't have an active console, try the primary - */ - if ((!viopath_isactive(port_info[0].lp)) && - (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && - (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) { - printk(KERN_INFO "hvc: opening console to primary partition\n"); - while (atomic_read(&wait_flag) == 0) - mb(); - } - - /* Register as a vio device to receive callbacks */ - rc = vio_register_driver(&hvc_vio_driver); - - return rc; -} -module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ - -static void __exit hvc_vio_exit(void) -{ - vio_unregister_driver(&hvc_vio_driver); -} -module_exit(hvc_vio_exit); - -/* the device tree order defines our numbering */ -static int __init hvc_find_vtys(void) -{ - struct device_node *vty; - int num_found = 0; - - for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; - vty = of_find_node_by_name(vty, "vty")) { - const uint32_t *vtermno; - - /* We have statically defined space for only a certain number - * of console adapters. - */ - if ((num_found >= MAX_NR_HVC_CONSOLES) || - (num_found >= VTTY_PORTS)) { - of_node_put(vty); - break; - } - - vtermno = of_get_property(vty, "reg", NULL); - if (!vtermno) - continue; - - if (!of_device_is_compatible(vty, "IBM,iSeries-vty")) - continue; - - if (num_found == 0) - add_preferred_console("hvc", 0, NULL); - hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); - ++num_found; - } - - return num_found; -} -console_initcall(hvc_find_vtys); -- cgit v0.10.2 From c17a9d4c8495c3a8e724cc846240c46f660a726c Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 7 Mar 2012 18:39:31 +0000 Subject: tty: powerpc: remove SERIAL_ICOM dependency on PPC_ISERIES The PowerPC legacy iSeries platform is being removed so this is no longer selectable. Cc: Alan Cox Cc: Greg Kroah-Hartman Cc: linux-serial@vger.kernel.org Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2de9924..59e7983 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -853,7 +853,7 @@ config SERIAL_MPC52xx_CONSOLE_BAUD config SERIAL_ICOM tristate "IBM Multiport Serial Adapter" - depends on PCI && (PPC_ISERIES || PPC_PSERIES) + depends on PCI && PPC_PSERIES select SERIAL_CORE select FW_LOADER help -- cgit v0.10.2 From fcd6f76202024763f2f4a348ab666240e4f7ac50 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 7 Mar 2012 18:41:09 +0000 Subject: driver-core: remove legacy iSeries hack The PowerPC legacy iSeries plateform is being removed along with the "one looney iseries driver", so this code can now be removed as well. cc: Greg Kroah-Hartman Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/base/driver.c b/drivers/base/driver.c index b631f7c..db4f54c 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -124,36 +124,6 @@ void driver_remove_file(struct device_driver *drv, EXPORT_SYMBOL_GPL(driver_remove_file); /** - * driver_add_kobj - add a kobject below the specified driver - * @drv: requesting device driver - * @kobj: kobject to add below this driver - * @fmt: format string that names the kobject - * - * You really don't want to do this, this is only here due to one looney - * iseries driver, go poke those developers if you are annoyed about - * this... - */ -int driver_add_kobj(struct device_driver *drv, struct kobject *kobj, - const char *fmt, ...) -{ - va_list args; - char *name; - int ret; - - va_start(args, fmt); - name = kvasprintf(GFP_KERNEL, fmt, args); - va_end(args); - - if (!name) - return -ENOMEM; - - ret = kobject_add(kobj, &drv->p->kobj, "%s", name); - kfree(name); - return ret; -} -EXPORT_SYMBOL_GPL(driver_add_kobj); - -/** * get_driver - increment driver reference count. * @drv: driver. */ diff --git a/include/linux/device.h b/include/linux/device.h index b63fb39..46baa1f 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -264,10 +264,6 @@ extern int __must_check driver_create_file(struct device_driver *driver, extern void driver_remove_file(struct device_driver *driver, const struct driver_attribute *attr); -extern int __must_check driver_add_kobj(struct device_driver *drv, - struct kobject *kobj, - const char *fmt, ...); - extern int __must_check driver_for_each_device(struct device_driver *drv, struct device *start, void *data, -- cgit v0.10.2 From b0787660260604ba63621881851de0032279819b Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 7 Mar 2012 18:43:10 +0000 Subject: powerpc: clean up vio.c This cleans up vio.c after the removal of the legacy iSeries platform. It also removes some no longer referenced include files. Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/iseries/hv_call_event.h b/arch/powerpc/include/asm/iseries/hv_call_event.h deleted file mode 100644 index cc029d3..0000000 --- a/arch/powerpc/include/asm/iseries/hv_call_event.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - * - * This file contains the "hypervisor call" interface which is used to - * drive the hypervisor from the OS. - */ -#ifndef _ASM_POWERPC_ISERIES_HV_CALL_EVENT_H -#define _ASM_POWERPC_ISERIES_HV_CALL_EVENT_H - -#include -#include - -#include -#include -#include - -struct HvLpEvent; - -typedef u8 HvLpEvent_Type; -typedef u8 HvLpEvent_AckInd; -typedef u8 HvLpEvent_AckType; - -typedef u8 HvLpDma_Direction; -typedef u8 HvLpDma_AddressType; - -typedef u64 HvLpEvent_Rc; -typedef u64 HvLpDma_Rc; - -#define HvCallEventAckLpEvent HvCallEvent + 0 -#define HvCallEventCancelLpEvent HvCallEvent + 1 -#define HvCallEventCloseLpEventPath HvCallEvent + 2 -#define HvCallEventDmaBufList HvCallEvent + 3 -#define HvCallEventDmaSingle HvCallEvent + 4 -#define HvCallEventDmaToSp HvCallEvent + 5 -#define HvCallEventGetOverflowLpEvents HvCallEvent + 6 -#define HvCallEventGetSourceLpInstanceId HvCallEvent + 7 -#define HvCallEventGetTargetLpInstanceId HvCallEvent + 8 -#define HvCallEventOpenLpEventPath HvCallEvent + 9 -#define HvCallEventSetLpEventStack HvCallEvent + 10 -#define HvCallEventSignalLpEvent HvCallEvent + 11 -#define HvCallEventSignalLpEventParms HvCallEvent + 12 -#define HvCallEventSetInterLpQueueIndex HvCallEvent + 13 -#define HvCallEventSetLpEventQueueInterruptProc HvCallEvent + 14 -#define HvCallEventRouter15 HvCallEvent + 15 - -static inline void HvCallEvent_getOverflowLpEvents(u8 queueIndex) -{ - HvCall1(HvCallEventGetOverflowLpEvents, queueIndex); -} - -static inline void HvCallEvent_setInterLpQueueIndex(u8 queueIndex) -{ - HvCall1(HvCallEventSetInterLpQueueIndex, queueIndex); -} - -static inline void HvCallEvent_setLpEventStack(u8 queueIndex, - char *eventStackAddr, u32 eventStackSize) -{ - HvCall3(HvCallEventSetLpEventStack, queueIndex, - virt_to_abs(eventStackAddr), eventStackSize); -} - -static inline void HvCallEvent_setLpEventQueueInterruptProc(u8 queueIndex, - u16 lpLogicalProcIndex) -{ - HvCall2(HvCallEventSetLpEventQueueInterruptProc, queueIndex, - lpLogicalProcIndex); -} - -static inline HvLpEvent_Rc HvCallEvent_signalLpEvent(struct HvLpEvent *event) -{ - return HvCall1(HvCallEventSignalLpEvent, virt_to_abs(event)); -} - -static inline HvLpEvent_Rc HvCallEvent_signalLpEventFast(HvLpIndex targetLp, - HvLpEvent_Type type, u16 subtype, HvLpEvent_AckInd ackInd, - HvLpEvent_AckType ackType, HvLpInstanceId sourceInstanceId, - HvLpInstanceId targetInstanceId, u64 correlationToken, - u64 eventData1, u64 eventData2, u64 eventData3, - u64 eventData4, u64 eventData5) -{ - /* Pack the misc bits into a single Dword to pass to PLIC */ - union { - struct { - u8 ack_and_target; - u8 type; - u16 subtype; - HvLpInstanceId src_inst; - HvLpInstanceId target_inst; - } parms; - u64 dword; - } packed; - - packed.parms.ack_and_target = (ackType << 7) | (ackInd << 6) | targetLp; - packed.parms.type = type; - packed.parms.subtype = subtype; - packed.parms.src_inst = sourceInstanceId; - packed.parms.target_inst = targetInstanceId; - - return HvCall7(HvCallEventSignalLpEventParms, packed.dword, - correlationToken, eventData1, eventData2, - eventData3, eventData4, eventData5); -} - -extern void *iseries_hv_alloc(size_t size, dma_addr_t *dma_handle, gfp_t flag); -extern void iseries_hv_free(size_t size, void *vaddr, dma_addr_t dma_handle); -extern dma_addr_t iseries_hv_map(void *vaddr, size_t size, - enum dma_data_direction direction); -extern void iseries_hv_unmap(dma_addr_t dma_handle, size_t size, - enum dma_data_direction direction); - -static inline HvLpEvent_Rc HvCallEvent_ackLpEvent(struct HvLpEvent *event) -{ - return HvCall1(HvCallEventAckLpEvent, virt_to_abs(event)); -} - -static inline HvLpEvent_Rc HvCallEvent_cancelLpEvent(struct HvLpEvent *event) -{ - return HvCall1(HvCallEventCancelLpEvent, virt_to_abs(event)); -} - -static inline HvLpInstanceId HvCallEvent_getSourceLpInstanceId( - HvLpIndex targetLp, HvLpEvent_Type type) -{ - return HvCall2(HvCallEventGetSourceLpInstanceId, targetLp, type); -} - -static inline HvLpInstanceId HvCallEvent_getTargetLpInstanceId( - HvLpIndex targetLp, HvLpEvent_Type type) -{ - return HvCall2(HvCallEventGetTargetLpInstanceId, targetLp, type); -} - -static inline void HvCallEvent_openLpEventPath(HvLpIndex targetLp, - HvLpEvent_Type type) -{ - HvCall2(HvCallEventOpenLpEventPath, targetLp, type); -} - -static inline void HvCallEvent_closeLpEventPath(HvLpIndex targetLp, - HvLpEvent_Type type) -{ - HvCall2(HvCallEventCloseLpEventPath, targetLp, type); -} - -static inline HvLpDma_Rc HvCallEvent_dmaBufList(HvLpEvent_Type type, - HvLpIndex remoteLp, HvLpDma_Direction direction, - HvLpInstanceId localInstanceId, - HvLpInstanceId remoteInstanceId, - HvLpDma_AddressType localAddressType, - HvLpDma_AddressType remoteAddressType, - /* Do these need to be converted to absolute addresses? */ - u64 localBufList, u64 remoteBufList, u32 transferLength) -{ - /* Pack the misc bits into a single Dword to pass to PLIC */ - union { - struct { - u8 flags; - HvLpIndex remote; - u8 type; - u8 reserved; - HvLpInstanceId local_inst; - HvLpInstanceId remote_inst; - } parms; - u64 dword; - } packed; - - packed.parms.flags = (direction << 7) | - (localAddressType << 6) | (remoteAddressType << 5); - packed.parms.remote = remoteLp; - packed.parms.type = type; - packed.parms.reserved = 0; - packed.parms.local_inst = localInstanceId; - packed.parms.remote_inst = remoteInstanceId; - - return HvCall4(HvCallEventDmaBufList, packed.dword, localBufList, - remoteBufList, transferLength); -} - -static inline HvLpDma_Rc HvCallEvent_dmaToSp(void *local, u32 remote, - u32 length, HvLpDma_Direction dir) -{ - return HvCall4(HvCallEventDmaToSp, virt_to_abs(local), remote, - length, dir); -} - -#endif /* _ASM_POWERPC_ISERIES_HV_CALL_EVENT_H */ diff --git a/arch/powerpc/include/asm/iseries/hv_lp_event.h b/arch/powerpc/include/asm/iseries/hv_lp_event.h deleted file mode 100644 index 8f5da7d..0000000 --- a/arch/powerpc/include/asm/iseries/hv_lp_event.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ - -/* This file contains the class for HV events in the system. */ - -#ifndef _ASM_POWERPC_ISERIES_HV_LP_EVENT_H -#define _ASM_POWERPC_ISERIES_HV_LP_EVENT_H - -#include -#include -#include -#include - -/* - * HvLpEvent is the structure for Lp Event messages passed between - * partitions through PLIC. - */ - -struct HvLpEvent { - u8 flags; /* Event flags x00-x00 */ - u8 xType; /* Type of message x01-x01 */ - u16 xSubtype; /* Subtype for event x02-x03 */ - u8 xSourceLp; /* Source LP x04-x04 */ - u8 xTargetLp; /* Target LP x05-x05 */ - u8 xSizeMinus1; /* Size of Derived class - 1 x06-x06 */ - u8 xRc; /* RC for Ack flows x07-x07 */ - u16 xSourceInstanceId; /* Source sides instance id x08-x09 */ - u16 xTargetInstanceId; /* Target sides instance id x0A-x0B */ - union { - u32 xSubtypeData; /* Data usable by the subtype x0C-x0F */ - u16 xSubtypeDataShort[2]; /* Data as 2 shorts */ - u8 xSubtypeDataChar[4]; /* Data as 4 chars */ - } x; - - u64 xCorrelationToken; /* Unique value for source/type x10-x17 */ -}; - -typedef void (*LpEventHandler)(struct HvLpEvent *); - -/* Register a handler for an event type - returns 0 on success */ -extern int HvLpEvent_registerHandler(HvLpEvent_Type eventType, - LpEventHandler hdlr); - -/* - * Unregister a handler for an event type - * - * This call will sleep until the handler being removed is guaranteed to - * be no longer executing on any CPU. Do not call with locks held. - * - * returns 0 on success - * Unregister will fail if there are any paths open for the type - */ -extern int HvLpEvent_unregisterHandler(HvLpEvent_Type eventType); - -/* - * Open an Lp Event Path for an event type - * returns 0 on success - * openPath will fail if there is no handler registered for the event type. - * The lpIndex specified is the partition index for the target partition - * (for VirtualIo, VirtualLan and SessionMgr) other types specify zero) - */ -extern int HvLpEvent_openPath(HvLpEvent_Type eventType, HvLpIndex lpIndex); - -/* - * Close an Lp Event Path for a type and partition - * returns 0 on success - */ -extern int HvLpEvent_closePath(HvLpEvent_Type eventType, HvLpIndex lpIndex); - -#define HvLpEvent_Type_Hypervisor 0 -#define HvLpEvent_Type_MachineFac 1 -#define HvLpEvent_Type_SessionMgr 2 -#define HvLpEvent_Type_SpdIo 3 -#define HvLpEvent_Type_VirtualBus 4 -#define HvLpEvent_Type_PciIo 5 -#define HvLpEvent_Type_RioIo 6 -#define HvLpEvent_Type_VirtualLan 7 -#define HvLpEvent_Type_VirtualIo 8 -#define HvLpEvent_Type_NumTypes 9 - -#define HvLpEvent_Rc_Good 0 -#define HvLpEvent_Rc_BufferNotAvailable 1 -#define HvLpEvent_Rc_Cancelled 2 -#define HvLpEvent_Rc_GenericError 3 -#define HvLpEvent_Rc_InvalidAddress 4 -#define HvLpEvent_Rc_InvalidPartition 5 -#define HvLpEvent_Rc_InvalidSize 6 -#define HvLpEvent_Rc_InvalidSubtype 7 -#define HvLpEvent_Rc_InvalidSubtypeData 8 -#define HvLpEvent_Rc_InvalidType 9 -#define HvLpEvent_Rc_PartitionDead 10 -#define HvLpEvent_Rc_PathClosed 11 -#define HvLpEvent_Rc_SubtypeError 12 - -#define HvLpEvent_Function_Ack 0 -#define HvLpEvent_Function_Int 1 - -#define HvLpEvent_AckInd_NoAck 0 -#define HvLpEvent_AckInd_DoAck 1 - -#define HvLpEvent_AckType_ImmediateAck 0 -#define HvLpEvent_AckType_DeferredAck 1 - -#define HV_LP_EVENT_INT 0x01 -#define HV_LP_EVENT_DO_ACK 0x02 -#define HV_LP_EVENT_DEFERRED_ACK 0x04 -#define HV_LP_EVENT_VALID 0x80 - -#define HvLpDma_Direction_LocalToRemote 0 -#define HvLpDma_Direction_RemoteToLocal 1 - -#define HvLpDma_AddressType_TceIndex 0 -#define HvLpDma_AddressType_RealAddress 1 - -#define HvLpDma_Rc_Good 0 -#define HvLpDma_Rc_Error 1 -#define HvLpDma_Rc_PartitionDead 2 -#define HvLpDma_Rc_PathClosed 3 -#define HvLpDma_Rc_InvalidAddress 4 -#define HvLpDma_Rc_InvalidLength 5 - -static inline int hvlpevent_is_valid(struct HvLpEvent *h) -{ - return h->flags & HV_LP_EVENT_VALID; -} - -static inline void hvlpevent_invalidate(struct HvLpEvent *h) -{ - h->flags &= ~ HV_LP_EVENT_VALID; -} - -static inline int hvlpevent_is_int(struct HvLpEvent *h) -{ - return h->flags & HV_LP_EVENT_INT; -} - -static inline int hvlpevent_is_ack(struct HvLpEvent *h) -{ - return !hvlpevent_is_int(h); -} - -static inline int hvlpevent_need_ack(struct HvLpEvent *h) -{ - return h->flags & HV_LP_EVENT_DO_ACK; -} - -#endif /* _ASM_POWERPC_ISERIES_HV_LP_EVENT_H */ diff --git a/arch/powerpc/include/asm/iseries/iommu.h b/arch/powerpc/include/asm/iseries/iommu.h deleted file mode 100644 index 1b9692c..0000000 --- a/arch/powerpc/include/asm/iseries/iommu.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _ASM_POWERPC_ISERIES_IOMMU_H -#define _ASM_POWERPC_ISERIES_IOMMU_H - -/* - * Copyright (C) 2005 Stephen Rothwell, IBM Corporation - * - * 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. - * - * 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 - */ - -struct pci_dev; -struct vio_dev; -struct device_node; -struct iommu_table; - -/* Get table parameters from HV */ -extern void iommu_table_getparms_iSeries(unsigned long busno, - unsigned char slotno, unsigned char virtbus, - struct iommu_table *tbl); - -extern struct iommu_table *vio_build_iommu_table_iseries(struct vio_dev *dev); -extern void iommu_vio_init(void); - -#endif /* _ASM_POWERPC_ISERIES_IOMMU_H */ diff --git a/arch/powerpc/include/asm/iseries/vio.h b/arch/powerpc/include/asm/iseries/vio.h deleted file mode 100644 index f9ac0d0..0000000 --- a/arch/powerpc/include/asm/iseries/vio.h +++ /dev/null @@ -1,265 +0,0 @@ -/* -*- linux-c -*- - * - * iSeries Virtual I/O Message Path header - * - * Authors: Dave Boutcher - * Ryan Arnold - * Colin Devilbiss - * - * (C) Copyright 2000 IBM Corporation - * - * This header file is used by the iSeries virtual I/O device - * drivers. It defines the interfaces to the common functions - * (implemented in drivers/char/viopath.h) as well as defining - * common functions and structures. Currently (at the time I - * wrote this comment) the iSeries virtual I/O device drivers - * that use this are - * drivers/block/viodasd.c - * drivers/char/viocons.c - * drivers/char/viotape.c - * drivers/cdrom/viocd.c - * - * The iSeries virtual ethernet support (veth.c) uses a whole - * different set of functions. - * - * 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) anyu later version. - * - * 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 - * - */ -#ifndef _ASM_POWERPC_ISERIES_VIO_H -#define _ASM_POWERPC_ISERIES_VIO_H - -#include -#include - -/* - * iSeries virtual I/O events use the subtype field in - * HvLpEvent to figure out what kind of vio event is coming - * in. We use a table to route these, and this defines - * the maximum number of distinct subtypes - */ -#define VIO_MAX_SUBTYPES 8 - -#define VIOMAXBLOCKDMA 12 - -struct open_data { - u64 disk_size; - u16 max_disk; - u16 cylinders; - u16 tracks; - u16 sectors; - u16 bytes_per_sector; -}; - -struct rw_data { - u64 offset; - struct { - u32 token; - u32 reserved; - u64 len; - } dma_info[VIOMAXBLOCKDMA]; -}; - -struct vioblocklpevent { - struct HvLpEvent event; - u32 reserved; - u16 version; - u16 sub_result; - u16 disk; - u16 flags; - union { - struct open_data open_data; - struct rw_data rw_data; - u64 changed; - } u; -}; - -#define vioblockflags_ro 0x0001 - -enum vioblocksubtype { - vioblockopen = 0x0001, - vioblockclose = 0x0002, - vioblockread = 0x0003, - vioblockwrite = 0x0004, - vioblockflush = 0x0005, - vioblockcheck = 0x0007 -}; - -struct viocdlpevent { - struct HvLpEvent event; - u32 reserved; - u16 version; - u16 sub_result; - u16 disk; - u16 flags; - u32 token; - u64 offset; /* On open, max number of disks */ - u64 len; /* On open, size of the disk */ - u32 block_size; /* Only set on open */ - u32 media_size; /* Only set on open */ -}; - -enum viocdsubtype { - viocdopen = 0x0001, - viocdclose = 0x0002, - viocdread = 0x0003, - viocdwrite = 0x0004, - viocdlockdoor = 0x0005, - viocdgetinfo = 0x0006, - viocdcheck = 0x0007 -}; - -struct viotapelpevent { - struct HvLpEvent event; - u32 reserved; - u16 version; - u16 sub_type_result; - u16 tape; - u16 flags; - u32 token; - u64 len; - union { - struct { - u32 tape_op; - u32 count; - } op; - struct { - u32 type; - u32 resid; - u32 dsreg; - u32 gstat; - u32 erreg; - u32 file_no; - u32 block_no; - } get_status; - struct { - u32 block_no; - } get_pos; - } u; -}; - -enum viotapesubtype { - viotapeopen = 0x0001, - viotapeclose = 0x0002, - viotaperead = 0x0003, - viotapewrite = 0x0004, - viotapegetinfo = 0x0005, - viotapeop = 0x0006, - viotapegetpos = 0x0007, - viotapesetpos = 0x0008, - viotapegetstatus = 0x0009 -}; - -/* - * Each subtype can register a handler to process their events. - * The handler must have this interface. - */ -typedef void (vio_event_handler_t) (struct HvLpEvent * event); - -extern int viopath_open(HvLpIndex remoteLp, int subtype, int numReq); -extern int viopath_close(HvLpIndex remoteLp, int subtype, int numReq); -extern int vio_setHandler(int subtype, vio_event_handler_t * beh); -extern int vio_clearHandler(int subtype); -extern int viopath_isactive(HvLpIndex lp); -extern HvLpInstanceId viopath_sourceinst(HvLpIndex lp); -extern HvLpInstanceId viopath_targetinst(HvLpIndex lp); -extern void vio_set_hostlp(void); -extern void *vio_get_event_buffer(int subtype); -extern void vio_free_event_buffer(int subtype, void *buffer); - -extern struct vio_dev *vio_create_viodasd(u32 unit); - -extern HvLpIndex viopath_hostLp; -extern HvLpIndex viopath_ourLp; - -#define VIOCHAR_MAX_DATA 200 - -#define VIOMAJOR_SUBTYPE_MASK 0xff00 -#define VIOMINOR_SUBTYPE_MASK 0x00ff -#define VIOMAJOR_SUBTYPE_SHIFT 8 - -#define VIOVERSION 0x0101 - -/* - * This is the general structure for VIO errors; each module should have - * a table of them, and each table should be terminated by an entry of - * { 0, 0, NULL }. Then, to find a specific error message, a module - * should pass its local table and the return code. - */ -struct vio_error_entry { - u16 rc; - int errno; - const char *msg; -}; -extern const struct vio_error_entry *vio_lookup_rc( - const struct vio_error_entry *local_table, u16 rc); - -enum viosubtypes { - viomajorsubtype_monitor = 0x0100, - viomajorsubtype_blockio = 0x0200, - viomajorsubtype_chario = 0x0300, - viomajorsubtype_config = 0x0400, - viomajorsubtype_cdio = 0x0500, - viomajorsubtype_tape = 0x0600, - viomajorsubtype_scsi = 0x0700 -}; - -enum vioconfigsubtype { - vioconfigget = 0x0001, -}; - -enum viorc { - viorc_good = 0x0000, - viorc_noConnection = 0x0001, - viorc_noReceiver = 0x0002, - viorc_noBufferAvailable = 0x0003, - viorc_invalidMessageType = 0x0004, - viorc_invalidRange = 0x0201, - viorc_invalidToken = 0x0202, - viorc_DMAError = 0x0203, - viorc_useError = 0x0204, - viorc_releaseError = 0x0205, - viorc_invalidDisk = 0x0206, - viorc_openRejected = 0x0301 -}; - -/* - * The structure of the events that flow between us and OS/400 for chario - * events. You can't mess with this unless the OS/400 side changes too. - */ -struct viocharlpevent { - struct HvLpEvent event; - u32 reserved; - u16 version; - u16 subtype_result_code; - u8 virtual_device; - u8 len; - u8 data[VIOCHAR_MAX_DATA]; -}; - -#define VIOCHAR_WINDOW 10 - -enum viocharsubtype { - viocharopen = 0x0001, - viocharclose = 0x0002, - viochardata = 0x0003, - viocharack = 0x0004, - viocharconfig = 0x0005 -}; - -enum viochar_rc { - viochar_rc_ebusy = 1 -}; - -#endif /* _ASM_POWERPC_ISERIES_VIO_H */ diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index 8b08629..bca3fc4 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -34,11 +34,6 @@ #include #include #include -#include -#include -#include -#include -#include static struct bus_type vio_bus_type; @@ -1042,7 +1037,6 @@ static void vio_cmo_sysfs_init(void) vio_bus_type.bus_attrs = vio_cmo_bus_attrs; } #else /* CONFIG_PPC_SMLPAR */ -/* Dummy functions for iSeries platform */ int vio_cmo_entitlement_update(size_t new_entitlement) { return 0; } void vio_cmo_set_dev_desired(struct vio_dev *viodev, size_t desired) {} static int vio_cmo_bus_probe(struct vio_dev *viodev) { return 0; } @@ -1060,9 +1054,6 @@ static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev) struct iommu_table *tbl; unsigned long offset, size; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return vio_build_iommu_table_iseries(dev); - dma_window = of_get_property(dev->dev.of_node, "ibm,my-dma-window", NULL); if (!dma_window) @@ -1195,8 +1186,7 @@ static void __devinit vio_dev_release(struct device *dev) { struct iommu_table *tbl = get_iommu_table_base(dev); - /* iSeries uses a common table for all vio devices */ - if (!firmware_has_feature(FW_FEATURE_ISERIES) && tbl) + if (tbl) iommu_free_table(tbl, dev->of_node ? dev->of_node->full_name : dev_name(dev)); of_node_put(dev->of_node); @@ -1244,12 +1234,6 @@ struct vio_dev *vio_register_device_node(struct device_node *of_node) viodev->name = of_node->name; viodev->type = of_node->type; viodev->unit_address = *unit_address; - if (firmware_has_feature(FW_FEATURE_ISERIES)) { - unit_address = of_get_property(of_node, - "linux,unit_address", NULL); - if (unit_address != NULL) - viodev->unit_address = *unit_address; - } viodev->dev.of_node = of_node_get(of_node); if (firmware_has_feature(FW_FEATURE_CMO)) -- cgit v0.10.2 From 4f8cf36f48b4648a5231e9fc8e49faea377246f4 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 28 Feb 2012 13:44:58 +1100 Subject: powerpc: Remove legacy iSeries bits from assembly files This removes the various bits of assembly in the kernel entry, exception handling and SLB management code that were specific to running under the legacy iSeries hypervisor which is no longer supported. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 8057f4f..cc2bcf4 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -272,26 +272,11 @@ label##_hv: \ _MASKABLE_EXCEPTION_PSERIES(vec, label, \ EXC_HV, SOFTEN_TEST_HV) -#ifdef CONFIG_PPC_ISERIES -#define DISABLE_INTS \ - li r11,0; \ - stb r11,PACASOFTIRQEN(r13); \ -BEGIN_FW_FTR_SECTION; \ - stb r11,PACAHARDIRQEN(r13); \ -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES); \ - TRACE_DISABLE_INTS; \ -BEGIN_FW_FTR_SECTION; \ - mfmsr r10; \ - ori r10,r10,MSR_EE; \ - mtmsrd r10,1; \ -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#else #define DISABLE_INTS \ li r11,0; \ stb r11,PACASOFTIRQEN(r13); \ stb r11,PACAHARDIRQEN(r13); \ TRACE_DISABLE_INTS -#endif /* CONFIG_PPC_ISERIES */ #define ENABLE_INTS \ ld r12,_MSR(r1); \ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 866462c..0c3764b 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -127,17 +127,6 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) stb r10,PACASOFTIRQEN(r13) stb r10,PACAHARDIRQEN(r13) std r10,SOFTE(r1) -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION - /* Hack for handling interrupts when soft-enabling on iSeries */ - cmpdi cr1,r0,0x5555 /* syscall 0x5555 */ - andi. r10,r12,MSR_PR /* from kernel */ - crand 4*cr0+eq,4*cr1+eq,4*cr0+eq - bne 2f - b hardware_interrupt_entry -2: -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif /* CONFIG_PPC_ISERIES */ /* Hard enable interrupts */ #ifdef CONFIG_PPC_BOOK3E @@ -591,15 +580,10 @@ _GLOBAL(ret_from_except_lite) ld r4,TI_FLAGS(r9) andi. r0,r4,_TIF_USER_WORK_MASK bne do_work -#endif +#endif /* !CONFIG_PREEMPT */ restore: -BEGIN_FW_FTR_SECTION ld r5,SOFTE(r1) -FW_FTR_SECTION_ELSE - b .Liseries_check_pending_irqs -ALT_FW_FTR_SECTION_END_IFCLR(FW_FEATURE_ISERIES) -2: TRACE_AND_RESTORE_IRQ(r5); /* extract EE bit and use it to restore paca->hard_enabled */ @@ -669,30 +653,6 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) #endif /* CONFIG_PPC_BOOK3E */ -.Liseries_check_pending_irqs: -#ifdef CONFIG_PPC_ISERIES - ld r5,SOFTE(r1) - cmpdi 0,r5,0 - beq 2b - /* Check for pending interrupts (iSeries) */ - ld r3,PACALPPACAPTR(r13) - ld r3,LPPACAANYINT(r3) - cmpdi r3,0 - beq+ 2b /* skip do_IRQ if no interrupts */ - - li r3,0 - stb r3,PACASOFTIRQEN(r13) /* ensure we are soft-disabled */ -#ifdef CONFIG_TRACE_IRQFLAGS - bl .trace_hardirqs_off - mfmsr r10 -#endif - ori r10,r10,MSR_EE - mtmsrd r10 /* hard-enable again */ - addi r3,r1,STACK_FRAME_OVERHEAD - bl .do_IRQ - b .ret_from_except_lite /* loop back and handle more */ -#endif - do_work: #ifdef CONFIG_PREEMPT andi. r0,r3,MSR_PR /* Returning to user mode? */ diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 15c5a4f..fea8a69 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -19,7 +19,7 @@ * We layout physical memory as follows: * 0x0000 - 0x00ff : Secondary processor spin code * 0x0100 - 0x2fff : pSeries Interrupt prologs - * 0x3000 - 0x5fff : interrupt support, iSeries and common interrupt prologs + * 0x3000 - 0x5fff : interrupt support common interrupt prologs * 0x6000 - 0x6fff : Initial (CPU0) segment table * 0x7000 - 0x7fff : FWNMI data area * 0x8000 - : Early init and support code @@ -458,6 +458,7 @@ machine_check_common: bl .machine_check_exception b .ret_from_except + STD_EXCEPTION_COMMON_LITE(0x500, hardware_interrupt, do_IRQ) STD_EXCEPTION_COMMON_LITE(0x900, decrementer, .timer_interrupt) STD_EXCEPTION_COMMON(0xa00, trap_0a, .unknown_exception) STD_EXCEPTION_COMMON(0xb00, trap_0b, .unknown_exception) @@ -672,12 +673,6 @@ _GLOBAL(slb_miss_realmode) ld r10,PACA_EXSLB+EX_LR(r13) ld r3,PACA_EXSLB+EX_R3(r13) lwz r9,PACA_EXSLB+EX_CCR(r13) /* get saved CR */ -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION - ld r11,PACALPPACAPTR(r13) - ld r11,LPPACASRR0(r11) /* get SRR0 value */ -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif /* CONFIG_PPC_ISERIES */ mtlr r10 @@ -690,12 +685,6 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) mtcrf 0x01,r9 /* slb_allocate uses cr0 and cr7 */ .machine pop -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION - mtspr SPRN_SRR0,r11 - mtspr SPRN_SRR1,r12 -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif /* CONFIG_PPC_ISERIES */ ld r9,PACA_EXSLB+EX_R9(r13) ld r10,PACA_EXSLB+EX_R10(r13) ld r11,PACA_EXSLB+EX_R11(r13) @@ -704,13 +693,7 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) rfid b . /* prevent speculative execution */ -2: -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION - b unrecov_slb -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif /* CONFIG_PPC_ISERIES */ - mfspr r11,SPRN_SRR0 +2: mfspr r11,SPRN_SRR0 ld r10,PACAKBASE(r13) LOAD_HANDLER(r10,unrecov_slb) mtspr SPRN_SRR0,r10 @@ -727,20 +710,6 @@ unrecov_slb: bl .unrecoverable_exception b 1b - .align 7 - .globl hardware_interrupt_common - .globl hardware_interrupt_entry -hardware_interrupt_common: - EXCEPTION_PROLOG_COMMON(0x500, PACA_EXGEN) - FINISH_NAP -hardware_interrupt_entry: - DISABLE_INTS -BEGIN_FTR_SECTION - bl .ppc64_runlatch_on -END_FTR_SECTION_IFSET(CPU_FTR_CTRL) - addi r3,r1,STACK_FRAME_OVERHEAD - bl .do_IRQ - b .ret_from_except_lite #ifdef CONFIG_PPC_970_NAP power4_fixup_nap: @@ -913,11 +882,7 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) andis. r0,r0,NMI_MASK@h /* (i.e. an irq when soft-disabled) */ bne 77f /* then don't call hash_page now */ - /* - * On iSeries, we soft-disable interrupts here, then - * hard-enable interrupts so that the hash_page code can spin on - * the hash_table_lock without problems on a shared processor. - */ + /* We run with interrupts both soft and hard disabled */ DISABLE_INTS /* @@ -956,25 +921,11 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) bl .hash_page /* build HPTE if possible */ cmpdi r3,0 /* see if hash_page succeeded */ -BEGIN_FW_FTR_SECTION - /* - * If we had interrupts soft-enabled at the point where the - * DSI/ISI occurred, and an interrupt came in during hash_page, - * handle it now. - * We jump to ret_from_except_lite rather than fast_exception_return - * because ret_from_except_lite will check for and handle pending - * interrupts if necessary. - */ - beq 13f -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) - -BEGIN_FW_FTR_SECTION /* * Here we have interrupts hard-disabled, so it is sufficient * to restore paca->{soft,hard}_enable and get out. */ beq fast_exc_return_irq /* Return from exception on success */ -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) /* For a hash failure, we don't bother re-enabling interrupts */ ble- 12f @@ -1141,51 +1092,19 @@ _GLOBAL(do_stab_bolted) .= 0x7000 .globl fwnmi_data_area fwnmi_data_area: -#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ - - /* iSeries does not use the FWNMI stuff, so it is safe to put - * this here, even if we later allow kernels that will boot on - * both pSeries and iSeries */ -#ifdef CONFIG_PPC_ISERIES - . = LPARMAP_PHYS - .globl xLparMap -xLparMap: - .quad HvEsidsToMap /* xNumberEsids */ - .quad HvRangesToMap /* xNumberRanges */ - .quad STAB0_PAGE /* xSegmentTableOffs */ - .zero 40 /* xRsvd */ - /* xEsids (HvEsidsToMap entries of 2 quads) */ - .quad PAGE_OFFSET_ESID /* xKernelEsid */ - .quad PAGE_OFFSET_VSID /* xKernelVsid */ - .quad VMALLOC_START_ESID /* xKernelEsid */ - .quad VMALLOC_START_VSID /* xKernelVsid */ - /* xRanges (HvRangesToMap entries of 3 quads) */ - .quad HvPagesToMap /* xPages */ - .quad 0 /* xOffset */ - .quad PAGE_OFFSET_VSID << (SID_SHIFT - HW_PAGE_SHIFT) /* xVPN */ - -#endif /* CONFIG_PPC_ISERIES */ -#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* pseries and powernv need to keep the whole page from * 0x7000 to 0x8000 free for use by the firmware */ . = 0x8000 #endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ -/* - * Space for CPU0's segment table. - * - * On iSeries, the hypervisor must fill in at least one entry before - * we get control (with relocate on). The address is given to the hv - * as a page number (see xLparMap above), so this must be at a - * fixed address (the linker can't compute (u64)&initial_stab >> - * PAGE_SHIFT). - */ - . = STAB0_OFFSET /* 0x8000 */ +/* Space for CPU0's segment table */ + .balign 4096 .globl initial_stab initial_stab: .space 4096 + #ifdef CONFIG_PPC_POWERNV _GLOBAL(opal_mc_secondary_handler) HMT_MEDIUM diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 06c7251..40759fb 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -57,10 +56,6 @@ * entry in r9 for debugging purposes * 2. Secondary processors enter at 0x60 with PIR in gpr3 * - * For iSeries: - * 1. The MMU is on (as it always is for iSeries) - * 2. The kernel is entered at system_reset_iSeries - * * For Book3E processors: * 1. The MMU is on running in AS0 in a state defined in ePAPR * 2. The kernel is entered at __start @@ -93,15 +88,6 @@ __secondary_hold_spinloop: __secondary_hold_acknowledge: .llong 0x0 -#ifdef CONFIG_PPC_ISERIES - /* - * At offset 0x20, there is a pointer to iSeries LPAR data. - * This is required by the hypervisor - */ - . = 0x20 - .llong hvReleaseData-KERNELBASE -#endif /* CONFIG_PPC_ISERIES */ - #ifdef CONFIG_RELOCATABLE /* This flag is set to 1 by a loader if the kernel should run * at the loaded address instead of the linked address. This @@ -582,7 +568,7 @@ _GLOBAL(pmac_secondary_start) * 1. Processor number * 2. Segment table pointer (virtual address) * On entry the following are set: - * r1 = stack pointer. vaddr for iSeries, raddr (temp stack) for pSeries + * r1 = stack pointer (real addr of temp stack) * r24 = cpu# (in Linux terms) * r13 = paca virtual address * SPRG_PACA = paca virtual address @@ -595,7 +581,7 @@ __secondary_start: /* Set thread priority to MEDIUM */ HMT_MEDIUM - /* Initialize the kernel stack. Just a repeat for iSeries. */ + /* Initialize the kernel stack */ LOAD_REG_ADDR(r3, current_set) sldi r28,r24,3 /* get current_set[cpu#] */ ldx r14,r3,r28 @@ -615,20 +601,13 @@ __secondary_start: li r7,0 mtlr r7 + /* Mark interrupts both hard and soft disabled */ + stb r7,PACAHARDIRQEN(r13) + stb r7,PACASOFTIRQEN(r13) + /* enable MMU and jump to start_secondary */ LOAD_REG_ADDR(r3, .start_secondary_prolog) LOAD_REG_IMMEDIATE(r4, MSR_KERNEL) -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION - ori r4,r4,MSR_EE - li r8,1 - stb r8,PACAHARDIRQEN(r13) -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif -BEGIN_FW_FTR_SECTION - stb r7,PACAHARDIRQEN(r13) -END_FW_FTR_SECTION_IFCLR(FW_FEATURE_ISERIES) - stb r7,PACASOFTIRQEN(r13) mtspr SPRN_SRR0,r3 mtspr SPRN_SRR1,r4 @@ -774,17 +753,8 @@ _INIT_GLOBAL(start_here_common) bl .setup_system /* Load up the kernel context */ -5: - li r5,0 +5: li r5,0 stb r5,PACASOFTIRQEN(r13) /* Soft Disabled */ -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION - mfmsr r5 - ori r5,r5,MSR_EE /* Hard Enabled on iSeries*/ - mtmsrd r5 - li r5,1 -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif stb r5,PACAHARDIRQEN(r13) /* Hard Disabled on others */ bl .start_kernel diff --git a/arch/powerpc/kernel/misc.S b/arch/powerpc/kernel/misc.S index b69463e..ba16874 100644 --- a/arch/powerpc/kernel/misc.S +++ b/arch/powerpc/kernel/misc.S @@ -5,7 +5,6 @@ * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) * and Paul Mackerras. * - * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com) * * setjmp/longjmp code by Paul Mackerras. diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 710a540..65d1c08 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -109,11 +109,6 @@ SECTIONS __ptov_table_begin = .; *(.ptov_fixup); __ptov_table_end = .; -#ifdef CONFIG_PPC_ISERIES - __dt_strings_start = .; - *(.dt_strings); - __dt_strings_end = .; -#endif } .init.setup : AT(ADDR(.init.setup) - LOAD_OFFSET) { diff --git a/arch/powerpc/mm/slb_low.S b/arch/powerpc/mm/slb_low.S index ef653dc..b9ee79ce 100644 --- a/arch/powerpc/mm/slb_low.S +++ b/arch/powerpc/mm/slb_low.S @@ -217,21 +217,6 @@ slb_finish_load: * free slot first but that took too long. Unfortunately we * dont have any LRU information to help us choose a slot. */ -#ifdef CONFIG_PPC_ISERIES -BEGIN_FW_FTR_SECTION - /* - * On iSeries, the "bolted" stack segment can be cast out on - * shared processor switch so we need to check for a miss on - * it and restore it to the right slot. - */ - ld r9,PACAKSAVE(r13) - clrrdi r9,r9,28 - clrrdi r3,r3,28 - li r10,SLB_NUM_BOLTED-1 /* Stack goes in last bolted slot */ - cmpld r9,r3 - beq 3f -END_FW_FTR_SECTION_IFSET(FW_FEATURE_ISERIES) -#endif /* CONFIG_PPC_ISERIES */ 7: ld r10,PACASTABRR(r13) addi r10,r10,1 @@ -282,7 +267,6 @@ _GLOBAL(slb_compare_rr_to_size) /* * Finish loading of a 1T SLB entry (for the kernel linear mapping) and return. - * We assume legacy iSeries will never have 1T segments. * * r3 = EA, r10 = proto-VSID, r11 = flags, clobbers r9 */ -- cgit v0.10.2 From 7450f6f03e9d6dc95d2014c4cceac8adf98560e8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 1 Mar 2012 10:52:01 +1100 Subject: powerpc: Use the same interrupt prolog for perfmon as other interrupts The perfmon interrupt is the sole user of a special variant of the interrupt prolog which differs from the one used by external and timer interrupts in that it saves the non-volatile GPRs and doesn't turn the runlatch on. The former is unnecessary and the later is arguably incorrect, so let's clean that up by using the same prolog. While at it we rename that prolog to use the _ASYNC prefix. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index cc2bcf4..041b222 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -297,21 +297,10 @@ label##_common: \ /* * Like STD_EXCEPTION_COMMON, but for exceptions that can occur - * in the idle task and therefore need the special idle handling. + * in the idle task and therefore need the special idle handling + * (finish nap and runlatch) */ -#define STD_EXCEPTION_COMMON_IDLE(trap, label, hdlr) \ - .align 7; \ - .globl label##_common; \ -label##_common: \ - EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN); \ - FINISH_NAP; \ - DISABLE_INTS; \ - bl .save_nvgprs; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - bl hdlr; \ - b .ret_from_except - -#define STD_EXCEPTION_COMMON_LITE(trap, label, hdlr) \ +#define STD_EXCEPTION_COMMON_ASYNC(trap, label, hdlr) \ .align 7; \ .globl label##_common; \ label##_common: \ diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index fea8a69..2240d4e 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -458,15 +458,15 @@ machine_check_common: bl .machine_check_exception b .ret_from_except - STD_EXCEPTION_COMMON_LITE(0x500, hardware_interrupt, do_IRQ) - STD_EXCEPTION_COMMON_LITE(0x900, decrementer, .timer_interrupt) + STD_EXCEPTION_COMMON_ASYNC(0x500, hardware_interrupt, do_IRQ) + STD_EXCEPTION_COMMON_ASYNC(0x900, decrementer, .timer_interrupt) STD_EXCEPTION_COMMON(0xa00, trap_0a, .unknown_exception) STD_EXCEPTION_COMMON(0xb00, trap_0b, .unknown_exception) STD_EXCEPTION_COMMON(0xd00, single_step, .single_step_exception) STD_EXCEPTION_COMMON(0xe00, trap_0e, .unknown_exception) STD_EXCEPTION_COMMON(0xe40, emulation_assist, .program_check_exception) STD_EXCEPTION_COMMON(0xe60, hmi_exception, .unknown_exception) - STD_EXCEPTION_COMMON_IDLE(0xf00, performance_monitor, .performance_monitor_exception) + STD_EXCEPTION_COMMON_ASYNC(0xf00, performance_monitor, .performance_monitor_exception) STD_EXCEPTION_COMMON(0x1300, instruction_breakpoint, .instruction_breakpoint_exception) #ifdef CONFIG_ALTIVEC STD_EXCEPTION_COMMON(0x1700, altivec_assist, .altivec_assist_exception) -- cgit v0.10.2 From fe1952fc0afb9a2e4c79f103c08aef5d13db1873 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 1 Mar 2012 12:45:27 +1100 Subject: powerpc: Rework runlatch code This moves the inlines into system.h and changes the runlatch code to use the thread local flags (non-atomic) rather than the TIF flags (atomic) to keep track of the latch state. The code to turn it back on in an asynchronous interrupt is now simplified and partially inlined. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 041b222..bdc9eeb 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -284,35 +284,39 @@ label##_hv: \ rlwimi r11,r12,0,MSR_EE; \ mtmsrd r11,1 -#define STD_EXCEPTION_COMMON(trap, label, hdlr) \ - .align 7; \ - .globl label##_common; \ -label##_common: \ - EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN); \ - DISABLE_INTS; \ - bl .save_nvgprs; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - bl hdlr; \ - b .ret_from_except +#define ADD_NVGPRS \ + bl .save_nvgprs + +#define RUNLATCH_ON \ +BEGIN_FTR_SECTION \ + clrrdi r3,r1,THREAD_SHIFT; \ + ld r4,TI_LOCAL_FLAGS(r3); \ + andi. r0,r4,_TLF_RUNLATCH; \ + beql ppc64_runlatch_on_trampoline; \ +END_FTR_SECTION_IFSET(CPU_FTR_CTRL) + +#define EXCEPTION_COMMON(trap, label, hdlr, ret, additions) \ + .align 7; \ + .globl label##_common; \ +label##_common: \ + EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN); \ + additions; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + bl hdlr; \ + b ret + +#define STD_EXCEPTION_COMMON(trap, label, hdlr) \ + EXCEPTION_COMMON(trap, label, hdlr, ret_from_except, \ + ADD_NVGPRS;DISABLE_INTS) /* * Like STD_EXCEPTION_COMMON, but for exceptions that can occur * in the idle task and therefore need the special idle handling * (finish nap and runlatch) */ -#define STD_EXCEPTION_COMMON_ASYNC(trap, label, hdlr) \ - .align 7; \ - .globl label##_common; \ -label##_common: \ - EXCEPTION_PROLOG_COMMON(trap, PACA_EXGEN); \ - FINISH_NAP; \ - DISABLE_INTS; \ -BEGIN_FTR_SECTION \ - bl .ppc64_runlatch_on; \ -END_FTR_SECTION_IFSET(CPU_FTR_CTRL) \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - bl hdlr; \ - b .ret_from_except_lite +#define STD_EXCEPTION_COMMON_ASYNC(trap, label, hdlr) \ + EXCEPTION_COMMON(trap, label, hdlr, ret_from_except_lite, \ + FINISH_NAP;RUNLATCH_ON;DISABLE_INTS) /* * When the idle code in power4_idle puts the CPU into NAP mode, diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 7fdc2c0..b1a215e 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -1079,30 +1079,12 @@ #define proc_trap() asm volatile("trap") -#ifdef CONFIG_PPC64 - -extern void ppc64_runlatch_on(void); -extern void __ppc64_runlatch_off(void); - -#define ppc64_runlatch_off() \ - do { \ - if (cpu_has_feature(CPU_FTR_CTRL) && \ - test_thread_flag(TIF_RUNLATCH)) \ - __ppc64_runlatch_off(); \ - } while (0) +#define __get_SP() ({unsigned long sp; \ + asm volatile("mr %0,1": "=r" (sp)); sp;}) extern unsigned long scom970_read(unsigned int address); extern void scom970_write(unsigned int address, unsigned long value); -#else -#define ppc64_runlatch_on() -#define ppc64_runlatch_off() - -#endif /* CONFIG_PPC64 */ - -#define __get_SP() ({unsigned long sp; \ - asm volatile("mr %0,1": "=r" (sp)); sp;}) - struct pt_regs; extern void ppc_save_regs(struct pt_regs *regs); diff --git a/arch/powerpc/include/asm/system.h b/arch/powerpc/include/asm/system.h index c377457..a02883d 100644 --- a/arch/powerpc/include/asm/system.h +++ b/arch/powerpc/include/asm/system.h @@ -550,5 +550,43 @@ extern void reloc_got2(unsigned long); extern struct dentry *powerpc_debugfs_root; +#ifdef CONFIG_PPC64 + +extern void __ppc64_runlatch_on(void); +extern void __ppc64_runlatch_off(void); + +/* + * We manually hard enable-disable, this is called + * in the idle loop and we don't want to mess up + * with soft-disable/enable & interrupt replay. + */ +#define ppc64_runlatch_off() \ + do { \ + if (cpu_has_feature(CPU_FTR_CTRL) && \ + test_thread_local_flags(_TLF_RUNLATCH)) { \ + unsigned long msr = mfmsr(); \ + __hard_irq_disable(); \ + __ppc64_runlatch_off(); \ + if (msr & MSR_EE) \ + __hard_irq_enable(); \ + } \ + } while (0) + +#define ppc64_runlatch_on() \ + do { \ + if (cpu_has_feature(CPU_FTR_CTRL) && \ + !test_thread_local_flags(_TLF_RUNLATCH)) { \ + unsigned long msr = mfmsr(); \ + __hard_irq_disable(); \ + __ppc64_runlatch_on(); \ + if (msr & MSR_EE) \ + __hard_irq_enable(); \ + } \ + } while (0) +#else +#define ppc64_runlatch_on() +#define ppc64_runlatch_off() +#endif /* CONFIG_PPC64 */ + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_SYSTEM_H */ diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h index 96471494..4a741c7 100644 --- a/arch/powerpc/include/asm/thread_info.h +++ b/arch/powerpc/include/asm/thread_info.h @@ -110,7 +110,6 @@ static inline struct thread_info *current_thread_info(void) #define TIF_NOERROR 12 /* Force successful syscall return */ #define TIF_NOTIFY_RESUME 13 /* callback before returning to user */ #define TIF_SYSCALL_TRACEPOINT 15 /* syscall tracepoint instrumentation */ -#define TIF_RUNLATCH 16 /* Is the runlatch enabled? */ /* as above, but as bit values */ #define _TIF_SYSCALL_TRACE (1<flags); } +static inline bool test_thread_local_flags(unsigned int flags) +{ + struct thread_info *ti = current_thread_info(); + return (ti->local_flags & flags) != 0; +} + #ifdef CONFIG_PPC64 #define is_32bit_task() (test_thread_flag(TIF_32BIT)) #else diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 2240d4e..3af80e8 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -483,6 +483,9 @@ machine_check_common: system_call_entry: b system_call_common +ppc64_runlatch_on_trampoline: + b .__ppc64_runlatch_on + /* * Here we have detected that the kernel stack pointer is bad. * R9 contains the saved CR, r13 points to the paca, diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index d817ab0..bf80a1d 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -1220,34 +1220,32 @@ void dump_stack(void) EXPORT_SYMBOL(dump_stack); #ifdef CONFIG_PPC64 -void ppc64_runlatch_on(void) +/* Called with hard IRQs off */ +void __ppc64_runlatch_on(void) { + struct thread_info *ti = current_thread_info(); unsigned long ctrl; - if (cpu_has_feature(CPU_FTR_CTRL) && !test_thread_flag(TIF_RUNLATCH)) { - HMT_medium(); - - ctrl = mfspr(SPRN_CTRLF); - ctrl |= CTRL_RUNLATCH; - mtspr(SPRN_CTRLT, ctrl); + ctrl = mfspr(SPRN_CTRLF); + ctrl |= CTRL_RUNLATCH; + mtspr(SPRN_CTRLT, ctrl); - set_thread_flag(TIF_RUNLATCH); - } + ti->local_flags |= TLF_RUNLATCH; } +/* Called with hard IRQs off */ void __ppc64_runlatch_off(void) { + struct thread_info *ti = current_thread_info(); unsigned long ctrl; - HMT_medium(); - - clear_thread_flag(TIF_RUNLATCH); + ti->local_flags &= ~TLF_RUNLATCH; ctrl = mfspr(SPRN_CTRLF); ctrl &= ~CTRL_RUNLATCH; mtspr(SPRN_CTRLT, ctrl); } -#endif +#endif /* CONFIG_PPC64 */ #if THREAD_SHIFT < PAGE_SHIFT -- cgit v0.10.2 From 1421ae0b29e0003395613bf67610d15fb7047e09 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 1 Mar 2012 15:40:23 +1100 Subject: powerpc: Improve 64-bit syscall entry/exit We unconditionally hard enable interrupts. This is unnecessary as syscalls are expected to always be called with interrupts enabled. While at it, we add a WARN_ON if that is not the case and CONFIG_TRACE_IRQFLAGS is enabled (we don't want to add overhead to the fast path when this is not set though). Thus let's remove the enabling (and associated irq tracing) from the syscall entry path. Also on Book3S, replace a few mfmsr instructions with loads of PACAMSR from the PACA, which should be faster & schedule better. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 0c3764b..cc030b7 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -115,28 +115,33 @@ BEGIN_FW_FTR_SECTION END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) #endif /* CONFIG_VIRT_CPU_ACCOUNTING && CONFIG_PPC_SPLPAR */ -#ifdef CONFIG_TRACE_IRQFLAGS - bl .trace_hardirqs_on - REST_GPR(0,r1) - REST_4GPRS(3,r1) - REST_2GPRS(7,r1) - addi r9,r1,STACK_FRAME_OVERHEAD - ld r12,_MSR(r1) -#endif /* CONFIG_TRACE_IRQFLAGS */ - li r10,1 - stb r10,PACASOFTIRQEN(r13) - stb r10,PACAHARDIRQEN(r13) - std r10,SOFTE(r1) + /* + * A syscall should always be called with interrupts enabled + * so we just unconditionally hard-enable here. When some kind + * of irq tracing is used, we additionally check that condition + * is correct + */ +#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_BUG) + lbz r10,PACASOFTIRQEN(r13) + xori r10,r10,1 +1: tdnei r10,0 + EMIT_BUG_ENTRY 1b,__FILE__,__LINE__,BUGFLAG_WARNING +#endif - /* Hard enable interrupts */ #ifdef CONFIG_PPC_BOOK3E wrteei 1 #else - mfmsr r11 + ld r11,PACAKMSR(r13) ori r11,r11,MSR_EE mtmsrd r11,1 #endif /* CONFIG_PPC_BOOK3E */ + /* We do need to set SOFTE in the stack frame or the return + * from interrupt will be painful + */ + li r10,1 + std r10,SOFTE(r1) + #ifdef SHOW_SYSCALLS bl .do_show_syscall REST_GPR(0,r1) @@ -187,16 +192,14 @@ syscall_exit: andi. r10,r8,MSR_RI beq- unrecov_restore #endif - - /* Disable interrupts so current_thread_info()->flags can't change, + /* + * Disable interrupts so current_thread_info()->flags can't change, * and so that we don't get interrupted after loading SRR0/1. */ #ifdef CONFIG_PPC_BOOK3E wrteei 0 #else - mfmsr r10 - rldicl r10,r10,48,1 - rotldi r10,r10,16 + ld r10,PACAKMSR(r13) mtmsrd r10,1 #endif /* CONFIG_PPC_BOOK3E */ @@ -308,7 +311,7 @@ syscall_exit_work: #ifdef CONFIG_PPC_BOOK3E wrteei 1 #else - mfmsr r10 + ld r10,PACAKMSR(r13) ori r10,r10,MSR_EE mtmsrd r10,1 #endif /* CONFIG_PPC_BOOK3E */ -- cgit v0.10.2 From 1b70117924a4f254840ed70fbe3020d4519a1a9a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 1 Mar 2012 15:42:56 +1100 Subject: powerpc: Improve behaviour of irq tracing on 64-bit exception entry Some exceptions would unconditionally disable interrupts on entry, which is fine, but calling lockdep every time not only adds more overhead than strictly needed, but also means we get quite a few "redudant" disable logged, which makes it hard to spot the really bad ones. So instead, split the macro used by the exception code into a normal one and a separate one used when CONFIG_TRACE_IRQFLAGS is enabled, and make the later skip th tracing if interrupts were already disabled. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index bdc9eeb..7f4718c 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -272,15 +272,34 @@ label##_hv: \ _MASKABLE_EXCEPTION_PSERIES(vec, label, \ EXC_HV, SOFTEN_TEST_HV) +/* + * Our exception common code can be passed various "additions" + * to specify the behaviour of interrupts, whether to kick the + * runlatch, etc... + */ + +/* Exception addition: Hard disable interrupts */ +#ifdef CONFIG_TRACE_IRQFLAGS #define DISABLE_INTS \ + lbz r10,PACASOFTIRQEN(r13); \ li r11,0; \ - stb r11,PACASOFTIRQEN(r13); \ + cmpwi cr0,r10,0; \ stb r11,PACAHARDIRQEN(r13); \ - TRACE_DISABLE_INTS + beq 44f; \ + stb r11,PACASOFTIRQEN(r13); \ + TRACE_DISABLE_INTS; \ +44: +#else +#define DISABLE_INTS \ + li r11,0; \ + stb r11,PACASOFTIRQEN(r13); \ + stb r11,PACAHARDIRQEN(r13) +#endif /* CONFIG_TRACE_IRQFLAGS */ +/* Exception addition: Keep interrupt state */ #define ENABLE_INTS \ - ld r12,_MSR(r1); \ mfmsr r11; \ + ld r12,_MSR(r1); \ rlwimi r11,r12,0,MSR_EE; \ mtmsrd r11,1 -- cgit v0.10.2 From a546498f3bf9aac311c66f965186373aee2ca0b0 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 7 Mar 2012 16:48:45 +1100 Subject: powerpc: Call do_page_fault() with interrupts off We currently turn interrupts back to their previous state before calling do_page_fault(). This can be annoying when debugging as a bad fault will potentially have lost some processor state before getting into the debugger. We also end up calling some generic code with interrupts enabled such as notify_page_fault() with interrupts enabled, which could be unexpected. This changes our code to behave more like other architectures, and make the assembly entry code call into do_page_faults() with interrupts disabled. They are conditionally re-enabled from within do_page_fault() in the same spot x86 does it. While there, add the might_sleep() test in the case of a successful trylock of the mmap semaphore, again like x86. Also fix a bug in the existing assembly where r12 (_MSR) could get clobbered by C calls (the DTL accounting in the exception common macro and DISABLE_INTS) in some cases. Signed-off-by: Benjamin Herrenschmidt --- v2. Add the r12 clobber fix diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index bb712c9..531ba00 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h @@ -79,6 +79,11 @@ static inline bool arch_irqs_disabled(void) get_paca()->hard_enabled = 0; \ } while(0) +static inline bool arch_irq_disabled_regs(struct pt_regs *regs) +{ + return !regs->softe; +} + #else /* CONFIG_PPC64 */ #define SET_MSR_EE(x) mtmsr(x) @@ -139,6 +144,11 @@ static inline bool arch_irqs_disabled(void) #define hard_irq_disable() arch_local_irq_disable() +static inline bool arch_irq_disabled_regs(struct pt_regs *regs) +{ + return !(regs->msr & MSR_EE); +} + #endif /* CONFIG_PPC64 */ #define ARCH_IRQ_INIT_FLAGS IRQ_NOREQUEST diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 429983c..573613d 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -313,7 +313,7 @@ interrupt_end_book3e: NORMAL_EXCEPTION_PROLOG(0x300, PROLOG_ADDITION_2REGS) mfspr r14,SPRN_DEAR mfspr r15,SPRN_ESR - EXCEPTION_COMMON(0x300, PACA_EXGEN, INTS_KEEP) + EXCEPTION_COMMON(0x300, PACA_EXGEN, INTS_DISABLE_ALL) b storage_fault_common /* Instruction Storage Interrupt */ @@ -321,7 +321,7 @@ interrupt_end_book3e: NORMAL_EXCEPTION_PROLOG(0x400, PROLOG_ADDITION_2REGS) li r15,0 mr r14,r10 - EXCEPTION_COMMON(0x400, PACA_EXGEN, INTS_KEEP) + EXCEPTION_COMMON(0x400, PACA_EXGEN, INTS_DISABLE_ALL) b storage_fault_common /* External Input Interrupt */ @@ -591,7 +591,6 @@ storage_fault_common: mr r5,r15 ld r14,PACA_EXGEN+EX_R14(r13) ld r15,PACA_EXGEN+EX_R15(r13) - INTS_RESTORE_HARD bl .do_page_fault cmpdi r3,0 bne- 1f diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 3af80e8..d8ff6d3 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -559,6 +559,8 @@ data_access_common: mfspr r10,SPRN_DSISR stw r10,PACA_EXGEN+EX_DSISR(r13) EXCEPTION_PROLOG_COMMON(0x300, PACA_EXGEN) + DISABLE_INTS + ld r12,_MSR(r1) ld r3,PACA_EXGEN+EX_DAR(r13) lwz r4,PACA_EXGEN+EX_DSISR(r13) li r5,0x300 @@ -573,6 +575,7 @@ h_data_storage_common: stw r10,PACA_EXGEN+EX_DSISR(r13) EXCEPTION_PROLOG_COMMON(0xe00, PACA_EXGEN) bl .save_nvgprs + DISABLE_INTS addi r3,r1,STACK_FRAME_OVERHEAD bl .unknown_exception b .ret_from_except @@ -581,6 +584,8 @@ h_data_storage_common: .globl instruction_access_common instruction_access_common: EXCEPTION_PROLOG_COMMON(0x400, PACA_EXGEN) + DISABLE_INTS + ld r12,_MSR(r1) ld r3,_NIP(r1) andis. r4,r12,0x5820 li r5,0x400 @@ -884,24 +889,6 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) lwz r0,TI_PREEMPT(r11) /* If we're in an "NMI" */ andis. r0,r0,NMI_MASK@h /* (i.e. an irq when soft-disabled) */ bne 77f /* then don't call hash_page now */ - - /* We run with interrupts both soft and hard disabled */ - DISABLE_INTS - - /* - * Currently, trace_hardirqs_off() will be called by DISABLE_INTS - * and will clobber volatile registers when irq tracing is enabled - * so we need to reload them. It may be possible to be smarter here - * and move the irq tracing elsewhere but let's keep it simple for - * now - */ -#ifdef CONFIG_TRACE_IRQFLAGS - ld r3,_DAR(r1) - ld r4,_DSISR(r1) - ld r5,_TRAP(r1) - ld r12,_MSR(r1) - clrrdi r5,r5,4 -#endif /* CONFIG_TRACE_IRQFLAGS */ /* * We need to set the _PAGE_USER bit if MSR_PR is set or if we are * accessing a userspace segment (even from the kernel). We assume @@ -931,36 +918,16 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) beq fast_exc_return_irq /* Return from exception on success */ /* For a hash failure, we don't bother re-enabling interrupts */ - ble- 12f - - /* - * hash_page couldn't handle it, set soft interrupt enable back - * to what it was before the trap. Note that .arch_local_irq_restore - * handles any interrupts pending at this point. - */ - ld r3,SOFTE(r1) - TRACE_AND_RESTORE_IRQ_PARTIAL(r3, 11f) - bl .arch_local_irq_restore - b 11f - -/* We have a data breakpoint exception - handle it */ -handle_dabr_fault: - bl .save_nvgprs - ld r4,_DAR(r1) - ld r5,_DSISR(r1) - addi r3,r1,STACK_FRAME_OVERHEAD - bl .do_dabr - b .ret_from_except_lite + ble- 13f /* Here we have a page fault that hash_page can't handle. */ handle_page_fault: - ENABLE_INTS 11: ld r4,_DAR(r1) ld r5,_DSISR(r1) addi r3,r1,STACK_FRAME_OVERHEAD bl .do_page_fault cmpdi r3,0 - beq+ 13f + beq+ 12f bl .save_nvgprs mr r5,r3 addi r3,r1,STACK_FRAME_OVERHEAD @@ -968,12 +935,20 @@ handle_page_fault: bl .bad_page_fault b .ret_from_except -13: b .ret_from_except_lite +/* We have a data breakpoint exception - handle it */ +handle_dabr_fault: + bl .save_nvgprs + ld r4,_DAR(r1) + ld r5,_DSISR(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + bl .do_dabr +12: b .ret_from_except_lite + /* We have a page fault that hash_page could handle but HV refused * the PTE insertion */ -12: bl .save_nvgprs +13: bl .save_nvgprs mr r5,r3 addi r3,r1,STACK_FRAME_OVERHEAD ld r4,_DAR(r1) diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index 0654dba..dc0488b 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -395,7 +395,7 @@ DataAccess: bl hash_page 1: lwz r5,_DSISR(r11) /* get DSISR value */ mfspr r4,SPRN_DAR - EXC_XFER_EE_LITE(0x300, handle_page_fault) + EXC_XFER_LITE(0x300, handle_page_fault) /* Instruction access exception. */ @@ -410,7 +410,7 @@ InstructionAccess: bl hash_page 1: mr r4,r12 mr r5,r9 - EXC_XFER_EE_LITE(0x400, handle_page_fault) + EXC_XFER_LITE(0x400, handle_page_fault) /* External interrupt */ EXCEPTION(0x500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE) diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S index 872a6af..4989661 100644 --- a/arch/powerpc/kernel/head_40x.S +++ b/arch/powerpc/kernel/head_40x.S @@ -394,7 +394,7 @@ label: NORMAL_EXCEPTION_PROLOG mr r4,r12 /* Pass SRR0 as arg2 */ li r5,0 /* Pass zero as arg3 */ - EXC_XFER_EE_LITE(0x400, handle_page_fault) + EXC_XFER_LITE(0x400, handle_page_fault) /* 0x0500 - External Interrupt Exception */ EXCEPTION(0x0500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE) @@ -747,7 +747,7 @@ DataAccess: mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ stw r5,_ESR(r11) mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ - EXC_XFER_EE_LITE(0x300, handle_page_fault) + EXC_XFER_LITE(0x300, handle_page_fault) /* Other PowerPC processors, namely those derived from the 6xx-series * have vectors from 0x2100 through 0x2F00 defined, but marked as reserved. diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S index b68cb17..b2a5860 100644 --- a/arch/powerpc/kernel/head_8xx.S +++ b/arch/powerpc/kernel/head_8xx.S @@ -220,7 +220,7 @@ DataAccess: mfspr r4,SPRN_DAR li r10,0x00f0 mtspr SPRN_DAR,r10 /* Tag DAR, to be used in DTLB Error */ - EXC_XFER_EE_LITE(0x300, handle_page_fault) + EXC_XFER_LITE(0x300, handle_page_fault) /* Instruction access exception. * This is "never generated" by the MPC8xx. We jump to it for other @@ -231,7 +231,7 @@ InstructionAccess: EXCEPTION_PROLOG mr r4,r12 mr r5,r9 - EXC_XFER_EE_LITE(0x400, handle_page_fault) + EXC_XFER_LITE(0x400, handle_page_fault) /* External interrupt */ EXCEPTION(0x500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE) diff --git a/arch/powerpc/kernel/head_booke.h b/arch/powerpc/kernel/head_booke.h index fc921bf..0e41753 100644 --- a/arch/powerpc/kernel/head_booke.h +++ b/arch/powerpc/kernel/head_booke.h @@ -359,7 +359,7 @@ label: mfspr r5,SPRN_ESR; /* Grab the ESR and save it */ \ stw r5,_ESR(r11); \ mfspr r4,SPRN_DEAR; /* Grab the DEAR */ \ - EXC_XFER_EE_LITE(0x0300, handle_page_fault) + EXC_XFER_LITE(0x0300, handle_page_fault) #define INSTRUCTION_STORAGE_EXCEPTION \ START_EXCEPTION(InstructionStorage) \ @@ -368,7 +368,7 @@ label: stw r5,_ESR(r11); \ mr r4,r12; /* Pass SRR0 as arg2 */ \ li r5,0; /* Pass zero as arg3 */ \ - EXC_XFER_EE_LITE(0x0400, handle_page_fault) + EXC_XFER_LITE(0x0400, handle_page_fault) #define ALIGNMENT_EXCEPTION \ START_EXCEPTION(Alignment) \ diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index d5d78c4..28e6259 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -319,7 +319,7 @@ interrupt_base: mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ andis. r10,r5,(ESR_ILK|ESR_DLK)@h bne 1f - EXC_XFER_EE_LITE(0x0300, handle_page_fault) + EXC_XFER_LITE(0x0300, handle_page_fault) 1: addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_EE_LITE(0x0300, CacheLockingException) diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 2f0d1b0..7e89006 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -179,6 +179,10 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, } #endif + /* We restore the interrupt state now */ + if (!arch_irq_disabled_regs(regs)) + local_irq_enable(); + if (in_atomic() || mm == NULL) { if (!user_mode(regs)) return SIGSEGV; @@ -213,6 +217,13 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, goto bad_area_nosemaphore; down_read(&mm->mmap_sem); + } else { + /* + * The above down_read_trylock() might have succeeded in + * which case we'll have missed the might_sleep() from + * down_read(): + */ + might_sleep(); } vma = find_vma(mm, address); -- cgit v0.10.2 From 9f2f79e3a3c19ae745d0439d6e0eed31df28de3c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 1 Mar 2012 15:47:44 +1100 Subject: powerpc: Disable interrupts in 64-bit kernel FP and vector faults If we get a floating point, altivec or vsx unavaible interrupt in kernel, we trigger a kernel error. There is no point preserving the interrupt state, in fact, that can even make debugging harder as the processor state might change (we may even preempt) between taking the exception and landing in a debugger. So just make those 3 disable interrupts unconditionally. Signed-off-by: Benjamin Herrenschmidt --- v2: On BookE only disable when hitting the kernel unavailable path, otherwise it will fail to restore softe as fast_exception_return doesn't do it. diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 573613d..3de9993 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -354,9 +354,9 @@ interrupt_end_book3e: /* we can probably do a shorter exception entry for that one... */ EXCEPTION_COMMON(0x800, PACA_EXGEN, INTS_KEEP) bne 1f /* if from user, just load it up */ + INTS_DISABLE_ALL bl .save_nvgprs addi r3,r1,STACK_FRAME_OVERHEAD - INTS_RESTORE_HARD bl .kernel_fp_unavailable_exception BUG_OPCODE 1: ld r12,_MSR(r1) @@ -391,10 +391,9 @@ interrupt_end_book3e: /* Auxiliary Processor Unavailable Interrupt */ START_EXCEPTION(ap_unavailable); NORMAL_EXCEPTION_PROLOG(0xf20, PROLOG_ADDITION_NONE) - EXCEPTION_COMMON(0xf20, PACA_EXGEN, INTS_KEEP) - addi r3,r1,STACK_FRAME_OVERHEAD + EXCEPTION_COMMON(0xf20, PACA_EXGEN, INTS_DISABLE_ALL) bl .save_nvgprs - INTS_RESTORE_HARD + addi r3,r1,STACK_FRAME_OVERHEAD bl .unknown_exception b .ret_from_except diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index d8ff6d3..0fb42ae 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -762,8 +762,8 @@ fp_unavailable_common: EXCEPTION_PROLOG_COMMON(0x800, PACA_EXGEN) bne 1f /* if from user, just load it up */ bl .save_nvgprs + DISABLE_INTS addi r3,r1,STACK_FRAME_OVERHEAD - ENABLE_INTS bl .kernel_fp_unavailable_exception BUG_OPCODE 1: bl .load_up_fpu @@ -782,8 +782,8 @@ BEGIN_FTR_SECTION END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) #endif bl .save_nvgprs + DISABLE_INTS addi r3,r1,STACK_FRAME_OVERHEAD - ENABLE_INTS bl .altivec_unavailable_exception b .ret_from_except @@ -798,8 +798,8 @@ BEGIN_FTR_SECTION END_FTR_SECTION_IFSET(CPU_FTR_VSX) #endif bl .save_nvgprs + DISABLE_INTS addi r3,r1,STACK_FRAME_OVERHEAD - ENABLE_INTS bl .vsx_unavailable_exception b .ret_from_except diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 5d40e59..a750409 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -247,6 +247,9 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) addr, regs->nip, regs->link, code); } + if (!arch_irq_disabled_regs(regs)) + local_irq_enable(); + memset(&info, 0, sizeof(info)); info.si_signo = signr; info.si_code = code; -- cgit v0.10.2 From 9be72573a80648866ed0045db22d97c6e160a540 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 1 Mar 2012 18:14:45 +1100 Subject: powerpc: Add support for page fault retry and fatal signals Other architectures such as x86 and ARM have been growing new support for features like retrying page faults after dropping the mm semaphore to break contention, or being able to return from a stuck page fault when a SIGKILL is pending. This refactors our implementation of do_page_fault() to move the error handling out of line in a way similar to x86 and adds support for those two features. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 7e89006..19f2f94 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -105,6 +105,82 @@ static int store_updates_sp(struct pt_regs *regs) } return 0; } +/* + * do_page_fault error handling helpers + */ + +#define MM_FAULT_RETURN 0 +#define MM_FAULT_CONTINUE -1 +#define MM_FAULT_ERR(sig) (sig) + +static int out_of_memory(struct pt_regs *regs) +{ + /* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ + up_read(¤t->mm->mmap_sem); + if (!user_mode(regs)) + return MM_FAULT_ERR(SIGKILL); + pagefault_out_of_memory(); + return MM_FAULT_RETURN; +} + +static int do_sigbus(struct pt_regs *regs, unsigned long address) +{ + siginfo_t info; + + up_read(¤t->mm->mmap_sem); + + if (user_mode(regs)) { + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void __user *)address; + force_sig_info(SIGBUS, &info, current); + return MM_FAULT_RETURN; + } + return MM_FAULT_ERR(SIGBUS); +} + +static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault) +{ + /* + * Pagefault was interrupted by SIGKILL. We have no reason to + * continue the pagefault. + */ + if (fatal_signal_pending(current)) { + /* + * If we have retry set, the mmap semaphore will have + * alrady been released in __lock_page_or_retry(). Else + * we release it now. + */ + if (!(fault & VM_FAULT_RETRY)) + up_read(¤t->mm->mmap_sem); + /* Coming from kernel, we need to deal with uaccess fixups */ + if (user_mode(regs)) + return MM_FAULT_RETURN; + return MM_FAULT_ERR(SIGKILL); + } + + /* No fault: be happy */ + if (!(fault & VM_FAULT_ERROR)) + return MM_FAULT_CONTINUE; + + /* Out of memory */ + if (fault & VM_FAULT_OOM) + return out_of_memory(regs); + + /* Bus error. x86 handles HWPOISON here, we'll add this if/when + * we support the feature in HW + */ + if (fault & VM_FAULT_SIGBUS) + return do_sigbus(regs, addr); + + /* We don't understand the fault code, this is fatal */ + BUG(); + return MM_FAULT_CONTINUE; +} /* * For 600- and 800-family processors, the error_code parameter is DSISR @@ -124,11 +200,12 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, { struct vm_area_struct * vma; struct mm_struct *mm = current->mm; - siginfo_t info; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; int code = SEGV_MAPERR; - int is_write = 0, ret; + int is_write = 0; int trap = TRAP(regs); int is_exec = trap == 0x400; + int fault; #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE)) /* @@ -145,6 +222,9 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, is_write = error_code & ESR_DST; #endif /* CONFIG_4xx || CONFIG_BOOKE */ + if (is_write) + flags |= FAULT_FLAG_WRITE; + #ifdef CONFIG_PPC_ICSWX /* * we need to do this early because this "data storage @@ -152,13 +232,11 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, * look at it */ if (error_code & ICSWX_DSI_UCT) { - int ret; - - ret = acop_handle_fault(regs, address, error_code); - if (ret) - return ret; + int rc = acop_handle_fault(regs, address, error_code); + if (rc) + return rc; } -#endif +#endif /* CONFIG_PPC_ICSWX */ if (notify_page_fault(regs)) return 0; @@ -216,6 +294,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, if (!user_mode(regs) && !search_exception_tables(regs->nip)) goto bad_area_nosemaphore; +retry: down_read(&mm->mmap_sem); } else { /* @@ -338,30 +417,43 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - ret = handle_mm_fault(mm, vma, address, is_write ? FAULT_FLAG_WRITE : 0); - if (unlikely(ret & VM_FAULT_ERROR)) { - if (ret & VM_FAULT_OOM) - goto out_of_memory; - else if (ret & VM_FAULT_SIGBUS) - goto do_sigbus; - BUG(); + fault = handle_mm_fault(mm, vma, address, flags); + if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) { + int rc = mm_fault_error(regs, address, fault); + if (rc >= MM_FAULT_RETURN) + return rc; } - if (ret & VM_FAULT_MAJOR) { - current->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, - regs, address); + + /* + * Major/minor page fault accounting is only done on the + * initial attempt. If we go through a retry, it is extremely + * likely that the page will be found in page cache at that point. + */ + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) { + current->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, + regs, address); #ifdef CONFIG_PPC_SMLPAR - if (firmware_has_feature(FW_FEATURE_CMO)) { - preempt_disable(); - get_lppaca()->page_ins += (1 << PAGE_FACTOR); - preempt_enable(); + if (firmware_has_feature(FW_FEATURE_CMO)) { + preempt_disable(); + get_lppaca()->page_ins += (1 << PAGE_FACTOR); + preempt_enable(); + } +#endif /* CONFIG_PPC_SMLPAR */ + } else { + current->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, + regs, address); + } + if (fault & VM_FAULT_RETRY) { + /* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk + * of starvation. */ + flags &= ~FAULT_FLAG_ALLOW_RETRY; + goto retry; } -#endif - } else { - current->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, - regs, address); } + up_read(&mm->mmap_sem); return 0; @@ -382,28 +474,6 @@ bad_area_nosemaphore: return SIGSEGV; -/* - * We ran out of memory, or some other thing happened to us that made - * us unable to handle the page fault gracefully. - */ -out_of_memory: - up_read(&mm->mmap_sem); - if (!user_mode(regs)) - return SIGKILL; - pagefault_out_of_memory(); - return 0; - -do_sigbus: - up_read(&mm->mmap_sem); - if (user_mode(regs)) { - info.si_signo = SIGBUS; - info.si_errno = 0; - info.si_code = BUS_ADRERR; - info.si_addr = (void __user *)address; - force_sig_info(SIGBUS, &info, current); - return 0; - } - return SIGBUS; } /* -- cgit v0.10.2 From 7ac21cd465391802d931bd5e692302639383b8f5 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 2 Mar 2012 10:10:09 +1100 Subject: powerpc/xmon: Add display of soft & hard irq states Also use local_paca instead of get_paca() to avoid getting into the smp_processor_id() debugging code from the debugger Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index cb95eea..63846eb 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -1437,7 +1437,8 @@ static void excprint(struct pt_regs *fp) printf(" current = 0x%lx\n", current); #ifdef CONFIG_PPC64 - printf(" paca = 0x%lx\n", get_paca()); + printf(" paca = 0x%lx\t softe: %d\t harde: %d\n", + local_paca, local_paca->soft_enabled, local_paca->hard_enabled); #endif if (current) { printf(" pid = %ld, comm = %s\n", @@ -1641,7 +1642,7 @@ static void super_regs(void) /* Dump out relevant Paca data areas. */ printf("Paca: \n"); - ptrPaca = get_paca(); + ptrPaca = local_paca; printf(" Local Processor Control Area (LpPaca): \n"); ptrLpPaca = ptrPaca->lppaca_ptr; @@ -2644,7 +2645,7 @@ static void dump_slb(void) static void dump_stab(void) { int i; - unsigned long *tmp = (unsigned long *)get_paca()->stab_addr; + unsigned long *tmp = (unsigned long *)local_paca->stab_addr; printf("Segment table contents of cpu %x\n", smp_processor_id()); -- cgit v0.10.2 From 990118c84b3e90b2b5354b6e2acd961044d7fa12 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 2 Mar 2012 11:01:31 +1100 Subject: powerpc: Fix register clobbering when accumulating stolen time When running under a hypervisor that supports stolen time accounting, we may call C code from the macro EXCEPTION_PROLOG_COMMON in the exception entry path, which clobbers CR0. However, the FPU and vector traps rely on CR0 indicating whether we are coming from userspace or kernel to decide what to do. So we need to restore that value after the C call Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 368f72f..50f73aa 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -60,6 +60,8 @@ BEGIN_FW_FTR_SECTION; \ cmpd cr1,r11,r10; \ beq+ cr1,33f; \ bl .accumulate_stolen_time; \ + ld r12,_MSR(r1); \ + andi. r10,r12,MSR_PR; /* Restore cr0 (coming from user) */ \ 33: \ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) -- cgit v0.10.2 From 9424fabf8617c15e18a5ffd29bc3bcfa36620473 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 5 Mar 2012 10:55:04 +1100 Subject: powerpc: Fix 64-bit BookE FP unavailable exceptions We were using CR0.EQ after EXCEPTION_COMMON, hoping it still contained whether we came from userspace or kernel space. However, under some circumstances, EXCEPTION_COMMON will call C code and clobber non-volatile registers, so we really need to re-load the previous MSR from the stackframe and re-test. While there, invert the condition to make the fast path more obvious and remove the BUG_OPCODE which was a debugging leftover and call .ret_from_except as we should. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 3de9993..c4c3466 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -353,15 +353,16 @@ interrupt_end_book3e: NORMAL_EXCEPTION_PROLOG(0x800, PROLOG_ADDITION_NONE) /* we can probably do a shorter exception entry for that one... */ EXCEPTION_COMMON(0x800, PACA_EXGEN, INTS_KEEP) - bne 1f /* if from user, just load it up */ - INTS_DISABLE_ALL + ld r12,_MSR(r1) + andi. r0,r12,MSR_PR; + beq- 1f + bl .load_up_fpu + b fast_exception_return +1: INTS_DISABLE_ALL bl .save_nvgprs addi r3,r1,STACK_FRAME_OVERHEAD bl .kernel_fp_unavailable_exception - BUG_OPCODE -1: ld r12,_MSR(r1) - bl .load_up_fpu - b fast_exception_return + b .ret_from_except /* Decrementer Interrupt */ MASKABLE_EXCEPTION(0x900, decrementer, .timer_interrupt, ACK_DEC) -- cgit v0.10.2 From d9ada91ae2969ae6b6dc3574fd08a6ebda5df766 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 2 Mar 2012 11:33:52 +1100 Subject: powerpc: Replace mfmsr instructions with load from PACA kernel_msr field On 64-bit, the mfmsr instruction can be quite slow, slower than loading a field from the cache-hot PACA, which happens to already contain the value we want in most cases. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 7f4718c..70354af 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -298,7 +298,7 @@ label##_hv: \ /* Exception addition: Keep interrupt state */ #define ENABLE_INTS \ - mfmsr r11; \ + ld r11,PACAKMSR(r13); \ ld r12,_MSR(r1); \ rlwimi r11,r12,0,MSR_EE; \ mtmsrd r11,1 diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index 531ba00..6c6fa95 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h @@ -68,8 +68,8 @@ static inline bool arch_irqs_disabled(void) #define __hard_irq_enable() asm volatile("wrteei 1" : : : "memory"); #define __hard_irq_disable() asm volatile("wrteei 0" : : : "memory"); #else -#define __hard_irq_enable() __mtmsrd(mfmsr() | MSR_EE, 1) -#define __hard_irq_disable() __mtmsrd(mfmsr() & ~MSR_EE, 1) +#define __hard_irq_enable() __mtmsrd(local_paca->kernel_msr | MSR_EE, 1) +#define __hard_irq_disable() __mtmsrd(local_paca->kernel_msr, 1) #endif #define hard_irq_disable() \ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index cc030b7..c513beb 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -557,10 +557,8 @@ _GLOBAL(ret_from_except_lite) #ifdef CONFIG_PPC_BOOK3E wrteei 0 #else - mfmsr r10 /* Get current interrupt state */ - rldicl r9,r10,48,1 /* clear MSR_EE */ - rotldi r9,r9,16 - mtmsrd r9,1 /* Update machine state */ + ld r10,PACAKMSR(r13) /* Get kernel MSR without EE */ + mtmsrd r10,1 /* Update machine state */ #endif /* CONFIG_PPC_BOOK3E */ #ifdef CONFIG_PREEMPT @@ -625,8 +623,8 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) * userspace and we take an exception after restoring r13, * we end up corrupting the userspace r13 value. */ - mfmsr r4 - andc r4,r4,r0 /* r0 contains MSR_RI here */ + ld r4,PACAKMSR(r13) /* Get kernel MSR without EE */ + andc r4,r4,r0 /* r0 contains MSR_RI here */ mtmsrd r4,1 /* @@ -686,9 +684,7 @@ do_work: #ifdef CONFIG_PPC_BOOK3E wrteei 0 #else - mfmsr r10 - rldicl r10,r10,48,1 - rotldi r10,r10,16 + ld r10,PACAKMSR(r13) /* Get kernel MSR without EE */ mtmsrd r10,1 #endif /* CONFIG_PPC_BOOK3E */ li r0,0 diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 0fb42ae..02448ea 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -848,9 +848,8 @@ fast_exception_return: REST_GPR(0, r1) REST_8GPRS(2, r1) - mfmsr r10 - rldicl r10,r10,48,1 /* clear EE */ - rldicr r10,r10,16,61 /* clear RI (LE is 0 already) */ + ld r10,PACAKMSR(r13) + clrrdi r10,r10,2 /* clear RI */ mtmsrd r10,1 mtspr SPRN_SRR1,r12 -- cgit v0.10.2 From cb3bc9d0de1e247268622acd40e0d2f8120f299c Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:51 +0000 Subject: powerpc/eeh: Cleanup comments in the EEH core The EEH has been implemented on pSeries platform. The original code looks a little bit nasty. The patch does cleanup on the current EEH implementation so that it looks more clean. * Duplicated comments have been removed from the corresponding header files. * Comments have been reorganized so that it looks more clean. * The leading comments of functions are adjusted for a little bit so that the result of "make pdfdocs" would be more unified. * Function definitions and calls have unified format as "xxx()". That means the format "xxx ()" has been replaced by "xxx()". * There're multiple functions implemented for resetting PE. The position of those functions have been move around so that they are adjacent to each other to reflect their relationship. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 66ea9b8..2328877 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -1,6 +1,6 @@ /* - * eeh.h * Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation. + * Copyright 2001-2012 IBM Corporation. * * 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 @@ -40,8 +40,10 @@ extern int eeh_subsystem_enabled; #define EEH_MODE_RECOVERING (1<<3) #define EEH_MODE_IRQ_DISABLED (1<<4) -/* Max number of EEH freezes allowed before we consider the device - * to be permanently disabled. */ +/* + * Max number of EEH freezes allowed before we consider the device + * to be permanently disabled. + */ #define EEH_MAX_ALLOWED_FREEZES 5 void __init eeh_init(void); @@ -49,26 +51,8 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val); int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev); void __init pci_addr_cache_build(void); - -/** - * eeh_add_device_early - * eeh_add_device_late - * - * Perform eeh initialization for devices added after boot. - * Call eeh_add_device_early before doing any i/o to the - * device (including config space i/o). Call eeh_add_device_late - * to finish the eeh setup for this device. - */ void eeh_add_device_tree_early(struct device_node *); void eeh_add_device_tree_late(struct pci_bus *); - -/** - * eeh_remove_device_recursive - undo EEH for device & children. - * @dev: pci device to be removed - * - * As above, this removes the device; it also removes child - * pci devices as well. - */ void eeh_remove_bus_device(struct pci_dev *); /** diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 6d42297..221d82f 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -47,92 +47,27 @@ extern int rtas_setup_phb(struct pci_controller *phb); extern unsigned long pci_probe_only; -/* ---- EEH internal-use-only related routines ---- */ #ifdef CONFIG_EEH void pci_addr_cache_insert_device(struct pci_dev *dev); void pci_addr_cache_remove_device(struct pci_dev *dev); void pci_addr_cache_build(void); struct pci_dev *pci_get_device_by_addr(unsigned long addr); - -/** - * eeh_slot_error_detail -- record and EEH error condition to the log - * @pdn: pci device node - * @severity: EEH_LOG_TEMP_FAILURE or EEH_LOG_PERM_FAILURE - * - * Obtains the EEH error details from the RTAS subsystem, - * and then logs these details with the RTAS error log system. - */ #define EEH_LOG_TEMP_FAILURE 1 #define EEH_LOG_PERM_FAILURE 2 void eeh_slot_error_detail (struct pci_dn *pdn, int severity); - -/** - * rtas_pci_enable - enable IO transfers for this slot - * @pdn: pci device node - * @function: either EEH_THAW_MMIO or EEH_THAW_DMA - * - * Enable I/O transfers to this slot - */ #define EEH_THAW_MMIO 2 #define EEH_THAW_DMA 3 int rtas_pci_enable(struct pci_dn *pdn, int function); - -/** - * rtas_set_slot_reset -- unfreeze a frozen slot - * @pdn: pci device node - * - * Clear the EEH-frozen condition on a slot. This routine - * does this by asserting the PCI #RST line for 1/8th of - * a second; this routine will sleep while the adapter is - * being reset. - * - * Returns a non-zero value if the reset failed. - */ int rtas_set_slot_reset (struct pci_dn *); int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs); - -/** - * eeh_restore_bars - Restore device configuration info. - * @pdn: pci device node - * - * A reset of a PCI device will clear out its config space. - * This routines will restore the config space for this - * device, and is children, to values previously obtained - * from the firmware. - */ void eeh_restore_bars(struct pci_dn *); - -/** - * rtas_configure_bridge -- firmware initialization of pci bridge - * @pdn: pci device node - * - * Ask the firmware to configure all PCI bridges devices - * located behind the indicated node. Required after a - * pci device reset. Does essentially the same hing as - * eeh_restore_bars, but for brdges, and lets firmware - * do the work. - */ void rtas_configure_bridge(struct pci_dn *); - int rtas_write_config(struct pci_dn *, int where, int size, u32 val); int rtas_read_config(struct pci_dn *, int where, int size, u32 *val); - -/** - * eeh_mark_slot -- set mode flags for pertition endpoint - * @pdn: pci device node - * - * mark and clear slots: find "partition endpoint" PE and set or - * clear the flags for each subnode of the PE. - */ -void eeh_mark_slot (struct device_node *dn, int mode_flag); -void eeh_clear_slot (struct device_node *dn, int mode_flag); - -/** - * find_device_pe -- Find the associated "Partiationable Endpoint" PE - * @pdn: pci device node - */ -struct device_node * find_device_pe(struct device_node *dn); +void eeh_mark_slot(struct device_node *dn, int mode_flag); +void eeh_clear_slot(struct device_node *dn, int mode_flag); +struct device_node *find_device_pe(struct device_node *dn); void eeh_sysfs_add_device(struct pci_dev *pdev); void eeh_sysfs_remove_device(struct pci_dev *pdev); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index c0b40af..5f6d37b 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -1,8 +1,8 @@ /* - * eeh.c * Copyright IBM Corporation 2001, 2005, 2006 * Copyright Dave Engebretsen & Todd Inglett 2001 * Copyright Linas Vepstas 2005, 2006 + * Copyright 2001-2012 IBM Corporation. * * 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 @@ -22,7 +22,7 @@ */ #include -#include /* for init_mm */ +#include #include #include #include @@ -129,9 +129,16 @@ static unsigned long slot_resets; #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) -/* --------------------------------------------------------------- */ -/* Below lies the EEH event infrastructure */ - +/** + * rtas_slot_error_detail - Retrieve error log through RTAS call + * @pdn: device node + * @severity: temporary or permanent error log + * @driver_log: driver log to be combined with the retrieved error log + * @loglen: length of driver log + * + * This routine should be called to retrieve error log through the dedicated + * RTAS call. + */ static void rtas_slot_error_detail(struct pci_dn *pdn, int severity, char *driver_log, size_t loglen) { @@ -163,7 +170,7 @@ static void rtas_slot_error_detail(struct pci_dn *pdn, int severity, } /** - * gather_pci_data - copy assorted PCI config space registers to buff + * gather_pci_data - Copy assorted PCI config space registers to buff * @pdn: device to report data for * @buf: point to buffer in which to log * @len: amount of room in buffer @@ -258,6 +265,16 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) return n; } +/** + * eeh_slot_error_detail - Generate combined log including driver log and error log + * @pdn: device node + * @severity: temporary or permanent error log + * + * This routine should be called to generate the combined log, which + * is comprised of driver log and error log. The driver log is figured + * out from the config space of the corresponding PCI device, while + * the error log is fetched through platform dependent function call. + */ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) { size_t loglen = 0; @@ -275,6 +292,9 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) * read_slot_reset_state - Read the reset state of a device node's slot * @dn: device node to read * @rets: array to return results in + * + * Read the reset state of a device node's slot through platform dependent + * function call. */ static int read_slot_reset_state(struct pci_dn *pdn, int rets[]) { @@ -300,9 +320,9 @@ static int read_slot_reset_state(struct pci_dn *pdn, int rets[]) } /** - * eeh_wait_for_slot_status - returns error status of slot - * @pdn pci device node - * @max_wait_msecs maximum number to millisecs to wait + * eeh_wait_for_slot_status - Returns error status of slot + * @pdn: pci device node + * @max_wait_msecs: maximum number to millisecs to wait * * Return negative value if a permanent error, else return * Partition Endpoint (PE) status value. @@ -332,16 +352,16 @@ eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) mwait = rets[2]; if (mwait <= 0) { - printk (KERN_WARNING - "EEH: Firmware returned bad wait value=%d\n", mwait); + printk(KERN_WARNING "EEH: Firmware returned bad wait value=%d\n", + mwait); mwait = 1000; } else if (mwait > 300*1000) { - printk (KERN_WARNING - "EEH: Firmware is taking too long, time=%d\n", mwait); + printk(KERN_WARNING "EEH: Firmware is taking too long, time=%d\n", + mwait); mwait = 300*1000; } max_wait_msecs -= mwait; - msleep (mwait); + msleep(mwait); } printk(KERN_WARNING "EEH: Timed out waiting for slot status\n"); @@ -349,8 +369,11 @@ eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) } /** - * eeh_token_to_phys - convert EEH address token to phys address - * @token i/o token, should be address in the form 0xA.... + * eeh_token_to_phys - Convert EEH address token to phys address + * @token: I/O token, should be address in the form 0xA.... + * + * This routine should be called to convert virtual I/O address + * to physical one. */ static inline unsigned long eeh_token_to_phys(unsigned long token) { @@ -365,8 +388,11 @@ static inline unsigned long eeh_token_to_phys(unsigned long token) return pa | (token & (PAGE_SIZE-1)); } -/** - * Return the "partitionable endpoint" (pe) under which this device lies +/** + * find_device_pe - Retrieve the PE for the given device + * @dn: device node + * + * Return the PE under which this device lies */ struct device_node * find_device_pe(struct device_node *dn) { @@ -377,14 +403,18 @@ struct device_node * find_device_pe(struct device_node *dn) return dn; } -/** Mark all devices that are children of this device as failed. - * Mark the device driver too, so that it can see the failure - * immediately; this is critical, since some drivers poll - * status registers in interrupts ... If a driver is polling, - * and the slot is frozen, then the driver can deadlock in - * an interrupt context, which is bad. +/** + * __eeh_mark_slot - Mark all child devices as failed + * @parent: parent device + * @mode_flag: failure flag + * + * Mark all devices that are children of this device as failed. + * Mark the device driver too, so that it can see the failure + * immediately; this is critical, since some drivers poll + * status registers in interrupts ... If a driver is polling, + * and the slot is frozen, then the driver can deadlock in + * an interrupt context, which is bad. */ - static void __eeh_mark_slot(struct device_node *parent, int mode_flag) { struct device_node *dn; @@ -404,10 +434,18 @@ static void __eeh_mark_slot(struct device_node *parent, int mode_flag) } } -void eeh_mark_slot (struct device_node *dn, int mode_flag) +/** + * eeh_mark_slot - Mark the indicated device and its children as failed + * @dn: parent device + * @mode_flag: failure flag + * + * Mark the indicated device and its child devices as failed. + * The device drivers are marked as failed as well. + */ +void eeh_mark_slot(struct device_node *dn, int mode_flag) { struct pci_dev *dev; - dn = find_device_pe (dn); + dn = find_device_pe(dn); /* Back up one, since config addrs might be shared */ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) @@ -423,6 +461,13 @@ void eeh_mark_slot (struct device_node *dn, int mode_flag) __eeh_mark_slot(dn, mode_flag); } +/** + * __eeh_clear_slot - Clear failure flag for the child devices + * @parent: parent device + * @mode_flag: flag to be cleared + * + * Clear failure flag for the child devices. + */ static void __eeh_clear_slot(struct device_node *parent, int mode_flag) { struct device_node *dn; @@ -436,12 +481,19 @@ static void __eeh_clear_slot(struct device_node *parent, int mode_flag) } } -void eeh_clear_slot (struct device_node *dn, int mode_flag) +/** + * eeh_clear_slot - Clear failure flag for the indicated device and its children + * @dn: parent device + * @mode_flag: flag to be cleared + * + * Clear failure flag for the indicated device and its children. + */ +void eeh_clear_slot(struct device_node *dn, int mode_flag) { unsigned long flags; raw_spin_lock_irqsave(&confirm_error_lock, flags); - dn = find_device_pe (dn); + dn = find_device_pe(dn); /* Back up one, since config addrs might be shared */ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) @@ -453,43 +505,10 @@ void eeh_clear_slot (struct device_node *dn, int mode_flag) raw_spin_unlock_irqrestore(&confirm_error_lock, flags); } -void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset) -{ - struct device_node *dn; - - for_each_child_of_node(parent, dn) { - if (PCI_DN(dn)) { - - struct pci_dev *dev = PCI_DN(dn)->pcidev; - - if (dev && dev->driver) - *freset |= dev->needs_freset; - - __eeh_set_pe_freset(dn, freset); - } - } -} - -void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset) -{ - struct pci_dev *dev; - dn = find_device_pe(dn); - - /* Back up one, since config addrs might be shared */ - if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) - dn = dn->parent; - - dev = PCI_DN(dn)->pcidev; - if (dev) - *freset |= dev->needs_freset; - - __eeh_set_pe_freset(dn, freset); -} - /** - * eeh_dn_check_failure - check if all 1's data is due to EEH slot freeze - * @dn device node - * @dev pci device, if known + * eeh_dn_check_failure - Check if all 1's data is due to EEH slot freeze + * @dn: device node + * @dev: pci device, if known * * Check for an EEH failure for the given device node. Call this * routine if the result of a read was all 0xff's and you want to @@ -548,11 +567,11 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) pdn->eeh_check_count ++; if (pdn->eeh_check_count % EEH_MAX_FAILS == 0) { location = of_get_property(dn, "ibm,loc-code", NULL); - printk (KERN_ERR "EEH: %d reads ignored for recovering device at " + printk(KERN_ERR "EEH: %d reads ignored for recovering device at " "location=%s driver=%s pci addr=%s\n", pdn->eeh_check_count, location, eeh_driver_name(dev), eeh_pci_name(dev)); - printk (KERN_ERR "EEH: Might be infinite loop in %s driver\n", + printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n", eeh_driver_name(dev)); dump_stack(); } @@ -579,7 +598,8 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) } /* Note that config-io to empty slots may fail; - * they are empty when they don't have children. */ + * they are empty when they don't have children. + */ if ((rets[0] == 5) && (rets[2] == 0) && (dn->child == NULL)) { false_positives++; pdn->eeh_false_positives ++; @@ -609,15 +629,17 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) /* Avoid repeated reports of this failure, including problems * with other functions on this device, and functions under - * bridges. */ - eeh_mark_slot (dn, EEH_MODE_ISOLATED); + * bridges. + */ + eeh_mark_slot(dn, EEH_MODE_ISOLATED); raw_spin_unlock_irqrestore(&confirm_error_lock, flags); - eeh_send_failure_event (dn, dev); + eeh_send_failure_event(dn, dev); /* Most EEH events are due to device driver bugs. Having * a stack trace will help the device-driver authors figure - * out what happened. So print that out. */ + * out what happened. So print that out. + */ dump_stack(); return 1; @@ -629,9 +651,9 @@ dn_unlock: EXPORT_SYMBOL_GPL(eeh_dn_check_failure); /** - * eeh_check_failure - check if all 1's data is due to EEH slot freeze - * @token i/o token, should be address in the form 0xA.... - * @val value, should be all 1's (XXX why do we need this arg??) + * eeh_check_failure - Check if all 1's data is due to EEH slot freeze + * @token: I/O token, should be address in the form 0xA.... + * @val: value, should be all 1's (XXX why do we need this arg??) * * Check for an EEH failure at the given token address. Call this * routine if the result of a read was all 0xff's and you want to @@ -655,7 +677,7 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon } dn = pci_device_to_OF_node(dev); - eeh_dn_check_failure (dn, dev); + eeh_dn_check_failure(dn, dev); pci_dev_put(dev); return val; @@ -663,14 +685,15 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon EXPORT_SYMBOL(eeh_check_failure); -/* ------------------------------------------------------------- */ -/* The code below deals with error recovery */ /** - * rtas_pci_enable - enable MMIO or DMA transfers for this slot + * rtas_pci_enable - Enable MMIO or DMA transfers for this slot * @pdn pci device node + * + * This routine should be called to reenable frozen MMIO or DMA + * so that it would work correctly again. It's useful while doing + * recovery or log collection on the indicated device. */ - int rtas_pci_enable(struct pci_dn *pdn, int function) { @@ -692,7 +715,7 @@ rtas_pci_enable(struct pci_dn *pdn, int function) printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n", function, rc, pdn->node->full_name); - rc = eeh_wait_for_slot_status (pdn, PCI_BUS_RESET_WAIT_MSEC); + rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); if ((rc == 4) && (function == EEH_THAW_MMIO)) return 0; @@ -700,27 +723,25 @@ rtas_pci_enable(struct pci_dn *pdn, int function) } /** - * rtas_pci_slot_reset - raises/lowers the pci #RST line - * @pdn pci device node + * rtas_pci_slot_reset - Raises/Lowers the pci #RST line + * @pdn: pci device node * @state: 1/0 to raise/lower the #RST * * Clear the EEH-frozen condition on a slot. This routine * asserts the PCI #RST line if the 'state' argument is '1', * and drops the #RST line if 'state is '0'. This routine is * safe to call in an interrupt context. - * */ - static void rtas_pci_slot_reset(struct pci_dn *pdn, int state) { int config_addr; int rc; - BUG_ON (pdn==NULL); + BUG_ON(pdn==NULL); if (!pdn->phb) { - printk (KERN_WARNING "EEH: in slot reset, device node %s has no phb\n", + printk(KERN_WARNING "EEH: in slot reset, device node %s has no phb\n", pdn->node->full_name); return; } @@ -752,12 +773,12 @@ rtas_pci_slot_reset(struct pci_dn *pdn, int state) /** * pcibios_set_pcie_slot_reset - Set PCI-E reset state - * @dev: pci device struct - * @state: reset state to enter + * @dev: pci device struct + * @state: reset state to enter * * Return value: * 0 if success - **/ + */ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) { struct device_node *dn = pci_device_to_OF_node(dev); @@ -781,10 +802,62 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat } /** - * rtas_set_slot_reset -- assert the pci #RST line for 1/4 second - * @pdn: pci device node to be reset. + * __eeh_set_pe_freset - Check the required reset for child devices + * @parent: parent device + * @freset: return value + * + * Each device might have its preferred reset type: fundamental or + * hot reset. The routine is used to collect the information from + * the child devices so that they could be reset accordingly. */ +void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset) +{ + struct device_node *dn; + + for_each_child_of_node(parent, dn) { + if (PCI_DN(dn)) { + struct pci_dev *dev = PCI_DN(dn)->pcidev; + + if (dev && dev->driver) + *freset |= dev->needs_freset; + + __eeh_set_pe_freset(dn, freset); + } + } +} + +/** + * eeh_set_pe_freset - Check the required reset for the indicated device and its children + * @dn: parent device + * @freset: return value + * + * Each device might have its preferred reset type: fundamental or + * hot reset. The routine is used to collected the information for + * the indicated device and its children so that the bunch of the + * devices could be reset properly. + */ +void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset) +{ + struct pci_dev *dev; + dn = find_device_pe(dn); + + /* Back up one, since config addrs might be shared */ + if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) + dn = dn->parent; + dev = PCI_DN(dn)->pcidev; + if (dev) + *freset |= dev->needs_freset; + + __eeh_set_pe_freset(dn, freset); +} + +/** + * __rtas_set_slot_reset - Assert the pci #RST line for 1/4 second + * @pdn: pci device node to be reset. + * + * Assert the PCI #RST line for 1/4 second. + */ static void __rtas_set_slot_reset(struct pci_dn *pdn) { unsigned int freset = 0; @@ -803,25 +876,35 @@ static void __rtas_set_slot_reset(struct pci_dn *pdn) rtas_pci_slot_reset(pdn, 1); /* The PCI bus requires that the reset be held high for at least - * a 100 milliseconds. We wait a bit longer 'just in case'. */ - + * a 100 milliseconds. We wait a bit longer 'just in case'. + */ #define PCI_BUS_RST_HOLD_TIME_MSEC 250 - msleep (PCI_BUS_RST_HOLD_TIME_MSEC); + msleep(PCI_BUS_RST_HOLD_TIME_MSEC); /* We might get hit with another EEH freeze as soon as the * pci slot reset line is dropped. Make sure we don't miss - * these, and clear the flag now. */ - eeh_clear_slot (pdn->node, EEH_MODE_ISOLATED); + * these, and clear the flag now. + */ + eeh_clear_slot(pdn->node, EEH_MODE_ISOLATED); - rtas_pci_slot_reset (pdn, 0); + rtas_pci_slot_reset(pdn, 0); /* After a PCI slot has been reset, the PCI Express spec requires * a 1.5 second idle time for the bus to stabilize, before starting - * up traffic. */ + * up traffic. + */ #define PCI_BUS_SETTLE_TIME_MSEC 1800 - msleep (PCI_BUS_SETTLE_TIME_MSEC); + msleep(PCI_BUS_SETTLE_TIME_MSEC); } +/** + * rtas_set_slot_reset - Reset the indicated PE + * @pdn: PCI device node + * + * This routine should be called to reset indicated device, including + * PE. A PE might include multiple PCI devices and sometimes PCI bridges + * might be involved as well. + */ int rtas_set_slot_reset(struct pci_dn *pdn) { int i, rc; @@ -846,7 +929,6 @@ int rtas_set_slot_reset(struct pci_dn *pdn) return -1; } -/* ------------------------------------------------------- */ /** Save and restore of PCI BARs * * Although firmware will set up BARs during boot, it doesn't @@ -863,7 +945,7 @@ int rtas_set_slot_reset(struct pci_dn *pdn) * the expansion ROM base address, the latency timer, and etc. * from the saved values in the device node. */ -static inline void __restore_bars (struct pci_dn *pdn) +static inline void __restore_bars(struct pci_dn *pdn) { int i; u32 cmd; @@ -879,17 +961,18 @@ static inline void __restore_bars (struct pci_dn *pdn) #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) #define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)]) - rtas_write_config (pdn, PCI_CACHE_LINE_SIZE, 1, + rtas_write_config(pdn, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); - rtas_write_config (pdn, PCI_LATENCY_TIMER, 1, + rtas_write_config(pdn, PCI_LATENCY_TIMER, 1, SAVED_BYTE(PCI_LATENCY_TIMER)); /* max latency, min grant, interrupt pin and line */ rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]); /* Restore PERR & SERR bits, some devices require it, - don't touch the other command bits */ + * don't touch the other command bits + */ rtas_read_config(pdn, PCI_COMMAND, 4, &cmd); if (pdn->config_space[1] & PCI_COMMAND_PARITY) cmd |= PCI_COMMAND_PARITY; @@ -903,7 +986,8 @@ static inline void __restore_bars (struct pci_dn *pdn) } /** - * eeh_restore_bars - restore the PCI config space info + * eeh_restore_bars - Restore the PCI config space info + * @pdn: PCI device node * * This routine performs a recursive walk to the children * of this device as well. @@ -915,14 +999,15 @@ void eeh_restore_bars(struct pci_dn *pdn) return; if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code)) - __restore_bars (pdn); + __restore_bars(pdn); for_each_child_of_node(pdn->node, dn) - eeh_restore_bars (PCI_DN(dn)); + eeh_restore_bars(PCI_DN(dn)); } /** - * eeh_save_bars - save device bars + * eeh_save_bars - Save device bars + * @pdn: PCI device node * * Save the values of the device bars. Unlike the restore * routine, this routine is *not* recursive. This is because @@ -940,6 +1025,14 @@ static void eeh_save_bars(struct pci_dn *pdn) rtas_read_config(pdn, i * 4, 4, &pdn->config_space[i]); } +/** + * rtas_configure_bridge - Configure PCI bridges for the indicated PE + * @pdn: PCI device node + * + * PCI bridges might be included in PE. In order to make the PE work + * again. The included PCI bridges should be recovered after the PE + * encounters frozen state. + */ void rtas_configure_bridge(struct pci_dn *pdn) { @@ -963,17 +1056,11 @@ rtas_configure_bridge(struct pci_dn *pdn) BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid)); if (rc) { - printk (KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n", + printk(KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n", rc, pdn->node->full_name); } } -/* ------------------------------------------------------------- */ -/* The code below deals with enabling EEH for devices during the - * early boot sequence. EEH must be enabled before any PCI probing - * can be done. - */ - #define EEH_ENABLE 1 struct eeh_early_enable_info { @@ -981,7 +1068,18 @@ struct eeh_early_enable_info { unsigned int buid_lo; }; -static int get_pe_addr (int config_addr, +/** + * get_pe_addr - Retrieve PE address with given BDF address + * @config_addr: BDF address + * @info: BUID of the associated PHB + * + * There're 2 kinds of addresses existing in EEH core components: + * BDF address and PE address. Besides, there has dedicated platform + * dependent function call to retrieve the PE address according to + * the given BDF address. Further more, we prefer PE address on BDF + * address in EEH core components. + */ +static int get_pe_addr(int config_addr, struct eeh_early_enable_info *info) { unsigned int rets[3]; @@ -990,12 +1088,12 @@ static int get_pe_addr (int config_addr, /* Use latest config-addr token on power6 */ if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { /* Make sure we have a PE in hand */ - ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets, + ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, config_addr, info->buid_hi, info->buid_lo, 1); if (ret || (rets[0]==0)) return 0; - ret = rtas_call (ibm_get_config_addr_info2, 4, 2, rets, + ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, config_addr, info->buid_hi, info->buid_lo, 0); if (ret) return 0; @@ -1004,7 +1102,7 @@ static int get_pe_addr (int config_addr, /* Use older config-addr token on power5 */ if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { - ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets, + ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, config_addr, info->buid_hi, info->buid_lo, 0); if (ret) return 0; @@ -1013,7 +1111,15 @@ static int get_pe_addr (int config_addr, return 0; } -/* Enable eeh for the given device node. */ +/** + * early_enable_eeh - Early enable EEH on the indicated device + * @dn: device node + * @data: BUID + * + * Enable EEH functionality on the specified PCI device. The function + * is expected to be called before real PCI probing is done. However, + * the PHBs have been initialized at this point. + */ static void *early_enable_eeh(struct device_node *dn, void *data) { unsigned int rets[3]; @@ -1047,7 +1153,8 @@ static void *early_enable_eeh(struct device_node *dn, void *data) pdn->class_code = *class_code; /* Ok... see if this device supports EEH. Some do, some don't, - * and the only way to find out is to check each and every one. */ + * and the only way to find out is to check each and every one. + */ regs = of_get_property(dn, "reg", NULL); if (regs) { /* First register entry is addr (00BBSS00) */ @@ -1061,13 +1168,15 @@ static void *early_enable_eeh(struct device_node *dn, void *data) pdn->eeh_config_addr = regs[0]; /* If the newer, better, ibm,get-config-addr-info is supported, - * then use that instead. */ + * then use that instead. + */ pdn->eeh_pe_config_addr = get_pe_addr(pdn->eeh_config_addr, info); /* Some older systems (Power4) allow the * ibm,set-eeh-option call to succeed even on nodes * where EEH is not supported. Verify support - * explicitly. */ + * explicitly. + */ ret = read_slot_reset_state(pdn, rets); if ((ret == 0) && (rets[1] == 1)) enable = 1; @@ -1083,7 +1192,8 @@ static void *early_enable_eeh(struct device_node *dn, void *data) } else { /* This device doesn't support EEH, but it may have an - * EEH parent, in which case we mark it as supported. */ + * EEH parent, in which case we mark it as supported. + */ if (dn->parent && PCI_DN(dn->parent) && (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) { /* Parent supports EEH. */ @@ -1101,7 +1211,9 @@ static void *early_enable_eeh(struct device_node *dn, void *data) return NULL; } -/* +/** + * eeh_init - EEH initialization + * * Initialize EEH by trying to enable it for all of the adapters in the system. * As a side effect we can determine here if eeh is supported at all. * Note that we leave EEH on so failed config cycles won't cause a machine @@ -1133,7 +1245,7 @@ void __init eeh_init(void) ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info"); ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2"); - ibm_configure_bridge = rtas_token ("ibm,configure-bridge"); + ibm_configure_bridge = rtas_token("ibm,configure-bridge"); ibm_configure_pe = rtas_token("ibm,configure-pe"); if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) @@ -1170,7 +1282,7 @@ void __init eeh_init(void) } /** - * eeh_add_device_early - enable EEH for the indicated device_node + * eeh_add_device_early - Enable EEH for the indicated device_node * @dn: device node for which to set up EEH * * This routine must be used to perform EEH initialization for PCI @@ -1199,6 +1311,14 @@ static void eeh_add_device_early(struct device_node *dn) early_enable_eeh(dn, &info); } +/** + * eeh_add_device_tree_early - Enable EEH for the indicated device + * @dn: device node + * + * This routine must be used to perform EEH initialization for the + * indicated PCI device that was added after system boot (e.g. + * hotplug, dlpar). + */ void eeh_add_device_tree_early(struct device_node *dn) { struct device_node *sib; @@ -1210,7 +1330,7 @@ void eeh_add_device_tree_early(struct device_node *dn) EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); /** - * eeh_add_device_late - perform EEH initialization for the indicated pci device + * eeh_add_device_late - Perform EEH initialization for the indicated pci device * @dev: pci device for which to set up EEH * * This routine must be used to complete EEH initialization for PCI @@ -1234,13 +1354,21 @@ static void eeh_add_device_late(struct pci_dev *dev) } WARN_ON(pdn->pcidev); - pci_dev_get (dev); + pci_dev_get(dev); pdn->pcidev = dev; pci_addr_cache_insert_device(dev); eeh_sysfs_add_device(dev); } +/** + * eeh_add_device_tree_late - Perform EEH initialization for the indicated PCI bus + * @bus: PCI bus + * + * This routine must be used to perform EEH initialization for PCI + * devices which are attached to the indicated PCI bus. The PCI bus + * is added after system boot through hotplug or dlpar. + */ void eeh_add_device_tree_late(struct pci_bus *bus) { struct pci_dev *dev; @@ -1257,7 +1385,7 @@ void eeh_add_device_tree_late(struct pci_bus *bus) EXPORT_SYMBOL_GPL(eeh_add_device_tree_late); /** - * eeh_remove_device - undo EEH setup for the indicated pci device + * eeh_remove_device - Undo EEH setup for the indicated pci device * @dev: pci device to be removed * * This routine should be called when a device is removed from @@ -1281,12 +1409,20 @@ static void eeh_remove_device(struct pci_dev *dev) return; } PCI_DN(dn)->pcidev = NULL; - pci_dev_put (dev); + pci_dev_put(dev); pci_addr_cache_remove_device(dev); eeh_sysfs_remove_device(dev); } +/** + * eeh_remove_bus_device - Undo EEH setup for the indicated PCI device + * @dev: PCI device + * + * This routine must be called when a device is removed from the + * running system through hotplug or dlpar. The corresponding + * PCI address cache will be removed. + */ void eeh_remove_bus_device(struct pci_dev *dev) { struct pci_bus *bus = dev->subordinate; -- cgit v0.10.2 From cce4b2d243ddd9e8d5da159b893117afd7ccad5c Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:52 +0000 Subject: powerpc/eeh: Cleanup function names in the EEH core The EEH has been implemented on pSeries platform. The original code looks a little bit nasty. The patch does cleanup on the current EEH implementation so that it looks more clean. * Try adding prefix "eeh" for functions. * Some function names have been adjusted so that they looks shorter and meaningful. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 221d82f..605a970 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -58,16 +58,16 @@ struct pci_dev *pci_get_device_by_addr(unsigned long addr); void eeh_slot_error_detail (struct pci_dn *pdn, int severity); #define EEH_THAW_MMIO 2 #define EEH_THAW_DMA 3 -int rtas_pci_enable(struct pci_dn *pdn, int function); -int rtas_set_slot_reset (struct pci_dn *); +int eeh_pci_enable(struct pci_dn *pdn, int function); +int eeh_reset_pe(struct pci_dn *); int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs); void eeh_restore_bars(struct pci_dn *); -void rtas_configure_bridge(struct pci_dn *); +void eeh_configure_bridge(struct pci_dn *); int rtas_write_config(struct pci_dn *, int where, int size, u32 val); int rtas_read_config(struct pci_dn *, int where, int size, u32 *val); void eeh_mark_slot(struct device_node *dn, int mode_flag); void eeh_clear_slot(struct device_node *dn, int mode_flag); -struct device_node *find_device_pe(struct device_node *dn); +struct device_node *eeh_find_device_pe(struct device_node *dn); void eeh_sysfs_add_device(struct pci_dev *pdev); void eeh_sysfs_remove_device(struct pci_dev *pdev); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 5f6d37b..fa88589 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -130,7 +130,7 @@ static unsigned long slot_resets; #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) /** - * rtas_slot_error_detail - Retrieve error log through RTAS call + * eeh_rtas_slot_error_detail - Retrieve error log through RTAS call * @pdn: device node * @severity: temporary or permanent error log * @driver_log: driver log to be combined with the retrieved error log @@ -139,7 +139,7 @@ static unsigned long slot_resets; * This routine should be called to retrieve error log through the dedicated * RTAS call. */ -static void rtas_slot_error_detail(struct pci_dn *pdn, int severity, +static void eeh_rtas_slot_error_detail(struct pci_dn *pdn, int severity, char *driver_log, size_t loglen) { int config_addr; @@ -170,7 +170,7 @@ static void rtas_slot_error_detail(struct pci_dn *pdn, int severity, } /** - * gather_pci_data - Copy assorted PCI config space registers to buff + * eeh_gather_pci_data - Copy assorted PCI config space registers to buff * @pdn: device to report data for * @buf: point to buffer in which to log * @len: amount of room in buffer @@ -178,7 +178,7 @@ static void rtas_slot_error_detail(struct pci_dn *pdn, int severity, * This routine captures assorted PCI configuration space data, * and puts them into a buffer for RTAS error logging. */ -static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) +static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) { struct pci_dev *dev = pdn->pcidev; u32 cfg; @@ -258,7 +258,7 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) for_each_child_of_node(pdn->node, dn) { pdn = PCI_DN(dn); if (pdn) - n += gather_pci_data(pdn, buf+n, len-n); + n += eeh_gather_pci_data(pdn, buf+n, len-n); } } @@ -280,23 +280,23 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) size_t loglen = 0; pci_regs_buf[0] = 0; - rtas_pci_enable(pdn, EEH_THAW_MMIO); - rtas_configure_bridge(pdn); + eeh_pci_enable(pdn, EEH_THAW_MMIO); + eeh_configure_bridge(pdn); eeh_restore_bars(pdn); - loglen = gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); + loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); - rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen); + eeh_rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen); } /** - * read_slot_reset_state - Read the reset state of a device node's slot + * eeh_read_slot_reset_state - Read the reset state of a device node's slot * @dn: device node to read * @rets: array to return results in * * Read the reset state of a device node's slot through platform dependent * function call. */ -static int read_slot_reset_state(struct pci_dn *pdn, int rets[]) +static int eeh_read_slot_reset_state(struct pci_dn *pdn, int rets[]) { int token, outputs; int config_addr; @@ -332,15 +332,14 @@ static int read_slot_reset_state(struct pci_dn *pdn, int rets[]) * the max allowed wait time is exceeded, in which case * a -2 is returned. */ -int -eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) +int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) { int rc; int rets[3]; int mwait; while (1) { - rc = read_slot_reset_state(pdn, rets); + rc = eeh_read_slot_reset_state(pdn, rets); if (rc) return rc; if (rets[1] == 0) return -1; /* EEH is not supported */ @@ -389,12 +388,12 @@ static inline unsigned long eeh_token_to_phys(unsigned long token) } /** - * find_device_pe - Retrieve the PE for the given device + * eeh_find_device_pe - Retrieve the PE for the given device * @dn: device node * * Return the PE under which this device lies */ -struct device_node * find_device_pe(struct device_node *dn) +struct device_node *eeh_find_device_pe(struct device_node *dn) { while ((dn->parent) && PCI_DN(dn->parent) && (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) { @@ -445,7 +444,7 @@ static void __eeh_mark_slot(struct device_node *parent, int mode_flag) void eeh_mark_slot(struct device_node *dn, int mode_flag) { struct pci_dev *dev; - dn = find_device_pe(dn); + dn = eeh_find_device_pe(dn); /* Back up one, since config addrs might be shared */ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) @@ -493,7 +492,7 @@ void eeh_clear_slot(struct device_node *dn, int mode_flag) unsigned long flags; raw_spin_lock_irqsave(&confirm_error_lock, flags); - dn = find_device_pe(dn); + dn = eeh_find_device_pe(dn); /* Back up one, since config addrs might be shared */ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) @@ -538,7 +537,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) no_dn++; return 0; } - dn = find_device_pe(dn); + dn = eeh_find_device_pe(dn); pdn = PCI_DN(dn); /* Access to IO BARs might get this far and still not want checking. */ @@ -585,11 +584,11 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) * function zero of a multi-function device. * In any case they must share a common PHB. */ - ret = read_slot_reset_state(pdn, rets); + ret = eeh_read_slot_reset_state(pdn, rets); /* If the call to firmware failed, punt */ if (ret != 0) { - printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n", + printk(KERN_WARNING "EEH: eeh_read_slot_reset_state() failed; rc=%d dn=%s\n", ret, dn->full_name); false_positives++; pdn->eeh_false_positives ++; @@ -687,15 +686,14 @@ EXPORT_SYMBOL(eeh_check_failure); /** - * rtas_pci_enable - Enable MMIO or DMA transfers for this slot + * eeh_pci_enable - Enable MMIO or DMA transfers for this slot * @pdn pci device node * * This routine should be called to reenable frozen MMIO or DMA * so that it would work correctly again. It's useful while doing * recovery or log collection on the indicated device. */ -int -rtas_pci_enable(struct pci_dn *pdn, int function) +int eeh_pci_enable(struct pci_dn *pdn, int function) { int config_addr; int rc; @@ -723,7 +721,7 @@ rtas_pci_enable(struct pci_dn *pdn, int function) } /** - * rtas_pci_slot_reset - Raises/Lowers the pci #RST line + * eeh_slot_reset - Raises/Lowers the pci #RST line * @pdn: pci device node * @state: 1/0 to raise/lower the #RST * @@ -732,8 +730,7 @@ rtas_pci_enable(struct pci_dn *pdn, int function) * and drops the #RST line if 'state is '0'. This routine is * safe to call in an interrupt context. */ -static void -rtas_pci_slot_reset(struct pci_dn *pdn, int state) +static void eeh_slot_reset(struct pci_dn *pdn, int state) { int config_addr; int rc; @@ -786,13 +783,13 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state stat switch (state) { case pcie_deassert_reset: - rtas_pci_slot_reset(pdn, 0); + eeh_slot_reset(pdn, 0); break; case pcie_hot_reset: - rtas_pci_slot_reset(pdn, 1); + eeh_slot_reset(pdn, 1); break; case pcie_warm_reset: - rtas_pci_slot_reset(pdn, 3); + eeh_slot_reset(pdn, 3); break; default: return -EINVAL; @@ -839,7 +836,7 @@ void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset) void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset) { struct pci_dev *dev; - dn = find_device_pe(dn); + dn = eeh_find_device_pe(dn); /* Back up one, since config addrs might be shared */ if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) @@ -853,12 +850,12 @@ void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset) } /** - * __rtas_set_slot_reset - Assert the pci #RST line for 1/4 second + * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second * @pdn: pci device node to be reset. * * Assert the PCI #RST line for 1/4 second. */ -static void __rtas_set_slot_reset(struct pci_dn *pdn) +static void eeh_reset_pe_once(struct pci_dn *pdn) { unsigned int freset = 0; @@ -871,9 +868,9 @@ static void __rtas_set_slot_reset(struct pci_dn *pdn) eeh_set_pe_freset(pdn->node, &freset); if (freset) - rtas_pci_slot_reset(pdn, 3); + eeh_slot_reset(pdn, 3); else - rtas_pci_slot_reset(pdn, 1); + eeh_slot_reset(pdn, 1); /* The PCI bus requires that the reset be held high for at least * a 100 milliseconds. We wait a bit longer 'just in case'. @@ -887,7 +884,7 @@ static void __rtas_set_slot_reset(struct pci_dn *pdn) */ eeh_clear_slot(pdn->node, EEH_MODE_ISOLATED); - rtas_pci_slot_reset(pdn, 0); + eeh_slot_reset(pdn, 0); /* After a PCI slot has been reset, the PCI Express spec requires * a 1.5 second idle time for the bus to stabilize, before starting @@ -898,20 +895,20 @@ static void __rtas_set_slot_reset(struct pci_dn *pdn) } /** - * rtas_set_slot_reset - Reset the indicated PE + * eeh_reset_pe - Reset the indicated PE * @pdn: PCI device node * * This routine should be called to reset indicated device, including * PE. A PE might include multiple PCI devices and sometimes PCI bridges * might be involved as well. */ -int rtas_set_slot_reset(struct pci_dn *pdn) +int eeh_reset_pe(struct pci_dn *pdn) { int i, rc; /* Take three shots at resetting the bus */ for (i=0; i<3; i++) { - __rtas_set_slot_reset(pdn); + eeh_reset_pe_once(pdn); rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); if (rc == 0) @@ -938,14 +935,14 @@ int rtas_set_slot_reset(struct pci_dn *pdn) */ /** - * __restore_bars - Restore the Base Address Registers + * eeh_restore_one_device_bars - Restore the Base Address Registers for one device * @pdn: pci device node * * Loads the PCI configuration space base address registers, * the expansion ROM base address, the latency timer, and etc. * from the saved values in the device node. */ -static inline void __restore_bars(struct pci_dn *pdn) +static inline void eeh_restore_one_device_bars(struct pci_dn *pdn) { int i; u32 cmd; @@ -999,7 +996,7 @@ void eeh_restore_bars(struct pci_dn *pdn) return; if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code)) - __restore_bars(pdn); + eeh_restore_one_device_bars(pdn); for_each_child_of_node(pdn->node, dn) eeh_restore_bars(PCI_DN(dn)); @@ -1026,15 +1023,14 @@ static void eeh_save_bars(struct pci_dn *pdn) } /** - * rtas_configure_bridge - Configure PCI bridges for the indicated PE + * eeh_configure_bridge - Configure PCI bridges for the indicated PE * @pdn: PCI device node * * PCI bridges might be included in PE. In order to make the PE work * again. The included PCI bridges should be recovered after the PE * encounters frozen state. */ -void -rtas_configure_bridge(struct pci_dn *pdn) +void eeh_configure_bridge(struct pci_dn *pdn) { int config_addr; int rc; @@ -1069,7 +1065,7 @@ struct eeh_early_enable_info { }; /** - * get_pe_addr - Retrieve PE address with given BDF address + * eeh_get_pe_addr - Retrieve PE address with given BDF address * @config_addr: BDF address * @info: BUID of the associated PHB * @@ -1079,7 +1075,7 @@ struct eeh_early_enable_info { * the given BDF address. Further more, we prefer PE address on BDF * address in EEH core components. */ -static int get_pe_addr(int config_addr, +static int eeh_get_pe_addr(int config_addr, struct eeh_early_enable_info *info) { unsigned int rets[3]; @@ -1112,7 +1108,7 @@ static int get_pe_addr(int config_addr, } /** - * early_enable_eeh - Early enable EEH on the indicated device + * eeh_early_enable - Early enable EEH on the indicated device * @dn: device node * @data: BUID * @@ -1120,7 +1116,7 @@ static int get_pe_addr(int config_addr, * is expected to be called before real PCI probing is done. However, * the PHBs have been initialized at this point. */ -static void *early_enable_eeh(struct device_node *dn, void *data) +static void *eeh_early_enable(struct device_node *dn, void *data) { unsigned int rets[3]; struct eeh_early_enable_info *info = data; @@ -1170,14 +1166,14 @@ static void *early_enable_eeh(struct device_node *dn, void *data) /* If the newer, better, ibm,get-config-addr-info is supported, * then use that instead. */ - pdn->eeh_pe_config_addr = get_pe_addr(pdn->eeh_config_addr, info); + pdn->eeh_pe_config_addr = eeh_get_pe_addr(pdn->eeh_config_addr, info); /* Some older systems (Power4) allow the * ibm,set-eeh-option call to succeed even on nodes * where EEH is not supported. Verify support * explicitly. */ - ret = read_slot_reset_state(pdn, rets); + ret = eeh_read_slot_reset_state(pdn, rets); if ((ret == 0) && (rets[1] == 1)) enable = 1; } @@ -1272,7 +1268,7 @@ void __init eeh_init(void) info.buid_lo = BUID_LO(buid); info.buid_hi = BUID_HI(buid); - traverse_pci_devices(phb, early_enable_eeh, &info); + traverse_pci_devices(phb, eeh_early_enable, &info); } if (eeh_subsystem_enabled) @@ -1308,7 +1304,7 @@ static void eeh_add_device_early(struct device_node *dn) info.buid_hi = BUID_HI(phb->buid); info.buid_lo = BUID_LO(phb->buid); - early_enable_eeh(dn, &info); + eeh_early_enable(dn, &info); } /** diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 1b6cb10..5315350 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -282,7 +282,7 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) /* Reset the pci controller. (Asserts RST#; resets config space). * Reconfigure bridges and devices. Don't try to bring the system * up if the reset failed for some reason. */ - rc = rtas_set_slot_reset(pe_dn); + rc = eeh_reset_pe(pe_dn); if (rc) return rc; @@ -295,7 +295,7 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) struct pci_dn *ppe = PCI_DN(dn); /* On Power4, always true because eeh_pe_config_addr=0 */ if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) { - rtas_configure_bridge(ppe); + eeh_configure_bridge(ppe); eeh_restore_bars(ppe); } dn = dn->sibling; @@ -330,7 +330,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) enum pci_ers_result result = PCI_ERS_RESULT_NONE; const char *location, *pci_str, *drv_str, *bus_pci_str, *bus_drv_str; - frozen_dn = find_device_pe(event->dn); + frozen_dn = eeh_find_device_pe(event->dn); if (!frozen_dn) { location = of_get_property(event->dn, "ibm,loc-code", NULL); @@ -422,7 +422,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) /* If all devices reported they can proceed, then re-enable MMIO */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { - rc = rtas_pci_enable(frozen_pdn, EEH_THAW_MMIO); + rc = eeh_pci_enable(frozen_pdn, EEH_THAW_MMIO); if (rc < 0) goto hard_fail; @@ -436,7 +436,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) /* If all devices reported they can proceed, then re-enable DMA */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { - rc = rtas_pci_enable(frozen_pdn, EEH_THAW_DMA); + rc = eeh_pci_enable(frozen_pdn, EEH_THAW_DMA); if (rc < 0) goto hard_fail; diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index 38d24e7..109fdb7 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c @@ -217,7 +217,7 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total) if (!dn) return NULL; - dn = find_device_pe(dn); + dn = eeh_find_device_pe(dn); if (!dn) return NULL; -- cgit v0.10.2 From aa1e6374ae11788752535ae0c8c6395c9cad1393 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:53 +0000 Subject: powerpc/eeh: Platform dependent EEH operations EEH has been implemented on RTAS-compliant pSeries platform. That's to say, the EEH operations will be implemented through RTAS calls eventually. The situation limited feasible extension on EEH. In order to support EEH on multiple platforms like pseries and powernv simutaneously. We have to split the platform dependent EEH options up out of current implementation. The patch addresses supporting EEH on multiple platforms. The pseries platform dependent EEH operations will be abstracted by struct eeh_ops. EEH core components will be built based on the registered EEH operations. With the mechanism, what the individual platform needs to do is implement platform dependent EEH operations. For now, the pseries platform is covered under the mechanism. That means we have to think about other platforms to support EEH, like powernv. Besides, we only have framework for the mechanism and we have to implement it for pseries platform later. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 2328877..0666c52 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -31,6 +31,26 @@ struct device_node; #ifdef CONFIG_EEH +/* + * The struct is used to trace the registered EEH operation + * callback functions. Actually, those operation callback + * functions are heavily platform dependent. That means the + * platform should register its own EEH operation callback + * functions before any EEH further operations. + */ +struct eeh_ops { + char *name; + int (*init)(void); + int (*set_option)(struct device_node *dn, int option); + int (*get_pe_addr)(struct device_node *dn); + int (*get_state)(struct device_node *dn, int *state); + int (*reset)(struct device_node *dn, int option); + int (*wait_state)(struct device_node *dn, int max_wait); + int (*get_log)(struct device_node *dn, int severity, char *drv_log, unsigned long len); + int (*configure_bridge)(struct device_node *dn); +}; + +extern struct eeh_ops *eeh_ops; extern int eeh_subsystem_enabled; /* Values for eeh_mode bits in device_node */ @@ -47,6 +67,11 @@ extern int eeh_subsystem_enabled; #define EEH_MAX_ALLOWED_FREEZES 5 void __init eeh_init(void); +#ifdef CONFIG_PPC_PSERIES +int __init eeh_pseries_init(void); +#endif +int __init eeh_ops_register(struct eeh_ops *ops); +int __exit eeh_ops_unregister(const char *name); unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val); int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev); @@ -73,6 +98,13 @@ void eeh_remove_bus_device(struct pci_dev *); #else /* !CONFIG_EEH */ static inline void eeh_init(void) { } +#ifdef CONFIG_PPC_PSERIES +static inline int eeh_pseries_init(void) +{ + return 0; +} +#endif /* CONFIG_PPC_PSERIES */ + static inline unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val) { return val; diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 67b3e6e..ee873cf 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -6,7 +6,7 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \ firmware.o power.o dlpar.o mobility.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SCANLOG) += scanlog.o -obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o +obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o eeh_pseries.o obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_PCI) += pci.o pci_dlpar.o obj-$(CONFIG_PSERIES_MSI) += msi.o diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index fa88589..b0e3fb0 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -97,6 +97,9 @@ static int ibm_get_config_addr_info2; static int ibm_configure_bridge; static int ibm_configure_pe; +/* Platform dependent EEH operations */ +struct eeh_ops *eeh_ops = NULL; + int eeh_subsystem_enabled; EXPORT_SYMBOL(eeh_subsystem_enabled); @@ -1208,6 +1211,56 @@ static void *eeh_early_enable(struct device_node *dn, void *data) } /** + * eeh_ops_register - Register platform dependent EEH operations + * @ops: platform dependent EEH operations + * + * Register the platform dependent EEH operation callback + * functions. The platform should call this function before + * any other EEH operations. + */ +int __init eeh_ops_register(struct eeh_ops *ops) +{ + if (!ops->name) { + pr_warning("%s: Invalid EEH ops name for %p\n", + __func__, ops); + return -EINVAL; + } + + if (eeh_ops && eeh_ops != ops) { + pr_warning("%s: EEH ops of platform %s already existing (%s)\n", + __func__, eeh_ops->name, ops->name); + return -EEXIST; + } + + eeh_ops = ops; + + return 0; +} + +/** + * eeh_ops_unregister - Unreigster platform dependent EEH operations + * @name: name of EEH platform operations + * + * Unregister the platform dependent EEH operation callback + * functions. + */ +int __exit eeh_ops_unregister(const char *name) +{ + if (!name || !strlen(name)) { + pr_warning("%s: Invalid EEH ops name\n", + __func__); + return -EINVAL; + } + + if (eeh_ops && !strcmp(eeh_ops->name, name)) { + eeh_ops = NULL; + return 0; + } + + return -EEXIST; +} + +/** * eeh_init - EEH initialization * * Initialize EEH by trying to enable it for all of the adapters in the system. diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c new file mode 100644 index 0000000..61a9050 --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -0,0 +1,183 @@ +/* + * The file intends to implement the platform dependent EEH operations on pseries. + * Actually, the pseries platform is built based on RTAS heavily. That means the + * pseries platform dependent EEH operations will be built on RTAS calls. The functions + * are devired from arch/powerpc/platforms/pseries/eeh.c and necessary cleanup has + * been done. + * + * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2011. + * Copyright IBM Corporation 2001, 2005, 2006 + * Copyright Dave Engebretsen & Todd Inglett 2001 + * Copyright Linas Vepstas 2005, 2006 + * + * 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. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * pseries_eeh_init - EEH platform dependent initialization + * + * EEH platform dependent initialization on pseries. + */ +static int pseries_eeh_init(void) +{ + return 0; +} + +/** + * pseries_eeh_set_option - Initialize EEH or MMIO/DMA reenable + * @dn: device node + * @option: operation to be issued + * + * The function is used to control the EEH functionality globally. + * Currently, following options are support according to PAPR: + * Enable EEH, Disable EEH, Enable MMIO and Enable DMA + */ +static int pseries_eeh_set_option(struct device_node *dn, int option) +{ + return 0; +} + +/** + * pseries_eeh_get_pe_addr - Retrieve PE address + * @dn: device node + * + * Retrieve the assocated PE address. Actually, there're 2 RTAS + * function calls dedicated for the purpose. We need implement + * it through the new function and then the old one. Besides, + * you should make sure the config address is figured out from + * FDT node before calling the function. + * + * It's notable that zero'ed return value means invalid PE config + * address. + */ +static int pseries_eeh_get_pe_addr(struct device_node *dn) +{ + return 0; +} + +/** + * pseries_eeh_get_state - Retrieve PE state + * @dn: PE associated device node + * @state: return value + * + * Retrieve the state of the specified PE. On RTAS compliant + * pseries platform, there already has one dedicated RTAS function + * for the purpose. It's notable that the associated PE config address + * might be ready when calling the function. Therefore, endeavour to + * use the PE config address if possible. Further more, there're 2 + * RTAS calls for the purpose, we need to try the new one and back + * to the old one if the new one couldn't work properly. + */ +static int pseries_eeh_get_state(struct device_node *dn, int *state) +{ + return 0; +} + +/** + * pseries_eeh_reset - Reset the specified PE + * @dn: PE associated device node + * @option: reset option + * + * Reset the specified PE + */ +static int pseries_eeh_reset(struct device_node *dn, int option) +{ + return 0; +} + +/** + * pseries_eeh_wait_state - Wait for PE state + * @dn: PE associated device node + * @max_wait: maximal period in microsecond + * + * Wait for the state of associated PE. It might take some time + * to retrieve the PE's state. + */ +static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) +{ + return 0; +} + +/** + * pseries_eeh_get_log - Retrieve error log + * @dn: device node + * @severity: temporary or permanent error log + * @drv_log: driver log to be combined with retrieved error log + * @len: length of driver log + * + * Retrieve the temporary or permanent error from the PE. + * Actually, the error will be retrieved through the dedicated + * RTAS call. + */ +static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len) +{ + return 0; +} + +/** + * pseries_eeh_configure_bridge - Configure PCI bridges in the indicated PE + * @dn: PE associated device node + * + * The function will be called to reconfigure the bridges included + * in the specified PE so that the mulfunctional PE would be recovered + * again. + */ +static int pseries_eeh_configure_bridge(struct device_node *dn) +{ + return 0; +} + +static struct eeh_ops pseries_eeh_ops = { + .name = "pseries", + .init = pseries_eeh_init, + .set_option = pseries_eeh_set_option, + .get_pe_addr = pseries_eeh_get_pe_addr, + .get_state = pseries_eeh_get_state, + .reset = pseries_eeh_reset, + .wait_state = pseries_eeh_wait_state, + .get_log = pseries_eeh_get_log, + .configure_bridge = pseries_eeh_configure_bridge +}; + +/** + * eeh_pseries_init - Register platform dependent EEH operations + * + * EEH initialization on pseries platform. This function should be + * called before any EEH related functions. + */ +int __init eeh_pseries_init(void) +{ + return eeh_ops_register(&pseries_eeh_ops); +} diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index d928412..62b8276 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -381,6 +381,7 @@ static void __init pSeries_setup_arch(void) /* Find and initialize PCI host bridges */ init_pci_config_tokens(); + eeh_pseries_init(); find_and_init_phbs(); pSeries_reconfig_notifier_register(&pci_dn_reconfig_nb); eeh_init(); -- cgit v0.10.2 From e2af155c2aea0e705ed4ef33aedc25e50a788be1 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:54 +0000 Subject: powerpc/eeh: pseries platform EEH initialization The platform specific EEH operations have been abstracted by struct eeh_ops. The individual platroms, including pSeries, needs doing necessary initialization before the platform dependent EEH operations work properly. The patch is addressing that and do necessary platform initialization for pSeries platform. More specificly, it will figure out the tokens of EEH related RTAS calls. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index b0e3fb0..bb6de6c 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -1279,6 +1279,18 @@ void __init eeh_init(void) { struct device_node *phb, *np; struct eeh_early_enable_info info; + int ret; + + /* call platform initialization function */ + if (!eeh_ops) { + pr_warning("%s: Platform EEH operation not found\n", + __func__); + return; + } else if ((ret = eeh_ops->init())) { + pr_warning("%s: Failed to call platform init function (%d)\n", + __func__, ret); + return; + } raw_spin_lock_init(&confirm_error_lock); spin_lock_init(&slot_errbuf_lock); diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 61a9050..1a9410a 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -45,6 +45,17 @@ #include #include +/* RTAS tokens */ +static int ibm_set_eeh_option; +static int ibm_set_slot_reset; +static int ibm_read_slot_reset_state; +static int ibm_read_slot_reset_state2; +static int ibm_slot_error_detail; +static int ibm_get_config_addr_info; +static int ibm_get_config_addr_info2; +static int ibm_configure_bridge; +static int ibm_configure_pe; + /** * pseries_eeh_init - EEH platform dependent initialization * @@ -52,6 +63,50 @@ */ static int pseries_eeh_init(void) { + /* figure out EEH RTAS function call tokens */ + ibm_set_eeh_option = rtas_token("ibm,set-eeh-option"); + ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); + ibm_read_slot_reset_state2 = rtas_token("ibm,read-slot-reset-state2"); + ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); + ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); + ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2"); + ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info"); + ibm_configure_pe = rtas_token("ibm,configure-pe"); + ibm_configure_bridge = rtas_token ("ibm,configure-bridge"); + + /* necessary sanity check */ + if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: RTAS service invalid\n", + __func__); + return -EINVAL; + } else if (ibm_set_slot_reset == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: RTAS service invalid\n", + __func__); + return -EINVAL; + } else if (ibm_read_slot_reset_state2 == RTAS_UNKNOWN_SERVICE && + ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: RTAS service and " + " invalid\n", + __func__); + return -EINVAL; + } else if (ibm_slot_error_detail == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: RTAS service invalid\n", + __func__); + return -EINVAL; + } else if (ibm_get_config_addr_info2 == RTAS_UNKNOWN_SERVICE && + ibm_get_config_addr_info == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: RTAS service and " + " invalid\n", + __func__); + return -EINVAL; + } else if (ibm_configure_pe == RTAS_UNKNOWN_SERVICE && + ibm_configure_bridge == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: RTAS service and " + " invalid\n", + __func__); + return -EINVAL; + } + return 0; } -- cgit v0.10.2 From 8fb8f709025c13ae72968a66a1ade24431a342b2 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:55 +0000 Subject: powerpc/eeh: pseries platform EEH operations There're 4 EEH operations that are covered by the dedicated RTAS call : enable or disable EEH, enable MMIO and enable DMA. At early stage of system boot, the EEH would be tried to enable on PCI device related device node. MMIO and DMA for particular PE should be enabled when doing recovery on EEH errors so that the PE could function properly again. The patch implements it and abstract that through struct eeh_ops::set_eeh. It would be help for EEH to support multiple platforms in future. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 0666c52..76f7b3f 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -38,6 +38,10 @@ struct device_node; * platform should register its own EEH operation callback * functions before any EEH further operations. */ +#define EEH_OPT_DISABLE 0 /* EEH disable */ +#define EEH_OPT_ENABLE 1 /* EEH enable */ +#define EEH_OPT_THAW_MMIO 2 /* MMIO enable */ +#define EEH_OPT_THAW_DMA 3 /* DMA enable */ struct eeh_ops { char *name; int (*init)(void); diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 605a970..6150349 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -56,8 +56,6 @@ struct pci_dev *pci_get_device_by_addr(unsigned long addr); #define EEH_LOG_TEMP_FAILURE 1 #define EEH_LOG_PERM_FAILURE 2 void eeh_slot_error_detail (struct pci_dn *pdn, int severity); -#define EEH_THAW_MMIO 2 -#define EEH_THAW_DMA 3 int eeh_pci_enable(struct pci_dn *pdn, int function); int eeh_reset_pe(struct pci_dn *); int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index bb6de6c..70a9617 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -87,7 +87,6 @@ #define PCI_BUS_RESET_WAIT_MSEC (60*1000) /* RTAS tokens */ -static int ibm_set_eeh_option; static int ibm_set_slot_reset; static int ibm_read_slot_reset_state; static int ibm_read_slot_reset_state2; @@ -283,7 +282,7 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) size_t loglen = 0; pci_regs_buf[0] = 0; - eeh_pci_enable(pdn, EEH_THAW_MMIO); + eeh_pci_enable(pdn, EEH_OPT_THAW_MMIO); eeh_configure_bridge(pdn); eeh_restore_bars(pdn); loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); @@ -698,26 +697,15 @@ EXPORT_SYMBOL(eeh_check_failure); */ int eeh_pci_enable(struct pci_dn *pdn, int function) { - int config_addr; int rc; - /* Use PE configuration address, if present */ - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; - - rc = rtas_call(ibm_set_eeh_option, 4, 1, NULL, - config_addr, - BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), - function); - + rc = eeh_ops->set_option(pdn->node, function); if (rc) printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n", function, rc, pdn->node->full_name); rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); - if ((rc == 4) && (function == EEH_THAW_MMIO)) + if ((rc == 4) && (function == EEH_OPT_THAW_MMIO)) return 0; return rc; @@ -1158,9 +1146,7 @@ static void *eeh_early_enable(struct device_node *dn, void *data) if (regs) { /* First register entry is addr (00BBSS00) */ /* Try to enable eeh */ - ret = rtas_call(ibm_set_eeh_option, 4, 1, NULL, - regs[0], info->buid_hi, info->buid_lo, - EEH_ENABLE); + ret = eeh_ops->set_option(dn, EEH_OPT_ENABLE); enable = 0; if (ret == 0) { @@ -1299,7 +1285,6 @@ void __init eeh_init(void) if (np == NULL) return; - ibm_set_eeh_option = rtas_token("ibm,set-eeh-option"); ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); ibm_read_slot_reset_state2 = rtas_token("ibm,read-slot-reset-state2"); ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); @@ -1309,9 +1294,6 @@ void __init eeh_init(void) ibm_configure_bridge = rtas_token("ibm,configure-bridge"); ibm_configure_pe = rtas_token("ibm,configure-pe"); - if (ibm_set_eeh_option == RTAS_UNKNOWN_SERVICE) - return; - eeh_error_buf_size = rtas_token("rtas-error-log-max"); if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) { eeh_error_buf_size = 1024; diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 5315350..02eab3b 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -422,7 +422,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) /* If all devices reported they can proceed, then re-enable MMIO */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { - rc = eeh_pci_enable(frozen_pdn, EEH_THAW_MMIO); + rc = eeh_pci_enable(frozen_pdn, EEH_OPT_THAW_MMIO); if (rc < 0) goto hard_fail; @@ -436,7 +436,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) /* If all devices reported they can proceed, then re-enable DMA */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { - rc = eeh_pci_enable(frozen_pdn, EEH_THAW_DMA); + rc = eeh_pci_enable(frozen_pdn, EEH_OPT_THAW_DMA); if (rc < 0) goto hard_fail; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 1a9410a..c48a9e6 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -121,7 +121,44 @@ static int pseries_eeh_init(void) */ static int pseries_eeh_set_option(struct device_node *dn, int option) { - return 0; + int ret = 0; + struct pci_dn *pdn; + const u32 *reg; + int config_addr; + + pdn = PCI_DN(dn); + + /* + * When we're enabling or disabling EEH functioality on + * the particular PE, the PE config address is possibly + * unavailable. Therefore, we have to figure it out from + * the FDT node. + */ + switch (option) { + case EEH_OPT_DISABLE: + case EEH_OPT_ENABLE: + reg = of_get_property(dn, "reg", NULL); + config_addr = reg[0]; + break; + + case EEH_OPT_THAW_MMIO: + case EEH_OPT_THAW_DMA: + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + break; + + default: + pr_err("%s: Invalid option %d\n", + __func__, option); + return -EINVAL; + } + + ret = rtas_call(ibm_set_eeh_option, 4, 1, NULL, + config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid), option); + + return ret; } /** -- cgit v0.10.2 From c8c29b38fbb9448f2a25484348dd5aec9a2a9671 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:56 +0000 Subject: powerpc/eeh: pseries platform EEH PE address retrieval There're 2 types of addresses used for EEH operations. The first one would be BDF (Bus/Device/Function) address which is retrieved from the reg property of the corresponding FDT node. Another one is PE address that should be enquired from firmware through RTAS call on pSeries platform. When issuing EEH operation, the PE address has precedence over BDF address. The patch implements retrieving PE address according to the given BDF address on pSeries platform. Also, the struct eeh_early_enable_info has been removed since the information can be figured out from dn->pdn->phb->buid directly and that simplifies the code. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 70a9617..00797e0 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -91,8 +91,6 @@ static int ibm_set_slot_reset; static int ibm_read_slot_reset_state; static int ibm_read_slot_reset_state2; static int ibm_slot_error_detail; -static int ibm_get_config_addr_info; -static int ibm_get_config_addr_info2; static int ibm_configure_bridge; static int ibm_configure_pe; @@ -1048,56 +1046,6 @@ void eeh_configure_bridge(struct pci_dn *pdn) } } -#define EEH_ENABLE 1 - -struct eeh_early_enable_info { - unsigned int buid_hi; - unsigned int buid_lo; -}; - -/** - * eeh_get_pe_addr - Retrieve PE address with given BDF address - * @config_addr: BDF address - * @info: BUID of the associated PHB - * - * There're 2 kinds of addresses existing in EEH core components: - * BDF address and PE address. Besides, there has dedicated platform - * dependent function call to retrieve the PE address according to - * the given BDF address. Further more, we prefer PE address on BDF - * address in EEH core components. - */ -static int eeh_get_pe_addr(int config_addr, - struct eeh_early_enable_info *info) -{ - unsigned int rets[3]; - int ret; - - /* Use latest config-addr token on power6 */ - if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { - /* Make sure we have a PE in hand */ - ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - config_addr, info->buid_hi, info->buid_lo, 1); - if (ret || (rets[0]==0)) - return 0; - - ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - config_addr, info->buid_hi, info->buid_lo, 0); - if (ret) - return 0; - return rets[0]; - } - - /* Use older config-addr token on power5 */ - if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { - ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, - config_addr, info->buid_hi, info->buid_lo, 0); - if (ret) - return 0; - return rets[0]; - } - return 0; -} - /** * eeh_early_enable - Early enable EEH on the indicated device * @dn: device node @@ -1110,7 +1058,6 @@ static int eeh_get_pe_addr(int config_addr, static void *eeh_early_enable(struct device_node *dn, void *data) { unsigned int rets[3]; - struct eeh_early_enable_info *info = data; int ret; const u32 *class_code = of_get_property(dn, "class-code", NULL); const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL); @@ -1155,7 +1102,7 @@ static void *eeh_early_enable(struct device_node *dn, void *data) /* If the newer, better, ibm,get-config-addr-info is supported, * then use that instead. */ - pdn->eeh_pe_config_addr = eeh_get_pe_addr(pdn->eeh_config_addr, info); + pdn->eeh_pe_config_addr = eeh_ops->get_pe_addr(dn); /* Some older systems (Power4) allow the * ibm,set-eeh-option call to succeed even on nodes @@ -1264,7 +1211,6 @@ int __exit eeh_ops_unregister(const char *name) void __init eeh_init(void) { struct device_node *phb, *np; - struct eeh_early_enable_info info; int ret; /* call platform initialization function */ @@ -1289,8 +1235,6 @@ void __init eeh_init(void) ibm_read_slot_reset_state2 = rtas_token("ibm,read-slot-reset-state2"); ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); - ibm_get_config_addr_info = rtas_token("ibm,get-config-addr-info"); - ibm_get_config_addr_info2 = rtas_token("ibm,get-config-addr-info2"); ibm_configure_bridge = rtas_token("ibm,configure-bridge"); ibm_configure_pe = rtas_token("ibm,configure-pe"); @@ -1313,9 +1257,7 @@ void __init eeh_init(void) if (buid == 0 || PCI_DN(phb) == NULL) continue; - info.buid_lo = BUID_LO(buid); - info.buid_hi = BUID_HI(buid); - traverse_pci_devices(phb, eeh_early_enable, &info); + traverse_pci_devices(phb, eeh_early_enable, NULL); } if (eeh_subsystem_enabled) @@ -1339,7 +1281,6 @@ void __init eeh_init(void) static void eeh_add_device_early(struct device_node *dn) { struct pci_controller *phb; - struct eeh_early_enable_info info; if (!dn || !PCI_DN(dn)) return; @@ -1349,9 +1290,7 @@ static void eeh_add_device_early(struct device_node *dn) if (NULL == phb || 0 == phb->buid) return; - info.buid_hi = BUID_HI(phb->buid); - info.buid_lo = BUID_LO(phb->buid); - eeh_early_enable(dn, &info); + eeh_early_enable(dn, NULL); } /** diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index c48a9e6..2b9543a 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -176,7 +176,51 @@ static int pseries_eeh_set_option(struct device_node *dn, int option) */ static int pseries_eeh_get_pe_addr(struct device_node *dn) { - return 0; + struct pci_dn *pdn; + int ret = 0; + int rets[3]; + + pdn = PCI_DN(dn); + + if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { + /* + * First of all, we need to make sure there has one PE + * associated with the device. Otherwise, PE address is + * meaningless. + */ + ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, + pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid), 1); + if (ret || (rets[0] == 0)) + return 0; + + /* Retrieve the associated PE config address */ + ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, + pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid), 0); + if (ret) { + pr_warning("%s: Failed to get PE address for %s\n", + __func__, dn->full_name); + return 0; + } + + return rets[0]; + } + + if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, + pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid), 0); + if (ret) { + pr_warning("%s: Failed to get PE address for %s\n", + __func__, dn->full_name); + return 0; + } + + return rets[0]; + } + + return ret; } /** -- cgit v0.10.2 From eb594a4754e71e41c048e0f1559bb6f13dab7070 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:57 +0000 Subject: powerpc/eeh: pseries platform PE state retrieval On pSeries platform, there're 2 dedicated RTAS calls introduced to retrieve the corresponding PE's state: ibm,read-slot-reset-state and ibm,read-slot-reset-state2. The patch implements the retrieval of PE's state according to the given PE address. Besides, the implementation has been abstracted by struct eeh_ops::get_state so that EEH core components could support multiple platforms in future. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 76f7b3f..1d3c9e5 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -42,6 +42,14 @@ struct device_node; #define EEH_OPT_ENABLE 1 /* EEH enable */ #define EEH_OPT_THAW_MMIO 2 /* MMIO enable */ #define EEH_OPT_THAW_DMA 3 /* DMA enable */ +#define EEH_STATE_UNAVAILABLE (1 << 0) /* State unavailable */ +#define EEH_STATE_NOT_SUPPORT (1 << 1) /* EEH not supported */ +#define EEH_STATE_RESET_ACTIVE (1 << 2) /* Active reset */ +#define EEH_STATE_MMIO_ACTIVE (1 << 3) /* Active MMIO */ +#define EEH_STATE_DMA_ACTIVE (1 << 4) /* Active DMA */ +#define EEH_STATE_MMIO_ENABLED (1 << 5) /* MMIO enabled */ +#define EEH_STATE_DMA_ENABLED (1 << 6) /* DMA enabled */ + struct eeh_ops { char *name; int (*init)(void); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 00797e0..8d11f1f 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -88,8 +88,6 @@ /* RTAS tokens */ static int ibm_set_slot_reset; -static int ibm_read_slot_reset_state; -static int ibm_read_slot_reset_state2; static int ibm_slot_error_detail; static int ibm_configure_bridge; static int ibm_configure_pe; @@ -289,37 +287,6 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) } /** - * eeh_read_slot_reset_state - Read the reset state of a device node's slot - * @dn: device node to read - * @rets: array to return results in - * - * Read the reset state of a device node's slot through platform dependent - * function call. - */ -static int eeh_read_slot_reset_state(struct pci_dn *pdn, int rets[]) -{ - int token, outputs; - int config_addr; - - if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) { - token = ibm_read_slot_reset_state2; - outputs = 4; - } else { - token = ibm_read_slot_reset_state; - rets[2] = 0; /* fake PE Unavailable info */ - outputs = 3; - } - - /* Use PE configuration address, if present */ - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; - - return rtas_call(token, 3, outputs, rets, config_addr, - BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid)); -} - -/** * eeh_wait_for_slot_status - Returns error status of slot * @pdn: pci device node * @max_wait_msecs: maximum number to millisecs to wait @@ -335,21 +302,15 @@ static int eeh_read_slot_reset_state(struct pci_dn *pdn, int rets[]) int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) { int rc; - int rets[3]; int mwait; while (1) { - rc = eeh_read_slot_reset_state(pdn, rets); - if (rc) return rc; - if (rets[1] == 0) return -1; /* EEH is not supported */ - - if (rets[0] != 5) return rets[0]; /* return actual status */ - - if (rets[2] == 0) return -1; /* permanently unavailable */ + rc = eeh_ops->get_state(pdn->node, &mwait); + if (rc != EEH_STATE_UNAVAILABLE) + return rc; if (max_wait_msecs <= 0) break; - mwait = rets[2]; if (mwait <= 0) { printk(KERN_WARNING "EEH: Firmware returned bad wait value=%d\n", mwait); @@ -522,7 +483,6 @@ void eeh_clear_slot(struct device_node *dn, int mode_flag) int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) { int ret; - int rets[3]; unsigned long flags; struct pci_dn *pdn; int rc = 0; @@ -584,40 +544,18 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) * function zero of a multi-function device. * In any case they must share a common PHB. */ - ret = eeh_read_slot_reset_state(pdn, rets); - - /* If the call to firmware failed, punt */ - if (ret != 0) { - printk(KERN_WARNING "EEH: eeh_read_slot_reset_state() failed; rc=%d dn=%s\n", - ret, dn->full_name); - false_positives++; - pdn->eeh_false_positives ++; - rc = 0; - goto dn_unlock; - } + ret = eeh_ops->get_state(pdn->node, NULL); /* Note that config-io to empty slots may fail; * they are empty when they don't have children. + * We will punt with the following conditions: Failure to get + * PE's state, EEH not support and Permanently unavailable + * state, PE is in good state. */ - if ((rets[0] == 5) && (rets[2] == 0) && (dn->child == NULL)) { - false_positives++; - pdn->eeh_false_positives ++; - rc = 0; - goto dn_unlock; - } - - /* If EEH is not supported on this device, punt. */ - if (rets[1] != 1) { - printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n", - ret, dn->full_name); - false_positives++; - pdn->eeh_false_positives ++; - rc = 0; - goto dn_unlock; - } - - /* If not the kind of error we know about, punt. */ - if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) { + if ((ret < 0) || + (ret == EEH_STATE_NOT_SUPPORT) || + (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) == + (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) { false_positives++; pdn->eeh_false_positives ++; rc = 0; @@ -703,7 +641,8 @@ int eeh_pci_enable(struct pci_dn *pdn, int function) function, rc, pdn->node->full_name); rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); - if ((rc == 4) && (function == EEH_OPT_THAW_MMIO)) + if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) && + (function == EEH_OPT_THAW_MMIO)) return 0; return rc; @@ -900,7 +839,7 @@ int eeh_reset_pe(struct pci_dn *pdn) eeh_reset_pe_once(pdn); rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); - if (rc == 0) + if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) return 0; if (rc < 0) { @@ -1057,7 +996,6 @@ void eeh_configure_bridge(struct pci_dn *pdn) */ static void *eeh_early_enable(struct device_node *dn, void *data) { - unsigned int rets[3]; int ret; const u32 *class_code = of_get_property(dn, "class-code", NULL); const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL); @@ -1109,8 +1047,8 @@ static void *eeh_early_enable(struct device_node *dn, void *data) * where EEH is not supported. Verify support * explicitly. */ - ret = eeh_read_slot_reset_state(pdn, rets); - if ((ret == 0) && (rets[1] == 1)) + ret = eeh_ops->get_state(pdn->node, NULL); + if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT) enable = 1; } @@ -1232,8 +1170,6 @@ void __init eeh_init(void) return; ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); - ibm_read_slot_reset_state2 = rtas_token("ibm,read-slot-reset-state2"); - ibm_read_slot_reset_state = rtas_token("ibm,read-slot-reset-state"); ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); ibm_configure_bridge = rtas_token("ibm,configure-bridge"); ibm_configure_pe = rtas_token("ibm,configure-pe"); diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 02eab3b..4c6e0c1c 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -397,7 +397,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) /* Get the current PCI slot state. This can take a long time, * sometimes over 3 seconds for certain systems. */ rc = eeh_wait_for_slot_status (frozen_pdn, MAX_WAIT_FOR_RECOVERY*1000); - if (rc < 0) { + if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) { printk(KERN_WARNING "EEH: Permanent failure\n"); goto hard_fail; } diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 2b9543a..39567b2 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -238,7 +238,75 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) */ static int pseries_eeh_get_state(struct device_node *dn, int *state) { - return 0; + struct pci_dn *pdn; + int config_addr; + int ret; + int rets[4]; + int result; + + /* Figure out PE config address if possible */ + pdn = PCI_DN(dn); + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + + if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call(ibm_read_slot_reset_state2, 3, 4, rets, + config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid)); + } else if (ibm_read_slot_reset_state != RTAS_UNKNOWN_SERVICE) { + /* Fake PE unavailable info */ + rets[2] = 0; + ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets, + config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid)); + } else { + return EEH_STATE_NOT_SUPPORT; + } + + if (ret) + return ret; + + /* Parse the result out */ + result = 0; + if (rets[1]) { + switch(rets[0]) { + case 0: + result &= ~EEH_STATE_RESET_ACTIVE; + result |= EEH_STATE_MMIO_ACTIVE; + result |= EEH_STATE_DMA_ACTIVE; + break; + case 1: + result |= EEH_STATE_RESET_ACTIVE; + result |= EEH_STATE_MMIO_ACTIVE; + result |= EEH_STATE_DMA_ACTIVE; + break; + case 2: + result &= ~EEH_STATE_RESET_ACTIVE; + result &= ~EEH_STATE_MMIO_ACTIVE; + result &= ~EEH_STATE_DMA_ACTIVE; + break; + case 4: + result &= ~EEH_STATE_RESET_ACTIVE; + result &= ~EEH_STATE_MMIO_ACTIVE; + result &= ~EEH_STATE_DMA_ACTIVE; + result |= EEH_STATE_MMIO_ENABLED; + break; + case 5: + if (rets[2]) { + if (state) *state = rets[2]; + result = EEH_STATE_UNAVAILABLE; + } else { + result = EEH_STATE_NOT_SUPPORT; + } + default: + result = EEH_STATE_NOT_SUPPORT; + } + } else { + result = EEH_STATE_NOT_SUPPORT; + } + + return result; } /** -- cgit v0.10.2 From b0e5f742f1816bebedabeb6844be1a598bd50a91 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:58 +0000 Subject: powerpc/eeh: pseries platform EEH wait PE state On pSeries platform, the PE state might be temporarily unavailable. In that case, the firmware will return the corresponding wait time. That means the kernel has to wait for appropriate time in order to get the PE state. The patch does the implementation for that. Besides, the function has been abstracted through struct eeh_ops::wait_state so that EEH core components could support multiple platforms in future. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 6150349..1cfb2b0 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -58,7 +58,6 @@ struct pci_dev *pci_get_device_by_addr(unsigned long addr); void eeh_slot_error_detail (struct pci_dn *pdn, int severity); int eeh_pci_enable(struct pci_dn *pdn, int function); int eeh_reset_pe(struct pci_dn *); -int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs); void eeh_restore_bars(struct pci_dn *); void eeh_configure_bridge(struct pci_dn *); int rtas_write_config(struct pci_dn *, int where, int size, u32 val); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 8d11f1f..b5b03d4 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -287,48 +287,6 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) } /** - * eeh_wait_for_slot_status - Returns error status of slot - * @pdn: pci device node - * @max_wait_msecs: maximum number to millisecs to wait - * - * Return negative value if a permanent error, else return - * Partition Endpoint (PE) status value. - * - * If @max_wait_msecs is positive, then this routine will - * sleep until a valid status can be obtained, or until - * the max allowed wait time is exceeded, in which case - * a -2 is returned. - */ -int eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs) -{ - int rc; - int mwait; - - while (1) { - rc = eeh_ops->get_state(pdn->node, &mwait); - if (rc != EEH_STATE_UNAVAILABLE) - return rc; - - if (max_wait_msecs <= 0) break; - - if (mwait <= 0) { - printk(KERN_WARNING "EEH: Firmware returned bad wait value=%d\n", - mwait); - mwait = 1000; - } else if (mwait > 300*1000) { - printk(KERN_WARNING "EEH: Firmware is taking too long, time=%d\n", - mwait); - mwait = 300*1000; - } - max_wait_msecs -= mwait; - msleep(mwait); - } - - printk(KERN_WARNING "EEH: Timed out waiting for slot status\n"); - return -2; -} - -/** * eeh_token_to_phys - Convert EEH address token to phys address * @token: I/O token, should be address in the form 0xA.... * @@ -640,7 +598,7 @@ int eeh_pci_enable(struct pci_dn *pdn, int function) printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n", function, rc, pdn->node->full_name); - rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); + rc = eeh_ops->wait_state(pdn->node, PCI_BUS_RESET_WAIT_MSEC); if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) && (function == EEH_OPT_THAW_MMIO)) return 0; @@ -838,7 +796,7 @@ int eeh_reset_pe(struct pci_dn *pdn) for (i=0; i<3; i++) { eeh_reset_pe_once(pdn); - rc = eeh_wait_for_slot_status(pdn, PCI_BUS_RESET_WAIT_MSEC); + rc = eeh_ops->wait_state(pdn->node, PCI_BUS_RESET_WAIT_MSEC); if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) return 0; diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 4c6e0c1c..584defe 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -396,7 +396,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) /* Get the current PCI slot state. This can take a long time, * sometimes over 3 seconds for certain systems. */ - rc = eeh_wait_for_slot_status (frozen_pdn, MAX_WAIT_FOR_RECOVERY*1000); + rc = eeh_ops->wait_state(frozen_pdn->node, MAX_WAIT_FOR_RECOVERY*1000); if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) { printk(KERN_WARNING "EEH: Permanent failure\n"); goto hard_fail; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 39567b2..7b60131 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -331,7 +331,52 @@ static int pseries_eeh_reset(struct device_node *dn, int option) */ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) { - return 0; + int ret; + int mwait; + + /* + * According to PAPR, the state of PE might be temporarily + * unavailable. Under the circumstance, we have to wait + * for indicated time determined by firmware. The maximal + * wait time is 5 minutes, which is acquired from the original + * EEH implementation. Also, the original implementation + * also defined the minimal wait time as 1 second. + */ +#define EEH_STATE_MIN_WAIT_TIME (1000) +#define EEH_STATE_MAX_WAIT_TIME (300 * 1000) + + while (1) { + ret = pseries_eeh_get_state(dn, &mwait); + + /* + * If the PE's state is temporarily unavailable, + * we have to wait for the specified time. Otherwise, + * the PE's state will be returned immediately. + */ + if (ret != EEH_STATE_UNAVAILABLE) + return ret; + + if (max_wait <= 0) { + pr_warning("%s: Timeout when getting PE's state (%d)\n", + __func__, max_wait); + return EEH_STATE_NOT_SUPPORT; + } + + if (mwait <= 0) { + pr_warning("%s: Firmware returned bad wait value %d\n", + __func__, mwait); + mwait = EEH_STATE_MIN_WAIT_TIME; + } else if (mwait > EEH_STATE_MAX_WAIT_TIME) { + pr_warning("%s: Firmware returned too long wait value %d\n", + __func__, mwait); + mwait = EEH_STATE_MAX_WAIT_TIME; + } + + max_wait -= mwait; + msleep(mwait); + } + + return EEH_STATE_NOT_SUPPORT; } /** -- cgit v0.10.2 From 2652481f75186940c4608f68c9fd76b32ec9b159 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:03:59 +0000 Subject: powerpc/eeh: pseries platform EEH reset PE On RTAS compliant pSeries platform, there is a dedicated RTAS call (ibm,set-slot-reset) to reset the specified PE. Furthermore, two types of resets are supported: hot and fundamental. the type of reset is to be used actually depends on the included PCI device's requirements. The patch implements resetting PE on pSeries platform through RTAS call. Besides, it has been abstracted through struct eeh_ops::reset so that EEH core components could support multiple platforms in future. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 1d3c9e5..894ea6c 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -49,6 +49,9 @@ struct device_node; #define EEH_STATE_DMA_ACTIVE (1 << 4) /* Active DMA */ #define EEH_STATE_MMIO_ENABLED (1 << 5) /* MMIO enabled */ #define EEH_STATE_DMA_ENABLED (1 << 6) /* DMA enabled */ +#define EEH_RESET_DEACTIVATE 0 /* Deactivate the PE reset */ +#define EEH_RESET_HOT 1 /* Hot reset */ +#define EEH_RESET_FUNDAMENTAL 3 /* Fundamental reset */ struct eeh_ops { char *name; diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index b5b03d4..4f329f5 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -87,7 +87,6 @@ #define PCI_BUS_RESET_WAIT_MSEC (60*1000) /* RTAS tokens */ -static int ibm_set_slot_reset; static int ibm_slot_error_detail; static int ibm_configure_bridge; static int ibm_configure_pe; @@ -607,54 +606,6 @@ int eeh_pci_enable(struct pci_dn *pdn, int function) } /** - * eeh_slot_reset - Raises/Lowers the pci #RST line - * @pdn: pci device node - * @state: 1/0 to raise/lower the #RST - * - * Clear the EEH-frozen condition on a slot. This routine - * asserts the PCI #RST line if the 'state' argument is '1', - * and drops the #RST line if 'state is '0'. This routine is - * safe to call in an interrupt context. - */ -static void eeh_slot_reset(struct pci_dn *pdn, int state) -{ - int config_addr; - int rc; - - BUG_ON(pdn==NULL); - - if (!pdn->phb) { - printk(KERN_WARNING "EEH: in slot reset, device node %s has no phb\n", - pdn->node->full_name); - return; - } - - /* Use PE configuration address, if present */ - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; - - rc = rtas_call(ibm_set_slot_reset, 4, 1, NULL, - config_addr, - BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), - state); - - /* Fundamental-reset not supported on this PE, try hot-reset */ - if (rc == -8 && state == 3) { - rc = rtas_call(ibm_set_slot_reset, 4, 1, NULL, - config_addr, - BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), 1); - if (rc) - printk(KERN_WARNING - "EEH: Unable to reset the failed slot," - " #RST=%d dn=%s\n", - rc, pdn->node->full_name); - } -} - -/** * pcibios_set_pcie_slot_reset - Set PCI-E reset state * @dev: pci device struct * @state: reset state to enter @@ -665,17 +616,16 @@ static void eeh_slot_reset(struct pci_dn *pdn, int state) int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) { struct device_node *dn = pci_device_to_OF_node(dev); - struct pci_dn *pdn = PCI_DN(dn); switch (state) { case pcie_deassert_reset: - eeh_slot_reset(pdn, 0); + eeh_ops->reset(dn, EEH_RESET_DEACTIVATE); break; case pcie_hot_reset: - eeh_slot_reset(pdn, 1); + eeh_ops->reset(dn, EEH_RESET_HOT); break; case pcie_warm_reset: - eeh_slot_reset(pdn, 3); + eeh_ops->reset(dn, EEH_RESET_FUNDAMENTAL); break; default: return -EINVAL; @@ -754,9 +704,9 @@ static void eeh_reset_pe_once(struct pci_dn *pdn) eeh_set_pe_freset(pdn->node, &freset); if (freset) - eeh_slot_reset(pdn, 3); + eeh_ops->reset(pdn->node, EEH_RESET_FUNDAMENTAL); else - eeh_slot_reset(pdn, 1); + eeh_ops->reset(pdn->node, EEH_RESET_HOT); /* The PCI bus requires that the reset be held high for at least * a 100 milliseconds. We wait a bit longer 'just in case'. @@ -770,7 +720,7 @@ static void eeh_reset_pe_once(struct pci_dn *pdn) */ eeh_clear_slot(pdn->node, EEH_MODE_ISOLATED); - eeh_slot_reset(pdn, 0); + eeh_ops->reset(pdn->node, EEH_RESET_DEACTIVATE); /* After a PCI slot has been reset, the PCI Express spec requires * a 1.5 second idle time for the bus to stabilize, before starting @@ -1127,7 +1077,6 @@ void __init eeh_init(void) if (np == NULL) return; - ibm_set_slot_reset = rtas_token("ibm,set-slot-reset"); ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); ibm_configure_bridge = rtas_token("ibm,configure-bridge"); ibm_configure_pe = rtas_token("ibm,configure-pe"); diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 7b60131..6643e06 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -318,7 +318,30 @@ static int pseries_eeh_get_state(struct device_node *dn, int *state) */ static int pseries_eeh_reset(struct device_node *dn, int option) { - return 0; + struct pci_dn *pdn; + int config_addr; + int ret; + + /* Figure out PE address */ + pdn = PCI_DN(dn); + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + + /* Reset PE through RTAS call */ + ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, + config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid), option); + + /* If fundamental-reset not supported, try hot-reset */ + if (option == EEH_RESET_FUNDAMENTAL && + ret == -8) { + ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, + config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid), EEH_RESET_HOT); + } + + return ret; } /** -- cgit v0.10.2 From 8d633291b4fc0539ecad31f972447104be6953ec Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:00 +0000 Subject: powerpc/eeh: pseries platform EEH error log retrieval On RTAS compliant pSeries platform, one dedicated RTAS call has been introduced to retrieve EEH temporary or permanent error log. The patch implements the function of retriving EEH error log through RTAS call. Besides, it has been abstracted by struct eeh_ops::get_log so that EEH core components could support multiple platforms in future. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index 894ea6c..ad8f318 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -52,6 +52,8 @@ struct device_node; #define EEH_RESET_DEACTIVATE 0 /* Deactivate the PE reset */ #define EEH_RESET_HOT 1 /* Hot reset */ #define EEH_RESET_FUNDAMENTAL 3 /* Fundamental reset */ +#define EEH_LOG_TEMP 1 /* EEH temporary error log */ +#define EEH_LOG_PERM 2 /* EEH permanent error log */ struct eeh_ops { char *name; diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index 1cfb2b0..bd1a84f 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -53,8 +53,6 @@ void pci_addr_cache_insert_device(struct pci_dev *dev); void pci_addr_cache_remove_device(struct pci_dev *dev); void pci_addr_cache_build(void); struct pci_dev *pci_get_device_by_addr(unsigned long addr); -#define EEH_LOG_TEMP_FAILURE 1 -#define EEH_LOG_PERM_FAILURE 2 void eeh_slot_error_detail (struct pci_dn *pdn, int severity); int eeh_pci_enable(struct pci_dn *pdn, int function); int eeh_reset_pe(struct pci_dn *); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 4f329f5..39fcecb 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -87,7 +87,6 @@ #define PCI_BUS_RESET_WAIT_MSEC (60*1000) /* RTAS tokens */ -static int ibm_slot_error_detail; static int ibm_configure_bridge; static int ibm_configure_pe; @@ -100,14 +99,6 @@ EXPORT_SYMBOL(eeh_subsystem_enabled); /* Lock to avoid races due to multiple reports of an error */ static DEFINE_RAW_SPINLOCK(confirm_error_lock); -/* Buffer for reporting slot-error-detail rtas calls. Its here - * in BSS, and not dynamically alloced, so that it ends up in - * RMO where RTAS can access it. - */ -static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX]; -static DEFINE_SPINLOCK(slot_errbuf_lock); -static int eeh_error_buf_size; - /* Buffer for reporting pci register dumps. Its here in BSS, and * not dynamically alloced, so that it ends up in RMO where RTAS * can access it. @@ -127,46 +118,6 @@ static unsigned long slot_resets; #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) /** - * eeh_rtas_slot_error_detail - Retrieve error log through RTAS call - * @pdn: device node - * @severity: temporary or permanent error log - * @driver_log: driver log to be combined with the retrieved error log - * @loglen: length of driver log - * - * This routine should be called to retrieve error log through the dedicated - * RTAS call. - */ -static void eeh_rtas_slot_error_detail(struct pci_dn *pdn, int severity, - char *driver_log, size_t loglen) -{ - int config_addr; - unsigned long flags; - int rc; - - /* Log the error with the rtas logger */ - spin_lock_irqsave(&slot_errbuf_lock, flags); - memset(slot_errbuf, 0, eeh_error_buf_size); - - /* Use PE configuration address, if present */ - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; - - rc = rtas_call(ibm_slot_error_detail, - 8, 1, NULL, config_addr, - BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), - virt_to_phys(driver_log), loglen, - virt_to_phys(slot_errbuf), - eeh_error_buf_size, - severity); - - if (rc == 0) - log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0); - spin_unlock_irqrestore(&slot_errbuf_lock, flags); -} - -/** * eeh_gather_pci_data - Copy assorted PCI config space registers to buff * @pdn: device to report data for * @buf: point to buffer in which to log @@ -282,7 +233,7 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) eeh_restore_bars(pdn); loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); - eeh_rtas_slot_error_detail(pdn, severity, pci_regs_buf, loglen); + eeh_ops->get_log(pdn->node, severity, pci_regs_buf, loglen); } /** @@ -1071,26 +1022,14 @@ void __init eeh_init(void) } raw_spin_lock_init(&confirm_error_lock); - spin_lock_init(&slot_errbuf_lock); np = of_find_node_by_path("/rtas"); if (np == NULL) return; - ibm_slot_error_detail = rtas_token("ibm,slot-error-detail"); ibm_configure_bridge = rtas_token("ibm,configure-bridge"); ibm_configure_pe = rtas_token("ibm,configure-pe"); - eeh_error_buf_size = rtas_token("rtas-error-log-max"); - if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) { - eeh_error_buf_size = 1024; - } - if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) { - printk(KERN_WARNING "EEH: rtas-error-log-max is bigger than allocated " - "buffer ! (%d vs %d)", eeh_error_buf_size, RTAS_ERROR_LOG_MAX); - eeh_error_buf_size = RTAS_ERROR_LOG_MAX; - } - /* Enable EEH for all adapters. Note that eeh requires buid's */ for (phb = of_find_node_by_name(NULL, "pci"); phb; phb = of_find_node_by_name(phb, "pci")) { diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 584defe..6840357 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -406,7 +406,7 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) * don't post the error log until after all dev drivers * have been informed. */ - eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP_FAILURE); + eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP); /* If all device drivers were EEH-unaware, then shut * down all of the device drivers, and hope they @@ -497,7 +497,7 @@ hard_fail: location, drv_str, pci_str); perm_error: - eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM_FAILURE); + eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM); /* Notify all devices that they're about to go down. */ pci_walk_bus(frozen_bus, eeh_report_failure, NULL); diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 6643e06..7c8434f 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -56,6 +56,15 @@ static int ibm_get_config_addr_info2; static int ibm_configure_bridge; static int ibm_configure_pe; +/* + * Buffer for reporting slot-error-detail rtas calls. Its here + * in BSS, and not dynamically alloced, so that it ends up in + * RMO where RTAS can access it. + */ +static unsigned char slot_errbuf[RTAS_ERROR_LOG_MAX]; +static DEFINE_SPINLOCK(slot_errbuf_lock); +static int eeh_error_buf_size; + /** * pseries_eeh_init - EEH platform dependent initialization * @@ -107,6 +116,19 @@ static int pseries_eeh_init(void) return -EINVAL; } + /* Initialize error log lock and size */ + spin_lock_init(&slot_errbuf_lock); + eeh_error_buf_size = rtas_token("rtas-error-log-max"); + if (eeh_error_buf_size == RTAS_UNKNOWN_SERVICE) { + pr_warning("%s: unknown EEH error log size\n", + __func__); + eeh_error_buf_size = 1024; + } else if (eeh_error_buf_size > RTAS_ERROR_LOG_MAX) { + pr_warning("%s: EEH error log size %d exceeds the maximal %d\n", + __func__, eeh_error_buf_size, RTAS_ERROR_LOG_MAX); + eeh_error_buf_size = RTAS_ERROR_LOG_MAX; + } + return 0; } @@ -415,7 +437,30 @@ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) */ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len) { - return 0; + struct pci_dn *pdn; + int config_addr; + unsigned long flags; + int ret; + + pdn = PCI_DN(dn); + spin_lock_irqsave(&slot_errbuf_lock, flags); + memset(slot_errbuf, 0, eeh_error_buf_size); + + /* Figure out the PE address */ + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + + ret = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr, + BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), + virt_to_phys(drv_log), len, + virt_to_phys(slot_errbuf), eeh_error_buf_size, + severity); + if (!ret) + log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, 0); + spin_unlock_irqrestore(&slot_errbuf_lock, flags); + + return ret; } /** -- cgit v0.10.2 From 1823fbf119e434dd3fb8eb7482613994c09d8527 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:01 +0000 Subject: powerpc/eeh: pseries platform EEH configure bridge In order to enable particular PCI device, which has been included in the parent PE. The involved PCI bridges should be enabled explicitly if there has. On pSeries platform, there're dedicated RTAS calls to fulfil the purpose. The patch implements the function of configuring PCI bridges through the dedicated RTAS calls. Besides, the function has been abstracted by struct eeh_ops::configure_bridge so that the EEH core components could support multiple platforms in future. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index bd1a84f..b4b18d8 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -57,7 +57,6 @@ void eeh_slot_error_detail (struct pci_dn *pdn, int severity); int eeh_pci_enable(struct pci_dn *pdn, int function); int eeh_reset_pe(struct pci_dn *); void eeh_restore_bars(struct pci_dn *); -void eeh_configure_bridge(struct pci_dn *); int rtas_write_config(struct pci_dn *, int where, int size, u32 val); int rtas_read_config(struct pci_dn *, int where, int size, u32 *val); void eeh_mark_slot(struct device_node *dn, int mode_flag); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 39fcecb..bd4ed83 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -86,10 +86,6 @@ /* Time to wait for a PCI slot to report status, in milliseconds */ #define PCI_BUS_RESET_WAIT_MSEC (60*1000) -/* RTAS tokens */ -static int ibm_configure_bridge; -static int ibm_configure_pe; - /* Platform dependent EEH operations */ struct eeh_ops *eeh_ops = NULL; @@ -229,7 +225,7 @@ void eeh_slot_error_detail(struct pci_dn *pdn, int severity) pci_regs_buf[0] = 0; eeh_pci_enable(pdn, EEH_OPT_THAW_MMIO); - eeh_configure_bridge(pdn); + eeh_ops->configure_bridge(pdn->node); eeh_restore_bars(pdn); loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); @@ -810,41 +806,6 @@ static void eeh_save_bars(struct pci_dn *pdn) } /** - * eeh_configure_bridge - Configure PCI bridges for the indicated PE - * @pdn: PCI device node - * - * PCI bridges might be included in PE. In order to make the PE work - * again. The included PCI bridges should be recovered after the PE - * encounters frozen state. - */ -void eeh_configure_bridge(struct pci_dn *pdn) -{ - int config_addr; - int rc; - int token; - - /* Use PE configuration address, if present */ - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; - - /* Use new configure-pe function, if supported */ - if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) - token = ibm_configure_pe; - else - token = ibm_configure_bridge; - - rc = rtas_call(token, 3, 1, NULL, - config_addr, - BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid)); - if (rc) { - printk(KERN_WARNING "EEH: Unable to configure device bridge (%d) for %s\n", - rc, pdn->node->full_name); - } -} - -/** * eeh_early_enable - Early enable EEH on the indicated device * @dn: device node * @data: BUID @@ -1027,9 +988,6 @@ void __init eeh_init(void) if (np == NULL) return; - ibm_configure_bridge = rtas_token("ibm,configure-bridge"); - ibm_configure_pe = rtas_token("ibm,configure-pe"); - /* Enable EEH for all adapters. Note that eeh requires buid's */ for (phb = of_find_node_by_name(NULL, "pci"); phb; phb = of_find_node_by_name(phb, "pci")) { diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 6840357..61450e1 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -295,7 +295,7 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) struct pci_dn *ppe = PCI_DN(dn); /* On Power4, always true because eeh_pe_config_addr=0 */ if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) { - eeh_configure_bridge(ppe); + eeh_ops->configure_bridge(dn); eeh_restore_bars(ppe); } dn = dn->sibling; diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 7c8434f..4ed06b2 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -473,7 +473,34 @@ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_l */ static int pseries_eeh_configure_bridge(struct device_node *dn) { - return 0; + struct pci_dn *pdn; + int config_addr; + int ret; + + /* Figure out the PE address */ + pdn = PCI_DN(dn); + config_addr = pdn->eeh_config_addr; + if (pdn->eeh_pe_config_addr) + config_addr = pdn->eeh_pe_config_addr; + + /* Use new configure-pe function, if supported */ + if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call(ibm_configure_pe, 3, 1, NULL, + config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid)); + } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) { + ret = rtas_call(ibm_configure_bridge, 3, 1, NULL, + config_addr, BUID_HI(pdn->phb->buid), + BUID_LO(pdn->phb->buid)); + } else { + return -EFAULT; + } + + if (ret) + pr_warning("%s: Unable to configure bridge %d for %s\n", + __func__, ret, dn->full_name); + + return ret; } static struct eeh_ops pseries_eeh_ops = { -- cgit v0.10.2 From 29f8bf1b7f79f689f33e9c4679d182a68b6a5b2c Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:02 +0000 Subject: powerpc/pseries: Cleanup comments in EEH aux components There're several EEH aux components and the patch does some cleanup for them so that they look more clean. * Duplicated comments have been removed from the header file. * Comments have been reorganized so that it looks more clean. * The leading comments of functions are adjusted for a little bit so that the result of "make pdfdocs" would be more unified. * Function calls "xxx ()" has been replaced by "xxx()". Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh_event.h b/arch/powerpc/include/asm/eeh_event.h index cc3cb04..25ebf6a 100644 --- a/arch/powerpc/include/asm/eeh_event.h +++ b/arch/powerpc/include/asm/eeh_event.h @@ -1,6 +1,4 @@ /* - * eeh_event.h - * * 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 @@ -22,32 +20,20 @@ #define ASM_POWERPC_EEH_EVENT_H #ifdef __KERNEL__ -/** EEH event -- structure holding pci controller data that describes - * a change in the isolation status of a PCI slot. A pointer - * to this struct is passed as the data pointer in a notify callback. +/* + * structure holding pci controller data that describes a + * change in the isolation status of a PCI slot. A pointer + * to this struct is passed as the data pointer in a notify + * callback. */ struct eeh_event { - struct list_head list; - struct device_node *dn; /* struct device node */ - struct pci_dev *dev; /* affected device */ + struct list_head list; /* to form event queue */ + struct device_node *dn; /* struct device node */ + struct pci_dev *dev; /* affected device */ }; -/** - * eeh_send_failure_event - generate a PCI error event - * @dev pci device - * - * This routine builds a PCI error event which will be delivered - * to all listeners on the eeh_notifier_chain. - * - * This routine can be called within an interrupt context; - * the actual event will be delivered in a normal context - * (from a workqueue). - */ -int eeh_send_failure_event (struct device_node *dn, - struct pci_dev *dev); - -/* Main recovery function */ -struct pci_dn * handle_eeh_events (struct eeh_event *); +int eeh_send_failure_event(struct device_node *dn, struct pci_dev *dev); +struct pci_dn *handle_eeh_events(struct eeh_event *); #endif /* __KERNEL__ */ #endif /* ASM_POWERPC_EEH_EVENT_H */ diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c index fc5ae76..850c00c 100644 --- a/arch/powerpc/platforms/pseries/eeh_cache.c +++ b/arch/powerpc/platforms/pseries/eeh_cache.c @@ -1,5 +1,4 @@ /* - * eeh_cache.c * PCI address cache; allows the lookup of PCI devices based on I/O address * * Copyright IBM Corporation 2004 @@ -47,8 +46,7 @@ * than any hash algo I could think of for this problem, even * with the penalty of slow pointer chases for d-cache misses). */ -struct pci_io_addr_range -{ +struct pci_io_addr_range { struct rb_node rb_node; unsigned long addr_lo; unsigned long addr_hi; @@ -56,8 +54,7 @@ struct pci_io_addr_range unsigned int flags; }; -static struct pci_io_addr_cache -{ +static struct pci_io_addr_cache { struct rb_root rb_root; spinlock_t piar_lock; } pci_io_addr_cache_root; @@ -166,7 +163,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo, #ifdef DEBUG printk(KERN_DEBUG "PIAR: insert range=[%lx:%lx] dev=%s\n", - alo, ahi, pci_name (dev)); + alo, ahi, pci_name(dev)); #endif rb_link_node(&piar->rb_node, parent, p); diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 61450e1..3f25fab 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -33,8 +33,14 @@ #include #include - -static inline const char * pcid_name (struct pci_dev *pdev) +/** + * eeh_pcid_name - Retrieve name of PCI device driver + * @pdev: PCI device + * + * This routine is used to retrieve the name of PCI device driver + * if that's valid. + */ +static inline const char *pcid_name(struct pci_dev *pdev) { if (pdev && pdev->dev.driver) return pdev->dev.driver->name; @@ -64,7 +70,14 @@ static void print_device_node_tree(struct pci_dn *pdn, int dent) #endif /** - * eeh_disable_irq - disable interrupt for the recovering device + * eeh_disable_irq - Disable interrupt for the recovering device + * @dev: PCI device + * + * This routine must be called when reporting temporary or permanent + * error to the particular PCI device to disable interrupt of that + * device. If the device has enabled MSI or MSI-X interrupt, we needn't + * do real work because EEH should freeze DMA transfers for those PCI + * devices encountering EEH errors, which includes MSI or MSI-X. */ static void eeh_disable_irq(struct pci_dev *dev) { @@ -73,7 +86,7 @@ static void eeh_disable_irq(struct pci_dev *dev) /* Don't disable MSI and MSI-X interrupts. They are * effectively disabled by the DMA Stopped state * when an EEH error occurs. - */ + */ if (dev->msi_enabled || dev->msix_enabled) return; @@ -85,7 +98,11 @@ static void eeh_disable_irq(struct pci_dev *dev) } /** - * eeh_enable_irq - enable interrupt for the recovering device + * eeh_enable_irq - Enable interrupt for the recovering device + * @dev: PCI device + * + * This routine must be called to enable interrupt while failed + * device could be resumed. */ static void eeh_enable_irq(struct pci_dev *dev) { @@ -97,15 +114,15 @@ static void eeh_enable_irq(struct pci_dev *dev) } } -/* ------------------------------------------------------- */ /** - * eeh_report_error - report pci error to each device driver + * eeh_report_error - Report pci error to each device driver + * @dev: PCI device + * @userdata: return value * * Report an EEH error to each device driver, collect up and * merge the device driver responses. Cumulative response * passed back in "userdata". */ - static int eeh_report_error(struct pci_dev *dev, void *userdata) { enum pci_ers_result rc, *res = userdata; @@ -122,7 +139,7 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata) !driver->err_handler->error_detected) return 0; - rc = driver->err_handler->error_detected (dev, pci_channel_io_frozen); + rc = driver->err_handler->error_detected(dev, pci_channel_io_frozen); /* A driver that needs a reset trumps all others */ if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; @@ -132,13 +149,14 @@ static int eeh_report_error(struct pci_dev *dev, void *userdata) } /** - * eeh_report_mmio_enabled - tell drivers that MMIO has been enabled + * eeh_report_mmio_enabled - Tell drivers that MMIO has been enabled + * @dev: PCI device + * @userdata: return value * * Tells each device driver that IO ports, MMIO and config space I/O * are now enabled. Collects up and merges the device driver responses. * Cumulative response passed back in "userdata". */ - static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata) { enum pci_ers_result rc, *res = userdata; @@ -149,7 +167,7 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata) !driver->err_handler->mmio_enabled) return 0; - rc = driver->err_handler->mmio_enabled (dev); + rc = driver->err_handler->mmio_enabled(dev); /* A driver that needs a reset trumps all others */ if (rc == PCI_ERS_RESULT_NEED_RESET) *res = rc; @@ -159,9 +177,15 @@ static int eeh_report_mmio_enabled(struct pci_dev *dev, void *userdata) } /** - * eeh_report_reset - tell device that slot has been reset + * eeh_report_reset - Tell device that slot has been reset + * @dev: PCI device + * @userdata: return value + * + * This routine must be called while EEH tries to reset particular + * PCI device so that the associated PCI device driver could take + * some actions, usually to save data the driver needs so that the + * driver can work again while the device is recovered. */ - static int eeh_report_reset(struct pci_dev *dev, void *userdata) { enum pci_ers_result rc, *res = userdata; @@ -188,9 +212,14 @@ static int eeh_report_reset(struct pci_dev *dev, void *userdata) } /** - * eeh_report_resume - tell device to resume normal operations + * eeh_report_resume - Tell device to resume normal operations + * @dev: PCI device + * @userdata: return value + * + * This routine must be called to notify the device driver that it + * could resume so that the device driver can do some initialization + * to make the recovered device work again. */ - static int eeh_report_resume(struct pci_dev *dev, void *userdata) { struct pci_driver *driver = dev->driver; @@ -212,12 +241,13 @@ static int eeh_report_resume(struct pci_dev *dev, void *userdata) } /** - * eeh_report_failure - tell device driver that device is dead. + * eeh_report_failure - Tell device driver that device is dead. + * @dev: PCI device + * @userdata: return value * * This informs the device driver that the device is permanently * dead, and that no further recovery attempts will be made on it. */ - static int eeh_report_failure(struct pci_dev *dev, void *userdata) { struct pci_driver *driver = dev->driver; @@ -238,37 +268,16 @@ static int eeh_report_failure(struct pci_dev *dev, void *userdata) return 0; } -/* ------------------------------------------------------- */ /** - * handle_eeh_events -- reset a PCI device after hard lockup. - * - * pSeries systems will isolate a PCI slot if the PCI-Host - * bridge detects address or data parity errors, DMA's - * occurring to wild addresses (which usually happen due to - * bugs in device drivers or in PCI adapter firmware). - * Slot isolations also occur if #SERR, #PERR or other misc - * PCI-related errors are detected. + * eeh_reset_device - Perform actual reset of a pci slot + * @pe_dn: PE associated device node + * @bus: PCI bus corresponding to the isolcated slot * - * Recovery process consists of unplugging the device driver - * (which generated hotplug events to userspace), then issuing - * a PCI #RST to the device, then reconfiguring the PCI config - * space for all bridges & devices under this slot, and then - * finally restarting the device drivers (which cause a second - * set of hotplug events to go out to userspace). + * This routine must be called to do reset on the indicated PE. + * During the reset, udev might be invoked because those affected + * PCI devices will be removed and then added. */ - -/** - * eeh_reset_device() -- perform actual reset of a pci slot - * @bus: pointer to the pci bus structure corresponding - * to the isolated slot. A non-null value will - * cause all devices under the bus to be removed - * and then re-added. - * @pe_dn: pointer to a "Partionable Endpoint" device node. - * This is the top-level structure on which pci - * bus resets can be performed. - */ - -static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) +static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus) { struct device_node *dn; int cnt, rc; @@ -281,12 +290,13 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) /* Reset the pci controller. (Asserts RST#; resets config space). * Reconfigure bridges and devices. Don't try to bring the system - * up if the reset failed for some reason. */ + * up if the reset failed for some reason. + */ rc = eeh_reset_pe(pe_dn); if (rc) return rc; - /* Walk over all functions on this device. */ + /* Walk over all functions on this device. */ dn = pe_dn->node; if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) dn = dn->parent->child; @@ -308,7 +318,7 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) * potentially weird things happen. */ if (bus) { - ssleep (5); + ssleep(5); pcibios_add_pci_devices(bus); } pe_dn->eeh_freeze_count = cnt; @@ -321,7 +331,24 @@ static int eeh_reset_device (struct pci_dn *pe_dn, struct pci_bus *bus) */ #define MAX_WAIT_FOR_RECOVERY 150 -struct pci_dn * handle_eeh_events (struct eeh_event *event) +/** + * eeh_handle_event - Reset a PCI device after hard lockup. + * @event: EEH event + * + * While PHB detects address or data parity errors on particular PCI + * slot, the associated PE will be frozen. Besides, DMA's occurring + * to wild addresses (which usually happen due to bugs in device + * drivers or in PCI adapter firmware) can cause EEH error. #SERR, + * #PERR or other misc PCI-related errors also can trigger EEH errors. + * + * Recovery process consists of unplugging the device driver (which + * generated hotplug events to userspace), then issuing a PCI #RST to + * the device, then reconfiguring the PCI config space for all bridges + * & devices under this slot, and then finally restarting the device + * drivers (which cause a second set of hotplug events to go out to + * userspace). + */ +struct pci_dn *handle_eeh_events(struct eeh_event *event) { struct device_node *frozen_dn; struct pci_dn *frozen_pdn; @@ -350,9 +377,10 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) * which was always an EADS pci bridge. In the new style, * there might not be any EADS bridges, and even when there are, * the firmware marks them as "EEH incapable". So another - * two-step is needed to find the pci bus.. */ + * two-step is needed to find the pci bus.. + */ if (!frozen_bus) - frozen_bus = pcibios_find_pci_bus (frozen_dn->parent); + frozen_bus = pcibios_find_pci_bus(frozen_dn->parent); if (!frozen_bus) { printk(KERN_ERR "EEH: Cannot find PCI bus " @@ -395,7 +423,8 @@ struct pci_dn * handle_eeh_events (struct eeh_event *event) pci_walk_bus(frozen_bus, eeh_report_error, &result); /* Get the current PCI slot state. This can take a long time, - * sometimes over 3 seconds for certain systems. */ + * sometimes over 3 seconds for certain systems. + */ rc = eeh_ops->wait_state(frozen_pdn->node, MAX_WAIT_FOR_RECOVERY*1000); if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) { printk(KERN_WARNING "EEH: Permanent failure\n"); @@ -508,4 +537,3 @@ perm_error: return NULL; } -/* ---------- end of file ---------- */ diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c index d2383cf..e98347c 100644 --- a/arch/powerpc/platforms/pseries/eeh_event.c +++ b/arch/powerpc/platforms/pseries/eeh_event.c @@ -1,6 +1,4 @@ /* - * eeh_event.c - * * 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 @@ -46,7 +44,7 @@ DECLARE_WORK(eeh_event_wq, eeh_thread_launcher); DEFINE_MUTEX(eeh_event_mutex); /** - * eeh_event_handler - dispatch EEH events. + * eeh_event_handler - Dispatch EEH events. * @dummy - unused * * The detection of a frozen slot can occur inside an interrupt, @@ -61,7 +59,7 @@ static int eeh_event_handler(void * dummy) struct eeh_event *event; struct pci_dn *pdn; - daemonize ("eehd"); + daemonize("eehd"); set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&eeh_eventlist_lock, flags); @@ -93,7 +91,7 @@ static int eeh_event_handler(void * dummy) /* If there are no new errors after an hour, clear the counter. */ if (pdn && pdn->eeh_freeze_count>0) { - msleep_interruptible (3600*1000); + msleep_interruptible(3600*1000); if (pdn->eeh_freeze_count>0) pdn->eeh_freeze_count--; } @@ -102,8 +100,11 @@ static int eeh_event_handler(void * dummy) } /** - * eeh_thread_launcher + * eeh_thread_launcher - Start kernel thread to handle EEH events * @dummy - unused + * + * This routine is called to start the kernel thread for processing + * EEH event. */ static void eeh_thread_launcher(struct work_struct *dummy) { @@ -112,14 +113,14 @@ static void eeh_thread_launcher(struct work_struct *dummy) } /** - * eeh_send_failure_event - generate a PCI error event - * @dev pci device + * eeh_send_failure_event - Generate a PCI error event + * @dev: pci device * * This routine can be called within an interrupt context; * the actual event will be delivered in a normal context * (from a workqueue). */ -int eeh_send_failure_event (struct device_node *dn, +int eeh_send_failure_event(struct device_node *dn, struct pci_dev *dev) { unsigned long flags; @@ -135,7 +136,7 @@ int eeh_send_failure_event (struct device_node *dn, } event = kmalloc(sizeof(*event), GFP_ATOMIC); if (event == NULL) { - printk (KERN_ERR "EEH: out of memory, event not handled\n"); + printk(KERN_ERR "EEH: out of memory, event not handled\n"); return 1; } @@ -154,5 +155,3 @@ int eeh_send_failure_event (struct device_node *dn, return 0; } - -/********************** END OF FILE ******************************/ diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c index eb744ee..5e4eab1 100644 --- a/arch/powerpc/platforms/pseries/eeh_sysfs.c +++ b/arch/powerpc/platforms/pseries/eeh_sysfs.c @@ -28,7 +28,7 @@ #include /** - * EEH_SHOW_ATTR -- create sysfs entry for eeh statistic + * EEH_SHOW_ATTR -- Create sysfs entry for eeh statistic * @_name: name of file in sysfs directory * @_memb: name of member in struct pci_dn to access * @_format: printf format for display -- cgit v0.10.2 From def9d83da4f8587dbad5ea57c57d91e53a51006a Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:03 +0000 Subject: powerpc/eeh: Cleanup function names in EEH aux components The patch does some cleanup on the function names of EEH aux components. Currently, only couple of function names from eeh_cache have been adjusted so that: * The function name has prefix "eeh_addr_cache". * Move around pci_addr_cache_build() in the header file to reflect function call sequence. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index b4b18d8..c02d5a7 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -49,10 +49,10 @@ extern unsigned long pci_probe_only; #ifdef CONFIG_EEH +void pci_addr_cache_build(void); void pci_addr_cache_insert_device(struct pci_dev *dev); void pci_addr_cache_remove_device(struct pci_dev *dev); -void pci_addr_cache_build(void); -struct pci_dev *pci_get_device_by_addr(unsigned long addr); +struct pci_dev *pci_addr_cache_get_device(unsigned long addr); void eeh_slot_error_detail (struct pci_dn *pdn, int severity); int eeh_pci_enable(struct pci_dn *pdn, int function); int eeh_reset_pe(struct pci_dn *); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index bd4ed83..646b520 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -511,7 +511,7 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon /* Finding the phys addr + pci device; this is pretty quick. */ addr = eeh_token_to_phys((unsigned long __force) token); - dev = pci_get_device_by_addr(addr); + dev = pci_addr_cache_get_device(addr); if (!dev) { no_device++; return val; diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c index 850c00c..7c36a9c 100644 --- a/arch/powerpc/platforms/pseries/eeh_cache.c +++ b/arch/powerpc/platforms/pseries/eeh_cache.c @@ -59,7 +59,7 @@ static struct pci_io_addr_cache { spinlock_t piar_lock; } pci_io_addr_cache_root; -static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr) +static inline struct pci_dev *__pci_addr_cache_get_device(unsigned long addr) { struct rb_node *n = pci_io_addr_cache_root.rb_root.rb_node; @@ -83,7 +83,7 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr) } /** - * pci_get_device_by_addr - Get device, given only address + * pci_addr_cache_get_device - Get device, given only address * @addr: mmio (PIO) phys address or i/o port number * * Given an mmio phys address, or a port number, find a pci device @@ -92,13 +92,13 @@ static inline struct pci_dev *__pci_get_device_by_addr(unsigned long addr) * from zero (that is, they do *not* have pci_io_addr added in). * It is safe to call this function within an interrupt. */ -struct pci_dev *pci_get_device_by_addr(unsigned long addr) +struct pci_dev *pci_addr_cache_get_device(unsigned long addr) { struct pci_dev *dev; unsigned long flags; spin_lock_irqsave(&pci_io_addr_cache_root.piar_lock, flags); - dev = __pci_get_device_by_addr(addr); + dev = __pci_addr_cache_get_device(addr); spin_unlock_irqrestore(&pci_io_addr_cache_root.piar_lock, flags); return dev; } -- cgit v0.10.2 From eb740b5f3e6559a8f1c22e2505914d07f9632881 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:04 +0000 Subject: powerpc/eeh: Introduce EEH device Original EEH implementation depends on struct pci_dn heavily. However, EEH shouldn't depend on that actually because EEH needn't share much information with other PCI components. That's to say, EEH should have worked independently. The patch introduces struct eeh_dev so that EEH core components needn't be working based on struct pci_dn in future. Also, struct pci_dn, struct eeh_dev instances are created in dynamic fasion and the binding with EEH device, OF node, PCI device is implemented as well. The EEH devices are created after PHBs are detected and initialized, but PCI emunation hasn't started yet. Apart from that, PHB might be created dynamically through DLPAR component and the EEH devices should be creatd as well. Another case might be OF node is created dynamically by DR (Dynamic Reconfiguration), which has been defined by PAPR. For those OF nodes created by DR, EEH devices should be also created accordingly. The binding between EEH device and OF node is done while the EEH device is initially created. The binding between EEH device and PCI device should be done after PCI emunation is done. Besides, PCI hotplug also needs the binding so that the EEH devices could be traced from the newly coming PCI buses or PCI devices. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index d57c08a..63d5ca4 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -31,6 +31,9 @@ struct dev_archdata { #ifdef CONFIG_SWIOTLB dma_addr_t max_direct_dma_addr; #endif +#ifdef CONFIG_EEH + struct eeh_dev *edev; +#endif }; struct pdev_archdata { diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index ad8f318..daaad91 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -32,6 +32,43 @@ struct device_node; #ifdef CONFIG_EEH /* + * The struct is used to trace EEH state for the associated + * PCI device node or PCI device. In future, it might + * represent PE as well so that the EEH device to form + * another tree except the currently existing tree of PCI + * buses and PCI devices + */ +#define EEH_MODE_SUPPORTED (1<<0) /* EEH supported on the device */ +#define EEH_MODE_NOCHECK (1<<1) /* EEH check should be skipped */ +#define EEH_MODE_ISOLATED (1<<2) /* The device has been isolated */ +#define EEH_MODE_RECOVERING (1<<3) /* Recovering the device */ +#define EEH_MODE_IRQ_DISABLED (1<<4) /* Interrupt disabled */ + +struct eeh_dev { + int mode; /* EEH mode */ + int class_code; /* Class code of the device */ + int config_addr; /* Config address */ + int pe_config_addr; /* PE config address */ + int check_count; /* Times of ignored error */ + int freeze_count; /* Times of froze up */ + int false_positives; /* Times of reported #ff's */ + u32 config_space[16]; /* Saved PCI config space */ + struct pci_controller *phb; /* Associated PHB */ + struct device_node *dn; /* Associated device node */ + struct pci_dev *pdev; /* Associated PCI device */ +}; + +static inline struct device_node *eeh_dev_to_of_node(struct eeh_dev *edev) +{ + return edev->dn; +} + +static inline struct pci_dev *eeh_dev_to_pci_dev(struct eeh_dev *edev) +{ + return edev->pdev; +} + +/* * The struct is used to trace the registered EEH operation * callback functions. Actually, those operation callback * functions are heavily platform dependent. That means the @@ -70,19 +107,15 @@ struct eeh_ops { extern struct eeh_ops *eeh_ops; extern int eeh_subsystem_enabled; -/* Values for eeh_mode bits in device_node */ -#define EEH_MODE_SUPPORTED (1<<0) -#define EEH_MODE_NOCHECK (1<<1) -#define EEH_MODE_ISOLATED (1<<2) -#define EEH_MODE_RECOVERING (1<<3) -#define EEH_MODE_IRQ_DISABLED (1<<4) - /* * Max number of EEH freezes allowed before we consider the device * to be permanently disabled. */ #define EEH_MAX_ALLOWED_FREEZES 5 +void * __devinit eeh_dev_init(struct device_node *dn, void *data); +void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb); +void __init eeh_dev_phb_init(void); void __init eeh_init(void); #ifdef CONFIG_PPC_PSERIES int __init eeh_pseries_init(void); @@ -113,6 +146,16 @@ void eeh_remove_bus_device(struct pci_dev *); #define EEH_IO_ERROR_VALUE(size) (~0U >> ((4 - (size)) * 8)) #else /* !CONFIG_EEH */ + +static inline void *eeh_dev_init(struct device_node *dn, void *data) +{ + return NULL; +} + +static inline void eeh_dev_phb_init_dynamic(struct pci_controller *phb) { } + +static inline void eeh_dev_phb_init(void) { } + static inline void eeh_init(void) { } #ifdef CONFIG_PPC_PSERIES diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index e1612df..2049f2d 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -21,12 +21,13 @@ #include #include #include +#include #include #include #include #include -#include +#include #ifdef CONFIG_PPC_OF_PLATFORM_PCI @@ -66,6 +67,9 @@ static int __devinit of_pci_phb_probe(struct platform_device *dev) /* Init pci_dn data structures */ pci_devs_phb_init_dynamic(phb); + /* Create EEH devices for the PHB */ + eeh_dev_phb_init_dynamic(phb); + /* Register devices with EEH */ #ifdef CONFIG_EEH if (dev->dev.of_node->child) diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c index 6cd8f01..517bd86 100644 --- a/arch/powerpc/kernel/rtas_pci.c +++ b/arch/powerpc/kernel/rtas_pci.c @@ -275,6 +275,9 @@ void __init find_and_init_phbs(void) of_node_put(root); pci_devs_phb_init(); + /* Create EEH devices for all PHBs */ + eeh_dev_phb_init(); + /* * pci_probe_only and pci_assign_all_buses can be set via properties * in chosen. diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index ee873cf..c222189 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -6,7 +6,8 @@ obj-y := lpar.o hvCall.o nvram.o reconfig.o \ firmware.o power.o dlpar.o mobility.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SCANLOG) += scanlog.o -obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o eeh_pseries.o +obj-$(CONFIG_EEH) += eeh.o eeh_dev.o eeh_cache.o eeh_driver.o \ + eeh_event.o eeh_sysfs.o eeh_pseries.o obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_PCI) += pci.o pci_dlpar.o obj-$(CONFIG_PSERIES_MSI) += msi.o diff --git a/arch/powerpc/platforms/pseries/eeh_dev.c b/arch/powerpc/platforms/pseries/eeh_dev.c new file mode 100644 index 0000000..f3aed7d --- /dev/null +++ b/arch/powerpc/platforms/pseries/eeh_dev.c @@ -0,0 +1,102 @@ +/* + * The file intends to implement dynamic creation of EEH device, which will + * be bound with OF node and PCI device simutaneously. The EEH devices would + * be foundamental information for EEH core components to work proerly. Besides, + * We have to support multiple situations where dynamic creation of EEH device + * is required: + * + * 1) Before PCI emunation starts, we need create EEH devices according to the + * PCI sensitive OF nodes. + * 2) When PCI emunation is done, we need do the binding between PCI device and + * the associated EEH device. + * 3) DR (Dynamic Reconfiguration) would create PCI sensitive OF node. EEH device + * will be created while PCI sensitive OF node is detected from DR. + * 4) PCI hotplug needs redoing the binding between PCI device and EEH device. If + * PHB is newly inserted, we also need create EEH devices accordingly. + * + * Copyright Benjamin Herrenschmidt & Gavin Shan, IBM Corporation 2012. + * + * 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. + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +/** + * eeh_dev_init - Create EEH device according to OF node + * @dn: device node + * @data: PHB + * + * It will create EEH device according to the given OF node. The function + * might be called by PCI emunation, DR, PHB hotplug. + */ +void * __devinit eeh_dev_init(struct device_node *dn, void *data) +{ + struct pci_controller *phb = data; + struct eeh_dev *edev; + + /* Allocate EEH device */ + edev = zalloc_maybe_bootmem(sizeof(*edev), GFP_KERNEL); + if (!edev) { + pr_warning("%s: out of memory\n", __func__); + return NULL; + } + + /* Associate EEH device with OF node */ + dn->edev = edev; + edev->dn = dn; + edev->phb = phb; + + return NULL; +} + +/** + * eeh_dev_phb_init_dynamic - Create EEH devices for devices included in PHB + * @phb: PHB + * + * Scan the PHB OF node and its child association, then create the + * EEH devices accordingly + */ +void __devinit eeh_dev_phb_init_dynamic(struct pci_controller *phb) +{ + struct device_node *dn = phb->dn; + + /* EEH device for PHB */ + eeh_dev_init(dn, phb); + + /* EEH devices for children OF nodes */ + traverse_pci_devices(dn, eeh_dev_init, phb); +} + +/** + * eeh_dev_phb_init - Create EEH devices for devices included in existing PHBs + * + * Scan all the existing PHBs and create EEH devices for their OF + * nodes and their children OF nodes + */ +void __init eeh_dev_phb_init(void) +{ + struct pci_controller *phb, *tmp; + + list_for_each_entry_safe(phb, tmp, &hose_list, list_node) + eeh_dev_phb_init_dynamic(phb); +} diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index 55d4ec1..fbb21fc 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -147,6 +147,9 @@ struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) pci_devs_phb_init_dynamic(phb); + /* Create EEH devices for the PHB */ + eeh_dev_phb_init_dynamic(phb); + if (dn->child) eeh_add_device_tree_early(dn); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 62b8276..8f137af 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -260,8 +260,12 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act switch (action) { case PSERIES_RECONFIG_ADD: pci = np->parent->data; - if (pci) + if (pci) { update_dn_pci_info(np, pci->phb); + + /* Create EEH device for the OF node */ + eeh_dev_init(np, pci->phb); + } break; default: err = NOTIFY_DONE; diff --git a/include/linux/of.h b/include/linux/of.h index a75a831..3e710d8 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -58,6 +58,9 @@ struct device_node { struct kref kref; unsigned long _flags; void *data; +#if defined(CONFIG_EEH) + struct eeh_dev *edev; +#endif #if defined(CONFIG_SPARC) char *path_component_name; unsigned int unique_id; @@ -72,6 +75,13 @@ struct of_phandle_args { uint32_t args[MAX_PHANDLE_ARGS]; }; +#if defined(CONFIG_EEH) +static inline struct eeh_dev *of_node_to_eeh_dev(struct device_node *dn) +{ + return dn->edev; +} +#endif + #if defined(CONFIG_SPARC) || !defined(CONFIG_OF) /* Dummy ref counting routines - to be implemented later */ static inline struct device_node *of_node_get(struct device_node *node) diff --git a/include/linux/pci.h b/include/linux/pci.h index a16b1df..cfeee2a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1647,6 +1647,13 @@ static inline void pci_set_bus_of_node(struct pci_bus *bus) { } static inline void pci_release_bus_of_node(struct pci_bus *bus) { } #endif /* CONFIG_OF */ +#ifdef CONFIG_EEH +static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) +{ + return pdev->dev.archdata.edev; +} +#endif + /** * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device * @pdev: the PCI device -- cgit v0.10.2 From 44da8edc6ade0fe53354bc6a4fd8ef9760fd7f2b Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:05 +0000 Subject: powerpc/eeh: Replace pci_dn with eeh_dev for EEH sysfs With original EEH implementation, all EEH related statistics have been put into struct pci_dn. We've introduced struct eeh_dev to replace struct pci_dn in EEH core components, including EEH sysfs component. The patch shows EEH statistics from struct eeh_dev instead of struct pci_dn in EEH sysfs component. Besides, it also fixed the EEH device retrieval from PCI device, which was introduced by the previous patch in the series of patch. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/eeh_sysfs.c b/arch/powerpc/platforms/pseries/eeh_sysfs.c index 5e4eab1..243b351 100644 --- a/arch/powerpc/platforms/pseries/eeh_sysfs.c +++ b/arch/powerpc/platforms/pseries/eeh_sysfs.c @@ -41,24 +41,21 @@ static ssize_t eeh_show_##_name(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct pci_dev *pdev = to_pci_dev(dev); \ - struct device_node *dn = pci_device_to_OF_node(pdev); \ - struct pci_dn *pdn; \ + struct eeh_dev *edev = pci_dev_to_eeh_dev(pdev); \ \ - if (!dn || PCI_DN(dn) == NULL) \ - return 0; \ + if (!edev) \ + return 0; \ \ - pdn = PCI_DN(dn); \ - return sprintf(buf, _format "\n", pdn->_memb); \ + return sprintf(buf, _format "\n", edev->_memb); \ } \ static DEVICE_ATTR(_name, S_IRUGO, eeh_show_##_name, NULL); - -EEH_SHOW_ATTR(eeh_mode, eeh_mode, "0x%x"); -EEH_SHOW_ATTR(eeh_config_addr, eeh_config_addr, "0x%x"); -EEH_SHOW_ATTR(eeh_pe_config_addr, eeh_pe_config_addr, "0x%x"); -EEH_SHOW_ATTR(eeh_check_count, eeh_check_count, "%d"); -EEH_SHOW_ATTR(eeh_freeze_count, eeh_freeze_count, "%d"); -EEH_SHOW_ATTR(eeh_false_positives, eeh_false_positives, "%d"); +EEH_SHOW_ATTR(eeh_mode, mode, "0x%x"); +EEH_SHOW_ATTR(eeh_config_addr, config_addr, "0x%x"); +EEH_SHOW_ATTR(eeh_pe_config_addr, pe_config_addr, "0x%x"); +EEH_SHOW_ATTR(eeh_check_count, check_count, "%d" ); +EEH_SHOW_ATTR(eeh_freeze_count, freeze_count, "%d" ); +EEH_SHOW_ATTR(eeh_false_positives, false_positives, "%d" ); void eeh_sysfs_add_device(struct pci_dev *pdev) { -- cgit v0.10.2 From d50a7d4c6fc2a2055fdcb5d7af9605ae80385206 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:06 +0000 Subject: powerpc/eeh: Replace pci_dn with eeh_dev for EEH address cache With original EEH implementation, struct pci_dn is used while building PCI I/O address cache, which helps on searching the corresponding PCI device according to the given physical I/O address. Besides, pci_dn is associated with the corresponding PCI device while building its I/O cache. The patch replaces struct pci_dn with struct eeh_dev so that EEH address cache won't depend on struct pci_dn. That will help EEH to become an independent module in future. Besides, the binding of eeh_dev and PCI device is done while building PCI device I/O cache. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c index 7c36a9c..e5ae1c6 100644 --- a/arch/powerpc/platforms/pseries/eeh_cache.c +++ b/arch/powerpc/platforms/pseries/eeh_cache.c @@ -175,7 +175,7 @@ pci_addr_cache_insert(struct pci_dev *dev, unsigned long alo, static void __pci_addr_cache_insert_device(struct pci_dev *dev) { struct device_node *dn; - struct pci_dn *pdn; + struct eeh_dev *edev; int i; dn = pci_device_to_OF_node(dev); @@ -184,13 +184,19 @@ static void __pci_addr_cache_insert_device(struct pci_dev *dev) return; } + edev = of_node_to_eeh_dev(dn); + if (!edev) { + pr_warning("PCI: no EEH dev found for dn=%s\n", + dn->full_name); + return; + } + /* Skip any devices for which EEH is not enabled. */ - pdn = PCI_DN(dn); - if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || - pdn->eeh_mode & EEH_MODE_NOCHECK) { + if (!(edev->mode & EEH_MODE_SUPPORTED) || + edev->mode & EEH_MODE_NOCHECK) { #ifdef DEBUG - printk(KERN_INFO "PCI: skip building address cache for=%s - %s\n", - pci_name(dev), pdn->node->full_name); + pr_info("PCI: skip building address cache for=%s - %s\n", + pci_name(dev), dn->full_name); #endif return; } @@ -281,6 +287,7 @@ void pci_addr_cache_remove_device(struct pci_dev *dev) void __init pci_addr_cache_build(void) { struct device_node *dn; + struct eeh_dev *edev; struct pci_dev *dev = NULL; spin_lock_init(&pci_io_addr_cache_root.piar_lock); @@ -291,8 +298,14 @@ void __init pci_addr_cache_build(void) dn = pci_device_to_OF_node(dev); if (!dn) continue; + + edev = of_node_to_eeh_dev(dn); + if (!edev) + continue; + pci_dev_get(dev); /* matching put is in eeh_remove_device() */ - PCI_DN(dn)->pcidev = dev; + dev->dev.archdata.edev = edev; + edev->pdev = dev; eeh_sysfs_add_device(dev); } -- cgit v0.10.2 From f631acd3e98c31b21937682d2b3f39bf9333a18e Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:07 +0000 Subject: powerpc/eeh: Replace pci_dn with eeh_dev for EEH core The original EEH implementation is heavily depending on struct pci_dn. We have to put EEH related information to pci_dn. Actually, we could split struct pci_dn so that the EEH sensitive information to form an individual struct, then EEH looks more independent. The patch replaces pci_dn with eeh_dev for EEH core. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/ppc-pci.h b/arch/powerpc/include/asm/ppc-pci.h index c02d5a7..e660b37 100644 --- a/arch/powerpc/include/asm/ppc-pci.h +++ b/arch/powerpc/include/asm/ppc-pci.h @@ -53,10 +53,10 @@ void pci_addr_cache_build(void); void pci_addr_cache_insert_device(struct pci_dev *dev); void pci_addr_cache_remove_device(struct pci_dev *dev); struct pci_dev *pci_addr_cache_get_device(unsigned long addr); -void eeh_slot_error_detail (struct pci_dn *pdn, int severity); -int eeh_pci_enable(struct pci_dn *pdn, int function); -int eeh_reset_pe(struct pci_dn *); -void eeh_restore_bars(struct pci_dn *); +void eeh_slot_error_detail(struct eeh_dev *edev, int severity); +int eeh_pci_enable(struct eeh_dev *edev, int function); +int eeh_reset_pe(struct eeh_dev *); +void eeh_restore_bars(struct eeh_dev *); int rtas_write_config(struct pci_dn *, int where, int size, u32 val); int rtas_read_config(struct pci_dn *, int where, int size, u32 *val); void eeh_mark_slot(struct device_node *dn, int mode_flag); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 646b520..aec10f6 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -115,28 +115,29 @@ static unsigned long slot_resets; /** * eeh_gather_pci_data - Copy assorted PCI config space registers to buff - * @pdn: device to report data for + * @edev: device to report data for * @buf: point to buffer in which to log * @len: amount of room in buffer * * This routine captures assorted PCI configuration space data, * and puts them into a buffer for RTAS error logging. */ -static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) +static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) { - struct pci_dev *dev = pdn->pcidev; + struct device_node *dn = eeh_dev_to_of_node(edev); + struct pci_dev *dev = eeh_dev_to_pci_dev(edev); u32 cfg; int cap, i; int n = 0; - n += scnprintf(buf+n, len-n, "%s\n", pdn->node->full_name); - printk(KERN_WARNING "EEH: of node=%s\n", pdn->node->full_name); + n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); + printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name); - rtas_read_config(pdn, PCI_VENDOR_ID, 4, &cfg); + rtas_read_config(PCI_DN(dn), PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg); - rtas_read_config(pdn, PCI_COMMAND, 4, &cfg); + rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg); @@ -147,11 +148,11 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) /* Gather bridge-specific registers */ if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) { - rtas_read_config(pdn, PCI_SEC_STATUS, 2, &cfg); + rtas_read_config(PCI_DN(dn), PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg); - rtas_read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg); + rtas_read_config(PCI_DN(dn), PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg); } @@ -159,11 +160,11 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) /* Dump out the PCI-X command and status regs */ cap = pci_find_capability(dev, PCI_CAP_ID_PCIX); if (cap) { - rtas_read_config(pdn, cap, 4, &cfg); + rtas_read_config(PCI_DN(dn), cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg); - rtas_read_config(pdn, cap+4, 4, &cfg); + rtas_read_config(PCI_DN(dn), cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg); } @@ -176,7 +177,7 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) "EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { - rtas_read_config(pdn, cap+4*i, 4, &cfg); + rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg); } @@ -188,7 +189,7 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) "EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<14; i++) { - rtas_read_config(pdn, cap+4*i, 4, &cfg); + rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg); } @@ -197,12 +198,11 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) /* Gather status on devices under the bridge */ if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) { - struct device_node *dn; + struct device_node *child; - for_each_child_of_node(pdn->node, dn) { - pdn = PCI_DN(dn); - if (pdn) - n += eeh_gather_pci_data(pdn, buf+n, len-n); + for_each_child_of_node(dn, child) { + if (of_node_to_eeh_dev(child)) + n += eeh_gather_pci_data(of_node_to_eeh_dev(child), buf+n, len-n); } } @@ -211,7 +211,7 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) /** * eeh_slot_error_detail - Generate combined log including driver log and error log - * @pdn: device node + * @edev: device to report error log for * @severity: temporary or permanent error log * * This routine should be called to generate the combined log, which @@ -219,17 +219,17 @@ static size_t eeh_gather_pci_data(struct pci_dn *pdn, char * buf, size_t len) * out from the config space of the corresponding PCI device, while * the error log is fetched through platform dependent function call. */ -void eeh_slot_error_detail(struct pci_dn *pdn, int severity) +void eeh_slot_error_detail(struct eeh_dev *edev, int severity) { size_t loglen = 0; pci_regs_buf[0] = 0; - eeh_pci_enable(pdn, EEH_OPT_THAW_MMIO); - eeh_ops->configure_bridge(pdn->node); - eeh_restore_bars(pdn); - loglen = eeh_gather_pci_data(pdn, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); + eeh_pci_enable(edev, EEH_OPT_THAW_MMIO); + eeh_ops->configure_bridge(eeh_dev_to_of_node(edev)); + eeh_restore_bars(edev); + loglen = eeh_gather_pci_data(edev, pci_regs_buf, EEH_PCI_REGS_LOG_LEN); - eeh_ops->get_log(pdn->node, severity, pci_regs_buf, loglen); + eeh_ops->get_log(eeh_dev_to_of_node(edev), severity, pci_regs_buf, loglen); } /** @@ -260,8 +260,8 @@ static inline unsigned long eeh_token_to_phys(unsigned long token) */ struct device_node *eeh_find_device_pe(struct device_node *dn) { - while ((dn->parent) && PCI_DN(dn->parent) && - (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) { + while (dn->parent && of_node_to_eeh_dev(dn->parent) && + (of_node_to_eeh_dev(dn->parent)->mode & EEH_MODE_SUPPORTED)) { dn = dn->parent; } return dn; @@ -284,11 +284,11 @@ static void __eeh_mark_slot(struct device_node *parent, int mode_flag) struct device_node *dn; for_each_child_of_node(parent, dn) { - if (PCI_DN(dn)) { + if (of_node_to_eeh_dev(dn)) { /* Mark the pci device driver too */ - struct pci_dev *dev = PCI_DN(dn)->pcidev; + struct pci_dev *dev = of_node_to_eeh_dev(dn)->pdev; - PCI_DN(dn)->eeh_mode |= mode_flag; + of_node_to_eeh_dev(dn)->mode |= mode_flag; if (dev && dev->driver) dev->error_state = pci_channel_io_frozen; @@ -312,13 +312,13 @@ void eeh_mark_slot(struct device_node *dn, int mode_flag) dn = eeh_find_device_pe(dn); /* Back up one, since config addrs might be shared */ - if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) + if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent)) dn = dn->parent; - PCI_DN(dn)->eeh_mode |= mode_flag; + of_node_to_eeh_dev(dn)->mode |= mode_flag; /* Mark the pci device too */ - dev = PCI_DN(dn)->pcidev; + dev = of_node_to_eeh_dev(dn)->pdev; if (dev) dev->error_state = pci_channel_io_frozen; @@ -337,9 +337,9 @@ static void __eeh_clear_slot(struct device_node *parent, int mode_flag) struct device_node *dn; for_each_child_of_node(parent, dn) { - if (PCI_DN(dn)) { - PCI_DN(dn)->eeh_mode &= ~mode_flag; - PCI_DN(dn)->eeh_check_count = 0; + if (of_node_to_eeh_dev(dn)) { + of_node_to_eeh_dev(dn)->mode &= ~mode_flag; + of_node_to_eeh_dev(dn)->check_count = 0; __eeh_clear_slot(dn, mode_flag); } } @@ -360,11 +360,11 @@ void eeh_clear_slot(struct device_node *dn, int mode_flag) dn = eeh_find_device_pe(dn); /* Back up one, since config addrs might be shared */ - if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) + if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent)) dn = dn->parent; - PCI_DN(dn)->eeh_mode &= ~mode_flag; - PCI_DN(dn)->eeh_check_count = 0; + of_node_to_eeh_dev(dn)->mode &= ~mode_flag; + of_node_to_eeh_dev(dn)->check_count = 0; __eeh_clear_slot(dn, mode_flag); raw_spin_unlock_irqrestore(&confirm_error_lock, flags); } @@ -388,7 +388,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) { int ret; unsigned long flags; - struct pci_dn *pdn; + struct eeh_dev *edev; int rc = 0; const char *location; @@ -402,18 +402,18 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) return 0; } dn = eeh_find_device_pe(dn); - pdn = PCI_DN(dn); + edev = of_node_to_eeh_dev(dn); /* Access to IO BARs might get this far and still not want checking. */ - if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) || - pdn->eeh_mode & EEH_MODE_NOCHECK) { + if (!(edev->mode & EEH_MODE_SUPPORTED) || + edev->mode & EEH_MODE_NOCHECK) { ignored_check++; pr_debug("EEH: Ignored check (%x) for %s %s\n", - pdn->eeh_mode, eeh_pci_name(dev), dn->full_name); + edev->mode, eeh_pci_name(dev), dn->full_name); return 0; } - if (!pdn->eeh_config_addr && !pdn->eeh_pe_config_addr) { + if (!edev->config_addr && !edev->pe_config_addr) { no_cfg_addr++; return 0; } @@ -426,13 +426,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) */ raw_spin_lock_irqsave(&confirm_error_lock, flags); rc = 1; - if (pdn->eeh_mode & EEH_MODE_ISOLATED) { - pdn->eeh_check_count ++; - if (pdn->eeh_check_count % EEH_MAX_FAILS == 0) { + if (edev->mode & EEH_MODE_ISOLATED) { + edev->check_count++; + if (edev->check_count % EEH_MAX_FAILS == 0) { location = of_get_property(dn, "ibm,loc-code", NULL); printk(KERN_ERR "EEH: %d reads ignored for recovering device at " "location=%s driver=%s pci addr=%s\n", - pdn->eeh_check_count, location, + edev->check_count, location, eeh_driver_name(dev), eeh_pci_name(dev)); printk(KERN_ERR "EEH: Might be infinite loop in %s driver\n", eeh_driver_name(dev)); @@ -448,7 +448,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) * function zero of a multi-function device. * In any case they must share a common PHB. */ - ret = eeh_ops->get_state(pdn->node, NULL); + ret = eeh_ops->get_state(dn, NULL); /* Note that config-io to empty slots may fail; * they are empty when they don't have children. @@ -461,7 +461,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) { false_positives++; - pdn->eeh_false_positives ++; + edev->false_positives ++; rc = 0; goto dn_unlock; } @@ -475,7 +475,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) eeh_mark_slot(dn, EEH_MODE_ISOLATED); raw_spin_unlock_irqrestore(&confirm_error_lock, flags); - eeh_send_failure_event(dn, dev); + eeh_send_failure_event(edev->dn, edev->pdev); /* Most EEH events are due to device driver bugs. Having * a stack trace will help the device-driver authors figure @@ -529,22 +529,23 @@ EXPORT_SYMBOL(eeh_check_failure); /** * eeh_pci_enable - Enable MMIO or DMA transfers for this slot - * @pdn pci device node + * @edev: pci device node * * This routine should be called to reenable frozen MMIO or DMA * so that it would work correctly again. It's useful while doing * recovery or log collection on the indicated device. */ -int eeh_pci_enable(struct pci_dn *pdn, int function) +int eeh_pci_enable(struct eeh_dev *edev, int function) { int rc; + struct device_node *dn = eeh_dev_to_of_node(edev); - rc = eeh_ops->set_option(pdn->node, function); + rc = eeh_ops->set_option(dn, function); if (rc) printk(KERN_WARNING "EEH: Unexpected state change %d, err=%d dn=%s\n", - function, rc, pdn->node->full_name); + function, rc, dn->full_name); - rc = eeh_ops->wait_state(pdn->node, PCI_BUS_RESET_WAIT_MSEC); + rc = eeh_ops->wait_state(dn, PCI_BUS_RESET_WAIT_MSEC); if (rc > 0 && (rc & EEH_STATE_MMIO_ENABLED) && (function == EEH_OPT_THAW_MMIO)) return 0; @@ -595,8 +596,8 @@ void __eeh_set_pe_freset(struct device_node *parent, unsigned int *freset) struct device_node *dn; for_each_child_of_node(parent, dn) { - if (PCI_DN(dn)) { - struct pci_dev *dev = PCI_DN(dn)->pcidev; + if (of_node_to_eeh_dev(dn)) { + struct pci_dev *dev = of_node_to_eeh_dev(dn)->pdev; if (dev && dev->driver) *freset |= dev->needs_freset; @@ -622,10 +623,10 @@ void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset) dn = eeh_find_device_pe(dn); /* Back up one, since config addrs might be shared */ - if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) + if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent)) dn = dn->parent; - dev = PCI_DN(dn)->pcidev; + dev = of_node_to_eeh_dev(dn)->pdev; if (dev) *freset |= dev->needs_freset; @@ -634,13 +635,14 @@ void eeh_set_pe_freset(struct device_node *dn, unsigned int *freset) /** * eeh_reset_pe_once - Assert the pci #RST line for 1/4 second - * @pdn: pci device node to be reset. + * @edev: pci device node to be reset. * * Assert the PCI #RST line for 1/4 second. */ -static void eeh_reset_pe_once(struct pci_dn *pdn) +static void eeh_reset_pe_once(struct eeh_dev *edev) { unsigned int freset = 0; + struct device_node *dn = eeh_dev_to_of_node(edev); /* Determine type of EEH reset required for * Partitionable Endpoint, a hot-reset (1) @@ -648,12 +650,12 @@ static void eeh_reset_pe_once(struct pci_dn *pdn) * A fundamental reset required by any device under * Partitionable Endpoint trumps hot-reset. */ - eeh_set_pe_freset(pdn->node, &freset); + eeh_set_pe_freset(dn, &freset); if (freset) - eeh_ops->reset(pdn->node, EEH_RESET_FUNDAMENTAL); + eeh_ops->reset(dn, EEH_RESET_FUNDAMENTAL); else - eeh_ops->reset(pdn->node, EEH_RESET_HOT); + eeh_ops->reset(dn, EEH_RESET_HOT); /* The PCI bus requires that the reset be held high for at least * a 100 milliseconds. We wait a bit longer 'just in case'. @@ -665,9 +667,9 @@ static void eeh_reset_pe_once(struct pci_dn *pdn) * pci slot reset line is dropped. Make sure we don't miss * these, and clear the flag now. */ - eeh_clear_slot(pdn->node, EEH_MODE_ISOLATED); + eeh_clear_slot(dn, EEH_MODE_ISOLATED); - eeh_ops->reset(pdn->node, EEH_RESET_DEACTIVATE); + eeh_ops->reset(dn, EEH_RESET_DEACTIVATE); /* After a PCI slot has been reset, the PCI Express spec requires * a 1.5 second idle time for the bus to stabilize, before starting @@ -679,31 +681,32 @@ static void eeh_reset_pe_once(struct pci_dn *pdn) /** * eeh_reset_pe - Reset the indicated PE - * @pdn: PCI device node + * @edev: PCI device associated EEH device * * This routine should be called to reset indicated device, including * PE. A PE might include multiple PCI devices and sometimes PCI bridges * might be involved as well. */ -int eeh_reset_pe(struct pci_dn *pdn) +int eeh_reset_pe(struct eeh_dev *edev) { int i, rc; + struct device_node *dn = eeh_dev_to_of_node(edev); /* Take three shots at resetting the bus */ for (i=0; i<3; i++) { - eeh_reset_pe_once(pdn); + eeh_reset_pe_once(edev); - rc = eeh_ops->wait_state(pdn->node, PCI_BUS_RESET_WAIT_MSEC); + rc = eeh_ops->wait_state(dn, PCI_BUS_RESET_WAIT_MSEC); if (rc == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) return 0; if (rc < 0) { printk(KERN_ERR "EEH: unrecoverable slot failure %s\n", - pdn->node->full_name); + dn->full_name); return -1; } printk(KERN_ERR "EEH: bus reset %d failed on slot %s, rc=%d\n", - i+1, pdn->node->full_name, rc); + i+1, dn->full_name, rc); } return -1; @@ -719,90 +722,95 @@ int eeh_reset_pe(struct pci_dn *pdn) /** * eeh_restore_one_device_bars - Restore the Base Address Registers for one device - * @pdn: pci device node + * @edev: PCI device associated EEH device * * Loads the PCI configuration space base address registers, * the expansion ROM base address, the latency timer, and etc. * from the saved values in the device node. */ -static inline void eeh_restore_one_device_bars(struct pci_dn *pdn) +static inline void eeh_restore_one_device_bars(struct eeh_dev *edev) { int i; u32 cmd; + struct device_node *dn = eeh_dev_to_of_node(edev); + + if (!edev->phb) + return; - if (NULL==pdn->phb) return; for (i=4; i<10; i++) { - rtas_write_config(pdn, i*4, 4, pdn->config_space[i]); + rtas_write_config(PCI_DN(dn), i*4, 4, edev->config_space[i]); } /* 12 == Expansion ROM Address */ - rtas_write_config(pdn, 12*4, 4, pdn->config_space[12]); + rtas_write_config(PCI_DN(dn), 12*4, 4, edev->config_space[12]); #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) -#define SAVED_BYTE(OFF) (((u8 *)(pdn->config_space))[BYTE_SWAP(OFF)]) +#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) - rtas_write_config(pdn, PCI_CACHE_LINE_SIZE, 1, + rtas_write_config(PCI_DN(dn), PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); - rtas_write_config(pdn, PCI_LATENCY_TIMER, 1, + rtas_write_config(PCI_DN(dn), PCI_LATENCY_TIMER, 1, SAVED_BYTE(PCI_LATENCY_TIMER)); /* max latency, min grant, interrupt pin and line */ - rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]); + rtas_write_config(PCI_DN(dn), 15*4, 4, edev->config_space[15]); /* Restore PERR & SERR bits, some devices require it, * don't touch the other command bits */ - rtas_read_config(pdn, PCI_COMMAND, 4, &cmd); - if (pdn->config_space[1] & PCI_COMMAND_PARITY) + rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cmd); + if (edev->config_space[1] & PCI_COMMAND_PARITY) cmd |= PCI_COMMAND_PARITY; else cmd &= ~PCI_COMMAND_PARITY; - if (pdn->config_space[1] & PCI_COMMAND_SERR) + if (edev->config_space[1] & PCI_COMMAND_SERR) cmd |= PCI_COMMAND_SERR; else cmd &= ~PCI_COMMAND_SERR; - rtas_write_config(pdn, PCI_COMMAND, 4, cmd); + rtas_write_config(PCI_DN(dn), PCI_COMMAND, 4, cmd); } /** * eeh_restore_bars - Restore the PCI config space info - * @pdn: PCI device node + * @edev: EEH device * * This routine performs a recursive walk to the children * of this device as well. */ -void eeh_restore_bars(struct pci_dn *pdn) +void eeh_restore_bars(struct eeh_dev *edev) { struct device_node *dn; - if (!pdn) + if (!edev) return; - if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code)) - eeh_restore_one_device_bars(pdn); + if ((edev->mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(edev->class_code)) + eeh_restore_one_device_bars(edev); - for_each_child_of_node(pdn->node, dn) - eeh_restore_bars(PCI_DN(dn)); + for_each_child_of_node(eeh_dev_to_of_node(edev), dn) + eeh_restore_bars(of_node_to_eeh_dev(dn)); } /** * eeh_save_bars - Save device bars - * @pdn: PCI device node + * @edev: PCI device associated EEH device * * Save the values of the device bars. Unlike the restore * routine, this routine is *not* recursive. This is because * PCI devices are added individually; but, for the restore, * an entire slot is reset at a time. */ -static void eeh_save_bars(struct pci_dn *pdn) +static void eeh_save_bars(struct eeh_dev *edev) { int i; + struct device_node *dn; - if (!pdn ) + if (!edev) return; + dn = eeh_dev_to_of_node(edev); for (i = 0; i < 16; i++) - rtas_read_config(pdn, i * 4, 4, &pdn->config_space[i]); + rtas_read_config(PCI_DN(dn), i * 4, 4, &edev->config_space[i]); } /** @@ -822,13 +830,13 @@ static void *eeh_early_enable(struct device_node *dn, void *data) const u32 *device_id = of_get_property(dn, "device-id", NULL); const u32 *regs; int enable; - struct pci_dn *pdn = PCI_DN(dn); + struct eeh_dev *edev = of_node_to_eeh_dev(dn); - pdn->class_code = 0; - pdn->eeh_mode = 0; - pdn->eeh_check_count = 0; - pdn->eeh_freeze_count = 0; - pdn->eeh_false_positives = 0; + edev->class_code = 0; + edev->mode = 0; + edev->check_count = 0; + edev->freeze_count = 0; + edev->false_positives = 0; if (!of_device_is_available(dn)) return NULL; @@ -839,10 +847,10 @@ static void *eeh_early_enable(struct device_node *dn, void *data) /* There is nothing to check on PCI to ISA bridges */ if (dn->type && !strcmp(dn->type, "isa")) { - pdn->eeh_mode |= EEH_MODE_NOCHECK; + edev->mode |= EEH_MODE_NOCHECK; return NULL; } - pdn->class_code = *class_code; + edev->class_code = *class_code; /* Ok... see if this device supports EEH. Some do, some don't, * and the only way to find out is to check each and every one. @@ -855,40 +863,40 @@ static void *eeh_early_enable(struct device_node *dn, void *data) enable = 0; if (ret == 0) { - pdn->eeh_config_addr = regs[0]; + edev->config_addr = regs[0]; /* If the newer, better, ibm,get-config-addr-info is supported, * then use that instead. */ - pdn->eeh_pe_config_addr = eeh_ops->get_pe_addr(dn); + edev->pe_config_addr = eeh_ops->get_pe_addr(dn); /* Some older systems (Power4) allow the * ibm,set-eeh-option call to succeed even on nodes * where EEH is not supported. Verify support * explicitly. */ - ret = eeh_ops->get_state(pdn->node, NULL); + ret = eeh_ops->get_state(dn, NULL); if (ret > 0 && ret != EEH_STATE_NOT_SUPPORT) enable = 1; } if (enable) { eeh_subsystem_enabled = 1; - pdn->eeh_mode |= EEH_MODE_SUPPORTED; + edev->mode |= EEH_MODE_SUPPORTED; pr_debug("EEH: %s: eeh enabled, config=%x pe_config=%x\n", - dn->full_name, pdn->eeh_config_addr, - pdn->eeh_pe_config_addr); + dn->full_name, edev->config_addr, + edev->pe_config_addr); } else { /* This device doesn't support EEH, but it may have an * EEH parent, in which case we mark it as supported. */ - if (dn->parent && PCI_DN(dn->parent) - && (PCI_DN(dn->parent)->eeh_mode & EEH_MODE_SUPPORTED)) { + if (dn->parent && of_node_to_eeh_dev(dn->parent) && + (of_node_to_eeh_dev(dn->parent)->mode & EEH_MODE_SUPPORTED)) { /* Parent supports EEH. */ - pdn->eeh_mode |= EEH_MODE_SUPPORTED; - pdn->eeh_config_addr = PCI_DN(dn->parent)->eeh_config_addr; + edev->mode |= EEH_MODE_SUPPORTED; + edev->config_addr = of_node_to_eeh_dev(dn->parent)->config_addr; return NULL; } } @@ -897,7 +905,7 @@ static void *eeh_early_enable(struct device_node *dn, void *data) dn->full_name); } - eeh_save_bars(pdn); + eeh_save_bars(edev); return NULL; } @@ -994,7 +1002,7 @@ void __init eeh_init(void) unsigned long buid; buid = get_phb_buid(phb); - if (buid == 0 || PCI_DN(phb) == NULL) + if (buid == 0 || !of_node_to_eeh_dev(phb)) continue; traverse_pci_devices(phb, eeh_early_enable, NULL); @@ -1022,9 +1030,9 @@ static void eeh_add_device_early(struct device_node *dn) { struct pci_controller *phb; - if (!dn || !PCI_DN(dn)) + if (!dn || !of_node_to_eeh_dev(dn)) return; - phb = PCI_DN(dn)->phb; + phb = of_node_to_eeh_dev(dn)->phb; /* USB Bus children of PCI devices will not have BUID's */ if (NULL == phb || 0 == phb->buid) @@ -1061,7 +1069,7 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); static void eeh_add_device_late(struct pci_dev *dev) { struct device_node *dn; - struct pci_dn *pdn; + struct eeh_dev *edev; if (!dev || !eeh_subsystem_enabled) return; @@ -1069,15 +1077,16 @@ static void eeh_add_device_late(struct pci_dev *dev) pr_debug("EEH: Adding device %s\n", pci_name(dev)); dn = pci_device_to_OF_node(dev); - pdn = PCI_DN(dn); - if (pdn->pcidev == dev) { + edev = pci_dev_to_eeh_dev(dev); + if (edev->pdev == dev) { pr_debug("EEH: Already referenced !\n"); return; } - WARN_ON(pdn->pcidev); + WARN_ON(edev->pdev); pci_dev_get(dev); - pdn->pcidev = dev; + edev->pdev = dev; + dev->dev.archdata.edev = edev; pci_addr_cache_insert_device(dev); eeh_sysfs_add_device(dev); @@ -1118,19 +1127,21 @@ EXPORT_SYMBOL_GPL(eeh_add_device_tree_late); */ static void eeh_remove_device(struct pci_dev *dev) { - struct device_node *dn; + struct eeh_dev *edev; + if (!dev || !eeh_subsystem_enabled) return; + edev = pci_dev_to_eeh_dev(dev); /* Unregister the device with the EEH/PCI address search system */ pr_debug("EEH: Removing device %s\n", pci_name(dev)); - dn = pci_device_to_OF_node(dev); - if (PCI_DN(dn)->pcidev == NULL) { + if (!edev || !edev->pdev) { pr_debug("EEH: Not referenced !\n"); return; } - PCI_DN(dn)->pcidev = NULL; + edev->pdev = NULL; + dev->dev.archdata.edev = NULL; pci_dev_put(dev); pci_addr_cache_remove_device(dev); -- cgit v0.10.2 From 40a7cd92197bfa947a1dab80c73a42dd33ac5067 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:08 +0000 Subject: powerpc/eeh: Replace pci_dn with eeh_dev for EEH aux components The original EEH implementation is heavily depending on struct pci_dn. We have to put EEH related information to pci_dn. Actually, we could split struct pci_dn so that the EEH sensitive information to form an individual struct, then EEH looks more independent. The patch replaces pci_dn with eeh_dev for EEH aux components like event and driver. Also, the eeh_event struct has been adjusted for a little bit since eeh_dev has linked the associated FDT (Flat Device Tree) node and PCI device. It's not necessary for eeh_event struct to trace FDT node and PCI device. We can just simply to trace eeh_dev in eeh_event. The patch also renames function pcid_name() to eeh_pcid_name(), which should be missed in the previous patch where the EEH aux components have been cleaned up. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh_event.h b/arch/powerpc/include/asm/eeh_event.h index 25ebf6a..c68b012 100644 --- a/arch/powerpc/include/asm/eeh_event.h +++ b/arch/powerpc/include/asm/eeh_event.h @@ -28,12 +28,11 @@ */ struct eeh_event { struct list_head list; /* to form event queue */ - struct device_node *dn; /* struct device node */ - struct pci_dev *dev; /* affected device */ + struct eeh_dev *edev; /* EEH device */ }; -int eeh_send_failure_event(struct device_node *dn, struct pci_dev *dev); -struct pci_dn *handle_eeh_events(struct eeh_event *); +int eeh_send_failure_event(struct eeh_dev *edev); +struct eeh_dev *handle_eeh_events(struct eeh_event *); #endif /* __KERNEL__ */ #endif /* ASM_POWERPC_EEH_EVENT_H */ diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index aec10f6..9b1fd0c 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -475,7 +475,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) eeh_mark_slot(dn, EEH_MODE_ISOLATED); raw_spin_unlock_irqrestore(&confirm_error_lock, flags); - eeh_send_failure_event(edev->dn, edev->pdev); + eeh_send_failure_event(edev); /* Most EEH events are due to device driver bugs. Having * a stack trace will help the device-driver authors figure diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index 3f25fab..baf92cd 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -40,7 +40,7 @@ * This routine is used to retrieve the name of PCI device driver * if that's valid. */ -static inline const char *pcid_name(struct pci_dev *pdev) +static inline const char *eeh_pcid_name(struct pci_dev *pdev) { if (pdev && pdev->dev.driver) return pdev->dev.driver->name; @@ -81,7 +81,7 @@ static void print_device_node_tree(struct pci_dn *pdn, int dent) */ static void eeh_disable_irq(struct pci_dev *dev) { - struct device_node *dn = pci_device_to_OF_node(dev); + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev); /* Don't disable MSI and MSI-X interrupts. They are * effectively disabled by the DMA Stopped state @@ -93,7 +93,7 @@ static void eeh_disable_irq(struct pci_dev *dev) if (!irq_has_action(dev->irq)) return; - PCI_DN(dn)->eeh_mode |= EEH_MODE_IRQ_DISABLED; + edev->mode |= EEH_MODE_IRQ_DISABLED; disable_irq_nosync(dev->irq); } @@ -106,10 +106,10 @@ static void eeh_disable_irq(struct pci_dev *dev) */ static void eeh_enable_irq(struct pci_dev *dev) { - struct device_node *dn = pci_device_to_OF_node(dev); + struct eeh_dev *edev = pci_dev_to_eeh_dev(dev); - if ((PCI_DN(dn)->eeh_mode) & EEH_MODE_IRQ_DISABLED) { - PCI_DN(dn)->eeh_mode &= ~EEH_MODE_IRQ_DISABLED; + if ((edev->mode) & EEH_MODE_IRQ_DISABLED) { + edev->mode &= ~EEH_MODE_IRQ_DISABLED; enable_irq(dev->irq); } } @@ -270,20 +270,20 @@ static int eeh_report_failure(struct pci_dev *dev, void *userdata) /** * eeh_reset_device - Perform actual reset of a pci slot - * @pe_dn: PE associated device node + * @edev: PE associated EEH device * @bus: PCI bus corresponding to the isolcated slot * * This routine must be called to do reset on the indicated PE. * During the reset, udev might be invoked because those affected * PCI devices will be removed and then added. */ -static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus) +static int eeh_reset_device(struct eeh_dev *edev, struct pci_bus *bus) { struct device_node *dn; int cnt, rc; /* pcibios will clear the counter; save the value */ - cnt = pe_dn->eeh_freeze_count; + cnt = edev->freeze_count; if (bus) pcibios_remove_pci_devices(bus); @@ -292,21 +292,22 @@ static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus) * Reconfigure bridges and devices. Don't try to bring the system * up if the reset failed for some reason. */ - rc = eeh_reset_pe(pe_dn); + rc = eeh_reset_pe(edev); if (rc) return rc; /* Walk over all functions on this device. */ - dn = pe_dn->node; - if (!pcibios_find_pci_bus(dn) && PCI_DN(dn->parent)) + dn = eeh_dev_to_of_node(edev); + if (!pcibios_find_pci_bus(dn) && of_node_to_eeh_dev(dn->parent)) dn = dn->parent->child; while (dn) { - struct pci_dn *ppe = PCI_DN(dn); + struct eeh_dev *pedev = of_node_to_eeh_dev(dn); + /* On Power4, always true because eeh_pe_config_addr=0 */ - if (pe_dn->eeh_pe_config_addr == ppe->eeh_pe_config_addr) { + if (edev->pe_config_addr == pedev->pe_config_addr) { eeh_ops->configure_bridge(dn); - eeh_restore_bars(ppe); + eeh_restore_bars(pedev); } dn = dn->sibling; } @@ -321,7 +322,7 @@ static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus) ssleep(5); pcibios_add_pci_devices(bus); } - pe_dn->eeh_freeze_count = cnt; + edev->freeze_count = cnt; return 0; } @@ -348,23 +349,22 @@ static int eeh_reset_device(struct pci_dn *pe_dn, struct pci_bus *bus) * drivers (which cause a second set of hotplug events to go out to * userspace). */ -struct pci_dn *handle_eeh_events(struct eeh_event *event) +struct eeh_dev *handle_eeh_events(struct eeh_event *event) { struct device_node *frozen_dn; - struct pci_dn *frozen_pdn; + struct eeh_dev *frozen_edev; struct pci_bus *frozen_bus; int rc = 0; enum pci_ers_result result = PCI_ERS_RESULT_NONE; const char *location, *pci_str, *drv_str, *bus_pci_str, *bus_drv_str; - frozen_dn = eeh_find_device_pe(event->dn); + frozen_dn = eeh_find_device_pe(eeh_dev_to_of_node(event->edev)); if (!frozen_dn) { - - location = of_get_property(event->dn, "ibm,loc-code", NULL); + location = of_get_property(eeh_dev_to_of_node(event->edev), "ibm,loc-code", NULL); location = location ? location : "unknown"; printk(KERN_ERR "EEH: Error: Cannot find partition endpoint " "for location=%s pci addr=%s\n", - location, eeh_pci_name(event->dev)); + location, eeh_pci_name(eeh_dev_to_pci_dev(event->edev))); return NULL; } @@ -389,22 +389,21 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event) return NULL; } - frozen_pdn = PCI_DN(frozen_dn); - frozen_pdn->eeh_freeze_count++; + frozen_edev = of_node_to_eeh_dev(frozen_dn); + frozen_edev->freeze_count++; + pci_str = eeh_pci_name(eeh_dev_to_pci_dev(event->edev)); + drv_str = eeh_pcid_name(eeh_dev_to_pci_dev(event->edev)); - pci_str = eeh_pci_name(event->dev); - drv_str = pcid_name(event->dev); - - if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES) + if (frozen_edev->freeze_count > EEH_MAX_ALLOWED_FREEZES) goto excess_failures; printk(KERN_WARNING "EEH: This PCI device has failed %d times in the last hour:\n", - frozen_pdn->eeh_freeze_count); + frozen_edev->freeze_count); - if (frozen_pdn->pcidev) { - bus_pci_str = pci_name(frozen_pdn->pcidev); - bus_drv_str = pcid_name(frozen_pdn->pcidev); + if (frozen_edev->pdev) { + bus_pci_str = pci_name(frozen_edev->pdev); + bus_drv_str = eeh_pcid_name(frozen_edev->pdev); printk(KERN_WARNING "EEH: Bus location=%s driver=%s pci addr=%s\n", location, bus_drv_str, bus_pci_str); @@ -425,7 +424,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event) /* Get the current PCI slot state. This can take a long time, * sometimes over 3 seconds for certain systems. */ - rc = eeh_ops->wait_state(frozen_pdn->node, MAX_WAIT_FOR_RECOVERY*1000); + rc = eeh_ops->wait_state(eeh_dev_to_of_node(frozen_edev), MAX_WAIT_FOR_RECOVERY*1000); if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) { printk(KERN_WARNING "EEH: Permanent failure\n"); goto hard_fail; @@ -435,14 +434,14 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event) * don't post the error log until after all dev drivers * have been informed. */ - eeh_slot_error_detail(frozen_pdn, EEH_LOG_TEMP); + eeh_slot_error_detail(frozen_edev, EEH_LOG_TEMP); /* If all device drivers were EEH-unaware, then shut * down all of the device drivers, and hope they * go down willingly, without panicing the system. */ if (result == PCI_ERS_RESULT_NONE) { - rc = eeh_reset_device(frozen_pdn, frozen_bus); + rc = eeh_reset_device(frozen_edev, frozen_bus); if (rc) { printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc); goto hard_fail; @@ -451,7 +450,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event) /* If all devices reported they can proceed, then re-enable MMIO */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { - rc = eeh_pci_enable(frozen_pdn, EEH_OPT_THAW_MMIO); + rc = eeh_pci_enable(frozen_edev, EEH_OPT_THAW_MMIO); if (rc < 0) goto hard_fail; @@ -465,7 +464,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event) /* If all devices reported they can proceed, then re-enable DMA */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { - rc = eeh_pci_enable(frozen_pdn, EEH_OPT_THAW_DMA); + rc = eeh_pci_enable(frozen_edev, EEH_OPT_THAW_DMA); if (rc < 0) goto hard_fail; @@ -483,7 +482,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event) /* If any device called out for a reset, then reset the slot */ if (result == PCI_ERS_RESULT_NEED_RESET) { - rc = eeh_reset_device(frozen_pdn, NULL); + rc = eeh_reset_device(frozen_edev, NULL); if (rc) { printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc); goto hard_fail; @@ -502,7 +501,7 @@ struct pci_dn *handle_eeh_events(struct eeh_event *event) /* Tell all device drivers that they can resume operations */ pci_walk_bus(frozen_bus, eeh_report_resume, NULL); - return frozen_pdn; + return frozen_edev; excess_failures: /* @@ -515,7 +514,7 @@ excess_failures: "has failed %d times in the last hour " "and has been permanently disabled.\n" "Please try reseating this device or replacing it.\n", - location, drv_str, pci_str, frozen_pdn->eeh_freeze_count); + location, drv_str, pci_str, frozen_edev->freeze_count); goto perm_error; hard_fail: @@ -526,7 +525,7 @@ hard_fail: location, drv_str, pci_str); perm_error: - eeh_slot_error_detail(frozen_pdn, EEH_LOG_PERM); + eeh_slot_error_detail(frozen_edev, EEH_LOG_PERM); /* Notify all devices that they're about to go down. */ pci_walk_bus(frozen_bus, eeh_report_failure, NULL); diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c index e98347c..4a47525 100644 --- a/arch/powerpc/platforms/pseries/eeh_event.c +++ b/arch/powerpc/platforms/pseries/eeh_event.c @@ -56,8 +56,8 @@ DEFINE_MUTEX(eeh_event_mutex); static int eeh_event_handler(void * dummy) { unsigned long flags; - struct eeh_event *event; - struct pci_dn *pdn; + struct eeh_event *event; + struct eeh_dev *edev; daemonize("eehd"); set_current_state(TASK_INTERRUPTIBLE); @@ -77,23 +77,26 @@ static int eeh_event_handler(void * dummy) /* Serialize processing of EEH events */ mutex_lock(&eeh_event_mutex); - eeh_mark_slot(event->dn, EEH_MODE_RECOVERING); + edev = event->edev; + eeh_mark_slot(eeh_dev_to_of_node(edev), EEH_MODE_RECOVERING); printk(KERN_INFO "EEH: Detected PCI bus error on device %s\n", - eeh_pci_name(event->dev)); + eeh_pci_name(edev->pdev)); + + edev = handle_eeh_events(event); - pdn = handle_eeh_events(event); + eeh_clear_slot(eeh_dev_to_of_node(edev), EEH_MODE_RECOVERING); + pci_dev_put(edev->pdev); - eeh_clear_slot(event->dn, EEH_MODE_RECOVERING); - pci_dev_put(event->dev); kfree(event); mutex_unlock(&eeh_event_mutex); /* If there are no new errors after an hour, clear the counter. */ - if (pdn && pdn->eeh_freeze_count>0) { + if (edev && edev->freeze_count>0) { msleep_interruptible(3600*1000); - if (pdn->eeh_freeze_count>0) - pdn->eeh_freeze_count--; + if (edev->freeze_count>0) + edev->freeze_count--; + } return 0; @@ -114,17 +117,17 @@ static void eeh_thread_launcher(struct work_struct *dummy) /** * eeh_send_failure_event - Generate a PCI error event - * @dev: pci device + * @edev: EEH device * * This routine can be called within an interrupt context; * the actual event will be delivered in a normal context * (from a workqueue). */ -int eeh_send_failure_event(struct device_node *dn, - struct pci_dev *dev) +int eeh_send_failure_event(struct eeh_dev *edev) { unsigned long flags; struct eeh_event *event; + struct device_node *dn = eeh_dev_to_of_node(edev); const char *location; if (!mem_init_done) { @@ -140,11 +143,10 @@ int eeh_send_failure_event(struct device_node *dn, return 1; } - if (dev) - pci_dev_get(dev); + if (edev->pdev) + pci_dev_get(edev->pdev); - event->dn = dn; - event->dev = dev; + event->edev = edev; /* We may or may not be called in an interrupt context */ spin_lock_irqsave(&eeh_eventlist_lock, flags); -- cgit v0.10.2 From 54793d0ef113145c8c0342d9bf2ade5c6b71cba5 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:09 +0000 Subject: powerpc/eeh: Replace pci_dn with eeh_dev for EEH on pSeries The pci_dn has been replaced with eeh_dev. In order to comply with the rule, the EEH platform implementation on pSeries should also be adjusted for a little bit so that it will depend on eeh_dev instead of pci_dn. The patch replaces pci_dn with eeh_dev. The corresponding information will be retrieved from eeh_dev instead of pci_dn. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 4ed06b2..36a1af1 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -144,11 +144,11 @@ static int pseries_eeh_init(void) static int pseries_eeh_set_option(struct device_node *dn, int option) { int ret = 0; - struct pci_dn *pdn; + struct eeh_dev *edev; const u32 *reg; int config_addr; - pdn = PCI_DN(dn); + edev = of_node_to_eeh_dev(dn); /* * When we're enabling or disabling EEH functioality on @@ -165,9 +165,9 @@ static int pseries_eeh_set_option(struct device_node *dn, int option) case EEH_OPT_THAW_MMIO: case EEH_OPT_THAW_DMA: - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; break; default: @@ -177,8 +177,8 @@ static int pseries_eeh_set_option(struct device_node *dn, int option) } ret = rtas_call(ibm_set_eeh_option, 4, 1, NULL, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), option); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), option); return ret; } @@ -198,11 +198,11 @@ static int pseries_eeh_set_option(struct device_node *dn, int option) */ static int pseries_eeh_get_pe_addr(struct device_node *dn) { - struct pci_dn *pdn; + struct eeh_dev *edev; int ret = 0; int rets[3]; - pdn = PCI_DN(dn); + edev = of_node_to_eeh_dev(dn); if (ibm_get_config_addr_info2 != RTAS_UNKNOWN_SERVICE) { /* @@ -211,15 +211,15 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) * meaningless. */ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), 1); + edev->config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), 1); if (ret || (rets[0] == 0)) return 0; /* Retrieve the associated PE config address */ ret = rtas_call(ibm_get_config_addr_info2, 4, 2, rets, - pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), 0); + edev->config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), 0); if (ret) { pr_warning("%s: Failed to get PE address for %s\n", __func__, dn->full_name); @@ -231,8 +231,8 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { ret = rtas_call(ibm_get_config_addr_info, 4, 2, rets, - pdn->eeh_config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), 0); + edev->config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), 0); if (ret) { pr_warning("%s: Failed to get PE address for %s\n", __func__, dn->full_name); @@ -260,28 +260,28 @@ static int pseries_eeh_get_pe_addr(struct device_node *dn) */ static int pseries_eeh_get_state(struct device_node *dn, int *state) { - struct pci_dn *pdn; + struct eeh_dev *edev; int config_addr; int ret; int rets[4]; int result; /* Figure out PE config address if possible */ - pdn = PCI_DN(dn); - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; + edev = of_node_to_eeh_dev(dn); + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; if (ibm_read_slot_reset_state2 != RTAS_UNKNOWN_SERVICE) { ret = rtas_call(ibm_read_slot_reset_state2, 3, 4, rets, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid)); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid)); } else if (ibm_read_slot_reset_state != RTAS_UNKNOWN_SERVICE) { /* Fake PE unavailable info */ rets[2] = 0; ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid)); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid)); } else { return EEH_STATE_NOT_SUPPORT; } @@ -340,27 +340,27 @@ static int pseries_eeh_get_state(struct device_node *dn, int *state) */ static int pseries_eeh_reset(struct device_node *dn, int option) { - struct pci_dn *pdn; + struct eeh_dev *edev; int config_addr; int ret; /* Figure out PE address */ - pdn = PCI_DN(dn); - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; + edev = of_node_to_eeh_dev(dn); + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; /* Reset PE through RTAS call */ ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), option); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), option); /* If fundamental-reset not supported, try hot-reset */ if (option == EEH_RESET_FUNDAMENTAL && ret == -8) { ret = rtas_call(ibm_set_slot_reset, 4, 1, NULL, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid), EEH_RESET_HOT); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid), EEH_RESET_HOT); } return ret; @@ -437,22 +437,22 @@ static int pseries_eeh_wait_state(struct device_node *dn, int max_wait) */ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_log, unsigned long len) { - struct pci_dn *pdn; + struct eeh_dev *edev; int config_addr; unsigned long flags; int ret; - pdn = PCI_DN(dn); + edev = of_node_to_eeh_dev(dn); spin_lock_irqsave(&slot_errbuf_lock, flags); memset(slot_errbuf, 0, eeh_error_buf_size); /* Figure out the PE address */ - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; ret = rtas_call(ibm_slot_error_detail, 8, 1, NULL, config_addr, - BUID_HI(pdn->phb->buid), BUID_LO(pdn->phb->buid), + BUID_HI(edev->phb->buid), BUID_LO(edev->phb->buid), virt_to_phys(drv_log), len, virt_to_phys(slot_errbuf), eeh_error_buf_size, severity); @@ -473,25 +473,25 @@ static int pseries_eeh_get_log(struct device_node *dn, int severity, char *drv_l */ static int pseries_eeh_configure_bridge(struct device_node *dn) { - struct pci_dn *pdn; + struct eeh_dev *edev; int config_addr; int ret; /* Figure out the PE address */ - pdn = PCI_DN(dn); - config_addr = pdn->eeh_config_addr; - if (pdn->eeh_pe_config_addr) - config_addr = pdn->eeh_pe_config_addr; + edev = of_node_to_eeh_dev(dn); + config_addr = edev->config_addr; + if (edev->pe_config_addr) + config_addr = edev->pe_config_addr; /* Use new configure-pe function, if supported */ if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) { ret = rtas_call(ibm_configure_pe, 3, 1, NULL, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid)); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid)); } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) { ret = rtas_call(ibm_configure_bridge, 3, 1, NULL, - config_addr, BUID_HI(pdn->phb->buid), - BUID_LO(pdn->phb->buid)); + config_addr, BUID_HI(edev->phb->buid), + BUID_LO(edev->phb->buid)); } else { return -EFAULT; } -- cgit v0.10.2 From e575f8db1ed8e73515350a0d569a0eb8367ab6b4 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Wed, 29 Feb 2012 15:47:45 +0000 Subject: powerpc/eeh: Introduce struct eeh_stats for EEH With the original EEH implementation, the EEH global statistics are maintained by individual global variables. That makes the code a little hard to maintain. The patch introduces extra struct eeh_stats for the EEH global statistics so that it can be maintained in collective fashion. It's the rework on the corresponding v5 patch. According to the comments from David Laight, the EEH global statistics have been changed for a litte bit so that they have fixed-type of "u64". Also, the format used to print them has been changed to "%llu" based on David's suggestion. Also, the output format of EEH global statistics should be kept as intacted according to Michael's suggestion that there might be tools parsing them. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 9b1fd0c..1d08cd7 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -102,14 +102,22 @@ static DEFINE_RAW_SPINLOCK(confirm_error_lock); #define EEH_PCI_REGS_LOG_LEN 4096 static unsigned char pci_regs_buf[EEH_PCI_REGS_LOG_LEN]; -/* System monitoring statistics */ -static unsigned long no_device; -static unsigned long no_dn; -static unsigned long no_cfg_addr; -static unsigned long ignored_check; -static unsigned long total_mmio_ffs; -static unsigned long false_positives; -static unsigned long slot_resets; +/* + * The struct is used to maintain the EEH global statistic + * information. Besides, the EEH global statistics will be + * exported to user space through procfs + */ +struct eeh_stats { + u64 no_device; /* PCI device not found */ + u64 no_dn; /* OF node not found */ + u64 no_cfg_addr; /* Config address not found */ + u64 ignored_check; /* EEH check skipped */ + u64 total_mmio_ffs; /* Total EEH checks */ + u64 false_positives; /* Unnecessary EEH checks */ + u64 slot_resets; /* PE reset */ +}; + +static struct eeh_stats eeh_stats; #define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE) @@ -392,13 +400,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) int rc = 0; const char *location; - total_mmio_ffs++; + eeh_stats.total_mmio_ffs++; if (!eeh_subsystem_enabled) return 0; if (!dn) { - no_dn++; + eeh_stats.no_dn++; return 0; } dn = eeh_find_device_pe(dn); @@ -407,14 +415,14 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) /* Access to IO BARs might get this far and still not want checking. */ if (!(edev->mode & EEH_MODE_SUPPORTED) || edev->mode & EEH_MODE_NOCHECK) { - ignored_check++; + eeh_stats.ignored_check++; pr_debug("EEH: Ignored check (%x) for %s %s\n", edev->mode, eeh_pci_name(dev), dn->full_name); return 0; } if (!edev->config_addr && !edev->pe_config_addr) { - no_cfg_addr++; + eeh_stats.no_cfg_addr++; return 0; } @@ -460,13 +468,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) (ret == EEH_STATE_NOT_SUPPORT) || (ret & (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) == (EEH_STATE_MMIO_ACTIVE | EEH_STATE_DMA_ACTIVE)) { - false_positives++; + eeh_stats.false_positives++; edev->false_positives ++; rc = 0; goto dn_unlock; } - slot_resets++; + eeh_stats.slot_resets++; /* Avoid repeated reports of this failure, including problems * with other functions on this device, and functions under @@ -513,7 +521,7 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon addr = eeh_token_to_phys((unsigned long __force) token); dev = pci_addr_cache_get_device(addr); if (!dev) { - no_device++; + eeh_stats.no_device++; return val; } @@ -1174,21 +1182,24 @@ static int proc_eeh_show(struct seq_file *m, void *v) { if (0 == eeh_subsystem_enabled) { seq_printf(m, "EEH Subsystem is globally disabled\n"); - seq_printf(m, "eeh_total_mmio_ffs=%ld\n", total_mmio_ffs); + seq_printf(m, "eeh_total_mmio_ffs=%llu\n", eeh_stats.total_mmio_ffs); } else { seq_printf(m, "EEH Subsystem is enabled\n"); seq_printf(m, - "no device=%ld\n" - "no device node=%ld\n" - "no config address=%ld\n" - "check not wanted=%ld\n" - "eeh_total_mmio_ffs=%ld\n" - "eeh_false_positives=%ld\n" - "eeh_slot_resets=%ld\n", - no_device, no_dn, no_cfg_addr, - ignored_check, total_mmio_ffs, - false_positives, - slot_resets); + "no device=%llu\n" + "no device node=%llu\n" + "no config address=%llu\n" + "check not wanted=%llu\n" + "eeh_total_mmio_ffs=%llu\n" + "eeh_false_positives=%llu\n" + "eeh_slot_resets=%llu\n", + eeh_stats.no_device, + eeh_stats.no_dn, + eeh_stats.no_cfg_addr, + eeh_stats.ignored_check, + eeh_stats.total_mmio_ffs, + eeh_stats.false_positives, + eeh_stats.slot_resets); } return 0; -- cgit v0.10.2 From 3780444c4fcec28c96ab7002858bb051215a5fc1 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Mon, 27 Feb 2012 20:04:11 +0000 Subject: powerpc/eeh: pseries platform config space access in EEH With the original EEH implementation, the access to config space of the corresponding PCI device is done by RTAS sensitive function. That depends on pci_dn heavily. That would limit EEH extension to other platforms like powernv because other platforms might have different ways to access PCI config space. The patch splits those functions used to access PCI config space and implement them in platform related EEH component. It would be helpful to support EEH on multiple platforms simutaneously in future. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index daaad91..d60f998 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -102,6 +102,8 @@ struct eeh_ops { int (*wait_state)(struct device_node *dn, int max_wait); int (*get_log)(struct device_node *dn, int severity, char *drv_log, unsigned long len); int (*configure_bridge)(struct device_node *dn); + int (*read_config)(struct device_node *dn, int where, int size, u32 *val); + int (*write_config)(struct device_node *dn, int where, int size, u32 val); }; extern struct eeh_ops *eeh_ops; diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 1d08cd7..8011088 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -141,11 +141,11 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) n += scnprintf(buf+n, len-n, "%s\n", dn->full_name); printk(KERN_WARNING "EEH: of node=%s\n", dn->full_name); - rtas_read_config(PCI_DN(dn), PCI_VENDOR_ID, 4, &cfg); + eeh_ops->read_config(dn, PCI_VENDOR_ID, 4, &cfg); n += scnprintf(buf+n, len-n, "dev/vend:%08x\n", cfg); printk(KERN_WARNING "EEH: PCI device/vendor: %08x\n", cfg); - rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cfg); + eeh_ops->read_config(dn, PCI_COMMAND, 4, &cfg); n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg); printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg); @@ -156,11 +156,11 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) /* Gather bridge-specific registers */ if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) { - rtas_read_config(PCI_DN(dn), PCI_SEC_STATUS, 2, &cfg); + eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg); n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg); printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg); - rtas_read_config(PCI_DN(dn), PCI_BRIDGE_CONTROL, 2, &cfg); + eeh_ops->read_config(dn, PCI_BRIDGE_CONTROL, 2, &cfg); n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg); printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg); } @@ -168,11 +168,11 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) /* Dump out the PCI-X command and status regs */ cap = pci_find_capability(dev, PCI_CAP_ID_PCIX); if (cap) { - rtas_read_config(PCI_DN(dn), cap, 4, &cfg); + eeh_ops->read_config(dn, cap, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg); printk(KERN_WARNING "EEH: PCI-X cmd: %08x\n", cfg); - rtas_read_config(PCI_DN(dn), cap+4, 4, &cfg); + eeh_ops->read_config(dn, cap+4, 4, &cfg); n += scnprintf(buf+n, len-n, "pcix-stat:%x\n", cfg); printk(KERN_WARNING "EEH: PCI-X status: %08x\n", cfg); } @@ -185,7 +185,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) "EEH: PCI-E capabilities and status follow:\n"); for (i=0; i<=8; i++) { - rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg); + eeh_ops->read_config(dn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg); } @@ -197,7 +197,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len) "EEH: PCI-E AER capability register set follows:\n"); for (i=0; i<14; i++) { - rtas_read_config(PCI_DN(dn), cap+4*i, 4, &cfg); + eeh_ops->read_config(dn, cap+4*i, 4, &cfg); n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg); printk(KERN_WARNING "EEH: PCI-E AER %02x: %08x\n", i, cfg); } @@ -746,28 +746,28 @@ static inline void eeh_restore_one_device_bars(struct eeh_dev *edev) return; for (i=4; i<10; i++) { - rtas_write_config(PCI_DN(dn), i*4, 4, edev->config_space[i]); + eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); } /* 12 == Expansion ROM Address */ - rtas_write_config(PCI_DN(dn), 12*4, 4, edev->config_space[12]); + eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); #define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) #define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) - rtas_write_config(PCI_DN(dn), PCI_CACHE_LINE_SIZE, 1, + eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); - rtas_write_config(PCI_DN(dn), PCI_LATENCY_TIMER, 1, + eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, SAVED_BYTE(PCI_LATENCY_TIMER)); /* max latency, min grant, interrupt pin and line */ - rtas_write_config(PCI_DN(dn), 15*4, 4, edev->config_space[15]); + eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); /* Restore PERR & SERR bits, some devices require it, * don't touch the other command bits */ - rtas_read_config(PCI_DN(dn), PCI_COMMAND, 4, &cmd); + eeh_ops->read_config(dn, PCI_COMMAND, 4, &cmd); if (edev->config_space[1] & PCI_COMMAND_PARITY) cmd |= PCI_COMMAND_PARITY; else @@ -776,7 +776,7 @@ static inline void eeh_restore_one_device_bars(struct eeh_dev *edev) cmd |= PCI_COMMAND_SERR; else cmd &= ~PCI_COMMAND_SERR; - rtas_write_config(PCI_DN(dn), PCI_COMMAND, 4, cmd); + eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); } /** @@ -818,7 +818,7 @@ static void eeh_save_bars(struct eeh_dev *edev) dn = eeh_dev_to_of_node(edev); for (i = 0; i < 16; i++) - rtas_read_config(PCI_DN(dn), i * 4, 4, &edev->config_space[i]); + eeh_ops->read_config(dn, i * 4, 4, &edev->config_space[i]); } /** diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c index 36a1af1..8752f79 100644 --- a/arch/powerpc/platforms/pseries/eeh_pseries.c +++ b/arch/powerpc/platforms/pseries/eeh_pseries.c @@ -503,6 +503,42 @@ static int pseries_eeh_configure_bridge(struct device_node *dn) return ret; } +/** + * pseries_eeh_read_config - Read PCI config space + * @dn: device node + * @where: PCI address + * @size: size to read + * @val: return value + * + * Read config space from the speicifed device + */ +static int pseries_eeh_read_config(struct device_node *dn, int where, int size, u32 *val) +{ + struct pci_dn *pdn; + + pdn = PCI_DN(dn); + + return rtas_read_config(pdn, where, size, val); +} + +/** + * pseries_eeh_write_config - Write PCI config space + * @dn: device node + * @where: PCI address + * @size: size to write + * @val: value to be written + * + * Write config space to the specified device + */ +static int pseries_eeh_write_config(struct device_node *dn, int where, int size, u32 val) +{ + struct pci_dn *pdn; + + pdn = PCI_DN(dn); + + return rtas_write_config(pdn, where, size, val); +} + static struct eeh_ops pseries_eeh_ops = { .name = "pseries", .init = pseries_eeh_init, @@ -512,7 +548,9 @@ static struct eeh_ops pseries_eeh_ops = { .reset = pseries_eeh_reset, .wait_state = pseries_eeh_wait_state, .get_log = pseries_eeh_get_log, - .configure_bridge = pseries_eeh_configure_bridge + .configure_bridge = pseries_eeh_configure_bridge, + .read_config = pseries_eeh_read_config, + .write_config = pseries_eeh_write_config }; /** -- cgit v0.10.2 From 7230c5644188cd9e3fb380cc97dde00c464a3ba7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 6 Mar 2012 18:27:59 +1100 Subject: powerpc: Rework lazy-interrupt handling The current implementation of lazy interrupts handling has some issues that this tries to address. We don't do the various workarounds we need to do when re-enabling interrupts in some cases such as when returning from an interrupt and thus we may still lose or get delayed decrementer or doorbell interrupts. The current scheme also makes it much harder to handle the external "edge" interrupts provided by some BookE processors when using the EPR facility (External Proxy) and the Freescale Hypervisor. Additionally, we tend to keep interrupts hard disabled in a number of cases, such as decrementer interrupts, external interrupts, or when a masked decrementer interrupt is pending. This is sub-optimal. This is an attempt at fixing it all in one go by reworking the way we do the lazy interrupt disabling from the ground up. The base idea is to replace the "hard_enabled" field with a "irq_happened" field in which we store a bit mask of what interrupt occurred while soft-disabled. When re-enabling, either via arch_local_irq_restore() or when returning from an interrupt, we can now decide what to do by testing bits in that field. We then implement replaying of the missed interrupts either by re-using the existing exception frame (in exception exit case) or via the creation of a new one from an assembly trampoline (in the arch_local_irq_enable case). This removes the need to play with the decrementer to try to create fake interrupts, among others. In addition, this adds a few refinements: - We no longer hard disable decrementer interrupts that occur while soft-disabled. We now simply bump the decrementer back to max (on BookS) or leave it stopped (on BookE) and continue with hard interrupts enabled, which means that we'll potentially get better sample quality from performance monitor interrupts. - Timer, decrementer and doorbell interrupts now hard-enable shortly after removing the source of the interrupt, which means they no longer run entirely hard disabled. Again, this will improve perf sample quality. - On Book3E 64-bit, we now make the performance monitor interrupt act as an NMI like Book3S (the necessary C code for that to work appear to already be present in the FSL perf code, notably calling nmi_enter instead of irq_enter). (This also fixes a bug where BookE perfmon interrupts could clobber r14 ... oops) - We could make "masked" decrementer interrupts act as NMIs when doing timer-based perf sampling to improve the sample quality. Signed-off-by-yet: Benjamin Herrenschmidt --- v2: - Add hard-enable to decrementer, timer and doorbells - Fix CR clobber in masked irq handling on BookE - Make embedded perf interrupt act as an NMI - Add a PACA_HAPPENED_EE_EDGE for use by FSL if they want to retrigger an interrupt without preventing hard-enable v3: - Fix or vs. ori bug on Book3E - Fix enabling of interrupts for some exceptions on Book3E v4: - Fix resend of doorbells on return from interrupt on Book3E v5: - Rebased on top of my latest series, which involves some significant rework of some aspects of the patch. v6: - 32-bit compile fix - more compile fixes with various .config combos - factor out the asm code to soft-disable interrupts - remove the C wrapper around preempt_schedule_irq v7: - Fix a bug with hard irq state tracking on native power7 diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 70354af..548da3a 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -232,23 +232,30 @@ label##_hv: \ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_HV, KVMTEST, vec) -#define __SOFTEN_TEST(h) \ +/* This associate vector numbers with bits in paca->irq_happened */ +#define SOFTEN_VALUE_0x500 PACA_IRQ_EE +#define SOFTEN_VALUE_0x502 PACA_IRQ_EE +#define SOFTEN_VALUE_0x900 PACA_IRQ_DEC +#define SOFTEN_VALUE_0x982 PACA_IRQ_DEC + +#define __SOFTEN_TEST(h, vec) \ lbz r10,PACASOFTIRQEN(r13); \ cmpwi r10,0; \ + li r10,SOFTEN_VALUE_##vec; \ beq masked_##h##interrupt -#define _SOFTEN_TEST(h) __SOFTEN_TEST(h) +#define _SOFTEN_TEST(h, vec) __SOFTEN_TEST(h, vec) #define SOFTEN_TEST_PR(vec) \ KVMTEST_PR(vec); \ - _SOFTEN_TEST(EXC_STD) + _SOFTEN_TEST(EXC_STD, vec) #define SOFTEN_TEST_HV(vec) \ KVMTEST(vec); \ - _SOFTEN_TEST(EXC_HV) + _SOFTEN_TEST(EXC_HV, vec) #define SOFTEN_TEST_HV_201(vec) \ KVMTEST(vec); \ - _SOFTEN_TEST(EXC_STD) + _SOFTEN_TEST(EXC_STD, vec) #define __MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) \ HMT_MEDIUM; \ @@ -279,22 +286,7 @@ label##_hv: \ */ /* Exception addition: Hard disable interrupts */ -#ifdef CONFIG_TRACE_IRQFLAGS -#define DISABLE_INTS \ - lbz r10,PACASOFTIRQEN(r13); \ - li r11,0; \ - cmpwi cr0,r10,0; \ - stb r11,PACAHARDIRQEN(r13); \ - beq 44f; \ - stb r11,PACASOFTIRQEN(r13); \ - TRACE_DISABLE_INTS; \ -44: -#else -#define DISABLE_INTS \ - li r11,0; \ - stb r11,PACASOFTIRQEN(r13); \ - stb r11,PACAHARDIRQEN(r13) -#endif /* CONFIG_TRACE_IRQFLAGS */ +#define DISABLE_INTS SOFT_DISABLE_INTS(r10,r11) /* Exception addition: Keep interrupt state */ #define ENABLE_INTS \ diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index 6c6fa95..51010bf 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h @@ -11,6 +11,27 @@ #include #include +#ifdef CONFIG_PPC64 + +/* + * PACA flags in paca->irq_happened. + * + * This bits are set when interrupts occur while soft-disabled + * and allow a proper replay. Additionally, PACA_IRQ_HARD_DIS + * is set whenever we manually hard disable. + */ +#define PACA_IRQ_HARD_DIS 0x01 +#define PACA_IRQ_DBELL 0x02 +#define PACA_IRQ_EE 0x04 +#define PACA_IRQ_DEC 0x08 /* Or FIT */ +#define PACA_IRQ_EE_EDGE 0x10 /* BookE only */ + +#endif /* CONFIG_PPC64 */ + +#ifndef __ASSEMBLY__ + +extern void __replay_interrupt(unsigned int vector); + extern void timer_interrupt(struct pt_regs *); #ifdef CONFIG_PPC64 @@ -42,7 +63,6 @@ static inline unsigned long arch_local_irq_disable(void) } extern void arch_local_irq_restore(unsigned long); -extern void iseries_handle_interrupts(void); static inline void arch_local_irq_enable(void) { @@ -72,12 +92,24 @@ static inline bool arch_irqs_disabled(void) #define __hard_irq_disable() __mtmsrd(local_paca->kernel_msr, 1) #endif -#define hard_irq_disable() \ - do { \ - __hard_irq_disable(); \ - get_paca()->soft_enabled = 0; \ - get_paca()->hard_enabled = 0; \ - } while(0) +static inline void hard_irq_disable(void) +{ + __hard_irq_disable(); + get_paca()->soft_enabled = 0; + get_paca()->irq_happened |= PACA_IRQ_HARD_DIS; +} + +/* + * This is called by asynchronous interrupts to conditionally + * re-enable hard interrupts when soft-disabled after having + * cleared the source of the interrupt + */ +static inline void may_hard_irq_enable(void) +{ + get_paca()->irq_happened &= ~PACA_IRQ_HARD_DIS; + if (!(get_paca()->irq_happened & PACA_IRQ_EE)) + __hard_irq_enable(); +} static inline bool arch_irq_disabled_regs(struct pt_regs *regs) { @@ -149,6 +181,8 @@ static inline bool arch_irq_disabled_regs(struct pt_regs *regs) return !(regs->msr & MSR_EE); } +static inline void may_hard_irq_enable(void) { } + #endif /* CONFIG_PPC64 */ #define ARCH_IRQ_INIT_FLAGS IRQ_NOREQUEST @@ -159,5 +193,6 @@ static inline bool arch_irq_disabled_regs(struct pt_regs *regs) */ struct irq_chip; +#endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_HW_IRQ_H */ diff --git a/arch/powerpc/include/asm/irqflags.h b/arch/powerpc/include/asm/irqflags.h index b0b06d8..6f9b6e2 100644 --- a/arch/powerpc/include/asm/irqflags.h +++ b/arch/powerpc/include/asm/irqflags.h @@ -39,24 +39,31 @@ #define TRACE_ENABLE_INTS TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_on) #define TRACE_DISABLE_INTS TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_off) -#define TRACE_AND_RESTORE_IRQ_PARTIAL(en,skip) \ - cmpdi en,0; \ - bne 95f; \ - stb en,PACASOFTIRQEN(r13); \ - TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_off) \ - b skip; \ -95: TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_on) \ - li en,1; -#define TRACE_AND_RESTORE_IRQ(en) \ - TRACE_AND_RESTORE_IRQ_PARTIAL(en,96f); \ - stb en,PACASOFTIRQEN(r13); \ -96: +/* + * This is used by assembly code to soft-disable interrupts + */ +#define SOFT_DISABLE_INTS(__rA, __rB) \ + lbz __rA,PACASOFTIRQEN(r13); \ + lbz __rB,PACAIRQHAPPENED(r13); \ + cmpwi cr0,__rA,0; \ + li __rA,0; \ + ori __rB,__rB,PACA_IRQ_HARD_DIS; \ + stb __rB,PACAIRQHAPPENED(r13); \ + beq 44f; \ + stb __rA,PACASOFTIRQEN(r13); \ + TRACE_DISABLE_INTS; \ +44: + #else #define TRACE_ENABLE_INTS #define TRACE_DISABLE_INTS -#define TRACE_AND_RESTORE_IRQ_PARTIAL(en,skip) -#define TRACE_AND_RESTORE_IRQ(en) \ - stb en,PACASOFTIRQEN(r13) + +#define SOFT_DISABLE_INTS(__rA, __rB) \ + lbz __rA,PACAIRQHAPPENED(r13); \ + li __rB,0; \ + ori __rA,__rA,PACA_IRQ_HARD_DIS; \ + stb __rB,PACASOFTIRQEN(r13); \ + stb __rA,PACAIRQHAPPENED(r13) #endif #endif diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index 269c05a..daf813f 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -132,7 +132,7 @@ struct paca_struct { u64 saved_msr; /* MSR saved here by enter_rtas */ u16 trap_save; /* Used when bad stack is encountered */ u8 soft_enabled; /* irq soft-enable flag */ - u8 hard_enabled; /* set if irqs are enabled in MSR */ + u8 irq_happened; /* irq happened while soft-disabled */ u8 io_sync; /* writel() needs spin_unlock sync */ u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */ u8 nap_state_lost; /* NV GPR values lost in power7_idle */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 04caee7..cdd0d26 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -147,7 +147,7 @@ int main(void) DEFINE(PACAKBASE, offsetof(struct paca_struct, kernelbase)); DEFINE(PACAKMSR, offsetof(struct paca_struct, kernel_msr)); DEFINE(PACASOFTIRQEN, offsetof(struct paca_struct, soft_enabled)); - DEFINE(PACAHARDIRQEN, offsetof(struct paca_struct, hard_enabled)); + DEFINE(PACAIRQHAPPENED, offsetof(struct paca_struct, irq_happened)); DEFINE(PACACONTEXTID, offsetof(struct paca_struct, context.id)); #ifdef CONFIG_PPC_MM_SLICES DEFINE(PACALOWSLICESPSIZE, offsetof(struct paca_struct, diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c index 2cc451a..5b25c80 100644 --- a/arch/powerpc/kernel/dbell.c +++ b/arch/powerpc/kernel/dbell.c @@ -37,6 +37,8 @@ void doorbell_exception(struct pt_regs *regs) irq_enter(); + may_hard_irq_enable(); + smp_ipi_demux(); irq_exit(); diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index c513beb..f8a7a1a 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -32,6 +32,7 @@ #include #include #include +#include /* * System calls. @@ -583,18 +584,72 @@ _GLOBAL(ret_from_except_lite) bne do_work #endif /* !CONFIG_PREEMPT */ + .globl fast_exc_return_irq +fast_exc_return_irq: restore: + /* + * This is the main kernel exit path, we first check if we + * have to change our interrupt state. + */ ld r5,SOFTE(r1) - TRACE_AND_RESTORE_IRQ(r5); + lbz r6,PACASOFTIRQEN(r13) + cmpwi cr1,r5,0 + cmpw cr0,r5,r6 + beq cr0,4f + + /* We do, handle disable first, which is easy */ + bne cr1,3f; + li r0,0 + stb r0,PACASOFTIRQEN(r13); + TRACE_DISABLE_INTS + b 4f - /* extract EE bit and use it to restore paca->hard_enabled */ - ld r3,_MSR(r1) - rldicl r4,r3,49,63 /* r0 = (r3 >> 15) & 1 */ - stb r4,PACAHARDIRQEN(r13) +3: /* + * We are about to soft-enable interrupts (we are hard disabled + * at this point). We check if there's anything that needs to + * be replayed first. + */ + lbz r0,PACAIRQHAPPENED(r13) + cmpwi cr0,r0,0 + bne- restore_check_irq_replay + + /* + * Get here when nothing happened while soft-disabled, just + * soft-enable and move-on. We will hard-enable as a side + * effect of rfi + */ +restore_no_replay: + TRACE_ENABLE_INTS + li r0,1 + stb r0,PACASOFTIRQEN(r13); + /* + * Final return path. BookE is handled in a different file + */ +4: #ifdef CONFIG_PPC_BOOK3E b .exception_return_book3e #else + /* + * Clear the reservation. If we know the CPU tracks the address of + * the reservation then we can potentially save some cycles and use + * a larx. On POWER6 and POWER7 this is significantly faster. + */ +BEGIN_FTR_SECTION + stdcx. r0,0,r1 /* to clear the reservation */ +FTR_SECTION_ELSE + ldarx r4,0,r1 +ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) + + /* + * Some code path such as load_up_fpu or altivec return directly + * here. They run entirely hard disabled and do not alter the + * interrupt state. They also don't use lwarx/stwcx. and thus + * are known not to leave dangling reservations. + */ + .globl fast_exception_return +fast_exception_return: + ld r3,_MSR(r1) ld r4,_CTR(r1) ld r0,_LINK(r1) mtctr r4 @@ -608,17 +663,6 @@ restore: beq- unrecov_restore /* - * Clear the reservation. If we know the CPU tracks the address of - * the reservation then we can potentially save some cycles and use - * a larx. On POWER6 and POWER7 this is significantly faster. - */ -BEGIN_FTR_SECTION - stdcx. r0,0,r1 /* to clear the reservation */ -FTR_SECTION_ELSE - ldarx r4,0,r1 -ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) - - /* * Clear RI before restoring r13. If we are returning to * userspace and we take an exception after restoring r13, * we end up corrupting the userspace r13 value. @@ -629,7 +673,8 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) /* * r13 is our per cpu area, only restore it if we are returning to - * userspace + * userspace the value stored in the stack frame may belong to + * another CPU. */ andi. r0,r3,MSR_PR beq 1f @@ -654,6 +699,55 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) #endif /* CONFIG_PPC_BOOK3E */ + /* + * Something did happen, check if a re-emit is needed + * (this also clears paca->irq_happened) + */ +restore_check_irq_replay: + /* XXX: We could implement a fast path here where we check + * for irq_happened being just 0x01, in which case we can + * clear it and return. That means that we would potentially + * miss a decrementer having wrapped all the way around. + * + * Still, this might be useful for things like hash_page + */ + bl .__check_irq_replay + cmpwi cr0,r3,0 + beq restore_no_replay + + /* + * We need to re-emit an interrupt. We do so by re-using our + * existing exception frame. We first change the trap value, + * but we need to ensure we preserve the low nibble of it + */ + ld r4,_TRAP(r1) + clrldi r4,r4,60 + or r4,r4,r3 + std r4,_TRAP(r1) + + /* + * Then find the right handler and call it. Interrupts are + * still soft-disabled and we keep them that way. + */ + cmpwi cr0,r3,0x500 + bne 1f + addi r3,r1,STACK_FRAME_OVERHEAD; + bl .do_IRQ + b .ret_from_except +1: cmpwi cr0,r3,0x900 + bne 1f + addi r3,r1,STACK_FRAME_OVERHEAD; + bl .timer_interrupt + b .ret_from_except +#ifdef CONFIG_PPC_BOOK3E +1: cmpwi cr0,r3,0x280 + bne 1f + addi r3,r1,STACK_FRAME_OVERHEAD; + bl .doorbell_exception + b .ret_from_except +#endif /* CONFIG_PPC_BOOK3E */ +1: b .ret_from_except /* What else to do here ? */ + do_work: #ifdef CONFIG_PREEMPT andi. r0,r3,MSR_PR /* Returning to user mode? */ @@ -666,18 +760,11 @@ do_work: crandc eq,cr1*4+eq,eq bne restore - /* Here we are preempting the current task. - * - * Ensure interrupts are soft-disabled. We also properly mark - * the PACA to reflect the fact that they are hard-disabled - * and trace the change + /* + * Here we are preempting the current task. We want to make + * sure we are soft-disabled first */ - li r0,0 - stb r0,PACASOFTIRQEN(r13) - stb r0,PACAHARDIRQEN(r13) - TRACE_DISABLE_INTS - - /* Call the scheduler with soft IRQs off */ + SOFT_DISABLE_INTS(r3,r4) 1: bl .preempt_schedule_irq /* Hard-disable interrupts again (and update PACA) */ @@ -687,8 +774,8 @@ do_work: ld r10,PACAKMSR(r13) /* Get kernel MSR without EE */ mtmsrd r10,1 #endif /* CONFIG_PPC_BOOK3E */ - li r0,0 - stb r0,PACAHARDIRQEN(r13) + li r0,PACA_IRQ_HARD_DIS + stb r0,PACAIRQHAPPENED(r13) /* Re-test flags and eventually loop */ clrrdi r9,r1,THREAD_SHIFT @@ -710,14 +797,12 @@ user_work: andi. r0,r4,_TIF_NEED_RESCHED beq 1f - li r5,1 - TRACE_AND_RESTORE_IRQ(r5); + bl .restore_interrupts bl .schedule b .ret_from_except_lite 1: bl .save_nvgprs - li r5,1 - TRACE_AND_RESTORE_IRQ(r5); + bl .restore_interrupts addi r3,r1,STACK_FRAME_OVERHEAD bl .do_notify_resume b .ret_from_except diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index c4c3466..7215cc2 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -24,6 +24,7 @@ #include #include #include +#include /* XXX This will ultimately add space for a special exception save * structure used to save things like SRR0/SRR1, SPRGs, MAS, etc... @@ -77,59 +78,55 @@ #define SPRN_MC_SRR1 SPRN_MCSRR1 #define NORMAL_EXCEPTION_PROLOG(n, addition) \ - EXCEPTION_PROLOG(n, GEN, addition##_GEN) + EXCEPTION_PROLOG(n, GEN, addition##_GEN(n)) #define CRIT_EXCEPTION_PROLOG(n, addition) \ - EXCEPTION_PROLOG(n, CRIT, addition##_CRIT) + EXCEPTION_PROLOG(n, CRIT, addition##_CRIT(n)) #define DBG_EXCEPTION_PROLOG(n, addition) \ - EXCEPTION_PROLOG(n, DBG, addition##_DBG) + EXCEPTION_PROLOG(n, DBG, addition##_DBG(n)) #define MC_EXCEPTION_PROLOG(n, addition) \ - EXCEPTION_PROLOG(n, MC, addition##_MC) + EXCEPTION_PROLOG(n, MC, addition##_MC(n)) /* Variants of the "addition" argument for the prolog */ -#define PROLOG_ADDITION_NONE_GEN -#define PROLOG_ADDITION_NONE_CRIT -#define PROLOG_ADDITION_NONE_DBG -#define PROLOG_ADDITION_NONE_MC +#define PROLOG_ADDITION_NONE_GEN(n) +#define PROLOG_ADDITION_NONE_CRIT(n) +#define PROLOG_ADDITION_NONE_DBG(n) +#define PROLOG_ADDITION_NONE_MC(n) -#define PROLOG_ADDITION_MASKABLE_GEN \ +#define PROLOG_ADDITION_MASKABLE_GEN(n) \ lbz r11,PACASOFTIRQEN(r13); /* are irqs soft-disabled ? */ \ cmpwi cr0,r11,0; /* yes -> go out of line */ \ - beq masked_interrupt_book3e; + beq masked_interrupt_book3e_##n -#define PROLOG_ADDITION_2REGS_GEN \ +#define PROLOG_ADDITION_2REGS_GEN(n) \ std r14,PACA_EXGEN+EX_R14(r13); \ std r15,PACA_EXGEN+EX_R15(r13) -#define PROLOG_ADDITION_1REG_GEN \ +#define PROLOG_ADDITION_1REG_GEN(n) \ std r14,PACA_EXGEN+EX_R14(r13); -#define PROLOG_ADDITION_2REGS_CRIT \ +#define PROLOG_ADDITION_2REGS_CRIT(n) \ std r14,PACA_EXCRIT+EX_R14(r13); \ std r15,PACA_EXCRIT+EX_R15(r13) -#define PROLOG_ADDITION_2REGS_DBG \ +#define PROLOG_ADDITION_2REGS_DBG(n) \ std r14,PACA_EXDBG+EX_R14(r13); \ std r15,PACA_EXDBG+EX_R15(r13) -#define PROLOG_ADDITION_2REGS_MC \ +#define PROLOG_ADDITION_2REGS_MC(n) \ std r14,PACA_EXMC+EX_R14(r13); \ std r15,PACA_EXMC+EX_R15(r13) -#define PROLOG_ADDITION_DOORBELL_GEN \ - lbz r11,PACASOFTIRQEN(r13); /* are irqs soft-disabled ? */ \ - cmpwi cr0,r11,0; /* yes -> go out of line */ \ - beq masked_doorbell_book3e - /* Core exception code for all exceptions except TLB misses. * XXX: Needs to make SPRN_SPRG_GEN depend on exception type */ #define EXCEPTION_COMMON(n, excf, ints) \ +exc_##n##_common: \ std r0,GPR0(r1); /* save r0 in stackframe */ \ std r2,GPR2(r1); /* save r2 in stackframe */ \ SAVE_4GPRS(3, r1); /* save r3 - r6 in stackframe */ \ @@ -167,20 +164,21 @@ std r0,RESULT(r1); /* clear regs->result */ \ ints; -/* Variants for the "ints" argument */ +/* Variants for the "ints" argument. This one does nothing when we want + * to keep interrupts in their original state + */ #define INTS_KEEP -#define INTS_DISABLE_SOFT \ - stb r0,PACASOFTIRQEN(r13); /* mark interrupts soft-disabled */ \ - TRACE_DISABLE_INTS; -#define INTS_DISABLE_HARD \ - stb r0,PACAHARDIRQEN(r13); /* and hard disabled */ -#define INTS_DISABLE_ALL \ - INTS_DISABLE_SOFT \ - INTS_DISABLE_HARD - -/* This is called by exceptions that used INTS_KEEP (that is did not clear - * neither soft nor hard IRQ indicators in the PACA. This will restore MSR:EE - * to it's previous value + +/* This second version is meant for exceptions that don't immediately + * hard-enable. We set a bit in paca->irq_happened to ensure that + * a subsequent call to arch_local_irq_restore() will properly + * hard-enable and avoid the fast-path + */ +#define INTS_DISABLE SOFT_DISABLE_INTS(r3,r4) + +/* This is called by exceptions that used INTS_KEEP (that did not touch + * irq indicators in the PACA). This will restore MSR:EE to it's previous + * value * * XXX In the long run, we may want to open-code it in order to separate the * load from the wrtee, thus limiting the latency caused by the dependency @@ -238,7 +236,7 @@ exc_##n##_bad_stack: \ #define MASKABLE_EXCEPTION(trapnum, label, hdlr, ack) \ START_EXCEPTION(label); \ NORMAL_EXCEPTION_PROLOG(trapnum, PROLOG_ADDITION_MASKABLE) \ - EXCEPTION_COMMON(trapnum, PACA_EXGEN, INTS_DISABLE_ALL) \ + EXCEPTION_COMMON(trapnum, PACA_EXGEN, INTS_DISABLE) \ ack(r8); \ CHECK_NAPPING(); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ @@ -289,7 +287,7 @@ interrupt_end_book3e: /* Critical Input Interrupt */ START_EXCEPTION(critical_input); CRIT_EXCEPTION_PROLOG(0x100, PROLOG_ADDITION_NONE) -// EXCEPTION_COMMON(0x100, PACA_EXCRIT, INTS_DISABLE_ALL) +// EXCEPTION_COMMON(0x100, PACA_EXCRIT, INTS_DISABLE) // bl special_reg_save_crit // CHECK_NAPPING(); // addi r3,r1,STACK_FRAME_OVERHEAD @@ -300,7 +298,7 @@ interrupt_end_book3e: /* Machine Check Interrupt */ START_EXCEPTION(machine_check); CRIT_EXCEPTION_PROLOG(0x200, PROLOG_ADDITION_NONE) -// EXCEPTION_COMMON(0x200, PACA_EXMC, INTS_DISABLE_ALL) +// EXCEPTION_COMMON(0x200, PACA_EXMC, INTS_DISABLE) // bl special_reg_save_mc // addi r3,r1,STACK_FRAME_OVERHEAD // CHECK_NAPPING(); @@ -313,7 +311,7 @@ interrupt_end_book3e: NORMAL_EXCEPTION_PROLOG(0x300, PROLOG_ADDITION_2REGS) mfspr r14,SPRN_DEAR mfspr r15,SPRN_ESR - EXCEPTION_COMMON(0x300, PACA_EXGEN, INTS_DISABLE_ALL) + EXCEPTION_COMMON(0x300, PACA_EXGEN, INTS_DISABLE) b storage_fault_common /* Instruction Storage Interrupt */ @@ -321,7 +319,7 @@ interrupt_end_book3e: NORMAL_EXCEPTION_PROLOG(0x400, PROLOG_ADDITION_2REGS) li r15,0 mr r14,r10 - EXCEPTION_COMMON(0x400, PACA_EXGEN, INTS_DISABLE_ALL) + EXCEPTION_COMMON(0x400, PACA_EXGEN, INTS_DISABLE) b storage_fault_common /* External Input Interrupt */ @@ -339,12 +337,11 @@ interrupt_end_book3e: START_EXCEPTION(program); NORMAL_EXCEPTION_PROLOG(0x700, PROLOG_ADDITION_1REG) mfspr r14,SPRN_ESR - EXCEPTION_COMMON(0x700, PACA_EXGEN, INTS_DISABLE_SOFT) + EXCEPTION_COMMON(0x700, PACA_EXGEN, INTS_DISABLE) std r14,_DSISR(r1) addi r3,r1,STACK_FRAME_OVERHEAD ld r14,PACA_EXGEN+EX_R14(r13) bl .save_nvgprs - INTS_RESTORE_HARD bl .program_check_exception b .ret_from_except @@ -358,7 +355,7 @@ interrupt_end_book3e: beq- 1f bl .load_up_fpu b fast_exception_return -1: INTS_DISABLE_ALL +1: INTS_DISABLE bl .save_nvgprs addi r3,r1,STACK_FRAME_OVERHEAD bl .kernel_fp_unavailable_exception @@ -373,7 +370,7 @@ interrupt_end_book3e: /* Watchdog Timer Interrupt */ START_EXCEPTION(watchdog); CRIT_EXCEPTION_PROLOG(0x9f0, PROLOG_ADDITION_NONE) -// EXCEPTION_COMMON(0x9f0, PACA_EXCRIT, INTS_DISABLE_ALL) +// EXCEPTION_COMMON(0x9f0, PACA_EXCRIT, INTS_DISABLE) // bl special_reg_save_crit // CHECK_NAPPING(); // addi r3,r1,STACK_FRAME_OVERHEAD @@ -392,7 +389,7 @@ interrupt_end_book3e: /* Auxiliary Processor Unavailable Interrupt */ START_EXCEPTION(ap_unavailable); NORMAL_EXCEPTION_PROLOG(0xf20, PROLOG_ADDITION_NONE) - EXCEPTION_COMMON(0xf20, PACA_EXGEN, INTS_DISABLE_ALL) + EXCEPTION_COMMON(0xf20, PACA_EXGEN, INTS_DISABLE) bl .save_nvgprs addi r3,r1,STACK_FRAME_OVERHEAD bl .unknown_exception @@ -450,7 +447,7 @@ interrupt_end_book3e: mfspr r15,SPRN_SPRG_CRIT_SCRATCH mtspr SPRN_SPRG_GEN_SCRATCH,r15 mfspr r14,SPRN_DBSR - EXCEPTION_COMMON(0xd00, PACA_EXCRIT, INTS_DISABLE_ALL) + EXCEPTION_COMMON(0xd00, PACA_EXCRIT, INTS_DISABLE) std r14,_DSISR(r1) addi r3,r1,STACK_FRAME_OVERHEAD mr r4,r14 @@ -465,7 +462,7 @@ kernel_dbg_exc: /* Debug exception as a debug interrupt*/ START_EXCEPTION(debug_debug); - DBG_EXCEPTION_PROLOG(0xd00, PROLOG_ADDITION_2REGS) + DBG_EXCEPTION_PROLOG(0xd08, PROLOG_ADDITION_2REGS) /* * If there is a single step or branch-taken exception in an @@ -515,7 +512,7 @@ kernel_dbg_exc: mfspr r15,SPRN_SPRG_DBG_SCRATCH mtspr SPRN_SPRG_GEN_SCRATCH,r15 mfspr r14,SPRN_DBSR - EXCEPTION_COMMON(0xd00, PACA_EXDBG, INTS_DISABLE_ALL) + EXCEPTION_COMMON(0xd08, PACA_EXDBG, INTS_DISABLE) std r14,_DSISR(r1) addi r3,r1,STACK_FRAME_OVERHEAD mr r4,r14 @@ -525,21 +522,20 @@ kernel_dbg_exc: bl .DebugException b .ret_from_except - MASKABLE_EXCEPTION(0x260, perfmon, .performance_monitor_exception, ACK_NONE) - -/* Doorbell interrupt */ - START_EXCEPTION(doorbell) - NORMAL_EXCEPTION_PROLOG(0x2070, PROLOG_ADDITION_DOORBELL) - EXCEPTION_COMMON(0x2070, PACA_EXGEN, INTS_DISABLE_ALL) - CHECK_NAPPING() + START_EXCEPTION(perfmon); + NORMAL_EXCEPTION_PROLOG(0x260, PROLOG_ADDITION_NONE) + EXCEPTION_COMMON(0x260, PACA_EXGEN, INTS_DISABLE) addi r3,r1,STACK_FRAME_OVERHEAD - bl .doorbell_exception + bl .performance_monitor_exception b .ret_from_except_lite +/* Doorbell interrupt */ + MASKABLE_EXCEPTION(0x280, doorbell, .doorbell_exception, ACK_NONE) + /* Doorbell critical Interrupt */ START_EXCEPTION(doorbell_crit); - CRIT_EXCEPTION_PROLOG(0x2080, PROLOG_ADDITION_NONE) -// EXCEPTION_COMMON(0x2080, PACA_EXCRIT, INTS_DISABLE_ALL) + CRIT_EXCEPTION_PROLOG(0x2a0, PROLOG_ADDITION_NONE) +// EXCEPTION_COMMON(0x2a0, PACA_EXCRIT, INTS_DISABLE) // bl special_reg_save_crit // CHECK_NAPPING(); // addi r3,r1,STACK_FRAME_OVERHEAD @@ -547,36 +543,114 @@ kernel_dbg_exc: // b ret_from_crit_except b . +/* Guest Doorbell */ MASKABLE_EXCEPTION(0x2c0, guest_doorbell, .unknown_exception, ACK_NONE) - MASKABLE_EXCEPTION(0x2e0, guest_doorbell_crit, .unknown_exception, ACK_NONE) - MASKABLE_EXCEPTION(0x310, hypercall, .unknown_exception, ACK_NONE) - MASKABLE_EXCEPTION(0x320, ehpriv, .unknown_exception, ACK_NONE) +/* Guest Doorbell critical Interrupt */ + START_EXCEPTION(guest_doorbell_crit); + CRIT_EXCEPTION_PROLOG(0x2e0, PROLOG_ADDITION_NONE) +// EXCEPTION_COMMON(0x2e0, PACA_EXCRIT, INTS_DISABLE) +// bl special_reg_save_crit +// CHECK_NAPPING(); +// addi r3,r1,STACK_FRAME_OVERHEAD +// bl .guest_doorbell_critical_exception +// b ret_from_crit_except + b . + +/* Hypervisor call */ + START_EXCEPTION(hypercall); + NORMAL_EXCEPTION_PROLOG(0x310, PROLOG_ADDITION_NONE) + EXCEPTION_COMMON(0x310, PACA_EXGEN, INTS_KEEP) + addi r3,r1,STACK_FRAME_OVERHEAD + bl .save_nvgprs + INTS_RESTORE_HARD + bl .unknown_exception + b .ret_from_except + +/* Embedded Hypervisor priviledged */ + START_EXCEPTION(ehpriv); + NORMAL_EXCEPTION_PROLOG(0x320, PROLOG_ADDITION_NONE) + EXCEPTION_COMMON(0x320, PACA_EXGEN, INTS_KEEP) + addi r3,r1,STACK_FRAME_OVERHEAD + bl .save_nvgprs + INTS_RESTORE_HARD + bl .unknown_exception + b .ret_from_except /* - * An interrupt came in while soft-disabled; clear EE in SRR1, - * clear paca->hard_enabled and return. + * An interrupt came in while soft-disabled; We mark paca->irq_happened + * accordingly and if the interrupt is level sensitive, we hard disable */ -masked_doorbell_book3e: - mtcr r10 - /* Resend the doorbell to fire again when ints enabled */ - mfspr r10,SPRN_PIR - PPC_MSGSND(r10) - b masked_interrupt_book3e_common -masked_interrupt_book3e: +masked_interrupt_book3e_0x500: + /* XXX When adding support for EPR, use PACA_IRQ_EE_EDGE */ + li r11,PACA_IRQ_EE + b masked_interrupt_book3e_full_mask + +masked_interrupt_book3e_0x900: + ACK_DEC(r11); + li r11,PACA_IRQ_DEC + b masked_interrupt_book3e_no_mask +masked_interrupt_book3e_0x980: + ACK_FIT(r11); + li r11,PACA_IRQ_DEC + b masked_interrupt_book3e_no_mask +masked_interrupt_book3e_0x280: +masked_interrupt_book3e_0x2c0: + li r11,PACA_IRQ_DBELL + b masked_interrupt_book3e_no_mask + +masked_interrupt_book3e_no_mask: mtcr r10 -masked_interrupt_book3e_common: - stb r11,PACAHARDIRQEN(r13) + lbz r10,PACAIRQHAPPENED(r13) + or r10,r10,r11 + stb r10,PACAIRQHAPPENED(r13) + b 1f +masked_interrupt_book3e_full_mask: + mtcr r10 + lbz r10,PACAIRQHAPPENED(r13) + or r10,r10,r11 + stb r10,PACAIRQHAPPENED(r13) mfspr r10,SPRN_SRR1 rldicl r11,r10,48,1 /* clear MSR_EE */ rotldi r10,r11,16 mtspr SPRN_SRR1,r10 - ld r10,PACA_EXGEN+EX_R10(r13); /* restore registers */ +1: ld r10,PACA_EXGEN+EX_R10(r13); ld r11,PACA_EXGEN+EX_R11(r13); mfspr r13,SPRN_SPRG_GEN_SCRATCH; rfi b . +/* + * Called from arch_local_irq_enable when an interrupt needs + * to be resent. r3 contains either 0x500,0x900,0x260 or 0x280 + * to indicate the kind of interrupt. MSR:EE is already off. + * We generate a stackframe like if a real interrupt had happened. + * + * Note: While MSR:EE is off, we need to make sure that _MSR + * in the generated frame has EE set to 1 or the exception + * handler will not properly re-enable them. + */ +_GLOBAL(__replay_interrupt) + /* We are going to jump to the exception common code which + * will retrieve various register values from the PACA which + * we don't give a damn about. + */ + mflr r10 + mfmsr r11 + mfcr r4 + mtspr SPRN_SPRG_GEN_SCRATCH,r13; + std r1,PACA_EXGEN+EX_R1(r13); + stw r4,PACA_EXGEN+EX_CR(r13); + ori r11,r11,MSR_EE + subi r1,r1,INT_FRAME_SIZE; + cmpwi cr0,r3,0x500 + beq exc_0x500_common + cmpwi cr0,r3,0x900 + beq exc_0x900_common + cmpwi cr0,r3,0x280 + beq exc_0x280_common + blr + /* * This is called from 0x300 and 0x400 handlers after the prologs with @@ -679,6 +753,8 @@ BAD_STACK_TRAMPOLINE(0x000) BAD_STACK_TRAMPOLINE(0x100) BAD_STACK_TRAMPOLINE(0x200) BAD_STACK_TRAMPOLINE(0x260) +BAD_STACK_TRAMPOLINE(0x280) +BAD_STACK_TRAMPOLINE(0x2a0) BAD_STACK_TRAMPOLINE(0x2c0) BAD_STACK_TRAMPOLINE(0x2e0) BAD_STACK_TRAMPOLINE(0x300) @@ -696,11 +772,10 @@ BAD_STACK_TRAMPOLINE(0xa00) BAD_STACK_TRAMPOLINE(0xb00) BAD_STACK_TRAMPOLINE(0xc00) BAD_STACK_TRAMPOLINE(0xd00) +BAD_STACK_TRAMPOLINE(0xd08) BAD_STACK_TRAMPOLINE(0xe00) BAD_STACK_TRAMPOLINE(0xf00) BAD_STACK_TRAMPOLINE(0xf20) -BAD_STACK_TRAMPOLINE(0x2070) -BAD_STACK_TRAMPOLINE(0x2080) .globl bad_stack_book3e bad_stack_book3e: diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 02448ea..2d0868a 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -12,6 +12,7 @@ * */ +#include #include #include @@ -356,34 +357,60 @@ do_stab_bolted_pSeries: KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf40) /* - * An interrupt came in while soft-disabled; clear EE in SRR1, - * clear paca->hard_enabled and return. + * An interrupt came in while soft-disabled. We set paca->irq_happened, + * then, if it was a decrementer interrupt, we bump the dec to max and + * and return, else we hard disable and return. This is called with + * r10 containing the value to OR to the paca field. */ -masked_interrupt: - stb r10,PACAHARDIRQEN(r13) - mtcrf 0x80,r9 - ld r9,PACA_EXGEN+EX_R9(r13) - mfspr r10,SPRN_SRR1 - rldicl r10,r10,48,1 /* clear MSR_EE */ - rotldi r10,r10,16 - mtspr SPRN_SRR1,r10 - ld r10,PACA_EXGEN+EX_R10(r13) - GET_SCRATCH0(r13) - rfid +#define MASKED_INTERRUPT(_H) \ +masked_##_H##interrupt: \ + std r11,PACA_EXGEN+EX_R11(r13); \ + lbz r11,PACAIRQHAPPENED(r13); \ + or r11,r11,r10; \ + stb r11,PACAIRQHAPPENED(r13); \ + andi. r10,r10,PACA_IRQ_DEC; \ + beq 1f; \ + lis r10,0x7fff; \ + ori r10,r10,0xffff; \ + mtspr SPRN_DEC,r10; \ + b 2f; \ +1: mfspr r10,SPRN_##_H##SRR1; \ + rldicl r10,r10,48,1; /* clear MSR_EE */ \ + rotldi r10,r10,16; \ + mtspr SPRN_##_H##SRR1,r10; \ +2: mtcrf 0x80,r9; \ + ld r9,PACA_EXGEN+EX_R9(r13); \ + ld r10,PACA_EXGEN+EX_R10(r13); \ + ld r11,PACA_EXGEN+EX_R11(r13); \ + GET_SCRATCH0(r13); \ + ##_H##rfid; \ b . + + MASKED_INTERRUPT() + MASKED_INTERRUPT(H) -masked_Hinterrupt: - stb r10,PACAHARDIRQEN(r13) - mtcrf 0x80,r9 - ld r9,PACA_EXGEN+EX_R9(r13) - mfspr r10,SPRN_HSRR1 - rldicl r10,r10,48,1 /* clear MSR_EE */ - rotldi r10,r10,16 - mtspr SPRN_HSRR1,r10 - ld r10,PACA_EXGEN+EX_R10(r13) - GET_SCRATCH0(r13) - hrfid - b . +/* + * Called from arch_local_irq_enable when an interrupt needs + * to be resent. r3 contains 0x500 or 0x900 to indicate which + * kind of interrupt. MSR:EE is already off. We generate a + * stackframe like if a real interrupt had happened. + * + * Note: While MSR:EE is off, we need to make sure that _MSR + * in the generated frame has EE set to 1 or the exception + * handler will not properly re-enable them. + */ +_GLOBAL(__replay_interrupt) + /* We are going to jump to the exception common code which + * will retrieve various register values from the PACA which + * we don't give a damn about, so we don't bother storing them. + */ + mfmsr r12 + mflr r11 + mfcr r9 + ori r12,r12,MSR_EE + andi. r3,r3,0x0800 + bne decrementer_common + b hardware_interrupt_common #ifdef CONFIG_PPC_PSERIES /* @@ -793,7 +820,8 @@ vsx_unavailable_common: EXCEPTION_PROLOG_COMMON(0xf40, PACA_EXGEN) #ifdef CONFIG_VSX BEGIN_FTR_SECTION - bne .load_up_vsx + beq 1f + b .load_up_vsx 1: END_FTR_SECTION_IFSET(CPU_FTR_VSX) #endif @@ -808,65 +836,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) __end_handlers: /* - * Return from an exception with minimal checks. - * The caller is assumed to have done EXCEPTION_PROLOG_COMMON. - * If interrupts have been enabled, or anything has been - * done that might have changed the scheduling status of - * any task or sent any task a signal, you should use - * ret_from_except or ret_from_except_lite instead of this. - */ -fast_exc_return_irq: /* restores irq state too */ - ld r3,SOFTE(r1) - TRACE_AND_RESTORE_IRQ(r3); - ld r12,_MSR(r1) - rldicl r4,r12,49,63 /* get MSR_EE to LSB */ - stb r4,PACAHARDIRQEN(r13) /* restore paca->hard_enabled */ - b 1f - - .globl fast_exception_return -fast_exception_return: - ld r12,_MSR(r1) -1: ld r11,_NIP(r1) - andi. r3,r12,MSR_RI /* check if RI is set */ - beq- unrecov_fer - -#ifdef CONFIG_VIRT_CPU_ACCOUNTING - andi. r3,r12,MSR_PR - beq 2f - ACCOUNT_CPU_USER_EXIT(r3, r4) -2: -#endif - - ld r3,_CCR(r1) - ld r4,_LINK(r1) - ld r5,_CTR(r1) - ld r6,_XER(r1) - mtcr r3 - mtlr r4 - mtctr r5 - mtxer r6 - REST_GPR(0, r1) - REST_8GPRS(2, r1) - - ld r10,PACAKMSR(r13) - clrrdi r10,r10,2 /* clear RI */ - mtmsrd r10,1 - - mtspr SPRN_SRR1,r12 - mtspr SPRN_SRR0,r11 - REST_4GPRS(10, r1) - ld r1,GPR1(r1) - rfid - b . /* prevent speculative execution */ - -unrecov_fer: - bl .save_nvgprs -1: addi r3,r1,STACK_FRAME_OVERHEAD - bl .unrecoverable_exception - b 1b - - -/* * Hash table stuff */ .align 7 @@ -905,19 +874,16 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) * r4 contains the required access permissions * r5 contains the trap number * - * at return r3 = 0 for success + * at return r3 = 0 for success, 1 for page fault, negative for error */ bl .hash_page /* build HPTE if possible */ cmpdi r3,0 /* see if hash_page succeeded */ - /* - * Here we have interrupts hard-disabled, so it is sufficient - * to restore paca->{soft,hard}_enable and get out. - */ + /* Success */ beq fast_exc_return_irq /* Return from exception on success */ - /* For a hash failure, we don't bother re-enabling interrupts */ - ble- 13f + /* Error */ + blt- 13f /* Here we have a page fault that hash_page can't handle. */ handle_page_fault: diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 40759fb..58bddee 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -38,6 +38,7 @@ #include #include #include +#include /* The physical memory is laid out such that the secondary processor * spin code sits at 0x0000...0x00ff. On server, the vectors follow @@ -550,7 +551,8 @@ _GLOBAL(pmac_secondary_start) */ li r0,0 stb r0,PACASOFTIRQEN(r13) - stb r0,PACAHARDIRQEN(r13) + li r0,PACA_IRQ_HARD_DIS + stb r0,PACAIRQHAPPENED(r13) /* Create a temp kernel stack for use before relocation is on. */ ld r1,PACAEMERGSP(r13) @@ -601,9 +603,12 @@ __secondary_start: li r7,0 mtlr r7 - /* Mark interrupts both hard and soft disabled */ - stb r7,PACAHARDIRQEN(r13) + /* Mark interrupts soft and hard disabled (they might be enabled + * in the PACA when doing hotplug) + */ stb r7,PACASOFTIRQEN(r13) + li r0,PACA_IRQ_HARD_DIS + stb r0,PACAIRQHAPPENED(r13) /* enable MMU and jump to start_secondary */ LOAD_REG_ADDR(r3, .start_secondary_prolog) @@ -750,13 +755,18 @@ _INIT_GLOBAL(start_here_common) /* Load the TOC (virtual address) */ ld r2,PACATOC(r13) + /* Do more system initializations in virtual mode */ bl .setup_system - /* Load up the kernel context */ -5: li r5,0 - stb r5,PACASOFTIRQEN(r13) /* Soft Disabled */ - stb r5,PACAHARDIRQEN(r13) /* Hard Disabled on others */ + /* Mark interrupts soft and hard disabled (they might be enabled + * in the PACA when doing hotplug) + */ + li r0,0 + stb r0,PACASOFTIRQEN(r13) + li r0,PACA_IRQ_HARD_DIS + stb r0,PACAIRQHAPPENED(r13) + /* Generic kernel entry */ bl .start_kernel /* Not reached */ diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c index 0a48bf5..8f7a2b6 100644 --- a/arch/powerpc/kernel/idle.c +++ b/arch/powerpc/kernel/idle.c @@ -84,7 +84,11 @@ void cpu_idle(void) start_critical_timings(); - local_irq_enable(); + /* Some power_save functions return with + * interrupts enabled, some don't. + */ + if (irqs_disabled()) + local_irq_enable(); set_thread_flag(TIF_POLLING_NRFLAG); } else { diff --git a/arch/powerpc/kernel/idle_book3e.S b/arch/powerpc/kernel/idle_book3e.S index 16c002d..ff007b5 100644 --- a/arch/powerpc/kernel/idle_book3e.S +++ b/arch/powerpc/kernel/idle_book3e.S @@ -29,43 +29,30 @@ _GLOBAL(book3e_idle) wrteei 0 /* Now check if an interrupt came in while we were soft disabled - * since we may otherwise lose it (doorbells etc...). We know - * that since PACAHARDIRQEN will have been cleared in that case. + * since we may otherwise lose it (doorbells etc...). */ - lbz r3,PACAHARDIRQEN(r13) + lbz r3,PACAIRQHAPPENED(r13) cmpwi cr0,r3,0 - beqlr + bnelr - /* Now we are going to mark ourselves as soft and hard enables in + /* Now we are going to mark ourselves as soft and hard enabled in * order to be able to take interrupts while asleep. We inform lockdep * of that. We don't actually turn interrupts on just yet tho. */ #ifdef CONFIG_TRACE_IRQFLAGS stdu r1,-128(r1) bl .trace_hardirqs_on + addi r1,r1,128 #endif li r0,1 stb r0,PACASOFTIRQEN(r13) - stb r0,PACAHARDIRQEN(r13) /* Interrupts will make use return to LR, so get something we want * in there */ bl 1f - /* Hard disable interrupts again */ - wrteei 0 - - /* Mark them off again in the PACA as well */ - li r0,0 - stb r0,PACASOFTIRQEN(r13) - stb r0,PACAHARDIRQEN(r13) - - /* Tell lockdep about it */ -#ifdef CONFIG_TRACE_IRQFLAGS - bl .trace_hardirqs_off - addi r1,r1,128 -#endif + /* And return (interrupts are on) */ ld r0,16(r1) mtlr r0 blr diff --git a/arch/powerpc/kernel/idle_power4.S b/arch/powerpc/kernel/idle_power4.S index ba31954..d8cdba4c2 100644 --- a/arch/powerpc/kernel/idle_power4.S +++ b/arch/powerpc/kernel/idle_power4.S @@ -14,6 +14,7 @@ #include #include #include +#include #undef DEBUG @@ -29,14 +30,31 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) cmpwi 0,r4,0 beqlr - /* Go to NAP now */ + /* Hard disable interrupts */ mfmsr r7 rldicl r0,r7,48,1 rotldi r0,r0,16 - mtmsrd r0,1 /* hard-disable interrupts */ + mtmsrd r0,1 + + /* Check if something happened while soft-disabled */ + lbz r0,PACAIRQHAPPENED(r13) + cmpwi cr0,r0,0 + bnelr + + /* Soft-enable interrupts */ +#ifdef CONFIG_TRACE_IRQFLAGS + mflr r0 + std r0,16(r1) + stdu r1,-128(r1) + bl .trace_hardirqs_on + addi r1,r1,128 + ld r0,16(r1) + mtlr r0 +#endif /* CONFIG_TRACE_IRQFLAGS */ + + TRACE_ENABLE_INTS li r0,1 stb r0,PACASOFTIRQEN(r13) /* we'll hard-enable shortly */ - stb r0,PACAHARDIRQEN(r13) BEGIN_FTR_SECTION DSSALL sync diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S index fcdff19..0cdc9a3 100644 --- a/arch/powerpc/kernel/idle_power7.S +++ b/arch/powerpc/kernel/idle_power7.S @@ -1,5 +1,5 @@ /* - * This file contains the power_save function for 970-family CPUs. + * This file contains the power_save function for Power7 CPUs. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,6 +15,7 @@ #include #include #include +#include #undef DEBUG @@ -51,9 +52,25 @@ _GLOBAL(power7_idle) rldicl r9,r9,48,1 rotldi r9,r9,16 mtmsrd r9,1 /* hard-disable interrupts */ + + /* Check if something happened while soft-disabled */ + lbz r0,PACAIRQHAPPENED(r13) + cmpwi cr0,r0,0 + beq 1f + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr + +1: /* We mark irqs hard disabled as this is the state we'll + * be in when returning and we need to tell arch_local_irq_restore() + * about it + */ + li r0,PACA_IRQ_HARD_DIS + stb r0,PACAIRQHAPPENED(r13) + + /* We haven't lost state ... yet */ li r0,0 - stb r0,PACASOFTIRQEN(r13) /* we'll hard-enable shortly */ - stb r0,PACAHARDIRQEN(r13) stb r0,PACA_NAPSTATELOST(r13) /* Continue saving state */ diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 9b6e806..eb804e1 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -95,14 +95,14 @@ extern int tau_interrupts(int); int distribute_irqs = 1; -static inline notrace unsigned long get_hard_enabled(void) +static inline notrace unsigned long get_irq_happened(void) { - unsigned long enabled; + unsigned long happened; __asm__ __volatile__("lbz %0,%1(13)" - : "=r" (enabled) : "i" (offsetof(struct paca_struct, hard_enabled))); + : "=r" (happened) : "i" (offsetof(struct paca_struct, irq_happened))); - return enabled; + return happened; } static inline notrace void set_soft_enabled(unsigned long enable) @@ -111,88 +111,167 @@ static inline notrace void set_soft_enabled(unsigned long enable) : : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled))); } -static inline notrace void decrementer_check_overflow(void) +static inline notrace int decrementer_check_overflow(void) { - u64 now = get_tb_or_rtc(); - u64 *next_tb; - - preempt_disable(); - next_tb = &__get_cpu_var(decrementers_next_tb); - + u64 now = get_tb_or_rtc(); + u64 *next_tb = &__get_cpu_var(decrementers_next_tb); + if (now >= *next_tb) set_dec(1); - preempt_enable(); + return now >= *next_tb; } -notrace void arch_local_irq_restore(unsigned long en) +/* This is called whenever we are re-enabling interrupts + * and returns either 0 (nothing to do) or 500/900 if there's + * either an EE or a DEC to generate. + * + * This is called in two contexts: From arch_local_irq_restore() + * before soft-enabling interrupts, and from the exception exit + * path when returning from an interrupt from a soft-disabled to + * a soft enabled context. In both case we have interrupts hard + * disabled. + * + * We take care of only clearing the bits we handled in the + * PACA irq_happened field since we can only re-emit one at a + * time and we don't want to "lose" one. + */ +notrace unsigned int __check_irq_replay(void) { /* - * get_paca()->soft_enabled = en; - * Is it ever valid to use local_irq_restore(0) when soft_enabled is 1? - * That was allowed before, and in such a case we do need to take care - * that gcc will set soft_enabled directly via r13, not choose to use - * an intermediate register, lest we're preempted to a different cpu. + * We use local_paca rather than get_paca() to avoid all + * the debug_smp_processor_id() business in this low level + * function */ - set_soft_enabled(en); - if (!en) - return; + unsigned char happened = local_paca->irq_happened; -#ifdef CONFIG_PPC_STD_MMU_64 - if (firmware_has_feature(FW_FEATURE_ISERIES)) { - /* - * Do we need to disable preemption here? Not really: in the - * unlikely event that we're preempted to a different cpu in - * between getting r13, loading its lppaca_ptr, and loading - * its any_int, we might call iseries_handle_interrupts without - * an interrupt pending on the new cpu, but that's no disaster, - * is it? And the business of preempting us off the old cpu - * would itself involve a local_irq_restore which handles the - * interrupt to that cpu. - * - * But use "local_paca->lppaca_ptr" instead of "get_lppaca()" - * to avoid any preemption checking added into get_paca(). - */ - if (local_paca->lppaca_ptr->int_dword.any_int) - iseries_handle_interrupts(); + /* Clear bit 0 which we wouldn't clear otherwise */ + local_paca->irq_happened &= ~PACA_IRQ_HARD_DIS; + + /* + * Force the delivery of pending soft-disabled interrupts on PS3. + * Any HV call will have this side effect. + */ + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { + u64 tmp, tmp2; + lv1_get_version_info(&tmp, &tmp2); } -#endif /* CONFIG_PPC_STD_MMU_64 */ /* - * if (get_paca()->hard_enabled) return; - * But again we need to take care that gcc gets hard_enabled directly - * via r13, not choose to use an intermediate register, lest we're - * preempted to a different cpu in between the two instructions. + * We may have missed a decrementer interrupt. We check the + * decrementer itself rather than the paca irq_happened field + * in case we also had a rollover while hard disabled + */ + local_paca->irq_happened &= ~PACA_IRQ_DEC; + if (decrementer_check_overflow()) + return 0x900; + + /* Finally check if an external interrupt happened */ + local_paca->irq_happened &= ~PACA_IRQ_EE; + if (happened & PACA_IRQ_EE) + return 0x500; + +#ifdef CONFIG_PPC_BOOK3E + /* Finally check if an EPR external interrupt happened + * this bit is typically set if we need to handle another + * "edge" interrupt from within the MPIC "EPR" handler + */ + local_paca->irq_happened &= ~PACA_IRQ_EE_EDGE; + if (happened & PACA_IRQ_EE_EDGE) + return 0x500; + + local_paca->irq_happened &= ~PACA_IRQ_DBELL; + if (happened & PACA_IRQ_DBELL) + return 0x280; +#endif /* CONFIG_PPC_BOOK3E */ + + /* There should be nothing left ! */ + BUG_ON(local_paca->irq_happened != 0); + + return 0; +} + +notrace void arch_local_irq_restore(unsigned long en) +{ + unsigned char irq_happened; + unsigned int replay; + + /* Write the new soft-enabled value */ + set_soft_enabled(en); + if (!en) + return; + /* + * From this point onward, we can take interrupts, preempt, + * etc... unless we got hard-disabled. We check if an event + * happened. If none happened, we know we can just return. + * + * We may have preempted before the check below, in which case + * we are checking the "new" CPU instead of the old one. This + * is only a problem if an event happened on the "old" CPU. + * + * External interrupt events on non-iseries will have caused + * interrupts to be hard-disabled, so there is no problem, we + * cannot have preempted. + * + * That leaves us with EEs on iSeries or decrementer interrupts, + * which I decided to safely ignore. The preemption would have + * itself been the result of an interrupt, upon which return we + * will have checked for pending events on the old CPU. */ - if (get_hard_enabled()) + irq_happened = get_irq_happened(); + if (!irq_happened) return; /* - * Need to hard-enable interrupts here. Since currently disabled, - * no need to take further asm precautions against preemption; but - * use local_paca instead of get_paca() to avoid preemption checking. + * We need to hard disable to get a trusted value from + * __check_irq_replay(). We also need to soft-disable + * again to avoid warnings in there due to the use of + * per-cpu variables. + * + * We know that if the value in irq_happened is exactly 0x01 + * then we are already hard disabled (there are other less + * common cases that we'll ignore for now), so we skip the + * (expensive) mtmsrd. */ - local_paca->hard_enabled = en; + if (unlikely(irq_happened != PACA_IRQ_HARD_DIS)) + __hard_irq_disable(); + set_soft_enabled(0); /* - * Trigger the decrementer if we have a pending event. Some processors - * only trigger on edge transitions of the sign bit. We might also - * have disabled interrupts long enough that the decrementer wrapped - * to positive. + * Check if anything needs to be re-emitted. We haven't + * soft-enabled yet to avoid warnings in decrementer_check_overflow + * accessing per-cpu variables */ - decrementer_check_overflow(); + replay = __check_irq_replay(); + + /* We can soft-enable now */ + set_soft_enabled(1); /* - * Force the delivery of pending soft-disabled interrupts on PS3. - * Any HV call will have this side effect. + * And replay if we have to. This will return with interrupts + * hard-enabled. */ - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { - u64 tmp, tmp2; - lv1_get_version_info(&tmp, &tmp2); + if (replay) { + __replay_interrupt(replay); + return; } + /* Finally, let's ensure we are hard enabled */ __hard_irq_enable(); } EXPORT_SYMBOL(arch_local_irq_restore); + +/* + * This is specifically called by assembly code to re-enable interrupts + * if they are currently disabled. This is typically called before + * schedule() or do_signal() when returning to userspace. We do it + * in C to avoid the burden of dealing with lockdep etc... + */ +void restore_interrupts(void) +{ + if (irqs_disabled()) + local_irq_enable(); +} + #endif /* CONFIG_PPC64 */ int arch_show_interrupts(struct seq_file *p, int prec) @@ -360,8 +439,17 @@ void do_IRQ(struct pt_regs *regs) check_stack_overflow(); + /* + * Query the platform PIC for the interrupt & ack it. + * + * This will typically lower the interrupt line to the CPU + */ irq = ppc_md.get_irq(); + /* We can hard enable interrupts now */ + may_hard_irq_enable(); + + /* And finally process it */ if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) handle_one_irq(irq); else if (irq != NO_IRQ_IGNORE) diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index bf80a1d..e407070 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -647,6 +647,9 @@ void show_regs(struct pt_regs * regs) printk("MSR: "REG" ", regs->msr); printbits(regs->msr, msr_bits); printk(" CR: %08lx XER: %08lx\n", regs->ccr, regs->xer); +#ifdef CONFIG_PPC64 + printk("SOFTE: %ld\n", regs->softe); +#endif trap = TRAP(regs); if ((regs->trap != 0xc00) && cpu_has_feature(CPU_FTR_CFAR)) printk("CFAR: "REG"\n", regs->orig_gpr3); diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 567dd7c..f81c81b 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -259,7 +259,6 @@ void accumulate_stolen_time(void) u64 sst, ust; u8 save_soft_enabled = local_paca->soft_enabled; - u8 save_hard_enabled = local_paca->hard_enabled; /* We are called early in the exception entry, before * soft/hard_enabled are sync'ed to the expected state @@ -268,7 +267,6 @@ void accumulate_stolen_time(void) * complain */ local_paca->soft_enabled = 0; - local_paca->hard_enabled = 0; sst = scan_dispatch_log(local_paca->starttime_user); ust = scan_dispatch_log(local_paca->starttime); @@ -277,7 +275,6 @@ void accumulate_stolen_time(void) local_paca->stolen_time += ust + sst; local_paca->soft_enabled = save_soft_enabled; - local_paca->hard_enabled = save_hard_enabled; } static inline u64 calculate_stolen_time(u64 stop_tb) @@ -580,6 +577,11 @@ void timer_interrupt(struct pt_regs * regs) if (!cpu_online(smp_processor_id())) return; + /* Conditionally hard-enable interrupts now that the DEC has been + * bumped to its maximum value + */ + may_hard_irq_enable(); + trace_timer_interrupt_entry(regs); __get_cpu_var(irq_stat).timer_irqs++; diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c index 085fd3f..a12e95a 100644 --- a/arch/powerpc/platforms/pseries/processor_idle.c +++ b/arch/powerpc/platforms/pseries/processor_idle.c @@ -96,6 +96,20 @@ out: return index; } +static void check_and_cede_processor(void) +{ + /* + * Interrupts are soft-disabled at this point, + * but not hard disabled. So an interrupt might have + * occurred before entering NAP, and would be potentially + * lost (edge events, decrementer events, etc...) unless + * we first hard disable then check. + */ + hard_irq_disable(); + if (get_paca()->irq_happened == 0) + cede_processor(); +} + static int dedicated_cede_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) @@ -108,7 +122,7 @@ static int dedicated_cede_loop(struct cpuidle_device *dev, ppc64_runlatch_off(); HMT_medium(); - cede_processor(); + check_and_cede_processor(); get_lppaca()->donate_dedicated_cpu = 0; dev->last_residency = @@ -132,7 +146,7 @@ static int shared_cede_loop(struct cpuidle_device *dev, * processor. When returning here, external interrupts * are enabled. */ - cede_processor(); + check_and_cede_processor(); dev->last_residency = (int)idle_loop_epilog(in_purr, kt_before); diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 63846eb..974a47b 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -1437,8 +1437,8 @@ static void excprint(struct pt_regs *fp) printf(" current = 0x%lx\n", current); #ifdef CONFIG_PPC64 - printf(" paca = 0x%lx\t softe: %d\t harde: %d\n", - local_paca, local_paca->soft_enabled, local_paca->hard_enabled); + printf(" paca = 0x%lx\t softe: %d\t irq_happened: 0x%02x\n", + local_paca, local_paca->soft_enabled, local_paca->irq_happened); #endif if (current) { printf(" pid = %ld, comm = %s\n", -- cgit v0.10.2 From cb41fa024e0cc306472c1c08c93758263724d0e8 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 19 Jan 2012 20:23:20 -0500 Subject: powerpc/85xx: fix Kconfig warning about missing 8250 dependency The SERIAL_8250_EXTENDED option just enables access to other less regularly used options, like SERIAL_8250_SHARE_IRQ. Select it to get rid of this warning when selecting the child option living underneath it. warning: (FSL_SOC_BOOKE && SERIAL_8250_RM9K) selects SERIAL_8250_SHARE_IRQ which has unmet direct dependencies (HAS_IOMEM && SERIAL_8250_EXTENDED) Signed-off-by: Paul Gortmaker Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index d7946be2..b221236 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -6,6 +6,7 @@ menuconfig FSL_SOC_BOOKE select MPIC select PPC_PCI_CHOICE select FSL_PCI if PCI + select SERIAL_8250_EXTENDED if SERIAL_8250 select SERIAL_8250_SHARE_IRQ if SERIAL_8250 default y -- cgit v0.10.2 From f0b8b3417d836f89d873f6d5de43d54f02cb11e2 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 5 Jan 2012 12:37:16 -0600 Subject: powerpc/fsl-booke: Fixup calc_cam_sz to support MMU v2 The registers that describe size supported by TLB are different on MMU v2 as well as we support power of two page sizes. For now we continue to assume that FSL variable size array supports all page sizes up to the maximum one reported in TLB1PS. Signed-off-by: Kumar Gala diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index 500fe1d..8a97aa7 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -62,6 +62,7 @@ #define SPRN_DVC2 0x13F /* Data Value Compare Register 2 */ #define SPRN_MAS8 0x155 /* MMU Assist Register 8 */ #define SPRN_TLB0PS 0x158 /* TLB 0 Page Size Register */ +#define SPRN_TLB1PS 0x159 /* TLB 1 Page Size Register */ #define SPRN_MAS5_MAS6 0x15c /* MMU Assist Register 5 || 6 */ #define SPRN_MAS8_MAS1 0x15d /* MMU Assist Register 8 || 1 */ #define SPRN_EPTCFG 0x15e /* Embedded Page Table Config */ diff --git a/arch/powerpc/mm/fsl_booke_mmu.c b/arch/powerpc/mm/fsl_booke_mmu.c index 66a6fd3..07ba45b 100644 --- a/arch/powerpc/mm/fsl_booke_mmu.c +++ b/arch/powerpc/mm/fsl_booke_mmu.c @@ -149,12 +149,19 @@ static void settlbcam(int index, unsigned long virt, phys_addr_t phys, unsigned long calc_cam_sz(unsigned long ram, unsigned long virt, phys_addr_t phys) { - unsigned int camsize = __ilog2(ram) & ~1U; - unsigned int align = __ffs(virt | phys) & ~1U; - unsigned long max_cam = (mfspr(SPRN_TLB1CFG) >> 16) & 0xf; - - /* Convert (4^max) kB to (2^max) bytes */ - max_cam = max_cam * 2 + 10; + unsigned int camsize = __ilog2(ram); + unsigned int align = __ffs(virt | phys); + unsigned long max_cam; + + if ((mfspr(SPRN_MMUCFG) & MMUCFG_MAVN) == MMUCFG_MAVN_V1) { + /* Convert (4^max) kB to (2^max) bytes */ + max_cam = ((mfspr(SPRN_TLB1CFG) >> 16) & 0xf) * 2 + 10; + camsize &= ~1U; + align &= ~1U; + } else { + /* Convert (2^max) kB to (2^max) bytes */ + max_cam = __ilog2(mfspr(SPRN_TLB1PS)) + 10; + } if (camsize > align) camsize = align; -- cgit v0.10.2 From 10241842fbe900276634fee8d37ec48a7d8a762f Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sun, 6 Nov 2011 11:51:07 -0600 Subject: powerpc: Add initial e6500 cpu support Add basic support for e6500 core in its single threaded mode. Signed-off-by: Kumar Gala diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index ad55a1c..b9219e9 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -390,6 +390,10 @@ extern const char *powerpc_base_platform; CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ CPU_FTR_DBELL | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_DEBUG_LVL_EXC) +#define CPU_FTRS_E6500 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ + CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ + CPU_FTR_DBELL | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ + CPU_FTR_DEBUG_LVL_EXC) #define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN) /* 64-bit CPUs */ @@ -442,7 +446,7 @@ extern const char *powerpc_base_platform; #ifdef __powerpc64__ #ifdef CONFIG_PPC_BOOK3E -#define CPU_FTRS_POSSIBLE (CPU_FTRS_E5500 | CPU_FTRS_A2) +#define CPU_FTRS_POSSIBLE (CPU_FTRS_E6500 | CPU_FTRS_E5500 | CPU_FTRS_A2) #else #define CPU_FTRS_POSSIBLE \ (CPU_FTRS_POWER3 | CPU_FTRS_RS64 | CPU_FTRS_POWER4 | \ @@ -483,7 +487,7 @@ enum { #endif #ifdef CONFIG_E500 CPU_FTRS_E500 | CPU_FTRS_E500_2 | CPU_FTRS_E500MC | - CPU_FTRS_E5500 | + CPU_FTRS_E5500 | CPU_FTRS_E6500 | #endif 0, }; @@ -491,7 +495,7 @@ enum { #ifdef __powerpc64__ #ifdef CONFIG_PPC_BOOK3E -#define CPU_FTRS_ALWAYS (CPU_FTRS_E5500 & CPU_FTRS_A2) +#define CPU_FTRS_ALWAYS (CPU_FTRS_E6500 & CPU_FTRS_E5500 & CPU_FTRS_A2) #else #define CPU_FTRS_ALWAYS \ (CPU_FTRS_POWER3 & CPU_FTRS_RS64 & CPU_FTRS_POWER4 & \ @@ -528,7 +532,7 @@ enum { #endif #ifdef CONFIG_E500 CPU_FTRS_E500 & CPU_FTRS_E500_2 & CPU_FTRS_E500MC & - CPU_FTRS_E5500 & + CPU_FTRS_E5500 & CPU_FTRS_E6500 & #endif CPU_FTRS_POSSIBLE, }; diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 81db9e2..4dccf51 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -2019,6 +2019,24 @@ static struct cpu_spec __initdata cpu_specs[] = { .machine_check = machine_check_e500mc, .platform = "ppce5500", }, + { /* e6500 */ + .pvr_mask = 0xffff0000, + .pvr_value = 0x80400000, + .cpu_name = "e6500", + .cpu_features = CPU_FTRS_E6500, + .cpu_user_features = COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU, + .mmu_features = MMU_FTR_TYPE_FSL_E | MMU_FTR_BIG_PHYS | + MMU_FTR_USE_TLBILX, + .icache_bsize = 64, + .dcache_bsize = 64, + .num_pmcs = 4, + .oprofile_cpu_type = "ppc/e6500", + .oprofile_type = PPC_OPROFILE_FSL_EMB, + .cpu_setup = __setup_cpu_e5500, + .cpu_restore = __restore_cpu_e5500, + .machine_check = machine_check_e500mc, + .platform = "ppce6500", + }, #ifdef CONFIG_PPC32 { /* default match */ .pvr_mask = 0x00000000, -- cgit v0.10.2 From ba7a4822b48fbc7afd6b567c18e316a03f46684d Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 8 Mar 2012 15:32:52 +1100 Subject: powerpc: Remove some of the legacy iSeries specific device drivers These drivers are specific to the PowerPC legacy iSeries platform and their Kconfig is specified in arch/powerpc. Legacy iSeries is being removed, so these drivers can no longer be selected. Cc: Jens Axboe Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c deleted file mode 100644 index 9a5b2a2..0000000 --- a/drivers/block/viodasd.c +++ /dev/null @@ -1,809 +0,0 @@ -/* -*- linux-c -*- - * viodasd.c - * Authors: Dave Boutcher - * Ryan Arnold - * Colin Devilbiss - * Stephen Rothwell - * - * (C) Copyright 2000-2004 IBM Corporation - * - * 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. - * - * 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 - * - * This routine provides access to disk space (termed "DASD" in historical - * IBM terms) owned and managed by an OS/400 partition running on the - * same box as this Linux partition. - * - * All disk operations are performed by sending messages back and forth to - * the OS/400 partition. - */ - -#define pr_fmt(fmt) "viod: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -MODULE_DESCRIPTION("iSeries Virtual DASD"); -MODULE_AUTHOR("Dave Boutcher"); -MODULE_LICENSE("GPL"); - -/* - * We only support 7 partitions per physical disk....so with minor - * numbers 0-255 we get a maximum of 32 disks. - */ -#define VIOD_GENHD_NAME "iseries/vd" - -#define VIOD_VERS "1.64" - -enum { - PARTITION_SHIFT = 3, - MAX_DISKNO = HVMAXARCHITECTEDVIRTUALDISKS, - MAX_DISK_NAME = FIELD_SIZEOF(struct gendisk, disk_name) -}; - -static DEFINE_MUTEX(viodasd_mutex); -static DEFINE_SPINLOCK(viodasd_spinlock); - -#define VIOMAXREQ 16 - -#define DEVICE_NO(cell) ((struct viodasd_device *)(cell) - &viodasd_devices[0]) - -struct viodasd_waitevent { - struct completion com; - int rc; - u16 sub_result; - int max_disk; /* open */ -}; - -static const struct vio_error_entry viodasd_err_table[] = { - { 0x0201, EINVAL, "Invalid Range" }, - { 0x0202, EINVAL, "Invalid Token" }, - { 0x0203, EIO, "DMA Error" }, - { 0x0204, EIO, "Use Error" }, - { 0x0205, EIO, "Release Error" }, - { 0x0206, EINVAL, "Invalid Disk" }, - { 0x0207, EBUSY, "Can't Lock" }, - { 0x0208, EIO, "Already Locked" }, - { 0x0209, EIO, "Already Unlocked" }, - { 0x020A, EIO, "Invalid Arg" }, - { 0x020B, EIO, "Bad IFS File" }, - { 0x020C, EROFS, "Read Only Device" }, - { 0x02FF, EIO, "Internal Error" }, - { 0x0000, 0, NULL }, -}; - -/* - * Figure out the biggest I/O request (in sectors) we can accept - */ -#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA) - -/* - * Number of disk I/O requests we've sent to OS/400 - */ -static int num_req_outstanding; - -/* - * This is our internal structure for keeping track of disk devices - */ -struct viodasd_device { - u16 cylinders; - u16 tracks; - u16 sectors; - u16 bytes_per_sector; - u64 size; - int read_only; - spinlock_t q_lock; - struct gendisk *disk; - struct device *dev; -} viodasd_devices[MAX_DISKNO]; - -/* - * External open entry point. - */ -static int viodasd_open(struct block_device *bdev, fmode_t mode) -{ - struct viodasd_device *d = bdev->bd_disk->private_data; - HvLpEvent_Rc hvrc; - struct viodasd_waitevent we; - u16 flags = 0; - - if (d->read_only) { - if (mode & FMODE_WRITE) - return -EROFS; - flags = vioblockflags_ro; - } - - init_completion(&we.com); - - /* Send the open event to OS/400 */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_blockio | vioblockopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)&we, VIOVERSION << 16, - ((u64)DEVICE_NO(d) << 48) | ((u64)flags << 32), - 0, 0, 0); - if (hvrc != 0) { - pr_warning("HV open failed %d\n", (int)hvrc); - return -EIO; - } - - wait_for_completion(&we.com); - - /* Check the return code */ - if (we.rc != 0) { - const struct vio_error_entry *err = - vio_lookup_rc(viodasd_err_table, we.sub_result); - - pr_warning("bad rc opening disk: %d:0x%04x (%s)\n", - (int)we.rc, we.sub_result, err->msg); - return -EIO; - } - - return 0; -} - -static int viodasd_unlocked_open(struct block_device *bdev, fmode_t mode) -{ - int ret; - - mutex_lock(&viodasd_mutex); - ret = viodasd_open(bdev, mode); - mutex_unlock(&viodasd_mutex); - - return ret; -} - - -/* - * External release entry point. - */ -static int viodasd_release(struct gendisk *disk, fmode_t mode) -{ - struct viodasd_device *d = disk->private_data; - HvLpEvent_Rc hvrc; - - mutex_lock(&viodasd_mutex); - /* Send the event to OS/400. We DON'T expect a response */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_blockio | vioblockclose, - HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - 0, VIOVERSION << 16, - ((u64)DEVICE_NO(d) << 48) /* | ((u64)flags << 32) */, - 0, 0, 0); - if (hvrc != 0) - pr_warning("HV close call failed %d\n", (int)hvrc); - - mutex_unlock(&viodasd_mutex); - - return 0; -} - - -/* External ioctl entry point. - */ -static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - struct gendisk *disk = bdev->bd_disk; - struct viodasd_device *d = disk->private_data; - - geo->sectors = d->sectors ? d->sectors : 32; - geo->heads = d->tracks ? d->tracks : 64; - geo->cylinders = d->cylinders ? d->cylinders : - get_capacity(disk) / (geo->sectors * geo->heads); - - return 0; -} - -/* - * Our file operations table - */ -static const struct block_device_operations viodasd_fops = { - .owner = THIS_MODULE, - .open = viodasd_unlocked_open, - .release = viodasd_release, - .getgeo = viodasd_getgeo, -}; - -/* - * End a request - */ -static void viodasd_end_request(struct request *req, int error, - int num_sectors) -{ - __blk_end_request(req, error, num_sectors << 9); -} - -/* - * Send an actual I/O request to OS/400 - */ -static int send_request(struct request *req) -{ - u64 start; - int direction; - int nsg; - u16 viocmd; - HvLpEvent_Rc hvrc; - struct vioblocklpevent *bevent; - struct HvLpEvent *hev; - struct scatterlist sg[VIOMAXBLOCKDMA]; - int sgindex; - struct viodasd_device *d; - unsigned long flags; - - start = (u64)blk_rq_pos(req) << 9; - - if (rq_data_dir(req) == READ) { - direction = DMA_FROM_DEVICE; - viocmd = viomajorsubtype_blockio | vioblockread; - } else { - direction = DMA_TO_DEVICE; - viocmd = viomajorsubtype_blockio | vioblockwrite; - } - - d = req->rq_disk->private_data; - - /* Now build the scatter-gather list */ - sg_init_table(sg, VIOMAXBLOCKDMA); - nsg = blk_rq_map_sg(req->q, req, sg); - nsg = dma_map_sg(d->dev, sg, nsg, direction); - - spin_lock_irqsave(&viodasd_spinlock, flags); - num_req_outstanding++; - - /* This optimization handles a single DMA block */ - if (nsg == 1) - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, viocmd, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)req, VIOVERSION << 16, - ((u64)DEVICE_NO(d) << 48), start, - ((u64)sg_dma_address(&sg[0])) << 32, - sg_dma_len(&sg[0])); - else { - bevent = (struct vioblocklpevent *) - vio_get_event_buffer(viomajorsubtype_blockio); - if (bevent == NULL) { - pr_warning("error allocating disk event buffer\n"); - goto error_ret; - } - - /* - * Now build up the actual request. Note that we store - * the pointer to the request in the correlation - * token so we can match the response up later - */ - memset(bevent, 0, sizeof(struct vioblocklpevent)); - hev = &bevent->event; - hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DO_ACK | - HV_LP_EVENT_INT; - hev->xType = HvLpEvent_Type_VirtualIo; - hev->xSubtype = viocmd; - hev->xSourceLp = HvLpConfig_getLpIndex(); - hev->xTargetLp = viopath_hostLp; - hev->xSizeMinus1 = - offsetof(struct vioblocklpevent, u.rw_data.dma_info) + - (sizeof(bevent->u.rw_data.dma_info[0]) * nsg) - 1; - hev->xSourceInstanceId = viopath_sourceinst(viopath_hostLp); - hev->xTargetInstanceId = viopath_targetinst(viopath_hostLp); - hev->xCorrelationToken = (u64)req; - bevent->version = VIOVERSION; - bevent->disk = DEVICE_NO(d); - bevent->u.rw_data.offset = start; - - /* - * Copy just the dma information from the sg list - * into the request - */ - for (sgindex = 0; sgindex < nsg; sgindex++) { - bevent->u.rw_data.dma_info[sgindex].token = - sg_dma_address(&sg[sgindex]); - bevent->u.rw_data.dma_info[sgindex].len = - sg_dma_len(&sg[sgindex]); - } - - /* Send the request */ - hvrc = HvCallEvent_signalLpEvent(&bevent->event); - vio_free_event_buffer(viomajorsubtype_blockio, bevent); - } - - if (hvrc != HvLpEvent_Rc_Good) { - pr_warning("error sending disk event to OS/400 (rc %d)\n", - (int)hvrc); - goto error_ret; - } - spin_unlock_irqrestore(&viodasd_spinlock, flags); - return 0; - -error_ret: - num_req_outstanding--; - spin_unlock_irqrestore(&viodasd_spinlock, flags); - dma_unmap_sg(d->dev, sg, nsg, direction); - return -1; -} - -/* - * This is the external request processing routine - */ -static void do_viodasd_request(struct request_queue *q) -{ - struct request *req; - - /* - * If we already have the maximum number of requests - * outstanding to OS/400 just bail out. We'll come - * back later. - */ - while (num_req_outstanding < VIOMAXREQ) { - req = blk_fetch_request(q); - if (req == NULL) - return; - /* check that request contains a valid command */ - if (req->cmd_type != REQ_TYPE_FS) { - viodasd_end_request(req, -EIO, blk_rq_sectors(req)); - continue; - } - /* Try sending the request */ - if (send_request(req) != 0) - viodasd_end_request(req, -EIO, blk_rq_sectors(req)); - } -} - -/* - * Probe a single disk and fill in the viodasd_device structure - * for it. - */ -static int probe_disk(struct viodasd_device *d) -{ - HvLpEvent_Rc hvrc; - struct viodasd_waitevent we; - int dev_no = DEVICE_NO(d); - struct gendisk *g; - struct request_queue *q; - u16 flags = 0; - -retry: - init_completion(&we.com); - - /* Send the open event to OS/400 */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_blockio | vioblockopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)&we, VIOVERSION << 16, - ((u64)dev_no << 48) | ((u64)flags<< 32), - 0, 0, 0); - if (hvrc != 0) { - pr_warning("bad rc on HV open %d\n", (int)hvrc); - return 0; - } - - wait_for_completion(&we.com); - - if (we.rc != 0) { - if (flags != 0) - return 0; - /* try again with read only flag set */ - flags = vioblockflags_ro; - goto retry; - } - if (we.max_disk > (MAX_DISKNO - 1)) { - printk_once(KERN_INFO pr_fmt("Only examining the first %d of %d disks connected\n"), - MAX_DISKNO, we.max_disk + 1); - } - - /* Send the close event to OS/400. We DON'T expect a response */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_blockio | vioblockclose, - HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - 0, VIOVERSION << 16, - ((u64)dev_no << 48) | ((u64)flags << 32), - 0, 0, 0); - if (hvrc != 0) { - pr_warning("bad rc sending event to OS/400 %d\n", (int)hvrc); - return 0; - } - - if (d->dev == NULL) { - /* this is when we reprobe for new disks */ - if (vio_create_viodasd(dev_no) == NULL) { - pr_warning("cannot allocate virtual device for disk %d\n", - dev_no); - return 0; - } - /* - * The vio_create_viodasd will have recursed into this - * routine with d->dev set to the new vio device and - * will finish the setup of the disk below. - */ - return 1; - } - - /* create the request queue for the disk */ - spin_lock_init(&d->q_lock); - q = blk_init_queue(do_viodasd_request, &d->q_lock); - if (q == NULL) { - pr_warning("cannot allocate queue for disk %d\n", dev_no); - return 0; - } - g = alloc_disk(1 << PARTITION_SHIFT); - if (g == NULL) { - pr_warning("cannot allocate disk structure for disk %d\n", - dev_no); - blk_cleanup_queue(q); - return 0; - } - - d->disk = g; - blk_queue_max_segments(q, VIOMAXBLOCKDMA); - blk_queue_max_hw_sectors(q, VIODASD_MAXSECTORS); - g->major = VIODASD_MAJOR; - g->first_minor = dev_no << PARTITION_SHIFT; - if (dev_no >= 26) - snprintf(g->disk_name, sizeof(g->disk_name), - VIOD_GENHD_NAME "%c%c", - 'a' + (dev_no / 26) - 1, 'a' + (dev_no % 26)); - else - snprintf(g->disk_name, sizeof(g->disk_name), - VIOD_GENHD_NAME "%c", 'a' + (dev_no % 26)); - g->fops = &viodasd_fops; - g->queue = q; - g->private_data = d; - g->driverfs_dev = d->dev; - set_capacity(g, d->size >> 9); - - pr_info("disk %d: %lu sectors (%lu MB) CHS=%d/%d/%d sector size %d%s\n", - dev_no, (unsigned long)(d->size >> 9), - (unsigned long)(d->size >> 20), - (int)d->cylinders, (int)d->tracks, - (int)d->sectors, (int)d->bytes_per_sector, - d->read_only ? " (RO)" : ""); - - /* register us in the global list */ - add_disk(g); - return 1; -} - -/* returns the total number of scatterlist elements converted */ -static int block_event_to_scatterlist(const struct vioblocklpevent *bevent, - struct scatterlist *sg, int *total_len) -{ - int i, numsg; - const struct rw_data *rw_data = &bevent->u.rw_data; - static const int offset = - offsetof(struct vioblocklpevent, u.rw_data.dma_info); - static const int element_size = sizeof(rw_data->dma_info[0]); - - numsg = ((bevent->event.xSizeMinus1 + 1) - offset) / element_size; - if (numsg > VIOMAXBLOCKDMA) - numsg = VIOMAXBLOCKDMA; - - *total_len = 0; - sg_init_table(sg, VIOMAXBLOCKDMA); - for (i = 0; (i < numsg) && (rw_data->dma_info[i].len > 0); ++i) { - sg_dma_address(&sg[i]) = rw_data->dma_info[i].token; - sg_dma_len(&sg[i]) = rw_data->dma_info[i].len; - *total_len += rw_data->dma_info[i].len; - } - return i; -} - -/* - * Restart all queues, starting with the one _after_ the disk given, - * thus reducing the chance of starvation of higher numbered disks. - */ -static void viodasd_restart_all_queues_starting_from(int first_index) -{ - int i; - - for (i = first_index + 1; i < MAX_DISKNO; ++i) - if (viodasd_devices[i].disk) - blk_run_queue(viodasd_devices[i].disk->queue); - for (i = 0; i <= first_index; ++i) - if (viodasd_devices[i].disk) - blk_run_queue(viodasd_devices[i].disk->queue); -} - -/* - * For read and write requests, decrement the number of outstanding requests, - * Free the DMA buffers we allocated. - */ -static int viodasd_handle_read_write(struct vioblocklpevent *bevent) -{ - int num_sg, num_sect, pci_direction, total_len; - struct request *req; - struct scatterlist sg[VIOMAXBLOCKDMA]; - struct HvLpEvent *event = &bevent->event; - unsigned long irq_flags; - struct viodasd_device *d; - int error; - spinlock_t *qlock; - - num_sg = block_event_to_scatterlist(bevent, sg, &total_len); - num_sect = total_len >> 9; - if (event->xSubtype == (viomajorsubtype_blockio | vioblockread)) - pci_direction = DMA_FROM_DEVICE; - else - pci_direction = DMA_TO_DEVICE; - req = (struct request *)bevent->event.xCorrelationToken; - d = req->rq_disk->private_data; - - dma_unmap_sg(d->dev, sg, num_sg, pci_direction); - - /* - * Since this is running in interrupt mode, we need to make sure - * we're not stepping on any global I/O operations - */ - spin_lock_irqsave(&viodasd_spinlock, irq_flags); - num_req_outstanding--; - spin_unlock_irqrestore(&viodasd_spinlock, irq_flags); - - error = (event->xRc == HvLpEvent_Rc_Good) ? 0 : -EIO; - if (error) { - const struct vio_error_entry *err; - err = vio_lookup_rc(viodasd_err_table, bevent->sub_result); - pr_warning("read/write error %d:0x%04x (%s)\n", - event->xRc, bevent->sub_result, err->msg); - num_sect = blk_rq_sectors(req); - } - qlock = req->q->queue_lock; - spin_lock_irqsave(qlock, irq_flags); - viodasd_end_request(req, error, num_sect); - spin_unlock_irqrestore(qlock, irq_flags); - - /* Finally, try to get more requests off of this device's queue */ - viodasd_restart_all_queues_starting_from(DEVICE_NO(d)); - - return 0; -} - -/* This routine handles incoming block LP events */ -static void handle_block_event(struct HvLpEvent *event) -{ - struct vioblocklpevent *bevent = (struct vioblocklpevent *)event; - struct viodasd_waitevent *pwe; - - if (event == NULL) - /* Notification that a partition went away! */ - return; - /* First, we should NEVER get an int here...only acks */ - if (hvlpevent_is_int(event)) { - pr_warning("Yikes! got an int in viodasd event handler!\n"); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } - - switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { - case vioblockopen: - /* - * Handle a response to an open request. We get all the - * disk information in the response, so update it. The - * correlation token contains a pointer to a waitevent - * structure that has a completion in it. update the - * return code in the waitevent structure and post the - * completion to wake up the guy who sent the request - */ - pwe = (struct viodasd_waitevent *)event->xCorrelationToken; - pwe->rc = event->xRc; - pwe->sub_result = bevent->sub_result; - if (event->xRc == HvLpEvent_Rc_Good) { - const struct open_data *data = &bevent->u.open_data; - struct viodasd_device *device = - &viodasd_devices[bevent->disk]; - device->read_only = - bevent->flags & vioblockflags_ro; - device->size = data->disk_size; - device->cylinders = data->cylinders; - device->tracks = data->tracks; - device->sectors = data->sectors; - device->bytes_per_sector = data->bytes_per_sector; - pwe->max_disk = data->max_disk; - } - complete(&pwe->com); - break; - case vioblockclose: - break; - case vioblockread: - case vioblockwrite: - viodasd_handle_read_write(bevent); - break; - - default: - pr_warning("invalid subtype!"); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - -/* - * Get the driver to reprobe for more disks. - */ -static ssize_t probe_disks(struct device_driver *drv, const char *buf, - size_t count) -{ - struct viodasd_device *d; - - for (d = viodasd_devices; d < &viodasd_devices[MAX_DISKNO]; d++) { - if (d->disk == NULL) - probe_disk(d); - } - return count; -} -static DRIVER_ATTR(probe, S_IWUSR, NULL, probe_disks); - -static int viodasd_probe(struct vio_dev *vdev, const struct vio_device_id *id) -{ - struct viodasd_device *d = &viodasd_devices[vdev->unit_address]; - - d->dev = &vdev->dev; - if (!probe_disk(d)) - return -ENODEV; - return 0; -} - -static int viodasd_remove(struct vio_dev *vdev) -{ - struct viodasd_device *d; - - d = &viodasd_devices[vdev->unit_address]; - if (d->disk) { - del_gendisk(d->disk); - blk_cleanup_queue(d->disk->queue); - put_disk(d->disk); - d->disk = NULL; - } - d->dev = NULL; - return 0; -} - -/** - * viodasd_device_table: Used by vio.c to match devices that we - * support. - */ -static struct vio_device_id viodasd_device_table[] __devinitdata = { - { "block", "IBM,iSeries-viodasd" }, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, viodasd_device_table); - -static struct vio_driver viodasd_driver = { - .id_table = viodasd_device_table, - .probe = viodasd_probe, - .remove = viodasd_remove, - .driver = { - .name = "viodasd", - .owner = THIS_MODULE, - } -}; - -static int need_delete_probe; - -/* - * Initialize the whole device driver. Handle module and non-module - * versions - */ -static int __init viodasd_init(void) -{ - int rc; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) { - rc = -ENODEV; - goto early_fail; - } - - /* Try to open to our host lp */ - if (viopath_hostLp == HvLpIndexInvalid) - vio_set_hostlp(); - - if (viopath_hostLp == HvLpIndexInvalid) { - pr_warning("invalid hosting partition\n"); - rc = -EIO; - goto early_fail; - } - - pr_info("vers " VIOD_VERS ", hosting partition %d\n", viopath_hostLp); - - /* register the block device */ - rc = register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); - if (rc) { - pr_warning("Unable to get major number %d for %s\n", - VIODASD_MAJOR, VIOD_GENHD_NAME); - goto early_fail; - } - /* Actually open the path to the hosting partition */ - rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, - VIOMAXREQ + 2); - if (rc) { - pr_warning("error opening path to host partition %d\n", - viopath_hostLp); - goto unregister_blk; - } - - /* Initialize our request handler */ - vio_setHandler(viomajorsubtype_blockio, handle_block_event); - - rc = vio_register_driver(&viodasd_driver); - if (rc) { - pr_warning("vio_register_driver failed\n"); - goto unset_handler; - } - - /* - * If this call fails, it just means that we cannot dynamically - * add virtual disks, but the driver will still work fine for - * all existing disk, so ignore the failure. - */ - if (!driver_create_file(&viodasd_driver.driver, &driver_attr_probe)) - need_delete_probe = 1; - - return 0; - -unset_handler: - vio_clearHandler(viomajorsubtype_blockio); - viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); -unregister_blk: - unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); -early_fail: - return rc; -} -module_init(viodasd_init); - -void __exit viodasd_exit(void) -{ - if (need_delete_probe) - driver_remove_file(&viodasd_driver.driver, &driver_attr_probe); - vio_unregister_driver(&viodasd_driver); - vio_clearHandler(viomajorsubtype_blockio); - viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); - unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); -} -module_exit(viodasd_exit); diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c deleted file mode 100644 index 7878da8..0000000 --- a/drivers/cdrom/viocd.c +++ /dev/null @@ -1,739 +0,0 @@ -/* -*- linux-c -*- - * drivers/cdrom/viocd.c - * - * iSeries Virtual CD Rom - * - * Authors: Dave Boutcher - * Ryan Arnold - * Colin Devilbiss - * Stephen Rothwell - * - * (C) Copyright 2000-2004 IBM Corporation - * - * 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) anyu later version. - * - * 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 - * - * This routine provides access to CD ROM drives owned and managed by an - * OS/400 partition running on the same box as this Linux partition. - * - * All operations are performed by sending messages back and forth to - * the OS/400 partition. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define VIOCD_DEVICE "iseries/vcd" - -#define VIOCD_VERS "1.06" - -/* - * Should probably make this a module parameter....sigh - */ -#define VIOCD_MAX_CD HVMAXARCHITECTEDVIRTUALCDROMS - -static DEFINE_MUTEX(viocd_mutex); -static const struct vio_error_entry viocd_err_table[] = { - {0x0201, EINVAL, "Invalid Range"}, - {0x0202, EINVAL, "Invalid Token"}, - {0x0203, EIO, "DMA Error"}, - {0x0204, EIO, "Use Error"}, - {0x0205, EIO, "Release Error"}, - {0x0206, EINVAL, "Invalid CD"}, - {0x020C, EROFS, "Read Only Device"}, - {0x020D, ENOMEDIUM, "Changed or Missing Volume (or Varied Off?)"}, - {0x020E, EIO, "Optical System Error (Varied Off?)"}, - {0x02FF, EIO, "Internal Error"}, - {0x3010, EIO, "Changed Volume"}, - {0xC100, EIO, "Optical System Error"}, - {0x0000, 0, NULL}, -}; - -/* - * This is the structure we use to exchange info between driver and interrupt - * handler - */ -struct viocd_waitevent { - struct completion com; - int rc; - u16 sub_result; - int changed; -}; - -/* this is a lookup table for the true capabilities of a device */ -struct capability_entry { - char *type; - int capability; -}; - -static struct capability_entry capability_table[] __initdata = { - { "6330", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, - { "6331", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, - { "6333", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, - { "632A", CDC_LOCK | CDC_DVD_RAM | CDC_RAM }, - { "6321", CDC_LOCK }, - { "632B", 0 }, - { NULL , CDC_LOCK }, -}; - -/* These are our internal structures for keeping track of devices */ -static int viocd_numdev; - -struct disk_info { - struct gendisk *viocd_disk; - struct cdrom_device_info viocd_info; - struct device *dev; - const char *rsrcname; - const char *type; - const char *model; -}; -static struct disk_info viocd_diskinfo[VIOCD_MAX_CD]; - -#define DEVICE_NR(di) ((di) - &viocd_diskinfo[0]) - -static spinlock_t viocd_reqlock; - -#define MAX_CD_REQ 1 - -/* procfs support */ -static int proc_viocd_show(struct seq_file *m, void *v) -{ - int i; - - for (i = 0; i < viocd_numdev; i++) { - seq_printf(m, "viocd device %d is iSeries resource %10.10s" - "type %4.4s, model %3.3s\n", - i, viocd_diskinfo[i].rsrcname, - viocd_diskinfo[i].type, - viocd_diskinfo[i].model); - } - return 0; -} - -static int proc_viocd_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_viocd_show, NULL); -} - -static const struct file_operations proc_viocd_operations = { - .owner = THIS_MODULE, - .open = proc_viocd_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int viocd_blk_open(struct block_device *bdev, fmode_t mode) -{ - struct disk_info *di = bdev->bd_disk->private_data; - int ret; - - mutex_lock(&viocd_mutex); - ret = cdrom_open(&di->viocd_info, bdev, mode); - mutex_unlock(&viocd_mutex); - - return ret; -} - -static int viocd_blk_release(struct gendisk *disk, fmode_t mode) -{ - struct disk_info *di = disk->private_data; - mutex_lock(&viocd_mutex); - cdrom_release(&di->viocd_info, mode); - mutex_unlock(&viocd_mutex); - return 0; -} - -static int viocd_blk_ioctl(struct block_device *bdev, fmode_t mode, - unsigned cmd, unsigned long arg) -{ - struct disk_info *di = bdev->bd_disk->private_data; - int ret; - - mutex_lock(&viocd_mutex); - ret = cdrom_ioctl(&di->viocd_info, bdev, mode, cmd, arg); - mutex_unlock(&viocd_mutex); - - return ret; -} - -static unsigned int viocd_blk_check_events(struct gendisk *disk, - unsigned int clearing) -{ - struct disk_info *di = disk->private_data; - return cdrom_check_events(&di->viocd_info, clearing); -} - -static const struct block_device_operations viocd_fops = { - .owner = THIS_MODULE, - .open = viocd_blk_open, - .release = viocd_blk_release, - .ioctl = viocd_blk_ioctl, - .check_events = viocd_blk_check_events, -}; - -static int viocd_open(struct cdrom_device_info *cdi, int purpose) -{ - struct disk_info *diskinfo = cdi->handle; - int device_no = DEVICE_NR(diskinfo); - HvLpEvent_Rc hvrc; - struct viocd_waitevent we; - - init_completion(&we.com); - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_cdio | viocdopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)&we, VIOVERSION << 16, ((u64)device_no << 48), - 0, 0, 0); - if (hvrc != 0) { - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", - (int)hvrc); - return -EIO; - } - - wait_for_completion(&we.com); - - if (we.rc) { - const struct vio_error_entry *err = - vio_lookup_rc(viocd_err_table, we.sub_result); - pr_warning("bad rc %d:0x%04X on open: %s\n", - we.rc, we.sub_result, err->msg); - return -err->errno; - } - - return 0; -} - -static void viocd_release(struct cdrom_device_info *cdi) -{ - int device_no = DEVICE_NR((struct disk_info *)cdi->handle); - HvLpEvent_Rc hvrc; - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_cdio | viocdclose, - HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), 0, - VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0); - if (hvrc != 0) - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", - (int)hvrc); -} - -/* Send a read or write request to OS/400 */ -static int send_request(struct request *req) -{ - HvLpEvent_Rc hvrc; - struct disk_info *diskinfo = req->rq_disk->private_data; - u64 len; - dma_addr_t dmaaddr; - int direction; - u16 cmd; - struct scatterlist sg; - - BUG_ON(req->nr_phys_segments > 1); - - if (rq_data_dir(req) == READ) { - direction = DMA_FROM_DEVICE; - cmd = viomajorsubtype_cdio | viocdread; - } else { - direction = DMA_TO_DEVICE; - cmd = viomajorsubtype_cdio | viocdwrite; - } - - sg_init_table(&sg, 1); - if (blk_rq_map_sg(req->q, req, &sg) == 0) { - pr_warning("error setting up scatter/gather list\n"); - return -1; - } - - if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) { - pr_warning("error allocating sg tce\n"); - return -1; - } - dmaaddr = sg_dma_address(&sg); - len = sg_dma_len(&sg); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, cmd, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)req, VIOVERSION << 16, - ((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr, - (u64)blk_rq_pos(req) * 512, len, 0); - if (hvrc != HvLpEvent_Rc_Good) { - pr_warning("hv error on op %d\n", (int)hvrc); - return -1; - } - - return 0; -} - -static int rwreq; - -static void do_viocd_request(struct request_queue *q) -{ - struct request *req; - - while ((rwreq == 0) && ((req = blk_fetch_request(q)) != NULL)) { - if (req->cmd_type != REQ_TYPE_FS) - __blk_end_request_all(req, -EIO); - else if (send_request(req) < 0) { - pr_warning("unable to send message to OS/400!\n"); - __blk_end_request_all(req, -EIO); - } else - rwreq++; - } -} - -static unsigned int viocd_check_events(struct cdrom_device_info *cdi, - unsigned int clearing, int disc_nr) -{ - struct viocd_waitevent we; - HvLpEvent_Rc hvrc; - int device_no = DEVICE_NR((struct disk_info *)cdi->handle); - - init_completion(&we.com); - - /* Send the open event to OS/400 */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_cdio | viocdcheck, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)&we, VIOVERSION << 16, ((u64)device_no << 48), - 0, 0, 0); - if (hvrc != 0) { - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", - (int)hvrc); - return 0; - } - - wait_for_completion(&we.com); - - /* Check the return code. If bad, assume no change */ - if (we.rc) { - const struct vio_error_entry *err = - vio_lookup_rc(viocd_err_table, we.sub_result); - pr_warning("bad rc %d:0x%04X on check_change: %s; Assuming no change\n", - we.rc, we.sub_result, err->msg); - return 0; - } - - return we.changed ? DISK_EVENT_MEDIA_CHANGE : 0; -} - -static int viocd_lock_door(struct cdrom_device_info *cdi, int locking) -{ - HvLpEvent_Rc hvrc; - u64 device_no = DEVICE_NR((struct disk_info *)cdi->handle); - /* NOTE: flags is 1 or 0 so it won't overwrite the device_no */ - u64 flags = !!locking; - struct viocd_waitevent we; - - init_completion(&we.com); - - /* Send the lockdoor event to OS/400 */ - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_cdio | viocdlockdoor, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)&we, VIOVERSION << 16, - (device_no << 48) | (flags << 32), 0, 0, 0); - if (hvrc != 0) { - pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", - (int)hvrc); - return -EIO; - } - - wait_for_completion(&we.com); - - if (we.rc != 0) - return -EIO; - return 0; -} - -static int viocd_packet(struct cdrom_device_info *cdi, - struct packet_command *cgc) -{ - unsigned int buflen = cgc->buflen; - int ret = -EIO; - - switch (cgc->cmd[0]) { - case GPCMD_READ_DISC_INFO: - { - disc_information *di = (disc_information *)cgc->buffer; - - if (buflen >= 2) { - di->disc_information_length = cpu_to_be16(1); - ret = 0; - } - if (buflen >= 3) - di->erasable = - (cdi->ops->capability & ~cdi->mask - & (CDC_DVD_RAM | CDC_RAM)) != 0; - } - break; - case GPCMD_GET_CONFIGURATION: - if (cgc->cmd[3] == CDF_RWRT) { - struct rwrt_feature_desc *rfd = (struct rwrt_feature_desc *)(cgc->buffer + sizeof(struct feature_header)); - - if ((buflen >= - (sizeof(struct feature_header) + sizeof(*rfd))) && - (cdi->ops->capability & ~cdi->mask - & (CDC_DVD_RAM | CDC_RAM))) { - rfd->feature_code = cpu_to_be16(CDF_RWRT); - rfd->curr = 1; - ret = 0; - } - } - break; - default: - if (cgc->sense) { - /* indicate Unknown code */ - cgc->sense->sense_key = 0x05; - cgc->sense->asc = 0x20; - cgc->sense->ascq = 0x00; - } - break; - } - - cgc->stat = ret; - return ret; -} - -static void restart_all_queues(int first_index) -{ - int i; - - for (i = first_index + 1; i < viocd_numdev; i++) - if (viocd_diskinfo[i].viocd_disk) - blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); - for (i = 0; i <= first_index; i++) - if (viocd_diskinfo[i].viocd_disk) - blk_run_queue(viocd_diskinfo[i].viocd_disk->queue); -} - -/* This routine handles incoming CD LP events */ -static void vio_handle_cd_event(struct HvLpEvent *event) -{ - struct viocdlpevent *bevent; - struct viocd_waitevent *pwe; - struct disk_info *di; - unsigned long flags; - struct request *req; - - - if (event == NULL) - /* Notification that a partition went away! */ - return; - /* First, we should NEVER get an int here...only acks */ - if (hvlpevent_is_int(event)) { - pr_warning("Yikes! got an int in viocd event handler!\n"); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } - - bevent = (struct viocdlpevent *)event; - - switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { - case viocdopen: - if (event->xRc == 0) { - di = &viocd_diskinfo[bevent->disk]; - blk_queue_logical_block_size(di->viocd_disk->queue, - bevent->block_size); - set_capacity(di->viocd_disk, - bevent->media_size * - bevent->block_size / 512); - } - /* FALLTHROUGH !! */ - case viocdlockdoor: - pwe = (struct viocd_waitevent *)event->xCorrelationToken; -return_complete: - pwe->rc = event->xRc; - pwe->sub_result = bevent->sub_result; - complete(&pwe->com); - break; - - case viocdcheck: - pwe = (struct viocd_waitevent *)event->xCorrelationToken; - pwe->changed = bevent->flags; - goto return_complete; - - case viocdclose: - break; - - case viocdwrite: - case viocdread: - /* - * Since this is running in interrupt mode, we need to - * make sure we're not stepping on any global I/O operations - */ - di = &viocd_diskinfo[bevent->disk]; - spin_lock_irqsave(&viocd_reqlock, flags); - dma_unmap_single(di->dev, bevent->token, bevent->len, - ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread) - ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - req = (struct request *)bevent->event.xCorrelationToken; - rwreq--; - - if (event->xRc != HvLpEvent_Rc_Good) { - const struct vio_error_entry *err = - vio_lookup_rc(viocd_err_table, - bevent->sub_result); - pr_warning("request %p failed with rc %d:0x%04X: %s\n", - req, event->xRc, - bevent->sub_result, err->msg); - __blk_end_request_all(req, -EIO); - } else - __blk_end_request_all(req, 0); - - /* restart handling of incoming requests */ - spin_unlock_irqrestore(&viocd_reqlock, flags); - restart_all_queues(bevent->disk); - break; - - default: - pr_warning("message with invalid subtype %0x04X!\n", - event->xSubtype & VIOMINOR_SUBTYPE_MASK); - if (hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - -static int viocd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, - void *arg) -{ - return -EINVAL; -} - -static struct cdrom_device_ops viocd_dops = { - .open = viocd_open, - .release = viocd_release, - .check_events = viocd_check_events, - .lock_door = viocd_lock_door, - .generic_packet = viocd_packet, - .audio_ioctl = viocd_audio_ioctl, - .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM -}; - -static int find_capability(const char *type) -{ - struct capability_entry *entry; - - for(entry = capability_table; entry->type; ++entry) - if(!strncmp(entry->type, type, 4)) - break; - return entry->capability; -} - -static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id) -{ - struct gendisk *gendisk; - int deviceno; - struct disk_info *d; - struct cdrom_device_info *c; - struct request_queue *q; - struct device_node *node = vdev->dev.of_node; - - deviceno = vdev->unit_address; - if (deviceno >= VIOCD_MAX_CD) - return -ENODEV; - if (!node) - return -ENODEV; - - if (deviceno >= viocd_numdev) - viocd_numdev = deviceno + 1; - - d = &viocd_diskinfo[deviceno]; - d->rsrcname = of_get_property(node, "linux,vio_rsrcname", NULL); - d->type = of_get_property(node, "linux,vio_type", NULL); - d->model = of_get_property(node, "linux,vio_model", NULL); - - c = &d->viocd_info; - - c->ops = &viocd_dops; - c->speed = 4; - c->capacity = 1; - c->handle = d; - c->mask = ~find_capability(d->type); - sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno); - - if (register_cdrom(c) != 0) { - pr_warning("Cannot register viocd CD-ROM %s!\n", c->name); - goto out; - } - pr_info("cd %s is iSeries resource %10.10s type %4.4s, model %3.3s\n", - c->name, d->rsrcname, d->type, d->model); - q = blk_init_queue(do_viocd_request, &viocd_reqlock); - if (q == NULL) { - pr_warning("Cannot allocate queue for %s!\n", c->name); - goto out_unregister_cdrom; - } - gendisk = alloc_disk(1); - if (gendisk == NULL) { - pr_warning("Cannot create gendisk for %s!\n", c->name); - goto out_cleanup_queue; - } - gendisk->major = VIOCD_MAJOR; - gendisk->first_minor = deviceno; - strncpy(gendisk->disk_name, c->name, - sizeof(gendisk->disk_name)); - blk_queue_max_segments(q, 1); - blk_queue_max_hw_sectors(q, 4096 / 512); - gendisk->queue = q; - gendisk->fops = &viocd_fops; - gendisk->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE | - GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; - set_capacity(gendisk, 0); - gendisk->private_data = d; - d->viocd_disk = gendisk; - d->dev = &vdev->dev; - gendisk->driverfs_dev = d->dev; - add_disk(gendisk); - return 0; - -out_cleanup_queue: - blk_cleanup_queue(q); -out_unregister_cdrom: - unregister_cdrom(c); -out: - return -ENODEV; -} - -static int viocd_remove(struct vio_dev *vdev) -{ - struct disk_info *d = &viocd_diskinfo[vdev->unit_address]; - - unregister_cdrom(&d->viocd_info); - del_gendisk(d->viocd_disk); - blk_cleanup_queue(d->viocd_disk->queue); - put_disk(d->viocd_disk); - return 0; -} - -/** - * viocd_device_table: Used by vio.c to match devices that we - * support. - */ -static struct vio_device_id viocd_device_table[] __devinitdata = { - { "block", "IBM,iSeries-viocd" }, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, viocd_device_table); - -static struct vio_driver viocd_driver = { - .id_table = viocd_device_table, - .probe = viocd_probe, - .remove = viocd_remove, - .driver = { - .name = "viocd", - .owner = THIS_MODULE, - } -}; - -static int __init viocd_init(void) -{ - int ret = 0; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - - if (viopath_hostLp == HvLpIndexInvalid) { - vio_set_hostlp(); - /* If we don't have a host, bail out */ - if (viopath_hostLp == HvLpIndexInvalid) - return -ENODEV; - } - - pr_info("vers " VIOCD_VERS ", hosting partition %d\n", viopath_hostLp); - - if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) { - pr_warning("Unable to get major %d for %s\n", - VIOCD_MAJOR, VIOCD_DEVICE); - return -EIO; - } - - ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio, - MAX_CD_REQ + 2); - if (ret) { - pr_warning("error opening path to host partition %d\n", - viopath_hostLp); - goto out_unregister; - } - - /* Initialize our request handler */ - vio_setHandler(viomajorsubtype_cdio, vio_handle_cd_event); - - spin_lock_init(&viocd_reqlock); - - ret = vio_register_driver(&viocd_driver); - if (ret) - goto out_free_info; - - proc_create("iSeries/viocd", S_IFREG|S_IRUGO, NULL, - &proc_viocd_operations); - return 0; - -out_free_info: - vio_clearHandler(viomajorsubtype_cdio); - viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); -out_unregister: - unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE); - return ret; -} - -static void __exit viocd_exit(void) -{ - remove_proc_entry("iSeries/viocd", NULL); - vio_unregister_driver(&viocd_driver); - viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ + 2); - vio_clearHandler(viomajorsubtype_cdio); - unregister_blkdev(VIOCD_MAJOR, VIOCD_DEVICE); -} - -module_init(viocd_init); -module_exit(viocd_exit); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c deleted file mode 100644 index ad6e64a..0000000 --- a/drivers/char/viotape.c +++ /dev/null @@ -1,1041 +0,0 @@ -/* -*- linux-c -*- - * drivers/char/viotape.c - * - * iSeries Virtual Tape - * - * Authors: Dave Boutcher - * Ryan Arnold - * Colin Devilbiss - * Stephen Rothwell - * - * (C) Copyright 2000-2004 IBM Corporation - * - * 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) anyu later version. - * - * 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 - * - * This routine provides access to tape drives owned and managed by an OS/400 - * partition running on the same box as this Linux partition. - * - * All tape operations are performed by sending messages back and forth to - * the OS/400 partition. The format of the messages is defined in - * iseries/vio.h - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define VIOTAPE_VERSION "1.2" -#define VIOTAPE_MAXREQ 1 - -#define VIOTAPE_KERN_WARN KERN_WARNING "viotape: " -#define VIOTAPE_KERN_INFO KERN_INFO "viotape: " - -static DEFINE_MUTEX(proc_viotape_mutex); -static int viotape_numdev; - -/* - * The minor number follows the conventions of the SCSI tape drives. The - * rewind and mode are encoded in the minor #. We use this struct to break - * them out - */ -struct viot_devinfo_struct { - int devno; - int mode; - int rewind; -}; - -#define VIOTAPOP_RESET 0 -#define VIOTAPOP_FSF 1 -#define VIOTAPOP_BSF 2 -#define VIOTAPOP_FSR 3 -#define VIOTAPOP_BSR 4 -#define VIOTAPOP_WEOF 5 -#define VIOTAPOP_REW 6 -#define VIOTAPOP_NOP 7 -#define VIOTAPOP_EOM 8 -#define VIOTAPOP_ERASE 9 -#define VIOTAPOP_SETBLK 10 -#define VIOTAPOP_SETDENSITY 11 -#define VIOTAPOP_SETPOS 12 -#define VIOTAPOP_GETPOS 13 -#define VIOTAPOP_SETPART 14 -#define VIOTAPOP_UNLOAD 15 - -enum viotaperc { - viotape_InvalidRange = 0x0601, - viotape_InvalidToken = 0x0602, - viotape_DMAError = 0x0603, - viotape_UseError = 0x0604, - viotape_ReleaseError = 0x0605, - viotape_InvalidTape = 0x0606, - viotape_InvalidOp = 0x0607, - viotape_TapeErr = 0x0608, - - viotape_AllocTimedOut = 0x0640, - viotape_BOTEnc = 0x0641, - viotape_BlankTape = 0x0642, - viotape_BufferEmpty = 0x0643, - viotape_CleanCartFound = 0x0644, - viotape_CmdNotAllowed = 0x0645, - viotape_CmdNotSupported = 0x0646, - viotape_DataCheck = 0x0647, - viotape_DecompressErr = 0x0648, - viotape_DeviceTimeout = 0x0649, - viotape_DeviceUnavail = 0x064a, - viotape_DeviceBusy = 0x064b, - viotape_EndOfMedia = 0x064c, - viotape_EndOfTape = 0x064d, - viotape_EquipCheck = 0x064e, - viotape_InsufficientRs = 0x064f, - viotape_InvalidLogBlk = 0x0650, - viotape_LengthError = 0x0651, - viotape_LibDoorOpen = 0x0652, - viotape_LoadFailure = 0x0653, - viotape_NotCapable = 0x0654, - viotape_NotOperational = 0x0655, - viotape_NotReady = 0x0656, - viotape_OpCancelled = 0x0657, - viotape_PhyLinkErr = 0x0658, - viotape_RdyNotBOT = 0x0659, - viotape_TapeMark = 0x065a, - viotape_WriteProt = 0x065b -}; - -static const struct vio_error_entry viotape_err_table[] = { - { viotape_InvalidRange, EIO, "Internal error" }, - { viotape_InvalidToken, EIO, "Internal error" }, - { viotape_DMAError, EIO, "DMA error" }, - { viotape_UseError, EIO, "Internal error" }, - { viotape_ReleaseError, EIO, "Internal error" }, - { viotape_InvalidTape, EIO, "Invalid tape device" }, - { viotape_InvalidOp, EIO, "Invalid operation" }, - { viotape_TapeErr, EIO, "Tape error" }, - { viotape_AllocTimedOut, EBUSY, "Allocate timed out" }, - { viotape_BOTEnc, EIO, "Beginning of tape encountered" }, - { viotape_BlankTape, EIO, "Blank tape" }, - { viotape_BufferEmpty, EIO, "Buffer empty" }, - { viotape_CleanCartFound, ENOMEDIUM, "Cleaning cartridge found" }, - { viotape_CmdNotAllowed, EIO, "Command not allowed" }, - { viotape_CmdNotSupported, EIO, "Command not supported" }, - { viotape_DataCheck, EIO, "Data check" }, - { viotape_DecompressErr, EIO, "Decompression error" }, - { viotape_DeviceTimeout, EBUSY, "Device timeout" }, - { viotape_DeviceUnavail, EIO, "Device unavailable" }, - { viotape_DeviceBusy, EBUSY, "Device busy" }, - { viotape_EndOfMedia, ENOSPC, "End of media" }, - { viotape_EndOfTape, ENOSPC, "End of tape" }, - { viotape_EquipCheck, EIO, "Equipment check" }, - { viotape_InsufficientRs, EOVERFLOW, "Insufficient tape resources" }, - { viotape_InvalidLogBlk, EIO, "Invalid logical block location" }, - { viotape_LengthError, EOVERFLOW, "Length error" }, - { viotape_LibDoorOpen, EBUSY, "Door open" }, - { viotape_LoadFailure, ENOMEDIUM, "Load failure" }, - { viotape_NotCapable, EIO, "Not capable" }, - { viotape_NotOperational, EIO, "Not operational" }, - { viotape_NotReady, EIO, "Not ready" }, - { viotape_OpCancelled, EIO, "Operation cancelled" }, - { viotape_PhyLinkErr, EIO, "Physical link error" }, - { viotape_RdyNotBOT, EIO, "Ready but not beginning of tape" }, - { viotape_TapeMark, EIO, "Tape mark" }, - { viotape_WriteProt, EROFS, "Write protection error" }, - { 0, 0, NULL }, -}; - -/* Maximum number of tapes we support */ -#define VIOTAPE_MAX_TAPE HVMAXARCHITECTEDVIRTUALTAPES -#define MAX_PARTITIONS 4 - -/* defines for current tape state */ -#define VIOT_IDLE 0 -#define VIOT_READING 1 -#define VIOT_WRITING 2 - -/* Our info on the tapes */ -static struct { - const char *rsrcname; - const char *type; - const char *model; -} viotape_unitinfo[VIOTAPE_MAX_TAPE]; - -static struct mtget viomtget[VIOTAPE_MAX_TAPE]; - -static struct class *tape_class; - -static struct device *tape_device[VIOTAPE_MAX_TAPE]; - -/* - * maintain the current state of each tape (and partition) - * so that we know when to write EOF marks. - */ -static struct { - unsigned char cur_part; - unsigned char part_stat_rwi[MAX_PARTITIONS]; -} state[VIOTAPE_MAX_TAPE]; - -/* We single-thread */ -static struct semaphore reqSem; - -/* - * When we send a request, we use this struct to get the response back - * from the interrupt handler - */ -struct op_struct { - void *buffer; - dma_addr_t dmaaddr; - size_t count; - int rc; - int non_blocking; - struct completion com; - struct device *dev; - struct op_struct *next; -}; - -static spinlock_t op_struct_list_lock; -static struct op_struct *op_struct_list; - -/* forward declaration to resolve interdependence */ -static int chg_state(int index, unsigned char new_state, struct file *file); - -/* procfs support */ -static int proc_viotape_show(struct seq_file *m, void *v) -{ - int i; - - seq_printf(m, "viotape driver version " VIOTAPE_VERSION "\n"); - for (i = 0; i < viotape_numdev; i++) { - seq_printf(m, "viotape device %d is iSeries resource %10.10s" - "type %4.4s, model %3.3s\n", - i, viotape_unitinfo[i].rsrcname, - viotape_unitinfo[i].type, - viotape_unitinfo[i].model); - } - return 0; -} - -static int proc_viotape_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_viotape_show, NULL); -} - -static const struct file_operations proc_viotape_operations = { - .owner = THIS_MODULE, - .open = proc_viotape_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* Decode the device minor number into its parts */ -void get_dev_info(struct inode *ino, struct viot_devinfo_struct *devi) -{ - devi->devno = iminor(ino) & 0x1F; - devi->mode = (iminor(ino) & 0x60) >> 5; - /* if bit is set in the minor, do _not_ rewind automatically */ - devi->rewind = (iminor(ino) & 0x80) == 0; -} - -/* This is called only from the exit and init paths, so no need for locking */ -static void clear_op_struct_pool(void) -{ - while (op_struct_list) { - struct op_struct *toFree = op_struct_list; - op_struct_list = op_struct_list->next; - kfree(toFree); - } -} - -/* Likewise, this is only called from the init path */ -static int add_op_structs(int structs) -{ - int i; - - for (i = 0; i < structs; ++i) { - struct op_struct *new_struct = - kmalloc(sizeof(*new_struct), GFP_KERNEL); - if (!new_struct) { - clear_op_struct_pool(); - return -ENOMEM; - } - new_struct->next = op_struct_list; - op_struct_list = new_struct; - } - return 0; -} - -/* Allocate an op structure from our pool */ -static struct op_struct *get_op_struct(void) -{ - struct op_struct *retval; - unsigned long flags; - - spin_lock_irqsave(&op_struct_list_lock, flags); - retval = op_struct_list; - if (retval) - op_struct_list = retval->next; - spin_unlock_irqrestore(&op_struct_list_lock, flags); - if (retval) { - memset(retval, 0, sizeof(*retval)); - init_completion(&retval->com); - } - - return retval; -} - -/* Return an op structure to our pool */ -static void free_op_struct(struct op_struct *op_struct) -{ - unsigned long flags; - - spin_lock_irqsave(&op_struct_list_lock, flags); - op_struct->next = op_struct_list; - op_struct_list = op_struct; - spin_unlock_irqrestore(&op_struct_list_lock, flags); -} - -/* Map our tape return codes to errno values */ -int tape_rc_to_errno(int tape_rc, char *operation, int tapeno) -{ - const struct vio_error_entry *err; - - if (tape_rc == 0) - return 0; - - err = vio_lookup_rc(viotape_err_table, tape_rc); - printk(VIOTAPE_KERN_WARN "error(%s) 0x%04x on Device %d (%-10s): %s\n", - operation, tape_rc, tapeno, - viotape_unitinfo[tapeno].rsrcname, err->msg); - return -err->errno; -} - -/* Write */ -static ssize_t viotap_write(struct file *file, const char *buf, - size_t count, loff_t * ppos) -{ - HvLpEvent_Rc hvrc; - unsigned short flags = file->f_flags; - int noblock = ((flags & O_NONBLOCK) != 0); - ssize_t ret; - struct viot_devinfo_struct devi; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - /* - * We need to make sure we can send a request. We use - * a semaphore to keep track of # requests in use. If - * we are non-blocking, make sure we don't block on the - * semaphore - */ - if (noblock) { - if (down_trylock(&reqSem)) { - ret = -EWOULDBLOCK; - goto free_op; - } - } else - down(&reqSem); - - /* Allocate a DMA buffer */ - op->dev = tape_device[devi.devno]; - op->buffer = dma_alloc_coherent(op->dev, count, &op->dmaaddr, - GFP_ATOMIC); - - if (op->buffer == NULL) { - printk(VIOTAPE_KERN_WARN - "error allocating dma buffer for len %ld\n", - count); - ret = -EFAULT; - goto up_sem; - } - - /* Copy the data into the buffer */ - if (copy_from_user(op->buffer, buf, count)) { - printk(VIOTAPE_KERN_WARN "tape: error on copy from user\n"); - ret = -EFAULT; - goto free_dma; - } - - op->non_blocking = noblock; - init_completion(&op->com); - op->count = count; - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapewrite, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48) | op->dmaaddr, count, 0, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", - (int)hvrc); - ret = -EIO; - goto free_dma; - } - - if (noblock) - return count; - - wait_for_completion(&op->com); - - if (op->rc) - ret = tape_rc_to_errno(op->rc, "write", devi.devno); - else { - chg_state(devi.devno, VIOT_WRITING, file); - ret = op->count; - } - -free_dma: - dma_free_coherent(op->dev, count, op->buffer, op->dmaaddr); -up_sem: - up(&reqSem); -free_op: - free_op_struct(op); - return ret; -} - -/* read */ -static ssize_t viotap_read(struct file *file, char *buf, size_t count, - loff_t *ptr) -{ - HvLpEvent_Rc hvrc; - unsigned short flags = file->f_flags; - struct op_struct *op = get_op_struct(); - int noblock = ((flags & O_NONBLOCK) != 0); - ssize_t ret; - struct viot_devinfo_struct devi; - - if (op == NULL) - return -ENOMEM; - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - /* - * We need to make sure we can send a request. We use - * a semaphore to keep track of # requests in use. If - * we are non-blocking, make sure we don't block on the - * semaphore - */ - if (noblock) { - if (down_trylock(&reqSem)) { - ret = -EWOULDBLOCK; - goto free_op; - } - } else - down(&reqSem); - - chg_state(devi.devno, VIOT_READING, file); - - /* Allocate a DMA buffer */ - op->dev = tape_device[devi.devno]; - op->buffer = dma_alloc_coherent(op->dev, count, &op->dmaaddr, - GFP_ATOMIC); - if (op->buffer == NULL) { - ret = -EFAULT; - goto up_sem; - } - - op->count = count; - init_completion(&op->com); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotaperead, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48) | op->dmaaddr, count, 0, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "tape hv error on op %d\n", - (int)hvrc); - ret = -EIO; - goto free_dma; - } - - wait_for_completion(&op->com); - - if (op->rc) - ret = tape_rc_to_errno(op->rc, "read", devi.devno); - else { - ret = op->count; - if (ret && copy_to_user(buf, op->buffer, ret)) { - printk(VIOTAPE_KERN_WARN "error on copy_to_user\n"); - ret = -EFAULT; - } - } - -free_dma: - dma_free_coherent(op->dev, count, op->buffer, op->dmaaddr); -up_sem: - up(&reqSem); -free_op: - free_op_struct(op); - return ret; -} - -/* ioctl */ -static int viotap_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - HvLpEvent_Rc hvrc; - int ret; - struct viot_devinfo_struct devi; - struct mtop mtc; - u32 myOp; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - down(&reqSem); - - ret = -EINVAL; - - switch (cmd) { - case MTIOCTOP: - ret = -EFAULT; - /* - * inode is null if and only if we (the kernel) - * made the request - */ - if (inode == NULL) - memcpy(&mtc, (void *) arg, sizeof(struct mtop)); - else if (copy_from_user((char *)&mtc, (char *)arg, - sizeof(struct mtop))) - goto free_op; - - ret = -EIO; - switch (mtc.mt_op) { - case MTRESET: - myOp = VIOTAPOP_RESET; - break; - case MTFSF: - myOp = VIOTAPOP_FSF; - break; - case MTBSF: - myOp = VIOTAPOP_BSF; - break; - case MTFSR: - myOp = VIOTAPOP_FSR; - break; - case MTBSR: - myOp = VIOTAPOP_BSR; - break; - case MTWEOF: - myOp = VIOTAPOP_WEOF; - break; - case MTREW: - myOp = VIOTAPOP_REW; - break; - case MTNOP: - myOp = VIOTAPOP_NOP; - break; - case MTEOM: - myOp = VIOTAPOP_EOM; - break; - case MTERASE: - myOp = VIOTAPOP_ERASE; - break; - case MTSETBLK: - myOp = VIOTAPOP_SETBLK; - break; - case MTSETDENSITY: - myOp = VIOTAPOP_SETDENSITY; - break; - case MTTELL: - myOp = VIOTAPOP_GETPOS; - break; - case MTSEEK: - myOp = VIOTAPOP_SETPOS; - break; - case MTSETPART: - myOp = VIOTAPOP_SETPART; - break; - case MTOFFL: - myOp = VIOTAPOP_UNLOAD; - break; - default: - printk(VIOTAPE_KERN_WARN "MTIOCTOP called " - "with invalid op 0x%x\n", mtc.mt_op); - goto free_op; - } - - /* - * if we moved the head, we are no longer - * reading or writing - */ - switch (mtc.mt_op) { - case MTFSF: - case MTBSF: - case MTFSR: - case MTBSR: - case MTTELL: - case MTSEEK: - case MTREW: - chg_state(devi.devno, VIOT_IDLE, file); - } - - init_completion(&op->com); - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeop, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, - VIOVERSION << 16, - ((u64)devi.devno << 48), 0, - (((u64)myOp) << 32) | mtc.mt_count, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", - (int)hvrc); - goto free_op; - } - wait_for_completion(&op->com); - ret = tape_rc_to_errno(op->rc, "tape operation", devi.devno); - goto free_op; - - case MTIOCGET: - ret = -EIO; - init_completion(&op->com); - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapegetstatus, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, 0, 0); - if (hvrc != HvLpEvent_Rc_Good) { - printk(VIOTAPE_KERN_WARN "hv error on op %d\n", - (int)hvrc); - goto free_op; - } - wait_for_completion(&op->com); - - /* Operation is complete - grab the error code */ - ret = tape_rc_to_errno(op->rc, "get status", devi.devno); - free_op_struct(op); - up(&reqSem); - - if ((ret == 0) && copy_to_user((void *)arg, - &viomtget[devi.devno], - sizeof(viomtget[0]))) - ret = -EFAULT; - return ret; - case MTIOCPOS: - printk(VIOTAPE_KERN_WARN "Got an (unsupported) MTIOCPOS\n"); - break; - default: - printk(VIOTAPE_KERN_WARN "got an unsupported ioctl 0x%0x\n", - cmd); - break; - } - -free_op: - free_op_struct(op); - up(&reqSem); - return ret; -} - -static long viotap_unlocked_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - long rc; - - mutex_lock(&proc_viotape_mutex); - rc = viotap_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - mutex_unlock(&proc_viotape_mutex); - return rc; -} - -static int viotap_open(struct inode *inode, struct file *file) -{ - HvLpEvent_Rc hvrc; - struct viot_devinfo_struct devi; - int ret; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - - mutex_lock(&proc_viotape_mutex); - get_dev_info(file->f_path.dentry->d_inode, &devi); - - /* Note: We currently only support one mode! */ - if ((devi.devno >= viotape_numdev) || (devi.mode)) { - ret = -ENODEV; - goto free_op; - } - - init_completion(&op->com); - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, 0, 0); - if (hvrc != 0) { - printk(VIOTAPE_KERN_WARN "bad rc on signalLpEvent %d\n", - (int) hvrc); - ret = -EIO; - goto free_op; - } - - wait_for_completion(&op->com); - ret = tape_rc_to_errno(op->rc, "open", devi.devno); - -free_op: - free_op_struct(op); - mutex_unlock(&proc_viotape_mutex); - return ret; -} - - -static int viotap_release(struct inode *inode, struct file *file) -{ - HvLpEvent_Rc hvrc; - struct viot_devinfo_struct devi; - int ret = 0; - struct op_struct *op = get_op_struct(); - - if (op == NULL) - return -ENOMEM; - init_completion(&op->com); - - get_dev_info(file->f_path.dentry->d_inode, &devi); - - if (devi.devno >= viotape_numdev) { - ret = -ENODEV; - goto free_op; - } - - chg_state(devi.devno, VIOT_IDLE, file); - - if (devi.rewind) { - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeop, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, - ((u64)VIOTAPOP_REW) << 32, 0); - wait_for_completion(&op->com); - - tape_rc_to_errno(op->rc, "rewind", devi.devno); - } - - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeclose, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)op, VIOVERSION << 16, - ((u64)devi.devno << 48), 0, 0, 0); - if (hvrc != 0) { - printk(VIOTAPE_KERN_WARN "bad rc on signalLpEvent %d\n", - (int) hvrc); - ret = -EIO; - goto free_op; - } - - wait_for_completion(&op->com); - - if (op->rc) - printk(VIOTAPE_KERN_WARN "close failed\n"); - -free_op: - free_op_struct(op); - return ret; -} - -const struct file_operations viotap_fops = { - .owner = THIS_MODULE, - .read = viotap_read, - .write = viotap_write, - .unlocked_ioctl = viotap_unlocked_ioctl, - .open = viotap_open, - .release = viotap_release, - .llseek = noop_llseek, -}; - -/* Handle interrupt events for tape */ -static void vioHandleTapeEvent(struct HvLpEvent *event) -{ - int tapeminor; - struct op_struct *op; - struct viotapelpevent *tevent = (struct viotapelpevent *)event; - - if (event == NULL) { - /* Notification that a partition went away! */ - if (!viopath_isactive(viopath_hostLp)) { - /* TODO! Clean up */ - } - return; - } - - tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; - op = (struct op_struct *)event->xCorrelationToken; - switch (tapeminor) { - case viotapeopen: - case viotapeclose: - op->rc = tevent->sub_type_result; - complete(&op->com); - break; - case viotaperead: - op->rc = tevent->sub_type_result; - op->count = tevent->len; - complete(&op->com); - break; - case viotapewrite: - if (op->non_blocking) { - dma_free_coherent(op->dev, op->count, - op->buffer, op->dmaaddr); - free_op_struct(op); - up(&reqSem); - } else { - op->rc = tevent->sub_type_result; - op->count = tevent->len; - complete(&op->com); - } - break; - case viotapeop: - case viotapegetpos: - case viotapesetpos: - case viotapegetstatus: - if (op) { - op->count = tevent->u.op.count; - op->rc = tevent->sub_type_result; - if (!op->non_blocking) - complete(&op->com); - } - break; - default: - printk(VIOTAPE_KERN_WARN "weird ack\n"); - } -} - -static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id) -{ - int i = vdev->unit_address; - int j; - struct device_node *node = vdev->dev.of_node; - - if (i >= VIOTAPE_MAX_TAPE) - return -ENODEV; - if (!node) - return -ENODEV; - - if (i >= viotape_numdev) - viotape_numdev = i + 1; - - tape_device[i] = &vdev->dev; - viotape_unitinfo[i].rsrcname = of_get_property(node, - "linux,vio_rsrcname", NULL); - viotape_unitinfo[i].type = of_get_property(node, "linux,vio_type", - NULL); - viotape_unitinfo[i].model = of_get_property(node, "linux,vio_model", - NULL); - - state[i].cur_part = 0; - for (j = 0; j < MAX_PARTITIONS; ++j) - state[i].part_stat_rwi[j] = VIOT_IDLE; - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i), NULL, - "iseries!vt%d", i); - device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80), NULL, - "iseries!nvt%d", i); - printk(VIOTAPE_KERN_INFO "tape iseries/vt%d is iSeries " - "resource %10.10s type %4.4s, model %3.3s\n", - i, viotape_unitinfo[i].rsrcname, - viotape_unitinfo[i].type, viotape_unitinfo[i].model); - return 0; -} - -static int viotape_remove(struct vio_dev *vdev) -{ - int i = vdev->unit_address; - - device_destroy(tape_class, MKDEV(VIOTAPE_MAJOR, i | 0x80)); - device_destroy(tape_class, MKDEV(VIOTAPE_MAJOR, i)); - return 0; -} - -/** - * viotape_device_table: Used by vio.c to match devices that we - * support. - */ -static struct vio_device_id viotape_device_table[] __devinitdata = { - { "byte", "IBM,iSeries-viotape" }, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, viotape_device_table); - -static struct vio_driver viotape_driver = { - .id_table = viotape_device_table, - .probe = viotape_probe, - .remove = viotape_remove, - .driver = { - .name = "viotape", - .owner = THIS_MODULE, - } -}; - - -int __init viotap_init(void) -{ - int ret; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - - op_struct_list = NULL; - if ((ret = add_op_structs(VIOTAPE_MAXREQ)) < 0) { - printk(VIOTAPE_KERN_WARN "couldn't allocate op structs\n"); - return ret; - } - spin_lock_init(&op_struct_list_lock); - - sema_init(&reqSem, VIOTAPE_MAXREQ); - - if (viopath_hostLp == HvLpIndexInvalid) { - vio_set_hostlp(); - if (viopath_hostLp == HvLpIndexInvalid) { - ret = -ENODEV; - goto clear_op; - } - } - - ret = viopath_open(viopath_hostLp, viomajorsubtype_tape, - VIOTAPE_MAXREQ + 2); - if (ret) { - printk(VIOTAPE_KERN_WARN - "error on viopath_open to hostlp %d\n", ret); - ret = -EIO; - goto clear_op; - } - - printk(VIOTAPE_KERN_INFO "vers " VIOTAPE_VERSION - ", hosting partition %d\n", viopath_hostLp); - - vio_setHandler(viomajorsubtype_tape, vioHandleTapeEvent); - - ret = register_chrdev(VIOTAPE_MAJOR, "viotape", &viotap_fops); - if (ret < 0) { - printk(VIOTAPE_KERN_WARN "Error registering viotape device\n"); - goto clear_handler; - } - - tape_class = class_create(THIS_MODULE, "tape"); - if (IS_ERR(tape_class)) { - printk(VIOTAPE_KERN_WARN "Unable to allocat class\n"); - ret = PTR_ERR(tape_class); - goto unreg_chrdev; - } - - ret = vio_register_driver(&viotape_driver); - if (ret) - goto unreg_class; - - proc_create("iSeries/viotape", S_IFREG|S_IRUGO, NULL, - &proc_viotape_operations); - - return 0; - -unreg_class: - class_destroy(tape_class); -unreg_chrdev: - unregister_chrdev(VIOTAPE_MAJOR, "viotape"); -clear_handler: - vio_clearHandler(viomajorsubtype_tape); - viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); -clear_op: - clear_op_struct_pool(); - return ret; -} - -/* Give a new state to the tape object */ -static int chg_state(int index, unsigned char new_state, struct file *file) -{ - unsigned char *cur_state = - &state[index].part_stat_rwi[state[index].cur_part]; - int rc = 0; - - /* if the same state, don't bother */ - if (*cur_state == new_state) - return 0; - - /* write an EOF if changing from writing to some other state */ - if (*cur_state == VIOT_WRITING) { - struct mtop write_eof = { MTWEOF, 1 }; - - rc = viotap_ioctl(NULL, file, MTIOCTOP, - (unsigned long)&write_eof); - } - *cur_state = new_state; - return rc; -} - -/* Cleanup */ -static void __exit viotap_exit(void) -{ - remove_proc_entry("iSeries/viotape", NULL); - vio_unregister_driver(&viotape_driver); - class_destroy(tape_class); - unregister_chrdev(VIOTAPE_MAJOR, "viotape"); - viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); - vio_clearHandler(viomajorsubtype_tape); - clear_op_struct_pool(); -} - -MODULE_LICENSE("GPL"); -module_init(viotap_init); -module_exit(viotap_exit); -- cgit v0.10.2 From 7d3d897a4697e4ff746e5e82f116b2346ed28150 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 14 Mar 2012 18:37:04 +1100 Subject: powerpc/hvc_udbg: Don't crash when udbg_putc is NULL Also while at it, add some help text indicating why you shouldn't enable that driver under normal circumstances Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 8ea7f07..48cb8d3 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -71,6 +71,10 @@ config HVC_UDBG depends on PPC && EXPERIMENTAL select HVC_DRIVER default n + help + This is meant to be used during HW bring up or debugging when + no other console mechanism exist but udbg, to get you a quick + console for userspace. Do NOT enable in production kernels. config HVC_DCC bool "ARM JTAG DCC console" diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c index b0957e6..2259c6e 100644 --- a/drivers/tty/hvc/hvc_udbg.c +++ b/drivers/tty/hvc/hvc_udbg.c @@ -36,7 +36,7 @@ static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) { int i; - for (i = 0; i < count; i++) + for (i = 0; i < count && udbg_putc; i++) udbg_putc(buf[i]); return i; @@ -67,6 +67,9 @@ static int __init hvc_udbg_init(void) { struct hvc_struct *hp; + if (!udbg_putc) + return -ENODEV; + BUG_ON(hvc_udbg_dev); hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16); @@ -88,6 +91,9 @@ module_exit(hvc_udbg_exit); static int __init hvc_udbg_console_init(void) { + if (!udbg_putc) + return -ENODEV; + hvc_instantiate(0, 0, &hvc_udbg_ops); add_preferred_console("hvc", 0, NULL); -- cgit v0.10.2 From 01e8ec4417d3c484986af0adaa0ae6632e0a59cd Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 16 Mar 2012 09:26:59 +1100 Subject: powerpc: Fix power4/970 idle code regression with lockdep in commit 7230c5644188cd9e3fb380cc97dde00c464a3ba7 "powerpc: Rework lazy-interrupt handling" I introduced a regression, accidentally calling irq tracing twice and not properly restoring a clobbered register (r7) later used for writing to the MSR. This caused lockups when booting on a G5 with lockdep enabled. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/idle_power4.S b/arch/powerpc/kernel/idle_power4.S index d8cdba4c2..2c71b0f 100644 --- a/arch/powerpc/kernel/idle_power4.S +++ b/arch/powerpc/kernel/idle_power4.S @@ -50,9 +50,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP) addi r1,r1,128 ld r0,16(r1) mtlr r0 + mfmsr r7 #endif /* CONFIG_TRACE_IRQFLAGS */ - TRACE_ENABLE_INTS li r0,1 stb r0,PACASOFTIRQEN(r13) /* we'll hard-enable shortly */ BEGIN_FTR_SECTION -- cgit v0.10.2 From 490bdb77b64376ead1ba0f4b011f5abea360bbc5 Mon Sep 17 00:00:00 2001 From: Xu Jiucheng Date: Tue, 17 Jan 2012 16:01:29 +0800 Subject: powerpc/85xx: Added dts for P1021RDB-PC board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1021RDB-PC Overview ----------------- 1Gbyte DDR3 (on board DDR) 16Mbyte NOR flash 32Mbyte eSLC NAND Flash 256 Kbit M24256 I2C EEPROM 128 Mbit SPI Flash memory Real-time clock on I2C bus SD/MMC connector to interface with the SD memory card PCIex - x1 PCIe slot or x1 PCIe to dual SATA controller - x1 mini-PCIe slot USB 2.0 - ULPI PHY interface: SMSC USB3300 USB PHY and Genesys Logic’s GL850A - Two USB2.0 Type A receptacles - One USB2.0 signal to Mini PCIe slot eTSEC1: Connected to RGMII PHY VSC7385 eTSEC2: Connected to SGMII PHY VSC8221 eTSEC3: Connected to SGMII PHY AR8021 DUART interface: supports two UARTs up to 115200 bps for console display Signed-off-by: Matthew McClintock Signed-off-by: Xu Jiucheng Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi index 38ba54d..b7929c9 100644 --- a/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi @@ -144,6 +144,10 @@ /include/ "pq3-usb2-dr-0.dtsi" /include/ "pq3-esdhc-0.dtsi" + sdhc@2e000 { + sdhci,auto-cmd12; + }; + /include/ "pq3-sec3.3-0.dtsi" /include/ "pq3-mpic.dtsi" diff --git a/arch/powerpc/boot/dts/p1021rdb.dts b/arch/powerpc/boot/dts/p1021rdb.dts new file mode 100644 index 0000000..90b6b4c --- /dev/null +++ b/arch/powerpc/boot/dts/p1021rdb.dts @@ -0,0 +1,96 @@ +/* + * P1021 RDB Device Tree Source + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1021si-pre.dtsi" +/ { + model = "fsl,P1021RDB"; + compatible = "fsl,P1021RDB-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@ffe05000 { + reg = <0 0xffe05000 0 0x1000>; + + /* NOR, NAND Flashes and Vitesse 5 port L2 switch */ + ranges = <0x0 0x0 0x0 0xef000000 0x01000000 + 0x1 0x0 0x0 0xff800000 0x00040000 + 0x2 0x0 0x0 0xffb00000 0x00020000>; + }; + + soc: soc@ffe00000 { + ranges = <0x0 0x0 0xffe00000 0x100000>; + }; + + pci0: pcie@ffe09000 { + ranges = <0x2000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc10000 0x0 0x10000>; + reg = <0 0xffe09000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xa0000000 + 0x2000000 0x0 0xa0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@ffe0a000 { + reg = <0 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0x80000000 0 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0x80000000 + 0x2000000 0x0 0x80000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + qe: qe@ffe80000 { + ranges = <0x0 0x0 0xffe80000 0x40000>; + reg = <0 0xffe80000 0 0x480>; + brg-frequency = <0>; + bus-frequency = <0>; + }; +}; + +/include/ "p1021rdb.dtsi" +/include/ "fsl/p1021si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1021rdb.dtsi b/arch/powerpc/boot/dts/p1021rdb.dtsi new file mode 100644 index 0000000..b973461 --- /dev/null +++ b/arch/powerpc/boot/dts/p1021rdb.dtsi @@ -0,0 +1,236 @@ +/* + * P1021 RDB Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x1000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* This location must not be altered */ + /* 256KB for Vitesse 7385 Switch firmware */ + reg = <0x0 0x00040000>; + label = "NOR Vitesse-7385 Firmware"; + read-only; + }; + + partition@40000 { + /* 256KB for DTB Image */ + reg = <0x00040000 0x00040000>; + label = "NOR DTB Image"; + }; + + partition@80000 { + /* 3.5 MB for Linux Kernel Image */ + reg = <0x00080000 0x00380000>; + label = "NOR Linux Kernel Image"; + }; + + partition@400000 { + /* 11MB for JFFS2 based Root file System */ + reg = <0x00400000 0x00b00000>; + label = "NOR JFFS2 Root File System"; + }; + + partition@f00000 { + /* This location must not be altered */ + /* 512KB for u-boot Bootloader Image */ + /* 512KB for u-boot Environment Variables */ + reg = <0x00f00000 0x00100000>; + label = "NOR U-Boot Image"; + }; + }; + + nand@1,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p1021-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x1 0x0 0x40000>; + + partition@0 { + /* This location must not be altered */ + /* 1MB for u-boot Bootloader Image */ + reg = <0x0 0x00100000>; + label = "NAND U-Boot Image"; + read-only; + }; + + partition@100000 { + /* 1MB for DTB Image */ + reg = <0x00100000 0x00100000>; + label = "NAND DTB Image"; + }; + + partition@200000 { + /* 4MB for Linux Kernel Image */ + reg = <0x00200000 0x00400000>; + label = "NAND Linux Kernel Image"; + }; + + partition@600000 { + /* 4MB for Compressed Root file System Image */ + reg = <0x00600000 0x00400000>; + label = "NAND Compressed RFS Image"; + }; + + partition@a00000 { + /* 7MB for JFFS2 based Root file System */ + reg = <0x00a00000 0x00700000>; + label = "NAND JFFS2 Root File System"; + }; + + partition@1100000 { + /* 15MB for User Writable Area */ + reg = <0x01100000 0x00f00000>; + label = "NAND Writable User area"; + }; + }; + + L2switch@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "vitesse-7385"; + reg = <0x2 0x0 0x20000>; + }; +}; + +&soc { + i2c@3000 { + rtc@68 { + compatible = "pericom,pt7c4338"; + reg = <0x68>; + }; + }; + + spi@7000 { + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25sl12801"; + reg = <0>; + spi-max-frequency = <40000000>; /* input clock */ + + partition@u-boot { + /* 512KB for u-boot Bootloader Image */ + reg = <0x0 0x00080000>; + label = "SPI Flash U-Boot Image"; + read-only; + }; + + partition@dtb { + /* 512KB for DTB Image */ + reg = <0x00080000 0x00080000>; + label = "SPI Flash DTB Image"; + }; + + partition@kernel { + /* 4MB for Linux Kernel Image */ + reg = <0x00100000 0x00400000>; + label = "SPI Flash Linux Kernel Image"; + }; + + partition@fs { + /* 4MB for Compressed RFS Image */ + reg = <0x00500000 0x00400000>; + label = "SPI Flash Compressed RFSImage"; + }; + + partition@jffs-fs { + /* 7MB for JFFS2 based RFS */ + reg = <0x00900000 0x00700000>; + label = "SPI Flash JFFS2 RFS"; + }; + }; + }; + + usb@22000 { + phy_type = "ulpi"; + }; + + mdio@24000 { + phy0: ethernet-phy@0 { + interrupt-parent = <&mpic>; + interrupts = <3 1 0 0>; + reg = <0x0>; + }; + + phy1: ethernet-phy@1 { + interrupt-parent = <&mpic>; + interrupts = <2 1 0 0>; + reg = <0x1>; + }; + + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@25000 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@26000 { + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet0: ethernet@b0000 { + fixed-link = <1 1 1000 0 0>; + phy-connection-type = "rgmii-id"; + + }; + + enet1: ethernet@b1000 { + phy-handle = <&phy0>; + tbi-handle = <&tbi1>; + phy-connection-type = "sgmii"; + }; + + enet2: ethernet@b2000 { + phy-handle = <&phy1>; + tbi-handle = <&tbi2>; + phy-connection-type = "rgmii-id"; + }; +}; diff --git a/arch/powerpc/boot/dts/p1021rdb_36b.dts b/arch/powerpc/boot/dts/p1021rdb_36b.dts new file mode 100644 index 0000000..ea6d8b5 --- /dev/null +++ b/arch/powerpc/boot/dts/p1021rdb_36b.dts @@ -0,0 +1,96 @@ +/* + * P1021 RDB Device Tree Source (36-bit address map) + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1021si-pre.dtsi" +/ { + model = "fsl,P1021RDB"; + compatible = "fsl,P1021RDB-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@fffe05000 { + reg = <0xf 0xffe05000 0 0x1000>; + + /* NOR, NAND Flashes and Vitesse 5 port L2 switch */ + ranges = <0x0 0x0 0xf 0xef000000 0x01000000 + 0x1 0x0 0xf 0xff800000 0x00040000 + 0x2 0x0 0xf 0xffb00000 0x00020000>; + }; + + soc: soc@fffe00000 { + ranges = <0x0 0xf 0xffe00000 0x100000>; + }; + + pci0: pcie@fffe09000 { + ranges = <0x2000000 0x0 0xc0000000 0xc 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; + reg = <0xf 0xffe09000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xa0000000 + 0x2000000 0x0 0xa0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@fffe0a000 { + reg = <0xf 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0x80000000 0xc 0x00000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xc0000000 + 0x2000000 0x0 0xc0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + qe: qe@fffe80000 { + ranges = <0x0 0xf 0xffe80000 0x40000>; + reg = <0xf 0xffe80000 0 0x480>; + brg-frequency = <0>; + bus-frequency = <0>; + }; +}; + +/include/ "p1021rdb.dtsi" +/include/ "fsl/p1021si-post.dtsi" -- cgit v0.10.2 From b73bdf48faf32fa9ac8e8b442bc58c6f7b17cce9 Mon Sep 17 00:00:00 2001 From: Xu Jiucheng Date: Tue, 17 Jan 2012 16:01:30 +0800 Subject: powerpc/85xx: Added P1021RDB-PC Platform support Signed-off-by: Xu Jiucheng Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 407c739..75c9eed 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -89,6 +89,7 @@ static void __init mpc85xx_rdb_setup_arch(void) machine_device_initcall(p2020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p1020_rdb, mpc85xx_common_publish_devices); +machine_device_initcall(p1021_rdb_pc, mpc85xx_common_publish_devices); /* * Called very early, device-tree isn't unflattened @@ -111,6 +112,15 @@ static int __init p1020_rdb_probe(void) return 0; } +static int __init p1021_rdb_pc_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,P1021RDB-PC")) + return 1; + return 0; +} + define_machine(p2020_rdb) { .name = "P2020 RDB", .probe = p2020_rdb_probe, @@ -138,3 +148,17 @@ define_machine(p1020_rdb) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, }; + +define_machine(p1021_rdb_pc) { + .name = "P1021 RDB-PC", + .probe = p1021_rdb_pc_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; -- cgit v0.10.2 From 35ce1b5a20be296c0f2691955dbc86fa717aa584 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Wed, 28 Dec 2011 11:41:47 +0800 Subject: powerpc/85xx: Adds Support for P2020RDB-PC board P2020RDB-PC Board shares the same design(PCB) as P102x RDB style platforms. The difference between this platform and the already existing P2020RDB is mainly with respect to DDR. The P2020RDB-PC has a DDR3 memory. The P2020RDB-PC also has a CPLD device connected to local bus. The main differences from the P102x RDB-PC is 64-bit DDR and SYSCLK of 100Mhz. Signed-off-by: Prabhakar Kushwaha Signed-off-by: Poonam Aggrwal Signed-off-by: Tang Yuantian Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 75c9eed..e77e813 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -88,6 +88,7 @@ static void __init mpc85xx_rdb_setup_arch(void) } machine_device_initcall(p2020_rdb, mpc85xx_common_publish_devices); +machine_device_initcall(p2020_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p1021_rdb_pc, mpc85xx_common_publish_devices); @@ -121,6 +122,15 @@ static int __init p1021_rdb_pc_probe(void) return 0; } +static int __init p2020_rdb_pc_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,P2020RDB-PC")) + return 1; + return 0; +} + define_machine(p2020_rdb) { .name = "P2020 RDB", .probe = p2020_rdb_probe, @@ -162,3 +172,17 @@ define_machine(p1021_rdb_pc) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, }; + +define_machine(p2020_rdb_pc) { + .name = "P2020RDB-PC", + .probe = p2020_rdb_pc_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; -- cgit v0.10.2 From 05413245fb993bb4e2d54ead0675226394d8a7c8 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Thu, 9 Feb 2012 21:59:57 +0000 Subject: powerpc/85xx: Add p2020rdb-pc dts support Signed-off-by: Prabhakar Kushwaha Signed-off-by: Poonam Aggrwal Signed-off-by: Tang Yuantian Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p2020rdb-pc.dtsi b/arch/powerpc/boot/dts/p2020rdb-pc.dtsi new file mode 100644 index 0000000..c21d1c7 --- /dev/null +++ b/arch/powerpc/boot/dts/p2020rdb-pc.dtsi @@ -0,0 +1,241 @@ +/* + * P2020 RDB-PC Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x1000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* This location must not be altered */ + /* 256KB for Vitesse 7385 Switch firmware */ + reg = <0x0 0x00040000>; + label = "NOR Vitesse-7385 Firmware"; + read-only; + }; + + partition@40000 { + /* 256KB for DTB Image */ + reg = <0x00040000 0x00040000>; + label = "NOR DTB Image"; + }; + + partition@80000 { + /* 3.5 MB for Linux Kernel Image */ + reg = <0x00080000 0x00380000>; + label = "NOR Linux Kernel Image"; + }; + + partition@400000 { + /* 11MB for JFFS2 based Root file System */ + reg = <0x00400000 0x00b00000>; + label = "NOR JFFS2 Root File System"; + }; + + partition@f00000 { + /* This location must not be altered */ + /* 512KB for u-boot Bootloader Image */ + /* 512KB for u-boot Environment Variables */ + reg = <0x00f00000 0x00100000>; + label = "NOR U-Boot Image"; + read-only; + }; + }; + + nand@1,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p2020-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x1 0x0 0x40000>; + + partition@0 { + /* This location must not be altered */ + /* 1MB for u-boot Bootloader Image */ + reg = <0x0 0x00100000>; + label = "NAND U-Boot Image"; + read-only; + }; + + partition@100000 { + /* 1MB for DTB Image */ + reg = <0x00100000 0x00100000>; + label = "NAND DTB Image"; + }; + + partition@200000 { + /* 4MB for Linux Kernel Image */ + reg = <0x00200000 0x00400000>; + label = "NAND Linux Kernel Image"; + }; + + partition@600000 { + /* 4MB for Compressed Root file System Image */ + reg = <0x00600000 0x00400000>; + label = "NAND Compressed RFS Image"; + }; + + partition@a00000 { + /* 7MB for JFFS2 based Root file System */ + reg = <0x00a00000 0x00700000>; + label = "NAND JFFS2 Root File System"; + }; + + partition@1100000 { + /* 15MB for JFFS2 based Root file System */ + reg = <0x01100000 0x00f00000>; + label = "NAND Writable User area"; + }; + }; + + L2switch@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "vitesse-7385"; + reg = <0x2 0x0 0x20000>; + }; + + cpld@3,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cpld"; + reg = <0x3 0x0 0x20000>; + read-only; + }; +}; + +&soc { + i2c@3000 { + rtc@68 { + compatible = "pericom,pt7c4338"; + reg = <0x68>; + }; + }; + + spi@7000 { + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,m25p80"; + reg = <0>; + spi-max-frequency = <40000000>; + + partition@0 { + /* 512KB for u-boot Bootloader Image */ + reg = <0x0 0x00080000>; + label = "SPI U-Boot Image"; + read-only; + }; + + partition@80000 { + /* 512KB for DTB Image */ + reg = <0x00080000 0x00080000>; + label = "SPI DTB Image"; + }; + + partition@100000 { + /* 4MB for Linux Kernel Image */ + reg = <0x00100000 0x00400000>; + label = "SPI Linux Kernel Image"; + }; + + partition@500000 { + /* 4MB for Compressed RFS Image */ + reg = <0x00500000 0x00400000>; + label = "SPI Compressed RFS Image"; + }; + + partition@900000 { + /* 7MB for JFFS2 based RFS */ + reg = <0x00900000 0x00700000>; + label = "SPI JFFS2 RFS"; + }; + }; + }; + + usb@22000 { + phy_type = "ulpi"; + }; + + mdio@24520 { + phy0: ethernet-phy@0 { + interrupts = <3 1 0 0>; + reg = <0x0>; + }; + phy1: ethernet-phy@1 { + interrupts = <2 1 0 0>; + reg = <0x1>; + }; + }; + + mdio@25520 { + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@26520 { + status = "disabled"; + }; + + ptp_clock@24e00 { + fsl,tclk-period = <5>; + fsl,tmr-prsc = <200>; + fsl,tmr-add = <0xCCCCCCCD>; + fsl,tmr-fiper1 = <0x3B9AC9FB>; + fsl,tmr-fiper2 = <0x0001869B>; + fsl,max-adj = <249999999>; + }; + + enet0: ethernet@24000 { + fixed-link = <1 1 1000 0 0>; + phy-connection-type = "rgmii-id"; + }; + + enet1: ethernet@25000 { + tbi-handle = <&tbi0>; + phy-handle = <&phy0>; + phy-connection-type = "sgmii"; + }; + + enet2: ethernet@26000 { + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; +}; diff --git a/arch/powerpc/boot/dts/p2020rdb-pc_32b.dts b/arch/powerpc/boot/dts/p2020rdb-pc_32b.dts new file mode 100644 index 0000000..852e5b2 --- /dev/null +++ b/arch/powerpc/boot/dts/p2020rdb-pc_32b.dts @@ -0,0 +1,96 @@ +/* + * P2020 RDB-PC 32Bit Physical Address Map Device Tree Source + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p2020si-pre.dtsi" + +/ { + model = "fsl,P2020RDB"; + compatible = "fsl,P2020RDB-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@ffe05000 { + reg = <0 0xffe05000 0 0x1000>; + + /* NOR and NAND Flashes */ + ranges = <0x0 0x0 0x0 0xef000000 0x01000000 + 0x1 0x0 0x0 0xff800000 0x00040000 + 0x2 0x0 0x0 0xffb00000 0x00020000 + 0x3 0x0 0x0 0xffa00000 0x00020000>; + }; + + soc: soc@ffe00000 { + ranges = <0x0 0x0 0xffe00000 0x100000>; + }; + + pci0: pcie@ffe08000 { + reg = <0 0xffe08000 0 0x1000>; + status = "disabled"; + }; + + pci1: pcie@ffe09000 { + reg = <0 0xffe09000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci2: pcie@ffe0a000 { + reg = <0 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p2020rdb-pc.dtsi" +/include/ "fsl/p2020si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p2020rdb-pc_36b.dts b/arch/powerpc/boot/dts/p2020rdb-pc_36b.dts new file mode 100644 index 0000000..b5a56ca --- /dev/null +++ b/arch/powerpc/boot/dts/p2020rdb-pc_36b.dts @@ -0,0 +1,96 @@ +/* + * P2020 RDB-PC 36Bit Physical Address Map Device Tree Source + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p2020si-pre.dtsi" + +/ { + model = "fsl,P2020RDB"; + compatible = "fsl,P2020RDB-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@fffe05000 { + reg = <0xf 0xffe05000 0 0x1000>; + + /* NOR and NAND Flashes */ + ranges = <0x0 0x0 0xf 0xef000000 0x01000000 + 0x1 0x0 0xf 0xff800000 0x00040000 + 0x2 0x0 0xf 0xffb00000 0x00020000 + 0x3 0x0 0xf 0xffa00000 0x00020000>; + }; + + soc: soc@fffe00000 { + ranges = <0x0 0xf 0xffe00000 0x100000>; + }; + + pci0: pcie@fffe08000 { + reg = <0xf 0xffe08000 0 0x1000>; + status = "disabled"; + }; + + pci1: pcie@fffe09000 { + reg = <0xf 0xffe09000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci2: pcie@fffe0a000 { + reg = <0xf 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p2020rdb-pc.dtsi" +/include/ "fsl/p2020si-post.dtsi" -- cgit v0.10.2 From 465aceb832fb54b342a098143dbdc1e1ae250416 Mon Sep 17 00:00:00 2001 From: Ramneek Mehresh Date: Wed, 18 Jan 2012 11:10:48 +0530 Subject: powerpc/85xx: Add usb controller version info Add usb controller version info for the following: MPC8536, P1010, P1020, P1021, P1022, P1023, P2020, P2041, P3041, P3060, P5020 Signed-off-by: Ramneek Mehresh Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi b/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi index b37da56..c8b2daa 100644 --- a/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/mpc8536si-post.dtsi @@ -202,7 +202,7 @@ /include/ "pq3-etsec1-timer-0.dtsi" usb@22000 { - compatible = "fsl,mpc8536-usb2-mph", "fsl-usb2-mph"; + compatible = "fsl-usb2-mph-v1.2", "fsl,mpc8536-usb2-mph", "fsl-usb2-mph"; reg = <0x22000 0x1000>; #address-cells = <1>; #size-cells = <0>; @@ -210,7 +210,7 @@ }; usb@23000 { - compatible = "fsl,mpc8536-usb2-mph", "fsl-usb2-mph"; + compatible = "fsl-usb2-mph-v1.2", "fsl,mpc8536-usb2-mph", "fsl-usb2-mph"; reg = <0x23000 0x1000>; #address-cells = <1>; #size-cells = <0>; diff --git a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi index a97d126..0bde9ee 100644 --- a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi @@ -156,6 +156,9 @@ /include/ "pq3-dma-0.dtsi" /include/ "pq3-usb2-dr-0.dtsi" + usb@22000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { compatible = "fsl,p1010-esdhc", "fsl,esdhc"; diff --git a/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi index 5de5fc3..68cc5e7 100644 --- a/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1020si-post.dtsi @@ -142,7 +142,13 @@ /include/ "pq3-dma-0.dtsi" /include/ "pq3-usb2-dr-0.dtsi" + usb@22000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; /include/ "pq3-usb2-dr-1.dtsi" + usb@23000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { diff --git a/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi index b7929c9..4252ef8 100644 --- a/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1021si-post.dtsi @@ -142,6 +142,9 @@ /include/ "pq3-dma-0.dtsi" /include/ "pq3-usb2-dr-0.dtsi" + usb@22000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { diff --git a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi index ff9ed1d..de45215 100644 --- a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi @@ -199,7 +199,13 @@ /include/ "pq3-dma-0.dtsi" /include/ "pq3-usb2-dr-0.dtsi" + usb@22000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; /include/ "pq3-usb2-dr-1.dtsi" + usb@23000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; /include/ "pq3-esdhc-0.dtsi" sdhc@2e000 { diff --git a/arch/powerpc/boot/dts/fsl/p1023si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1023si-post.dtsi index b06bb4c..941fa15 100644 --- a/arch/powerpc/boot/dts/fsl/p1023si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1023si-post.dtsi @@ -142,6 +142,9 @@ /include/ "pq3-dma-0.dtsi" /include/ "pq3-usb2-dr-0.dtsi" + usb@22000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; crypto: crypto@300000 { compatible = "fsl,sec-v4.2", "fsl,sec-v4.0"; diff --git a/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi index 332e9e7..884e01b 100644 --- a/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p2020si-post.dtsi @@ -171,6 +171,9 @@ /include/ "pq3-dma-0.dtsi" /include/ "pq3-usb2-dr-0.dtsi" + usb@22000 { + compatible = "fsl-usb2-dr-v1.6", "fsl-usb2-dr"; + }; /include/ "pq3-etsec1-0.dtsi" /include/ "pq3-etsec1-timer-0.dtsi" diff --git a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi index 234a399..531eab8 100644 --- a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi @@ -309,12 +309,14 @@ /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" usb0: usb@210000 { + compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; phy_type = "utmi"; port0; }; /include/ "qoriq-usb2-dr-0.dtsi" usb1: usb@211000 { + compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; dr_mode = "host"; phy_type = "utmi"; }; diff --git a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi index d41d08d..af4ebc8 100644 --- a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi @@ -336,12 +336,14 @@ /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" usb0: usb@210000 { + compatible = "fsl-usb2-mph-v1.6", "fsl-usb2-mph"; phy_type = "utmi"; port0; }; /include/ "qoriq-usb2-dr-0.dtsi" usb1: usb@211000 { + compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; dr_mode = "host"; phy_type = "utmi"; }; diff --git a/arch/powerpc/boot/dts/fsl/p3060si-post.dtsi b/arch/powerpc/boot/dts/fsl/p3060si-post.dtsi index a63edd1..b3e5692 100644 --- a/arch/powerpc/boot/dts/fsl/p3060si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p3060si-post.dtsi @@ -291,6 +291,12 @@ /include/ "qoriq-duart-1.dtsi" /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" + usb@210000 { + compatible = "fsl-usb2-mph-v2.2", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; + }; /include/ "qoriq-usb2-dr-0.dtsi" + usb@211000 { + compatible = "fsl-usb2-dr-v2.2", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + }; /include/ "qoriq-sec4.1-0.dtsi" }; diff --git a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi index 914074b..64b6abe 100644 --- a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi @@ -339,12 +339,14 @@ /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" usb0: usb@210000 { + compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; phy_type = "utmi"; port0; }; /include/ "qoriq-usb2-dr-0.dtsi" usb1: usb@211000 { + compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; dr_mode = "host"; phy_type = "utmi"; }; -- cgit v0.10.2 From 6886780abf8e3e059da822ed7066c449726e2f0c Mon Sep 17 00:00:00 2001 From: Zhicheng Fan Date: Mon, 13 Feb 2012 22:06:22 +0000 Subject: powerpc/85xx: Add p1025rdb platform support Signed-off-by: Zhicheng Fan Acked-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index e77e813..4976f4e 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -91,6 +91,7 @@ machine_device_initcall(p2020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p2020_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p1021_rdb_pc, mpc85xx_common_publish_devices); +machine_device_initcall(p1025_rdb, mpc85xx_common_publish_devices); /* * Called very early, device-tree isn't unflattened @@ -131,6 +132,13 @@ static int __init p2020_rdb_pc_probe(void) return 0; } +static int __init p1025_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P1025RDB"); +} + define_machine(p2020_rdb) { .name = "P2020 RDB", .probe = p2020_rdb_probe, @@ -186,3 +194,17 @@ define_machine(p2020_rdb_pc) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, }; + +define_machine(p1025_rdb) { + .name = "P1025 RDB", + .probe = p1025_rdb_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; -- cgit v0.10.2 From 79ad57400c6f943be48711fe3478c55affc5d5cc Mon Sep 17 00:00:00 2001 From: Zhicheng Fan Date: Mon, 13 Feb 2012 22:06:23 +0000 Subject: powerpc/85xx: Add dts for p1025rdb board P1025RDB Overview ------------------ 1Gbyte DDR3 SDRAM 32 Mbyte NAND flash 16Mbyte NOR flash 16 Mbyte SPI flash SD connector to interface with the SD memory card Real-time clock on I2C bus PCIe: - x1 PCIe slot - x1 mini-PCIe slot 10/100/1000 BaseT Ethernet ports: - eTSEC1, RGMII: one 10/100/1000 port using AtherosTM AR8021 - eTSEC2, SGMII: one 10/100/1000 port using VitesseTM VSC8221 - eTSEC3, RGMII: one 10/100/1000 port using AtherosTM AR8021 USB 2.0 port: - Two USB2.0 Type A receptacles - One USB2.0 signal to Mini PCIe slot Dual RJ45 UART ports: - DUART interface: supports two UARTs up to 115200 bps for console display Signed-off-by: Zhicheng Fan Acked-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p1025rdb.dtsi b/arch/powerpc/boot/dts/p1025rdb.dtsi new file mode 100644 index 0000000..cf3676f --- /dev/null +++ b/arch/powerpc/boot/dts/p1025rdb.dtsi @@ -0,0 +1,286 @@ +/* + * P1025 RDB Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x1000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* This location must not be altered */ + /* 256KB for Vitesse 7385 Switch firmware */ + reg = <0x0 0x00040000>; + label = "NOR Vitesse-7385 Firmware"; + read-only; + }; + + partition@40000 { + /* 256KB for DTB Image */ + reg = <0x00040000 0x00040000>; + label = "NOR DTB Image"; + }; + + partition@80000 { + /* 3.5 MB for Linux Kernel Image */ + reg = <0x00080000 0x00380000>; + label = "NOR Linux Kernel Image"; + }; + + partition@400000 { + /* 11MB for JFFS2 based Root file System */ + reg = <0x00400000 0x00b00000>; + label = "NOR JFFS2 Root File System"; + }; + + partition@f00000 { + /* This location must not be altered */ + /* 512KB for u-boot Bootloader Image */ + /* 512KB for u-boot Environment Variables */ + reg = <0x00f00000 0x00100000>; + label = "NOR U-Boot Image"; + read-only; + }; + }; + + nand@1,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p1025-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x1 0x0 0x40000>; + + partition@0 { + /* This location must not be altered */ + /* 1MB for u-boot Bootloader Image */ + reg = <0x0 0x00100000>; + label = "NAND U-Boot Image"; + read-only; + }; + + partition@100000 { + /* 1MB for DTB Image */ + reg = <0x00100000 0x00100000>; + label = "NAND DTB Image"; + }; + + partition@200000 { + /* 4MB for Linux Kernel Image */ + reg = <0x00200000 0x00400000>; + label = "NAND Linux Kernel Image"; + }; + + partition@600000 { + /* 4MB for Compressed Root file System Image */ + reg = <0x00600000 0x00400000>; + label = "NAND Compressed RFS Image"; + }; + + partition@a00000 { + /* 7MB for JFFS2 based Root file System */ + reg = <0x00a00000 0x00700000>; + label = "NAND JFFS2 Root File System"; + }; + + partition@1100000 { + /* 15MB for JFFS2 based Root file System */ + reg = <0x01100000 0x00f00000>; + label = "NAND Writable User area"; + }; + }; + +}; + +&soc { + i2c@3000 { + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; + }; + + spi@7000 { + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25sl12801"; + reg = <0>; + spi-max-frequency = <40000000>; /* input clock */ + + partition@u-boot { + /* 512KB for u-boot Bootloader Image */ + reg = <0x0 0x00080000>; + label = "u-boot"; + read-only; + }; + + partition@dtb { + /* 512KB for DTB Image */ + reg = <0x00080000 0x00080000>; + label = "dtb"; + }; + + partition@kernel { + /* 4MB for Linux Kernel Image */ + reg = <0x00100000 0x00400000>; + label = "kernel"; + }; + + partition@fs { + /* 4MB for Compressed RFS Image */ + reg = <0x00500000 0x00400000>; + label = "file system"; + }; + + partition@jffs-fs { + /* 7MB for JFFS2 based RFS */ + reg = <0x00900000 0x00700000>; + label = "file system jffs2"; + }; + }; + }; + + usb@22000 { + phy_type = "ulpi"; + }; + + /* USB2 is shared with localbus, so it must be disabled + by default. We can't put 'status = "disabled";' here + since U-Boot doesn't clear the status property when + it enables USB2. OTOH, U-Boot does create a new node + when there isn't any. So, just comment it out. + usb@23000 { + phy_type = "ulpi"; + }; + */ + + mdio@24000 { + phy0: ethernet-phy@0 { + interrupt-parent = <&mpic>; + interrupts = <3 1>; + reg = <0x0>; + }; + + phy1: ethernet-phy@1 { + interrupt-parent = <&mpic>; + interrupts = <2 1>; + reg = <0x1>; + }; + + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@25000 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@26000 { + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet0: ethernet@b0000 { + fixed-link = <1 1 1000 0 0>; + phy-connection-type = "rgmii-id"; + + }; + + enet1: ethernet@b1000 { + phy-handle = <&phy0>; + tbi-handle = <&tbi1>; + phy-connection-type = "sgmii"; + }; + + enet2: ethernet@b2000 { + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; + + par_io@e0100 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0xe0100 0x60>; + ranges = <0x0 0xe0100 0x60>; + device_type = "par_io"; + num-ports = <3>; + pio1: ucc_pin@01 { + pio-map = < + /* port pin dir open_drain assignment has_irq */ + 0x1 0x13 0x1 0x0 0x1 0x0 /* QE_MUX_MDC */ + 0x1 0x14 0x3 0x0 0x1 0x0 /* QE_MUX_MDIO */ + 0x0 0x17 0x2 0x0 0x2 0x0 /* CLK12 */ + 0x0 0x18 0x2 0x0 0x1 0x0 /* CLK9 */ + 0x0 0x7 0x1 0x0 0x2 0x0 /* ENET1_TXD0_SER1_TXD0 */ + 0x0 0x9 0x1 0x0 0x2 0x0 /* ENET1_TXD1_SER1_TXD1 */ + 0x0 0xb 0x1 0x0 0x2 0x0 /* ENET1_TXD2_SER1_TXD2 */ + 0x0 0xc 0x1 0x0 0x2 0x0 /* ENET1_TXD3_SER1_TXD3 */ + 0x0 0x6 0x2 0x0 0x2 0x0 /* ENET1_RXD0_SER1_RXD0 */ + 0x0 0xa 0x2 0x0 0x2 0x0 /* ENET1_RXD1_SER1_RXD1 */ + 0x0 0xe 0x2 0x0 0x2 0x0 /* ENET1_RXD2_SER1_RXD2 */ + 0x0 0xf 0x2 0x0 0x2 0x0 /* ENET1_RXD3_SER1_RXD3 */ + 0x0 0x5 0x1 0x0 0x2 0x0 /* ENET1_TX_EN_SER1_RTS_B */ + 0x0 0xd 0x1 0x0 0x2 0x0 /* ENET1_TX_ER */ + 0x0 0x4 0x2 0x0 0x2 0x0 /* ENET1_RX_DV_SER1_CTS_B */ + 0x0 0x8 0x2 0x0 0x2 0x0 /* ENET1_RX_ER_SER1_CD_B */ + 0x0 0x11 0x2 0x0 0x2 0x0 /* ENET1_CRS */ + 0x0 0x10 0x2 0x0 0x2 0x0>; /* ENET1_COL */ + }; + + pio2: ucc_pin@02 { + pio-map = < + /* port pin dir open_drain assignment has_irq */ + 0x1 0x13 0x1 0x0 0x1 0x0 /* QE_MUX_MDC */ + 0x1 0x14 0x3 0x0 0x1 0x0 /* QE_MUX_MDIO */ + 0x1 0xb 0x2 0x0 0x1 0x0 /* CLK13 */ + 0x1 0x7 0x1 0x0 0x2 0x0 /* ENET5_TXD0_SER5_TXD0 */ + 0x1 0xa 0x1 0x0 0x2 0x0 /* ENET5_TXD1_SER5_TXD1 */ + 0x1 0x6 0x2 0x0 0x2 0x0 /* ENET5_RXD0_SER5_RXD0 */ + 0x1 0x9 0x2 0x0 0x2 0x0 /* ENET5_RXD1_SER5_RXD1 */ + 0x1 0x5 0x1 0x0 0x2 0x0 /* ENET5_TX_EN_SER5_RTS_B */ + 0x1 0x4 0x2 0x0 0x2 0x0 /* ENET5_RX_DV_SER5_CTS_B */ + 0x1 0x8 0x2 0x0 0x2 0x0>; /* ENET5_RX_ER_SER5_CD_B */ + }; + }; +}; diff --git a/arch/powerpc/boot/dts/p1025rdb_32b.dts b/arch/powerpc/boot/dts/p1025rdb_32b.dts new file mode 100644 index 0000000..ac5729c --- /dev/null +++ b/arch/powerpc/boot/dts/p1025rdb_32b.dts @@ -0,0 +1,135 @@ +/* + * P1025 RDB Device Tree Source (32-bit address map) + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1021si-pre.dtsi" +/ { + model = "fsl,P1025RDB"; + compatible = "fsl,P1025RDB"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@ffe05000 { + reg = <0 0xffe05000 0 0x1000>; + + /* NOR, NAND Flashes */ + ranges = <0x0 0x0 0x0 0xef000000 0x01000000 + 0x1 0x0 0x0 0xff800000 0x00040000>; + }; + + soc: soc@ffe00000 { + ranges = <0x0 0x0 0xffe00000 0x100000>; + }; + + pci0: pcie@ffe09000 { + ranges = <0x2000000 0x0 0xe0000000 0 0xe0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc10000 0x0 0x10000>; + reg = <0 0xffe09000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@ffe0a000 { + reg = <0 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0 0xe0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + qe: qe@ffe80000 { + ranges = <0x0 0x0 0xffe80000 0x40000>; + reg = <0 0xffe80000 0 0x480>; + brg-frequency = <0>; + bus-frequency = <0>; + status = "disabled"; /* no firmware loaded */ + + enet3: ucc@2000 { + device_type = "network"; + compatible = "ucc_geth"; + rx-clock-name = "clk12"; + tx-clock-name = "clk9"; + pio-handle = <&pio1>; + phy-handle = <&qe_phy0>; + phy-connection-type = "mii"; + }; + + mdio@2120 { + qe_phy0: ethernet-phy@0 { + interrupt-parent = <&mpic>; + interrupts = <4 1 0 0>; + reg = <0x6>; + device_type = "ethernet-phy"; + }; + qe_phy1: ethernet-phy@03 { + interrupt-parent = <&mpic>; + interrupts = <5 1 0 0>; + reg = <0x3>; + device_type = "ethernet-phy"; + }; + tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet4: ucc@2400 { + device_type = "network"; + compatible = "ucc_geth"; + rx-clock-name = "none"; + tx-clock-name = "clk13"; + pio-handle = <&pio2>; + phy-handle = <&qe_phy1>; + phy-connection-type = "rmii"; + }; + }; +}; + +/include/ "p1025rdb.dtsi" +/include/ "fsl/p1021si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1025rdb_36b.dts b/arch/powerpc/boot/dts/p1025rdb_36b.dts new file mode 100644 index 0000000..4ce4bfa --- /dev/null +++ b/arch/powerpc/boot/dts/p1025rdb_36b.dts @@ -0,0 +1,88 @@ +/* + * P1025 RDB Device Tree Source (36-bit address map) + * + * Copyright 2011 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1021si-pre.dtsi" +/ { + model = "fsl,P1025RDB"; + compatible = "fsl,P1025RDB"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@fffe05000 { + reg = <0xf 0xffe05000 0 0x1000>; + + /* NOR, NAND Flashes */ + ranges = <0x0 0x0 0xf 0xef000000 0x01000000 + 0x1 0x0 0xf 0xff800000 0x00040000>; + }; + + soc: soc@fffe00000 { + ranges = <0x0 0xf 0xffe00000 0x100000>; + }; + + pci0: pcie@fffe09000 { + reg = <0xf 0xffe09000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xe 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@fffe0a000 { + reg = <0xf 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p1025rdb.dtsi" +/include/ "fsl/p1021si-post.dtsi" -- cgit v0.10.2 From b6c46dcf61173999d95597bea87628cc08b359fb Mon Sep 17 00:00:00 2001 From: Liu Gang Date: Tue, 6 Mar 2012 10:58:12 +0800 Subject: powerpc/srio: Fix the relocation errors when building with 64bit For the file "arch/powerpc/sysdev/fsl_rio.c", there will be some relocation errors while using the corenet64_smp_defconfig: WARNING: modpost: Found 6 section mismatch(es). To see full details build your kernel with: 'make CONFIG_DEBUG_SECTION_MISMATCH=y' GEN .version CHK include/generated/compile.h UPD include/generated/compile.h CC init/version.o LD init/built-in.o LD .tmp_vmlinux1 arch/powerpc/sysdev/built-in.o:(__ex_table+0x0): relocation truncated to fit: R_PPC64_ADDR16 against `.text'+3208 arch/powerpc/sysdev/built-in.o:(__ex_table+0x2): relocation truncated to fit: R_PPC64_ADDR16 against `.fixup' arch/powerpc/sysdev/built-in.o:(__ex_table+0x4): relocation truncated to fit: R_PPC64_ADDR16 against `.text'+3230 arch/powerpc/sysdev/built-in.o:(__ex_table+0x6): relocation truncated to fit: R_PPC64_ADDR16 against `.fixup'+c arch/powerpc/sysdev/built-in.o:(__ex_table+0x8): relocation truncated to fit: R_PPC64_ADDR16 against `.text'+3250 arch/powerpc/sysdev/built-in.o:(__ex_table+0xa): relocation truncated to fit: R_PPC64_ADDR16 against `.fixup'+18 Rewrote the corresponding code with the support of 64bit building. Signed-off-by: Liu Gang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index a4c4f4a..5b6f556 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -66,8 +66,8 @@ " li %0,%3\n" \ " b 2b\n" \ ".section __ex_table,\"a\"\n" \ - " .align 2\n" \ - " .long 1b,3b\n" \ + PPC_LONG_ALIGN "\n" \ + PPC_LONG "1b,3b\n" \ ".text" \ : "=r" (err), "=r" (x) \ : "b" (addr), "i" (-EFAULT), "0" (err)) -- cgit v0.10.2 From 2a2383dab097823d68accce65da043402bdeb57b Mon Sep 17 00:00:00 2001 From: Liu Gang Date: Fri, 9 Mar 2012 16:10:38 +0800 Subject: powerpc/srio: Fix the compile errors when building with 64bit For the file "arch/powerpc/sysdev/fsl_rmu.c", there will be some compile errors while using the corenet64_smp_defconfig: .../fsl_rmu.c:315: error: cast from pointer to integer of different size .../fsl_rmu.c:320: error: cast to pointer from integer of different size .../fsl_rmu.c:320: error: cast to pointer from integer of different size .../fsl_rmu.c:320: error: cast to pointer from integer of different size .../fsl_rmu.c:330: error: cast to pointer from integer of different size .../fsl_rmu.c:332: error: cast to pointer from integer of different size .../fsl_rmu.c:339: error: cast to pointer from integer of different size .../fsl_rmu.c:340: error: cast to pointer from integer of different size .../fsl_rmu.c:341: error: cast to pointer from integer of different size .../fsl_rmu.c:348: error: cast to pointer from integer of different size .../fsl_rmu.c:348: error: cast to pointer from integer of different size .../fsl_rmu.c:348: error: cast to pointer from integer of different size .../fsl_rmu.c:659: error: cast from pointer to integer of different size .../fsl_rmu.c:659: error: format '%8.8x' expects type 'unsigned int', but argument 5 has type 'size_t' .../fsl_rmu.c:985: error: cast from pointer to integer of different size .../fsl_rmu.c:997: error: cast to pointer from integer of different size Rewrote the corresponding code with the support of 64bit building. Signed-off-by: Liu Gang Signed-off-by: Shaohui Xie Reported-by: Paul Gortmaker Signed-off-by: Kumar Gala diff --git a/arch/powerpc/sysdev/fsl_rmu.c b/arch/powerpc/sysdev/fsl_rmu.c index 1548578..14bd522 100644 --- a/arch/powerpc/sysdev/fsl_rmu.c +++ b/arch/powerpc/sysdev/fsl_rmu.c @@ -100,14 +100,8 @@ #define DOORBELL_DSR_TE 0x00000080 #define DOORBELL_DSR_QFI 0x00000010 #define DOORBELL_DSR_DIQI 0x00000001 -#define DOORBELL_TID_OFFSET 0x02 -#define DOORBELL_SID_OFFSET 0x04 -#define DOORBELL_INFO_OFFSET 0x06 #define DOORBELL_MESSAGE_SIZE 0x08 -#define DBELL_SID(x) (*(u16 *)(x + DOORBELL_SID_OFFSET)) -#define DBELL_TID(x) (*(u16 *)(x + DOORBELL_TID_OFFSET)) -#define DBELL_INF(x) (*(u16 *)(x + DOORBELL_INFO_OFFSET)) struct rio_msg_regs { u32 omr; @@ -193,6 +187,13 @@ struct fsl_rmu { int rxirq; }; +struct rio_dbell_msg { + u16 pad1; + u16 tid; + u16 sid; + u16 info; +}; + /** * fsl_rio_tx_handler - MPC85xx outbound message interrupt handler * @irq: Linux interrupt number @@ -311,8 +312,8 @@ fsl_rio_dbell_handler(int irq, void *dev_instance) /* XXX Need to check/dispatch until queue empty */ if (dsr & DOORBELL_DSR_DIQI) { - u32 dmsg = - (u32) fsl_dbell->dbell_ring.virt + + struct rio_dbell_msg *dmsg = + fsl_dbell->dbell_ring.virt + (in_be32(&fsl_dbell->dbell_regs->dqdpar) & 0xfff); struct rio_dbell *dbell; int found = 0; @@ -320,25 +321,25 @@ fsl_rio_dbell_handler(int irq, void *dev_instance) pr_debug ("RIO: processing doorbell," " sid %2.2x tid %2.2x info %4.4x\n", - DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg)); + dmsg->sid, dmsg->tid, dmsg->info); for (i = 0; i < MAX_PORT_NUM; i++) { if (fsl_dbell->mport[i]) { list_for_each_entry(dbell, &fsl_dbell->mport[i]->dbells, node) { if ((dbell->res->start - <= DBELL_INF(dmsg)) + <= dmsg->info) && (dbell->res->end - >= DBELL_INF(dmsg))) { + >= dmsg->info)) { found = 1; break; } } if (found && dbell->dinb) { dbell->dinb(fsl_dbell->mport[i], - dbell->dev_id, DBELL_SID(dmsg), - DBELL_TID(dmsg), - DBELL_INF(dmsg)); + dbell->dev_id, dmsg->sid, + dmsg->tid, + dmsg->info); break; } } @@ -348,8 +349,8 @@ fsl_rio_dbell_handler(int irq, void *dev_instance) pr_debug ("RIO: spurious doorbell," " sid %2.2x tid %2.2x info %4.4x\n", - DBELL_SID(dmsg), DBELL_TID(dmsg), - DBELL_INF(dmsg)); + dmsg->sid, dmsg->tid, + dmsg->info); } setbits32(&fsl_dbell->dbell_regs->dmr, DOORBELL_DMR_DI); out_be32(&fsl_dbell->dbell_regs->dsr, DOORBELL_DSR_DIQI); @@ -657,7 +658,7 @@ fsl_add_outb_message(struct rio_mport *mport, struct rio_dev *rdev, int mbox, int ret = 0; pr_debug("RIO: fsl_add_outb_message(): destid %4.4x mbox %d buffer " \ - "%8.8x len %8.8x\n", rdev->destid, mbox, (int)buffer, len); + "%p len %8.8zx\n", rdev->destid, mbox, buffer, len); if ((len < 8) || (len > RIO_MAX_MSG_SIZE)) { ret = -EINVAL; goto out; @@ -972,7 +973,8 @@ out: void *fsl_get_inb_message(struct rio_mport *mport, int mbox) { struct fsl_rmu *rmu = GET_RMM_HANDLE(mport); - u32 phys_buf, virt_buf; + u32 phys_buf; + void *virt_buf; void *buf = NULL; int buf_idx; @@ -982,7 +984,7 @@ void *fsl_get_inb_message(struct rio_mport *mport, int mbox) if (phys_buf == in_be32(&rmu->msg_regs->ifqepar)) goto out2; - virt_buf = (u32) rmu->msg_rx_ring.virt + (phys_buf + virt_buf = rmu->msg_rx_ring.virt + (phys_buf - rmu->msg_rx_ring.phys); buf_idx = (phys_buf - rmu->msg_rx_ring.phys) / RIO_MAX_MSG_SIZE; buf = rmu->msg_rx_ring.virt_buffer[buf_idx]; @@ -994,7 +996,7 @@ void *fsl_get_inb_message(struct rio_mport *mport, int mbox) } /* Copy max message size, caller is expected to allocate that big */ - memcpy(buf, (void *)virt_buf, RIO_MAX_MSG_SIZE); + memcpy(buf, virt_buf, RIO_MAX_MSG_SIZE); /* Clear the available buffer */ rmu->msg_rx_ring.virt_buffer[buf_idx] = NULL; -- cgit v0.10.2 From 0c00f65653389a408dfbbee7578e671664eea26a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 15 Mar 2012 18:40:27 +0100 Subject: powerpc/85xx: p2020rdb - move the NAND address. It is not at 0xffa00000. According to current u-boot source the NAND controller is always at 0xff800000 and it is either at CS0 or CS1 depending on NAND or NAND+NOR mode. In 36bit mode it is shifted to 0xfff800000 but it has always an eight there and never an A. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p2020rdb.dts index eb8a6aa..8f25ef2 100644 --- a/arch/powerpc/boot/dts/p2020rdb.dts +++ b/arch/powerpc/boot/dts/p2020rdb.dts @@ -34,7 +34,7 @@ /* NOR and NAND Flashes */ ranges = <0x0 0x0 0x0 0xef000000 0x01000000 - 0x1 0x0 0x0 0xffa00000 0x00040000 + 0x1 0x0 0x0 0xff800000 0x00040000 0x2 0x0 0x0 0xffb00000 0x00020000>; nor@0,0 { -- cgit v0.10.2 From 564ee46fb7b3d1cb9214ab32dde60cbe044b1f16 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Thu, 15 Mar 2012 18:40:28 +0100 Subject: powerpc/85xx: p2020rdb & p1010rdb - lower spi flash freq to 40Mhz This is here most likely since the FSL bsp. Back in the FSL bsp it was set to 50Mhz and working. However the driver divided the SoC freq. only by 2. According to the TRM the platform clock (which the manual refers in its formula) is the system clock divided by two. So in the end it has to divide by 4 and this is what the fsl-spi driver in tree is doing. Since then the flash is not wokring I guess. After chaning the freq from 50Mhz to 40Mhz like others do then I can access the flash. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p1010rdb.dtsi b/arch/powerpc/boot/dts/p1010rdb.dtsi index d4c4a77..1c41ef0 100644 --- a/arch/powerpc/boot/dts/p1010rdb.dtsi +++ b/arch/powerpc/boot/dts/p1010rdb.dtsi @@ -138,7 +138,7 @@ #size-cells = <1>; compatible = "spansion,s25sl12801"; reg = <0>; - spi-max-frequency = <50000000>; + spi-max-frequency = <40000000>; partition@0 { /* 1MB for u-boot Bootloader Image */ diff --git a/arch/powerpc/boot/dts/p2020rdb.dts b/arch/powerpc/boot/dts/p2020rdb.dts index 8f25ef2..153bc76 100644 --- a/arch/powerpc/boot/dts/p2020rdb.dts +++ b/arch/powerpc/boot/dts/p2020rdb.dts @@ -157,7 +157,7 @@ #size-cells = <1>; compatible = "spansion,s25sl12801"; reg = <0>; - spi-max-frequency = <50000000>; + spi-max-frequency = <40000000>; partition@0 { /* 512KB for u-boot Bootloader Image */ -- cgit v0.10.2 From e131fbda56131bf85a7d66834596be603eee7720 Mon Sep 17 00:00:00 2001 From: Gustavo Zacarias Date: Tue, 28 Feb 2012 16:43:08 -0300 Subject: powerpc/85xx: fix typo in p1010rdb.dtsi Fix typo introduced by "powerpc: Add TBI PHY node to first MDIO bus" from Andy Fleming. It's device_type rather than device-type, which causes the mdio probe to fail thus making all gianfar ethernet interfaces unusable. Signed-off-by: Gustavo Zacarias Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p1010rdb.dtsi b/arch/powerpc/boot/dts/p1010rdb.dtsi index 1c41ef0..4977614 100644 --- a/arch/powerpc/boot/dts/p1010rdb.dtsi +++ b/arch/powerpc/boot/dts/p1010rdb.dtsi @@ -196,7 +196,7 @@ }; tbi-phy@3 { - device-type = "tbi-phy"; + device_type = "tbi-phy"; reg = <0x3>; }; }; -- cgit v0.10.2 From b53804c70258000026d0b68b86aaecd571f2ba8b Mon Sep 17 00:00:00 2001 From: Liu Shuo Date: Thu, 8 Mar 2012 14:47:37 -0800 Subject: powerpc/fsl_msi: return proper error value when ioremap failed. Signed-off-by: Liu Shuo Acked-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index ecb5c19..0bab156 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -410,6 +410,7 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev) msi->msi_regs = ioremap(res.start, resource_size(&res)); if (!msi->msi_regs) { + err = -ENOMEM; dev_err(&dev->dev, "could not map node %s\n", dev->dev.of_node->full_name); goto error_out; -- cgit v0.10.2 From 955abacd983ef6f8c8bd6abcff7fab0df9f7ec11 Mon Sep 17 00:00:00 2001 From: Xie Xiaobo Date: Tue, 17 Jan 2012 17:59:50 +0800 Subject: powerpc/85xx: Add some DTS nodes and attributes for mpc8536ds Add partitions for NOR and NAND Flash. Signed-off-by: Xie Xiaobo Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts index c158815..1973622 100644 --- a/arch/powerpc/boot/dts/mpc8536ds.dts +++ b/arch/powerpc/boot/dts/mpc8536ds.dts @@ -1,7 +1,7 @@ /* * MPC8536 DS Device Tree Source * - * Copyright 2008 Freescale Semiconductor, Inc. + * Copyright 2008, 2011 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 @@ -34,6 +34,10 @@ lbc: localbus@ffe05000 { reg = <0 0xffe05000 0 0x1000>; + + ranges = <0x0 0x0 0x0 0xe8000000 0x08000000 + 0x2 0x0 0x0 0xffa00000 0x00040000 + 0x3 0x0 0x0 0xffdf0000 0x00008000>; }; board_soc: soc: soc@ffe00000 { diff --git a/arch/powerpc/boot/dts/mpc8536ds.dtsi b/arch/powerpc/boot/dts/mpc8536ds.dtsi index 1462e4c..cc46dbd 100644 --- a/arch/powerpc/boot/dts/mpc8536ds.dtsi +++ b/arch/powerpc/boot/dts/mpc8536ds.dtsi @@ -32,6 +32,99 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +&lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + reg = <0x0 0x03000000>; + label = "ramdisk-nor"; + }; + + partition@3000000 { + reg = <0x03000000 0x00e00000>; + label = "diagnostic-nor"; + read-only; + }; + + partition@3e00000 { + reg = <0x03e00000 0x00200000>; + label = "dink-nor"; + read-only; + }; + + partition@4000000 { + reg = <0x04000000 0x00400000>; + label = "kernel-nor"; + }; + + partition@4400000 { + reg = <0x04400000 0x03b00000>; + label = "fs-nor"; + }; + + partition@7f00000 { + reg = <0x07f00000 0x00080000>; + label = "dtb-nor"; + }; + + partition@7f80000 { + reg = <0x07f80000 0x00080000>; + label = "u-boot-nor"; + read-only; + }; + }; + + nand@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc8536-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x2 0x0 0x40000>; + + partition@0 { + reg = <0x0 0x02000000>; + label = "u-boot-nand"; + read-only; + }; + + partition@2000000 { + reg = <0x02000000 0x10000000>; + label = "fs-nand"; + }; + + partition@12000000 { + reg = <0x12000000 0x08000000>; + label = "ramdisk-nand"; + }; + + partition@1a000000 { + reg = <0x1a000000 0x04000000>; + label = "kernel-nand"; + }; + + partition@1e000000 { + reg = <0x1e000000 0x01000000>; + label = "dtb-nand"; + }; + + partition@1f000000 { + reg = <0x1f000000 0x21000000>; + label = "empty-nand"; + }; + }; + + board-control@3,0 { + compatible = "fsl,mpc8536ds-fpga-pixis"; + reg = <0x3 0x0 0x8000>; + }; +}; + &board_soc { i2c@3100 { rtc@68 { diff --git a/arch/powerpc/boot/dts/mpc8536ds_36b.dts b/arch/powerpc/boot/dts/mpc8536ds_36b.dts index 8f4b929..f8a3b34 100644 --- a/arch/powerpc/boot/dts/mpc8536ds_36b.dts +++ b/arch/powerpc/boot/dts/mpc8536ds_36b.dts @@ -1,7 +1,7 @@ /* * MPC8536DS Device Tree Source (36-bit address map) * - * Copyright 2008-2009 Freescale Semiconductor, Inc. + * Copyright 2008-2009, 2011 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 @@ -33,7 +33,11 @@ }; lbc: localbus@ffe05000 { - reg = <0 0xffe05000 0 0x1000>; + reg = <0xf 0xffe05000 0 0x1000>; + + ranges = <0x0 0x0 0xf 0xe8000000 0x08000000 + 0x2 0x0 0xf 0xffa00000 0x00040000 + 0x3 0x0 0xf 0xffdf0000 0x00008000>; }; board_soc: soc: soc@fffe00000 { -- cgit v0.10.2 From 54a1e76573b0b2bbda193b5d42ab7e1aba7081f7 Mon Sep 17 00:00:00 2001 From: Xie Xiaobo Date: Tue, 17 Jan 2012 17:59:51 +0800 Subject: powerpc/85xx: Add magic-packet properties for etsec The properties indicates that the hardware supports waking up via magic packet. Signed-off-by: Xie Xiaobo Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec1-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec1-0.dtsi index a1979ae..3b0650a 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-etsec1-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-etsec1-0.dtsi @@ -1,7 +1,7 @@ /* * PQ3 eTSEC device tree stub [ @ offsets 0x24000 ] * - * Copyright 2011 Freescale Semiconductor Inc. + * Copyright 2011-2012 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -41,6 +41,7 @@ ethernet@24000 { compatible = "gianfar"; reg = <0x24000 0x1000>; ranges = <0x0 0x24000 0x1000>; + fsl,magic-packet; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <29 2 0 0 30 2 0 0 34 2 0 0>; }; diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec1-1.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec1-1.dtsi index 4c4fdde..96693b4 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-etsec1-1.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-etsec1-1.dtsi @@ -1,7 +1,7 @@ /* * PQ3 eTSEC device tree stub [ @ offsets 0x25000 ] * - * Copyright 2011 Freescale Semiconductor Inc. + * Copyright 2011-2012 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -41,6 +41,7 @@ ethernet@25000 { compatible = "gianfar"; reg = <0x25000 0x1000>; ranges = <0x0 0x25000 0x1000>; + fsl,magic-packet; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <35 2 0 0 36 2 0 0 40 2 0 0>; }; diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec1-2.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec1-2.dtsi index 4b8ab43..6b3fab1 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-etsec1-2.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-etsec1-2.dtsi @@ -1,7 +1,7 @@ /* * PQ3 eTSEC device tree stub [ @ offsets 0x26000 ] * - * Copyright 2011 Freescale Semiconductor Inc. + * Copyright 2011-2012 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -41,6 +41,7 @@ ethernet@26000 { compatible = "gianfar"; reg = <0x26000 0x1000>; ranges = <0x0 0x26000 0x1000>; + fsl,magic-packet; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <31 2 0 0 32 2 0 0 33 2 0 0>; }; diff --git a/arch/powerpc/boot/dts/fsl/pq3-etsec1-3.dtsi b/arch/powerpc/boot/dts/fsl/pq3-etsec1-3.dtsi index 40c9137..0da592d 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-etsec1-3.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-etsec1-3.dtsi @@ -1,7 +1,7 @@ /* * PQ3 eTSEC device tree stub [ @ offsets 0x27000 ] * - * Copyright 2011 Freescale Semiconductor Inc. + * Copyright 2011-2012 Freescale Semiconductor Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -41,6 +41,7 @@ ethernet@27000 { compatible = "gianfar"; reg = <0x27000 0x1000>; ranges = <0x0 0x27000 0x1000>; + fsl,magic-packet; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <37 2 0 0 38 2 0 0 39 2 0 0>; }; -- cgit v0.10.2 From 4a170d0198330b8615778f6f0e265cdaadfd54b4 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 15 Feb 2012 18:25:48 -0600 Subject: powerpc/85xx: create 32-bit DTS for the P1022DS Create a 32-bit address space version of p1022ds.dts. To avoid confusion, p1022ds.dts is renamed to p1022ds_36b.dts. We also create p1022ds.dtsi to store some common nodes. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p1022ds.dts b/arch/powerpc/boot/dts/p1022ds.dts deleted file mode 100644 index ef95717..0000000 --- a/arch/powerpc/boot/dts/p1022ds.dts +++ /dev/null @@ -1,274 +0,0 @@ -/* - * P1022 DS 36Bit Physical Address Map Device Tree Source - * - * Copyright 2010 Freescale Semiconductor, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -/include/ "fsl/p1022si-pre.dtsi" -/ { - model = "fsl,P1022DS"; - compatible = "fsl,P1022DS"; - - memory { - device_type = "memory"; - }; - - lbc: localbus@fffe05000 { - reg = <0xf 0xffe05000 0 0x1000>; - ranges = <0x0 0x0 0xf 0xe8000000 0x08000000 - 0x1 0x0 0xf 0xe0000000 0x08000000 - 0x2 0x0 0xf 0xff800000 0x00040000 - 0x3 0x0 0xf 0xffdf0000 0x00008000>; - - /* - * This node is used to access the pixis via "indirect" mode, - * which is done by writing the pixis register index to chip - * select 0 and the value to/from chip select 1. Indirect - * mode is the only way to access the pixis when DIU video - * is enabled. Note that this assumes that the first column - * of the 'ranges' property above is the chip select number. - */ - board-control@0,0 { - compatible = "fsl,p1022ds-indirect-pixis"; - reg = <0x0 0x0 1 /* CS0 */ - 0x1 0x0 1>; /* CS1 */ - }; - - nor@0,0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "cfi-flash"; - reg = <0x0 0x0 0x8000000>; - bank-width = <2>; - device-width = <1>; - - partition@0 { - reg = <0x0 0x03000000>; - label = "ramdisk-nor"; - read-only; - }; - - partition@3000000 { - reg = <0x03000000 0x00e00000>; - label = "diagnostic-nor"; - read-only; - }; - - partition@3e00000 { - reg = <0x03e00000 0x00200000>; - label = "dink-nor"; - read-only; - }; - - partition@4000000 { - reg = <0x04000000 0x00400000>; - label = "kernel-nor"; - read-only; - }; - - partition@4400000 { - reg = <0x04400000 0x03b00000>; - label = "jffs2-nor"; - }; - - partition@7f00000 { - reg = <0x07f00000 0x00080000>; - label = "dtb-nor"; - read-only; - }; - - partition@7f80000 { - reg = <0x07f80000 0x00080000>; - label = "u-boot-nor"; - read-only; - }; - }; - - nand@2,0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "fsl,elbc-fcm-nand"; - reg = <0x2 0x0 0x40000>; - - partition@0 { - reg = <0x0 0x02000000>; - label = "u-boot-nand"; - read-only; - }; - - partition@2000000 { - reg = <0x02000000 0x10000000>; - label = "jffs2-nand"; - }; - - partition@12000000 { - reg = <0x12000000 0x10000000>; - label = "ramdisk-nand"; - read-only; - }; - - partition@22000000 { - reg = <0x22000000 0x04000000>; - label = "kernel-nand"; - }; - - partition@26000000 { - reg = <0x26000000 0x01000000>; - label = "dtb-nand"; - read-only; - }; - - partition@27000000 { - reg = <0x27000000 0x19000000>; - label = "reserved-nand"; - }; - }; - - board-control@3,0 { - compatible = "fsl,p1022ds-fpga", "fsl,fpga-ngpixis"; - reg = <3 0 0x30>; - interrupt-parent = <&mpic>; - /* - * IRQ8 is generated if the "EVENT" switch is pressed - * and PX_CTL[EVESEL] is set to 00. - */ - interrupts = <8 8 0 0>; - }; - }; - - soc: soc@fffe00000 { - ranges = <0x0 0xf 0xffe00000 0x100000>; - - i2c@3100 { - wm8776:codec@1a { - compatible = "wlf,wm8776"; - reg = <0x1a>; - /* - * clock-frequency will be set by U-Boot if - * the clock is enabled. - */ - }; - }; - - spi@7000 { - flash@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "spansion,s25sl12801"; - reg = <0>; - spi-max-frequency = <40000000>; /* input clock */ - - partition@0 { - label = "u-boot-spi"; - reg = <0x00000000 0x00100000>; - read-only; - }; - partition@100000 { - label = "kernel-spi"; - reg = <0x00100000 0x00500000>; - read-only; - }; - partition@600000 { - label = "dtb-spi"; - reg = <0x00600000 0x00100000>; - read-only; - }; - partition@700000 { - label = "file system-spi"; - reg = <0x00700000 0x00900000>; - }; - }; - }; - - ssi@15000 { - fsl,mode = "i2s-slave"; - codec-handle = <&wm8776>; - fsl,ssi-asynchronous; - }; - - usb@22000 { - phy_type = "ulpi"; - }; - - usb@23000 { - status = "disabled"; - }; - - mdio@24000 { - phy0: ethernet-phy@0 { - interrupts = <3 1 0 0>; - reg = <0x1>; - }; - phy1: ethernet-phy@1 { - interrupts = <9 1 0 0>; - reg = <0x2>; - }; - tbi-phy@2 { - device_type = "tbi-phy"; - reg = <0x2>; - }; - }; - - ethernet@b0000 { - phy-handle = <&phy0>; - phy-connection-type = "rgmii-id"; - }; - - ethernet@b1000 { - phy-handle = <&phy1>; - phy-connection-type = "rgmii-id"; - }; - }; - - pci0: pcie@fffe09000 { - reg = <0xf 0xffe09000 0 0x1000>; - ranges = <0x2000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 - 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; - pcie@0 { - ranges = <0x2000000 0x0 0xe0000000 - 0x2000000 0x0 0xe0000000 - 0x0 0x20000000 - - 0x1000000 0x0 0x0 - 0x1000000 0x0 0x0 - 0x0 0x100000>; - }; - }; - - pci1: pcie@fffe0a000 { - reg = <0xf 0xffe0a000 0 0x1000>; - ranges = <0x2000000 0x0 0xe0000000 0xc 0x40000000 0x0 0x20000000 - 0x1000000 0x0 0x00000000 0xf 0xffc20000 0x0 0x10000>; - pcie@0 { - reg = <0x0 0x0 0x0 0x0 0x0>; - ranges = <0x2000000 0x0 0xe0000000 - 0x2000000 0x0 0xe0000000 - 0x0 0x20000000 - - 0x1000000 0x0 0x0 - 0x1000000 0x0 0x0 - 0x0 0x100000>; - }; - }; - - pci2: pcie@fffe0b000 { - reg = <0xf 0xffe0b000 0 0x1000>; - ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000 - 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; - pcie@0 { - ranges = <0x2000000 0x0 0xe0000000 - 0x2000000 0x0 0xe0000000 - 0x0 0x20000000 - - 0x1000000 0x0 0x0 - 0x1000000 0x0 0x0 - 0x0 0x100000>; - }; - }; -}; - -/include/ "fsl/p1022si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1022ds.dtsi b/arch/powerpc/boot/dts/p1022ds.dtsi new file mode 100644 index 0000000..7cdb505 --- /dev/null +++ b/arch/powerpc/boot/dts/p1022ds.dtsi @@ -0,0 +1,234 @@ +/* + * P1022 DS Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&board_lbc { + /* + * This node is used to access the pixis via "indirect" mode, + * which is done by writing the pixis register index to chip + * select 0 and the value to/from chip select 1. Indirect + * mode is the only way to access the pixis when DIU video + * is enabled. Note that this assumes that the first column + * of the 'ranges' property above is the chip select number. + */ + board-control@0,0 { + compatible = "fsl,p1022ds-indirect-pixis"; + reg = <0x0 0x0 1 /* CS0 */ + 0x1 0x0 1>; /* CS1 */ + interrupt-parent = <&mpic>; + interrupts = <8 0 0 0>; + }; + + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + reg = <0x0 0x03000000>; + label = "ramdisk-nor"; + read-only; + }; + + partition@3000000 { + reg = <0x03000000 0x00e00000>; + label = "diagnostic-nor"; + read-only; + }; + + partition@3e00000 { + reg = <0x03e00000 0x00200000>; + label = "dink-nor"; + read-only; + }; + + partition@4000000 { + reg = <0x04000000 0x00400000>; + label = "kernel-nor"; + read-only; + }; + + partition@4400000 { + reg = <0x04400000 0x03b00000>; + label = "jffs2-nor"; + }; + + partition@7f00000 { + reg = <0x07f00000 0x00080000>; + label = "dtb-nor"; + read-only; + }; + + partition@7f80000 { + reg = <0x07f80000 0x00080000>; + label = "u-boot-nor"; + read-only; + }; + }; + + nand@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,elbc-fcm-nand"; + reg = <0x2 0x0 0x40000>; + + partition@0 { + reg = <0x0 0x02000000>; + label = "u-boot-nand"; + read-only; + }; + + partition@2000000 { + reg = <0x02000000 0x10000000>; + label = "jffs2-nand"; + }; + + partition@12000000 { + reg = <0x12000000 0x10000000>; + label = "ramdisk-nand"; + read-only; + }; + + partition@22000000 { + reg = <0x22000000 0x04000000>; + label = "kernel-nand"; + }; + + partition@26000000 { + reg = <0x26000000 0x01000000>; + label = "dtb-nand"; + read-only; + }; + + partition@27000000 { + reg = <0x27000000 0x19000000>; + label = "reserved-nand"; + }; + }; + + board-control@3,0 { + compatible = "fsl,p1022ds-fpga", "fsl,fpga-ngpixis"; + reg = <3 0 0x30>; + interrupt-parent = <&mpic>; + /* + * IRQ8 is generated if the "EVENT" switch is pressed + * and PX_CTL[EVESEL] is set to 00. + */ + interrupts = <8 0 0 0>; + }; +}; + +&board_soc { + i2c@3100 { + wm8776:codec@1a { + compatible = "wlf,wm8776"; + reg = <0x1a>; + /* + * clock-frequency will be set by U-Boot if + * the clock is enabled. + */ + }; + }; + + spi@7000 { + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25sl12801"; + reg = <0>; + spi-max-frequency = <40000000>; /* input clock */ + + partition@0 { + label = "u-boot-spi"; + reg = <0x00000000 0x00100000>; + read-only; + }; + partition@100000 { + label = "kernel-spi"; + reg = <0x00100000 0x00500000>; + read-only; + }; + partition@600000 { + label = "dtb-spi"; + reg = <0x00600000 0x00100000>; + read-only; + }; + partition@700000 { + label = "file system-spi"; + reg = <0x00700000 0x00900000>; + }; + }; + }; + + ssi@15000 { + fsl,mode = "i2s-slave"; + codec-handle = <&wm8776>; + fsl,ssi-asynchronous; + }; + + usb@22000 { + phy_type = "ulpi"; + }; + + usb@23000 { + status = "disabled"; + }; + + mdio@24000 { + phy0: ethernet-phy@0 { + interrupts = <3 1 0 0>; + reg = <0x1>; + }; + phy1: ethernet-phy@1 { + interrupts = <9 1 0 0>; + reg = <0x2>; + }; + tbi-phy@2 { + device_type = "tbi-phy"; + reg = <0x2>; + }; + }; + + ethernet@b0000 { + phy-handle = <&phy0>; + phy-connection-type = "rgmii-id"; + }; + + ethernet@b1000 { + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; +}; diff --git a/arch/powerpc/boot/dts/p1022ds_32b.dts b/arch/powerpc/boot/dts/p1022ds_32b.dts new file mode 100644 index 0000000..d96cae0 --- /dev/null +++ b/arch/powerpc/boot/dts/p1022ds_32b.dts @@ -0,0 +1,103 @@ +/* + * P1022 DS 32-bit Physical Address Map Device Tree Source + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1022si-pre.dtsi" +/ { + model = "fsl,P1022DS"; + compatible = "fsl,P1022DS"; + + memory { + device_type = "memory"; + }; + + board_lbc: lbc: localbus@ffe05000 { + ranges = <0x0 0x0 0x0 0xe8000000 0x08000000 + 0x1 0x0 0x0 0xe0000000 0x08000000 + 0x2 0x0 0x0 0xff800000 0x00040000 + 0x3 0x0 0x0 0xffdf0000 0x00008000>; + reg = <0x0 0xffe05000 0 0x1000>; + }; + + board_soc: soc: soc@ffe00000 { + ranges = <0x0 0x0 0xffe00000 0x100000>; + }; + + pci0: pcie@ffe09000 { + ranges = <0x2000000 0x0 0xe0000000 0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc10000 0x0 0x10000>; + reg = <0x0 0xffe09000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@ffe0a000 { + ranges = <0x2000000 0x0 0xe0000000 0 0xc0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc20000 0x0 0x10000>; + reg = <0 0xffe0a000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci2: pcie@ffe0b000 { + ranges = <0x2000000 0x0 0xe0000000 0 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>; + reg = <0 0xffe0b000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "fsl/p1022si-post.dtsi" +/include/ "p1022ds.dtsi" diff --git a/arch/powerpc/boot/dts/p1022ds_36b.dts b/arch/powerpc/boot/dts/p1022ds_36b.dts new file mode 100644 index 0000000..f7aacce --- /dev/null +++ b/arch/powerpc/boot/dts/p1022ds_36b.dts @@ -0,0 +1,103 @@ +/* + * P1022 DS 36-bit Physical Address Map Device Tree Source + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1022si-pre.dtsi" +/ { + model = "fsl,P1022DS"; + compatible = "fsl,P1022DS"; + + memory { + device_type = "memory"; + }; + + board_lbc: lbc: localbus@fffe05000 { + ranges = <0x0 0x0 0xf 0xe8000000 0x08000000 + 0x1 0x0 0xf 0xe0000000 0x08000000 + 0x2 0x0 0xf 0xff800000 0x00040000 + 0x3 0x0 0xf 0xffdf0000 0x00008000>; + reg = <0xf 0xffe05000 0 0x1000>; + }; + + board_soc: soc: soc@fffe00000 { + ranges = <0x0 0xf 0xffe00000 0x100000>; + }; + + pci0: pcie@fffe09000 { + ranges = <0x2000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; + reg = <0xf 0xffe09000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@fffe0a000 { + ranges = <0x2000000 0x0 0xe0000000 0xc 0x40000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc20000 0x0 0x10000>; + reg = <0xf 0xffe0a000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci2: pcie@fffe0b000 { + ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; + reg = <0xf 0xffe0b000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xe0000000 + 0x2000000 0x0 0xe0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "fsl/p1022si-post.dtsi" +/include/ "p1022ds.dtsi" -- cgit v0.10.2 From 4951896aad0b73a8163eb23d44818993237bc0cf Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 15 Feb 2012 18:25:47 -0600 Subject: powerpc/85xx: p1022ds: disable the NOR flash node if video is enabled The Freescale P1022 has a unique pin muxing "feature" where the DIU video controller's video signals are muxed with 24 of the local bus address signals. When the DIU is enabled, the bulk of the local bus is disabled, preventing access to memory-mapped devices like NOR flash and the pixis FPGA. Therefore, if the DIU is going to be enabled, then memory-mapped devices on the localbus, like NOR flash, need to be disabled. This also means that the localbus is not a 'simple-bus' any more, so remove that string from the compatible node. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi index de45215..06216b8 100644 --- a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi @@ -35,7 +35,11 @@ &lbc { #address-cells = <2>; #size-cells = <1>; - compatible = "fsl,p1022-elbc", "fsl,elbc", "simple-bus"; + /* + * The localbus on the P1022 is not a simple-bus because of the eLBC + * pin muxing when the DIU is enabled. + */ + compatible = "fsl,p1022-elbc", "fsl,elbc"; interrupts = <19 2 0 0>; }; diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 63ba790..e211b0d 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -249,6 +249,49 @@ void __init p1022_ds_pic_init(void) mpic_init(mpic); } +#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) + +/* + * Disables a node in the device tree. + * + * This function is called before kmalloc() is available, so the 'new' object + * should be allocated in the global area. The easiest way is to do that is + * to allocate one static local variable for each call to this function. + */ +static void __init disable_one_node(struct device_node *np, struct property *new) +{ + struct property *old; + + old = of_find_property(np, new->name, NULL); + if (old) + prom_update_property(np, new, old); + else + prom_add_property(np, new); +} + +/* TRUE if there is a "video=fslfb" command-line parameter. */ +static bool fslfb; + +/* + * Search for a "video=fslfb" command-line parameter, and set 'fslfb' to + * true if we find it. + * + * We need to use early_param() instead of __setup() because the normal + * __setup() gets called to late. However, early_param() gets called very + * early, before the device tree is unflattened, so all we can do now is set a + * global variable. Later on, p1022_ds_setup_arch() will use that variable + * to determine if we need to update the device tree. + */ +static int __init early_video_setup(char *options) +{ + fslfb = (strncmp(options, "fslfb:", 6) == 0); + + return 0; +} +early_param("video", early_video_setup); + +#endif + /* * Setup the architecture */ @@ -286,6 +329,34 @@ static void __init p1022_ds_setup_arch(void) diu_ops.set_monitor_port = p1022ds_set_monitor_port; diu_ops.set_pixel_clock = p1022ds_set_pixel_clock; diu_ops.valid_monitor_port = p1022ds_valid_monitor_port; + + /* + * Disable the NOR flash node if there is video=fslfb... command-line + * parameter. When the DIU is active, NOR flash is unavailable, so we + * have to disable the node before the MTD driver loads. + */ + if (fslfb) { + struct device_node *np = + of_find_compatible_node(NULL, NULL, "fsl,p1022-elbc"); + + if (np) { + np = of_find_compatible_node(np, NULL, "cfi-flash"); + if (np) { + static struct property nor_status = { + .name = "status", + .value = "disabled", + .length = sizeof("disabled"), + }; + + pr_info("p1022ds: disabling %s node", + np->full_name); + disable_one_node(np, &nor_status); + of_node_put(np); + } + } + + } + #endif mpc85xx_smp_init(); -- cgit v0.10.2 From 9df8f73c404a1160952cf0e5ba65874200eccd14 Mon Sep 17 00:00:00 2001 From: Jia Hongtao Date: Tue, 21 Feb 2012 10:11:23 +0800 Subject: powerpc/85xx: Clean up partition nodes in dts for MPC8572DS Signed-off-by: Jin Qing Signed-off-by: Jia Hongtao Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/mpc8572ds.dtsi b/arch/powerpc/boot/dts/mpc8572ds.dtsi index c3d4fac..1417894 100644 --- a/arch/powerpc/boot/dts/mpc8572ds.dtsi +++ b/arch/powerpc/boot/dts/mpc8572ds.dtsi @@ -41,37 +41,47 @@ bank-width = <2>; device-width = <1>; - ramdisk@0 { + partition@0 { reg = <0x0 0x03000000>; - read-only; + label = "ramdisk-nor"; }; - diagnostic@3000000 { + partition@3000000 { reg = <0x03000000 0x00e00000>; + label = "diagnostic-nor"; read-only; }; - dink@3e00000 { + partition@3e00000 { reg = <0x03e00000 0x00200000>; + label = "dink-nor"; read-only; }; - kernel@4000000 { + partition@4000000 { reg = <0x04000000 0x00400000>; - read-only; + label = "kernel-nor"; }; - jffs2@4400000 { + partition@4400000 { reg = <0x04400000 0x03b00000>; + label = "fs-nor"; + }; + + partition@7f00000 { + reg = <0x07f00000 0x00060000>; + label = "dtb-nor"; }; - dtb@7f00000 { - reg = <0x07f00000 0x00080000>; + partition@7f60000 { + reg = <0x07f60000 0x00020000>; + label = "env-nor"; read-only; }; - u-boot@7f80000 { + partition@7f80000 { reg = <0x07f80000 0x00080000>; + label = "u-boot-nor"; read-only; }; }; @@ -83,31 +93,35 @@ "fsl,elbc-fcm-nand"; reg = <0x2 0x0 0x40000>; - u-boot@0 { + partition@0 { reg = <0x0 0x02000000>; + label = "u-boot-nand"; read-only; }; - jffs2@2000000 { + partition@2000000 { reg = <0x02000000 0x10000000>; + label = "fs-nand"; }; - ramdisk@12000000 { + partition@12000000 { reg = <0x12000000 0x08000000>; - read-only; + label = "ramdisk-nand"; }; - kernel@1a000000 { + partition@1a000000 { reg = <0x1a000000 0x04000000>; + label = "kernel-nand"; }; - dtb@1e000000 { + partition@1e000000 { reg = <0x1e000000 0x01000000>; - read-only; + label = "dtb-nand"; }; - empty@1f000000 { + partition@1f000000 { reg = <0x1f000000 0x21000000>; + label = "empty-nand"; }; }; -- cgit v0.10.2 From 82771882d96022369ac8092dc3ee3ef5a91b3ca6 Mon Sep 17 00:00:00 2001 From: Prabhakar Kushwaha Date: Thu, 15 Mar 2012 11:04:23 +0530 Subject: NAND Machine support for Integrated Flash Controller Integrated Flash Controller(IFC) can be used to hook NAND Flash chips using NAND Flash Machine available on it. Signed-off-by: Dipen Dudhat Signed-off-by: Scott Wood Signed-off-by: Li Yang Signed-off-by: Liu Shuo Signed-off-by: Poonam Aggrwal Signed-off-by: Prabhakar Kushwaha Signed-off-by: Kumar Gala diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 31b034b..3b1d6da 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -462,6 +462,16 @@ config MTD_NAND_FSL_ELBC Enabling this option will enable you to use this to control external NAND devices. +config MTD_NAND_FSL_IFC + tristate "NAND support for Freescale IFC controller" + depends on MTD_NAND && FSL_SOC + select FSL_IFC + help + Various Freescale chips e.g P1010, include a NAND Flash machine + with built-in hardware ECC capabilities. + Enabling this option will enable you to use this to control + external NAND devices. + config MTD_NAND_FSL_UPM tristate "Support for NAND on Freescale UPM" depends on PPC_83xx || PPC_85xx diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 618f4ba..19bc8cb 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c new file mode 100644 index 0000000..c30ac7b --- /dev/null +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -0,0 +1,1072 @@ +/* + * Freescale Integrated Flash Controller NAND driver + * + * Copyright 2011-2012 Freescale Semiconductor, Inc + * + * Author: Dipen Dudhat + * + * 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. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERR_BYTE 0xFF /* Value returned for read + bytes when read failed */ +#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait + for IFC NAND Machine */ + +struct fsl_ifc_ctrl; + +/* mtd information per set */ +struct fsl_ifc_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct fsl_ifc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ + u8 __iomem *vbase; /* Chip select base virtual address */ +}; + +/* overview of the fsl ifc controller */ +struct fsl_ifc_nand_ctrl { + struct nand_hw_control controller; + struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; + + u8 __iomem *addr; /* Address of assigned IFC buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes;/* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int eccread; /* Non zero for a full-page ECC read */ + unsigned int counter; /* counter for the initializations */ +}; + +static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; + +/* 512-byte page with 4-bit ECC, 8-bit */ +static struct nand_ecclayout oob_512_8bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {0, 5}, {6, 2} }, +}; + +/* 512-byte page with 4-bit ECC, 16-bit */ +static struct nand_ecclayout oob_512_16bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 6}, }, +}; + +/* 2048-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_2048_ecc4 = { + .eccbytes = 32, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + }, + .oobfree = { {2, 6}, {40, 24} }, +}; + +/* 4096-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_4096_ecc4 = { + .eccbytes = 64, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + }, + .oobfree = { {2, 6}, {72, 56} }, +}; + +/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_4096_ecc8 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 82} }, +}; + + +/* + * Generic flash bbt descriptors + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* + * Set up the IFC hardware block and page address fields, and the ifc nand + * structure addr field to point to the correct IFC buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + int buf_num; + + ifc_nand_ctrl->page = page_addr; + /* Program ROW0/COL0 */ + out_be32(&ifc->ifc_nand.row0, page_addr); + out_be32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column); + + buf_num = page_addr & priv->bufnum_mask; + + ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); + ifc_nand_ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ifc_nand_ctrl->index += mtd->writesize; +} + +static int is_blank(struct mtd_info *mtd, unsigned int bufnum) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2); + u32 __iomem *mainarea = (u32 *)addr; + u8 __iomem *oob = addr + mtd->writesize; + int i; + + for (i = 0; i < mtd->writesize / 4; i++) { + if (__raw_readl(&mainarea[i]) != 0xffffffff) + return 0; + } + + for (i = 0; i < chip->ecc.layout->eccbytes; i++) { + int pos = chip->ecc.layout->eccpos[i]; + + if (__raw_readb(&oob[pos]) != 0xff) + return 0; + } + + return 1; +} + +/* returns nonzero if entire page is blank */ +static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, + u32 *eccstat, unsigned int bufnum) +{ + u32 reg = eccstat[bufnum / 4]; + int errors; + + errors = (reg >> ((3 - bufnum % 4) * 8)) & 15; + + return errors; +} + +/* + * execute IFC NAND command and wait for it to complete + */ +static void fsl_ifc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + u32 eccstat[4]; + int i; + + /* set the chip select for NAND Transaction */ + out_be32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT); + + dev_vdbg(priv->dev, + "%s: fir0=%08x fcr0=%08x\n", + __func__, + in_be32(&ifc->ifc_nand.nand_fir0), + in_be32(&ifc->ifc_nand.nand_fcr0)); + + ctrl->nand_stat = 0; + + /* start read/write seq */ + out_be32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for command complete flag or timeout */ + wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, + IFC_TIMEOUT_MSECS * HZ/1000); + + /* ctrl->nand_stat will be updated from IRQ context */ + if (!ctrl->nand_stat) + dev_err(priv->dev, "Controller is not responding\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) + dev_err(priv->dev, "NAND Flash Timeout Error\n"); + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) + dev_err(priv->dev, "NAND Flash Write Protect Error\n"); + + if (nctrl->eccread) { + int errors; + int bufnum = nctrl->page & priv->bufnum_mask; + int sector = bufnum * chip->ecc.steps; + int sector_end = sector + chip->ecc.steps - 1; + + for (i = sector / 4; i <= sector_end / 4; i++) + eccstat[i] = in_be32(&ifc->ifc_nand.nand_eccstat[i]); + + for (i = sector; i <= sector_end; i++) { + errors = check_read_ecc(mtd, ctrl, eccstat, i); + + if (errors == 15) { + /* + * Uncorrectable error. + * OK only if the whole page is blank. + * + * We disable ECCER reporting due to... + * erratum IFC-A002770 -- so report it now if we + * see an uncorrectable error in ECCSTAT. + */ + if (!is_blank(mtd, bufnum)) + ctrl->nand_stat |= + IFC_NAND_EVTER_STAT_ECCER; + break; + } + + mtd->ecc_stats.corrected += errors; + } + + nctrl->eccread = 0; + } +} + +static void fsl_ifc_do_read(struct nand_chip *chip, + int oob, + struct mtd_info *mtd) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + + /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ + if (mtd->writesize > 512) { + out_be32(&ifc->ifc_nand.nand_fir0, + (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)); + out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + + out_be32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); + } else { + out_be32(&ifc->ifc_nand.nand_fir0, + (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)); + out_be32(&ifc->ifc_nand.nand_fir1, 0x0); + + if (oob) + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT); + else + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the IFC NAND Machine */ +static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + + /* clear the read buffer */ + ifc_nand_ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ifc_nand_ctrl->index = 0; + + switch (command) { + /* READ0 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ0: + out_be32(&ifc->ifc_nand.nand_fbcr, 0); + set_addr(mtd, 0, page_addr, 0); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ifc_nand_ctrl->index += column; + + if (chip->ecc.mode == NAND_ECC_HW) + ifc_nand_ctrl->eccread = 1; + + fsl_ifc_do_read(chip, 0, mtd); + fsl_ifc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + out_be32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_ifc_do_read(chip, 1, mtd); + fsl_ifc_run_command(mtd); + + return; + + /* READID must read all 8 possible bytes */ + case NAND_CMD_READID: + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); + /* 8 bytes for manuf, device and exts */ + out_be32(&ifc->ifc_nand.nand_fbcr, 8); + ifc_nand_ctrl->read_bytes = 8; + + set_addr(mtd, 0, 0, 0); + fsl_ifc_run_command(mtd); + return; + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + out_be32(&ifc->ifc_nand.nand_fir0, + (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)); + + out_be32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT)); + + out_be32(&ifc->ifc_nand.nand_fbcr, 0); + ifc_nand_ctrl->read_bytes = 0; + fsl_ifc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 nand_fcr0; + ifc_nand_ctrl->column = column; + ifc_nand_ctrl->oob = 0; + + if (mtd->writesize > 512) { + nand_fcr0 = + (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT); + + out_be32(&ifc->ifc_nand.nand_fir0, + (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_CW1 << IFC_NAND_FIR0_OP4_SHIFT)); + } else { + nand_fcr0 = ((NAND_CMD_PAGEPROG << + IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_SEQIN << + IFC_NAND_FCR0_CMD2_SHIFT)); + + out_be32(&ifc->ifc_nand.nand_fir0, + (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)); + out_be32(&ifc->ifc_nand.nand_fir1, + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT)); + + if (column >= mtd->writesize) + nand_fcr0 |= + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; + else + nand_fcr0 |= + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; + } + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + ifc_nand_ctrl->oob = 1; + } + out_be32(&ifc->ifc_nand.nand_fcr0, nand_fcr0); + set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + if (ifc_nand_ctrl->oob) { + out_be32(&ifc->ifc_nand.nand_fbcr, + ifc_nand_ctrl->index - ifc_nand_ctrl->column); + } else { + out_be32(&ifc->ifc_nand.nand_fbcr, 0); + } + + fsl_ifc_run_command(mtd); + return; + } + + case NAND_CMD_STATUS: + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT); + out_be32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP); + return; + + case NAND_CMD_RESET: + out_be32(&ifc->ifc_nand.nand_fir0, + IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT); + out_be32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT); + fsl_ifc_run_command(mtd); + return; + + default: + dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", + __func__, command); + } +} + +static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the IFC NAND Controller Data Buffer + */ +static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %u available)\n", + __func__, len, bufsize - ifc_nand_ctrl->index); + len = bufsize - ifc_nand_ctrl->index; + } + + memcpy_toio(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index], buf, len); + ifc_nand_ctrl->index += len; +} + +/* + * Read a byte from either the IFC hardware buffer + * read function for 8-bit buswidth + */ +static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) + return in_8(&ifc_nand_ctrl->addr[ifc_nand_ctrl->index++]); + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read two bytes from the IFC hardware buffer + * read function for 16-bit buswith + */ +static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + uint16_t data; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { + data = in_be16((uint16_t *)&ifc_nand_ctrl-> + addr[ifc_nand_ctrl->index]); + ifc_nand_ctrl->index += 2; + return (uint8_t) data; + } + + dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read from the IFC Controller Data Buffer + */ +static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + int avail; + + if (len < 0) { + dev_err(priv->dev, "%s: len %d bytes", __func__, len); + return; + } + + avail = min((unsigned int)len, + ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); + memcpy_fromio(buf, &ifc_nand_ctrl->addr[ifc_nand_ctrl->index], avail); + ifc_nand_ctrl->index += avail; + + if (len > avail) + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %d available)\n", + __func__, len, avail); +} + +/* + * Verify buffer against the IFC Controller Data Buffer + */ +static int fsl_ifc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; + int i; + + if (len < 0) { + dev_err(priv->dev, "%s: write_buf of %d bytes", __func__, len); + return -EINVAL; + } + + if ((unsigned int)len > nctrl->read_bytes - nctrl->index) { + dev_err(priv->dev, + "%s: beyond end of buffer (%d requested, %u available)\n", + __func__, len, nctrl->read_bytes - nctrl->index); + + nctrl->index = nctrl->read_bytes; + return -EINVAL; + } + + for (i = 0; i < len; i++) + if (in_8(&nctrl->addr[nctrl->index + i]) != buf[i]) + break; + + nctrl->index += len; + + if (i != len) + return -EIO; + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + return -EIO; + + return 0; +} + +/* + * This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + u32 nand_fsr; + + /* Use READ_STATUS command, but wait for the device to be ready */ + out_be32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT)); + out_be32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD0_SHIFT); + out_be32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ifc_nand_ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + nand_fsr = in_be32(&ifc->ifc_nand.nand_fsr); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + return nand_fsr | NAND_STATUS_WP; +} + +static int fsl_ifc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct fsl_ifc_mtd *priv = chip->priv; + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + + fsl_ifc_read_buf(mtd, buf, mtd->writesize); + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) + dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n"); + + if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static void fsl_ifc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf) +{ + fsl_ifc_write_buf(mtd, buf, mtd->writesize); + fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_ifc_mtd *priv = chip->priv; + + dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, + chip->numchips); + dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, + chip->chipsize); + dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, + chip->pagemask); + dev_dbg(priv->dev, "%s: nand->chip_delay = %d\n", __func__, + chip->chip_delay); + dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, + chip->badblockpos); + dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, + chip->chip_shift); + dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, + chip->page_shift); + dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, + chip->phys_erase_shift); + dev_dbg(priv->dev, "%s: nand->ecclayout = %p\n", __func__, + chip->ecclayout); + dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, + chip->ecc.mode); + dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, + chip->ecc.steps); + dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, + chip->ecc.bytes); + dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, + chip->ecc.total); + dev_dbg(priv->dev, "%s: nand->ecc.layout = %p\n", __func__, + chip->ecc.layout); + dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); + dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); + dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, + mtd->erasesize); + dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, + mtd->writesize); + dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, + mtd->oobsize); + + return 0; +} + +static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) +{ + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct nand_chip *chip = &priv->chip; + struct nand_ecclayout *layout; + u32 csor; + + /* Fill in fsl_ifc_mtd structure */ + priv->mtd.priv = chip; + priv->mtd.owner = THIS_MODULE; + + /* fill in nand_chip structure */ + /* set up function call table */ + if ((in_be32(&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; + + chip->write_buf = fsl_ifc_write_buf; + chip->read_buf = fsl_ifc_read_buf; + chip->verify_buf = fsl_ifc_verify_buf; + chip->select_chip = fsl_ifc_select_chip; + chip->cmdfunc = fsl_ifc_cmdfunc; + chip->waitfunc = fsl_ifc_wait; + + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + + out_be32(&ifc->ifc_nand.ncfgr, 0x0); + + /* set up nand options */ + chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->bbt_options = NAND_BBT_USE_FLASH; + + + if (in_be32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { + chip->read_byte = fsl_ifc_read_byte16; + chip->options |= NAND_BUSWIDTH_16; + } else { + chip->read_byte = fsl_ifc_read_byte; + } + + chip->controller = &ifc_nand_ctrl->controller; + chip->priv = priv; + + chip->ecc.read_page = fsl_ifc_read_page; + chip->ecc.write_page = fsl_ifc_write_page; + + csor = in_be32(&ifc->csor_cs[priv->bank].csor); + + /* Hardware generates ECC per 512 Bytes */ + chip->ecc.size = 512; + chip->ecc.bytes = 8; + + switch (csor & CSOR_NAND_PGS_MASK) { + case CSOR_NAND_PGS_512: + if (chip->options & NAND_BUSWIDTH_16) { + layout = &oob_512_16bit_ecc4; + } else { + layout = &oob_512_8bit_ecc4; + + /* Avoid conflict with bad block marker */ + bbt_main_descr.offs = 0; + bbt_mirror_descr.offs = 0; + } + + priv->bufnum_mask = 15; + break; + + case CSOR_NAND_PGS_2K: + layout = &oob_2048_ecc4; + priv->bufnum_mask = 3; + break; + + case CSOR_NAND_PGS_4K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_4096_ecc4; + } else { + layout = &oob_4096_ecc8; + chip->ecc.bytes = 16; + } + + priv->bufnum_mask = 1; + break; + + default: + dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); + return -ENODEV; + } + + /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ + if (csor & CSOR_NAND_ECC_DEC_EN) { + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.layout = layout; + } else { + chip->ecc.mode = NAND_ECC_SOFT; + } + + return 0; +} + +static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) +{ + nand_release(&priv->mtd); + + kfree(priv->mtd.name); + + if (priv->vbase) + iounmap(priv->vbase); + + ifc_nand_ctrl->chips[priv->bank] = NULL; + dev_set_drvdata(priv->dev, NULL); + kfree(priv); + + return 0; +} + +static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, + phys_addr_t addr) +{ + u32 cspr = in_be32(&ifc->cspr_cs[bank].cspr); + + if (!(cspr & CSPR_V)) + return 0; + if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) + return 0; + + return (cspr & CSPR_BA) == convert_ifc_address(addr); +} + +static DEFINE_MUTEX(fsl_ifc_nand_mutex); + +static int __devinit fsl_ifc_nand_probe(struct platform_device *dev) +{ + struct fsl_ifc_regs __iomem *ifc; + struct fsl_ifc_mtd *priv; + struct resource res; + static const char *part_probe_types[] + = { "cmdlinepart", "RedBoot", "ofpart", NULL }; + int ret; + int bank; + struct device_node *node = dev->dev.of_node; + struct mtd_part_parser_data ppdata; + + ppdata.of_node = dev->dev.of_node; + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + return -ENODEV; + ifc = fsl_ifc_ctrl_dev->regs; + + /* get, allocate and map the memory resource */ + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(&dev->dev, "%s: failed to get resource\n", __func__); + return ret; + } + + /* find which chip select it is connected to */ + for (bank = 0; bank < FSL_IFC_BANK_COUNT; bank++) { + if (match_bank(ifc, bank, res.start)) + break; + } + + if (bank >= FSL_IFC_BANK_COUNT) { + dev_err(&dev->dev, "%s: address did not match any chip selects\n", + __func__); + return -ENODEV; + } + + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_lock(&fsl_ifc_nand_mutex); + if (!fsl_ifc_ctrl_dev->nand) { + ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); + if (!ifc_nand_ctrl) { + dev_err(&dev->dev, "failed to allocate memory\n"); + mutex_unlock(&fsl_ifc_nand_mutex); + return -ENOMEM; + } + + ifc_nand_ctrl->read_bytes = 0; + ifc_nand_ctrl->index = 0; + ifc_nand_ctrl->addr = NULL; + fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; + + spin_lock_init(&ifc_nand_ctrl->controller.lock); + init_waitqueue_head(&ifc_nand_ctrl->controller.wq); + } else { + ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; + } + mutex_unlock(&fsl_ifc_nand_mutex); + + ifc_nand_ctrl->chips[bank] = priv; + priv->bank = bank; + priv->ctrl = fsl_ifc_ctrl_dev; + priv->dev = &dev->dev; + + priv->vbase = ioremap(res.start, resource_size(&res)); + if (!priv->vbase) { + dev_err(priv->dev, "%s: failed to map chip region\n", __func__); + ret = -ENOMEM; + goto err; + } + + dev_set_drvdata(priv->dev, priv); + + out_be32(&ifc->ifc_nand.nand_evter_en, + IFC_NAND_EVTER_EN_OPC_EN | + IFC_NAND_EVTER_EN_FTOER_EN | + IFC_NAND_EVTER_EN_WPER_EN); + + /* enable NAND Machine Interrupts */ + out_be32(&ifc->ifc_nand.nand_evter_intr_en, + IFC_NAND_EVTER_INTR_OPCIR_EN | + IFC_NAND_EVTER_INTR_FTOERIR_EN | + IFC_NAND_EVTER_INTR_WPERIR_EN); + + priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", (unsigned)res.start); + if (!priv->mtd.name) { + ret = -ENOMEM; + goto err; + } + + ret = fsl_ifc_chip_init(priv); + if (ret) + goto err; + + ret = nand_scan_ident(&priv->mtd, 1, NULL); + if (ret) + goto err; + + ret = fsl_ifc_chip_init_tail(&priv->mtd); + if (ret) + goto err; + + ret = nand_scan_tail(&priv->mtd); + if (ret) + goto err; + + /* First look for RedBoot table or partitions on the command + * line, these take precedence over device tree information */ + mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata, + NULL, 0); + + dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", + (unsigned long long)res.start, priv->bank); + return 0; + +err: + fsl_ifc_chip_remove(priv); + return ret; +} + +static int fsl_ifc_nand_remove(struct platform_device *dev) +{ + struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); + + fsl_ifc_chip_remove(priv); + + mutex_lock(&fsl_ifc_nand_mutex); + ifc_nand_ctrl->counter--; + if (!ifc_nand_ctrl->counter) { + fsl_ifc_ctrl_dev->nand = NULL; + kfree(ifc_nand_ctrl); + } + mutex_unlock(&fsl_ifc_nand_mutex); + + return 0; +} + +static const struct of_device_id fsl_ifc_nand_match[] = { + { + .compatible = "fsl,ifc-nand", + }, + {} +}; + +static struct platform_driver fsl_ifc_nand_driver = { + .driver = { + .name = "fsl,ifc-nand", + .owner = THIS_MODULE, + .of_match_table = fsl_ifc_nand_match, + }, + .probe = fsl_ifc_nand_probe, + .remove = fsl_ifc_nand_remove, +}; + +static int __init fsl_ifc_nand_init(void) +{ + int ret; + + ret = platform_driver_register(&fsl_ifc_nand_driver); + if (ret) + printk(KERN_ERR "fsl-ifc: Failed to register platform" + "driver\n"); + + return ret; +} + +static void __exit fsl_ifc_nand_exit(void) +{ + platform_driver_unregister(&fsl_ifc_nand_driver); +} + +module_init(fsl_ifc_nand_init); +module_exit(fsl_ifc_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale"); +MODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); -- cgit v0.10.2 From d1fb10609ae189c1294c793f833a5c6b1bc71001 Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Wed, 14 Mar 2012 17:08:27 +0800 Subject: powerpc/85xx: add P1020MBG-PC platform support The p1020mbg-pc has the similar feature as the p1020rdb. Therefore, p1020mbg-pc use the same platform file as the p1/p2 rdb board. Overview of P1020MBG-PC platform: - DDR3 2GB - NOR flash 64MB - I2C EEPROM 256Kb - eTSEC1 (RGMII PHY) connected to VSC7385 L2 switch - eTSEC2 (SGMII PHY) - eTSEC3 (RGMII PHY) - SDHC - 2 USB ports - 4 TDM ports - PCIe (Lane1 to dual SATA controller) Signed-off-by: Jerry Huang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 4976f4e..2c2a272 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -89,6 +89,7 @@ static void __init mpc85xx_rdb_setup_arch(void) machine_device_initcall(p2020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p2020_rdb_pc, mpc85xx_common_publish_devices); +machine_device_initcall(p1020_mbg_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p1021_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1025_rdb, mpc85xx_common_publish_devices); @@ -139,6 +140,13 @@ static int __init p1025_rdb_probe(void) return of_flat_dt_is_compatible(root, "fsl,P1025RDB"); } +static int __init p1020_mbg_pc_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P1020MBG-PC"); +} + define_machine(p2020_rdb) { .name = "P2020 RDB", .probe = p2020_rdb_probe, @@ -208,3 +216,17 @@ define_machine(p1025_rdb) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, }; + +define_machine(p1020_mbg_pc) { + .name = "P1020 MBG-PC", + .probe = p1020_mbg_pc_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; -- cgit v0.10.2 From 1a244b8318b5f67f9065362767409117609edc9a Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Wed, 14 Mar 2012 17:08:28 +0800 Subject: powerpc/85xx: add P1020UTM-PC platform support The p1020utm-pc has the similar feature as the p1020rdb. Therefore, p1020utm-pc use the same platform file as the p1/p2 rdb board. Overview of P1020UTM-PC platform: - DDR3 1GB - NOR flash 32MB - I2C EEPROM 256Kb - eTSEC1 (RGMII PHY Atheros AR8021) - eTSEC2 (SGMII PHY Vitesse VSC8221) - eTSEC3 (RGMII PHY Atheros AR8021) - SDHC - 2 USB ports - PCIe (Lane1 to dual SATA controller) Signed-off-by: Jerry Huang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 2c2a272..8a79896 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -91,6 +91,7 @@ machine_device_initcall(p2020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p2020_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_mbg_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_rdb, mpc85xx_common_publish_devices); +machine_device_initcall(p1020_utm_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1021_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1025_rdb, mpc85xx_common_publish_devices); @@ -147,6 +148,13 @@ static int __init p1020_mbg_pc_probe(void) return of_flat_dt_is_compatible(root, "fsl,P1020MBG-PC"); } +static int __init p1020_utm_pc_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P1020UTM-PC"); +} + define_machine(p2020_rdb) { .name = "P2020 RDB", .probe = p2020_rdb_probe, @@ -230,3 +238,17 @@ define_machine(p1020_mbg_pc) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, }; + +define_machine(p1020_utm_pc) { + .name = "P1020 UTM-PC", + .probe = p1020_utm_pc_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; -- cgit v0.10.2 From 7e6af144781a3ae9dff142f44dc26e585cb41b82 Mon Sep 17 00:00:00 2001 From: Zhicheng Fan Date: Fri, 10 Feb 2012 14:48:15 +0800 Subject: powerpc/85xx: Add p1020rdb-pc platform support Signed-off-by: Zhicheng Fan Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index 8a79896..e5b260c 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -1,7 +1,7 @@ /* * MPC85xx RDB Board Setup * - * Copyright 2009 Freescale Semiconductor Inc. + * Copyright 2009,2012 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 @@ -91,6 +91,7 @@ machine_device_initcall(p2020_rdb, mpc85xx_common_publish_devices); machine_device_initcall(p2020_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_mbg_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_rdb, mpc85xx_common_publish_devices); +machine_device_initcall(p1020_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1020_utm_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1021_rdb_pc, mpc85xx_common_publish_devices); machine_device_initcall(p1025_rdb, mpc85xx_common_publish_devices); @@ -116,6 +117,13 @@ static int __init p1020_rdb_probe(void) return 0; } +static int __init p1020_rdb_pc_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P1020RDB-PC"); +} + static int __init p1021_rdb_pc_probe(void) { unsigned long root = of_get_flat_dt_root(); @@ -252,3 +260,17 @@ define_machine(p1020_utm_pc) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, }; + +define_machine(p1020_rdb_pc) { + .name = "P1020RDB-PC", + .probe = p1020_rdb_pc_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; -- cgit v0.10.2 From 950740098c6745d69056b3acb5090ca9d8aa918c Mon Sep 17 00:00:00 2001 From: Zhicheng Fan Date: Fri, 10 Feb 2012 14:48:16 +0800 Subject: powerpc/85xx: Add dts for p1020rdb-pc board P1020RDB-PC Overview ------------------ 1Gbyte DDR3 SDRAM 32 Mbyte NAND flash 10 16Mbyte NOR flash 16 Mbyte SPI flash SD connector to interface with the SD memory card Real-time clock on I2C bus PCIe: - x1 PCIe slot - x1 mini-PCIe slot 10/100/1000 BaseT Ethernet ports: - eTSEC1, RGMII: one 10/100/1000 port using VitesseTM VSC7385 L2 switch - eTSEC2, SGMII: one 10/100/1000 port using VitesseTM VSC8221 - eTSEC3, RGMII: one 10/100/1000 port using AtherosTM AR8021 USB 2.0 port: - Two USB2.0 Type A receptacles - One USB2.0 signal to Mini PCIe slot Dual RJ45 UART ports: - DUART interface: supports two UARTs up to 115200 bps for console display Signed-off-by: Zhicheng Fan Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/p1020rdb-pc.dtsi b/arch/powerpc/boot/dts/p1020rdb-pc.dtsi new file mode 100644 index 0000000..c952cd3 --- /dev/null +++ b/arch/powerpc/boot/dts/p1020rdb-pc.dtsi @@ -0,0 +1,247 @@ +/* + * P1020 RDB-PC Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x1000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + /* This location must not be altered */ + /* 256KB for Vitesse 7385 Switch firmware */ + reg = <0x0 0x00040000>; + label = "NOR Vitesse-7385 Firmware"; + read-only; + }; + + partition@40000 { + /* 256KB for DTB Image */ + reg = <0x00040000 0x00040000>; + label = "NOR DTB Image"; + }; + + partition@80000 { + /* 3.5 MB for Linux Kernel Image */ + reg = <0x00080000 0x00380000>; + label = "NOR Linux Kernel Image"; + }; + + partition@400000 { + /* 11MB for JFFS2 based Root file System */ + reg = <0x00400000 0x00b00000>; + label = "NOR JFFS2 Root File System"; + }; + + partition@f00000 { + /* This location must not be altered */ + /* 512KB for u-boot Bootloader Image */ + /* 512KB for u-boot Environment Variables */ + reg = <0x00f00000 0x00100000>; + label = "NOR U-Boot Image"; + read-only; + }; + }; + + nand@1,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,p1020-fcm-nand", + "fsl,elbc-fcm-nand"; + reg = <0x1 0x0 0x40000>; + + partition@0 { + /* This location must not be altered */ + /* 1MB for u-boot Bootloader Image */ + reg = <0x0 0x00100000>; + label = "NAND U-Boot Image"; + read-only; + }; + + partition@100000 { + /* 1MB for DTB Image */ + reg = <0x00100000 0x00100000>; + label = "NAND DTB Image"; + }; + + partition@200000 { + /* 4MB for Linux Kernel Image */ + reg = <0x00200000 0x00400000>; + label = "NAND Linux Kernel Image"; + }; + + partition@600000 { + /* 4MB for Compressed Root file System Image */ + reg = <0x00600000 0x00400000>; + label = "NAND Compressed RFS Image"; + }; + + partition@a00000 { + /* 7MB for JFFS2 based Root file System */ + reg = <0x00a00000 0x00700000>; + label = "NAND JFFS2 Root File System"; + }; + + partition@1100000 { + /* 15MB for JFFS2 based Root file System */ + reg = <0x01100000 0x00f00000>; + label = "NAND Writable User area"; + }; + }; + + L2switch@2,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "vitesse-7385"; + reg = <0x2 0x0 0x20000>; + }; + + cpld@3,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cpld"; + reg = <0x3 0x0 0x20000>; + read-only; + }; +}; + +&soc { + i2c@3000 { + rtc@68 { + compatible = "pericom,pt7c4338"; + reg = <0x68>; + }; + }; + + spi@7000 { + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spansion,s25sl12801"; + reg = <0>; + spi-max-frequency = <40000000>; /* input clock */ + + partition@u-boot { + /* 512KB for u-boot Bootloader Image */ + reg = <0x0 0x00080000>; + label = "u-boot"; + read-only; + }; + + partition@dtb { + /* 512KB for DTB Image*/ + reg = <0x00080000 0x00080000>; + label = "dtb"; + }; + + partition@kernel { + /* 4MB for Linux Kernel Image */ + reg = <0x00100000 0x00400000>; + label = "kernel"; + }; + + partition@fs { + /* 4MB for Compressed RFS Image */ + reg = <0x00500000 0x00400000>; + label = "file system"; + }; + + partition@jffs-fs { + /* 7MB for JFFS2 based RFS */ + reg = <0x00900000 0x00700000>; + label = "file system jffs2"; + }; + }; + }; + + usb@22000 { + phy_type = "ulpi"; + }; + + /* USB2 is shared with localbus, so it must be disabled + by default. We can't put 'status = "disabled";' here + since U-Boot doesn't clear the status property when + it enables USB2. OTOH, U-Boot does create a new node + when there isn't any. So, just comment it out. + usb@23000 { + phy_type = "ulpi"; + }; + */ + + mdio@24000 { + phy0: ethernet-phy@0 { + interrupt-parent = <&mpic>; + interrupts = <3 1>; + reg = <0x0>; + }; + + phy1: ethernet-phy@1 { + interrupt-parent = <&mpic>; + interrupts = <2 1>; + reg = <0x1>; + }; + + tbi0: tbi-phy@11 { + device_type = "tbi-phy"; + reg = <0x11>; + }; + }; + + mdio@25000 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet0: ethernet@b0000 { + fixed-link = <1 1 1000 0 0>; + phy-connection-type = "rgmii-id"; + + }; + + enet1: ethernet@b1000 { + phy-handle = <&phy0>; + tbi-handle = <&tbi1>; + phy-connection-type = "sgmii"; + }; + + enet2: ethernet@b2000 { + phy-handle = <&phy1>; + phy-connection-type = "rgmii-id"; + }; +}; diff --git a/arch/powerpc/boot/dts/p1020rdb-pc_32b.dts b/arch/powerpc/boot/dts/p1020rdb-pc_32b.dts new file mode 100644 index 0000000..4de69b7 --- /dev/null +++ b/arch/powerpc/boot/dts/p1020rdb-pc_32b.dts @@ -0,0 +1,90 @@ +/* + * P1020 RDB-PC Device Tree Source (32-bit address map) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1020si-pre.dtsi" +/ { + model = "fsl,P1020RDB-PC"; + compatible = "fsl,P1020RDB-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@ffe05000 { + reg = <0 0xffe05000 0 0x1000>; + + /* NOR, NAND Flashes and Vitesse 5 port L2 switch */ + ranges = <0x0 0x0 0x0 0xef000000 0x01000000 + 0x1 0x0 0x0 0xff800000 0x00040000 + 0x2 0x0 0x0 0xffb00000 0x00020000 + 0x3 0x0 0x0 0xffa00000 0x00020000>; + }; + + soc: soc@ffe00000 { + ranges = <0x0 0x0 0xffe00000 0x100000>; + }; + + pci0: pcie@ffe09000 { + ranges = <0x2000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc10000 0x0 0x10000>; + reg = <0 0xffe09000 0 0x1000>; + pcie@0 { + ranges = <0x2000000 0x0 0xa0000000 + 0x2000000 0x0 0xa0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@ffe0a000 { + reg = <0 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0x80000000 0 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0x80000000 + 0x2000000 0x0 0x80000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p1020rdb-pc.dtsi" +/include/ "fsl/p1020si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1020rdb-pc_36b.dts b/arch/powerpc/boot/dts/p1020rdb-pc_36b.dts new file mode 100644 index 0000000..5237da7 --- /dev/null +++ b/arch/powerpc/boot/dts/p1020rdb-pc_36b.dts @@ -0,0 +1,90 @@ +/* + * P1020 RDB-PC Device Tree Source (36-bit address map) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/include/ "fsl/p1020si-pre.dtsi" +/ { + model = "fsl,P1020RDB-PC"; + compatible = "fsl,P1020RDB-PC"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@fffe05000 { + reg = <0xf 0xffe05000 0 0x1000>; + + /* NOR, NAND Flashes and Vitesse 5 port L2 switch */ + ranges = <0x0 0x0 0xf 0xef000000 0x01000000 + 0x1 0x0 0xf 0xff800000 0x00040000 + 0x2 0x0 0xf 0xffb00000 0x00040000 + 0x3 0x0 0xf 0xffa00000 0x00020000>; + }; + + soc: soc@fffe00000 { + ranges = <0x0 0xf 0xffe00000 0x100000>; + }; + + pci0: pcie@fffe09000 { + reg = <0xf 0xffe09000 0 0x1000>; + ranges = <0x2000000 0x0 0xc0000000 0xc 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc10000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0xc0000000 + 0x2000000 0x0 0xc0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + pci1: pcie@fffe0a000 { + reg = <0xf 0xffe0a000 0 0x1000>; + ranges = <0x2000000 0x0 0x80000000 0xc 0x00000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xffc00000 0x0 0x10000>; + pcie@0 { + ranges = <0x2000000 0x0 0x80000000 + 0x2000000 0x0 0x80000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; +}; + +/include/ "p1020rdb-pc.dtsi" +/include/ "fsl/p1020si-post.dtsi" diff --git a/arch/powerpc/boot/dts/p1020rdb-pc_camp_core0.dts b/arch/powerpc/boot/dts/p1020rdb-pc_camp_core0.dts new file mode 100644 index 0000000..f411515 --- /dev/null +++ b/arch/powerpc/boot/dts/p1020rdb-pc_camp_core0.dts @@ -0,0 +1,64 @@ +/* + * P1020 RDB-PC Core0 Device Tree Source in CAMP mode. + * + * In CAMP mode, each core needs to have its own dts. Only mpic and L2 cache + * can be shared, all the other devices must be assigned to one core only. + * This dts file allows core0 to have memory, l2, i2c, spi, gpio, tdm, dma, usb, + * eth1, eth2, sdhc, crypto, global-util, message, pci0, pci1, msi. + * + * Please note to add "-b 0" for core0's dts compiling. + * + * Copyright 2012 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/ "p1020rdb-pc_32b.dts" + +/ { + model = "fsl,P1020RDB-PC"; + compatible = "fsl,P1020RDB-PC"; + + aliases { + ethernet1 = &enet1; + ethernet2 = &enet2; + serial0 = &serial0; + pci0 = &pci0; + pci1 = &pci1; + }; + + cpus { + PowerPC,P1020@1 { + status = "disabled"; + }; + }; + + memory { + device_type = "memory"; + }; + + localbus@ffe05000 { + status = "disabled"; + }; + + soc@ffe00000 { + serial1: serial@4600 { + status = "disabled"; + }; + + enet0: ethernet@b0000 { + status = "disabled"; + }; + + mpic: pic@40000 { + protected-sources = < + 42 29 30 34 /* serial1, enet0-queue-group0 */ + 17 18 24 45 /* enet0-queue-group1, crypto */ + >; + pic-no-reset; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/p1020rdb-pc_camp_core1.dts b/arch/powerpc/boot/dts/p1020rdb-pc_camp_core1.dts new file mode 100644 index 0000000..a91335a --- /dev/null +++ b/arch/powerpc/boot/dts/p1020rdb-pc_camp_core1.dts @@ -0,0 +1,142 @@ +/* + * P1020 RDB-PC Core1 Device Tree Source in CAMP mode. + * + * In CAMP mode, each core needs to have its own dts. Only mpic and L2 cache + * can be shared, all the other devices must be assigned to one core only. + * This dts allows core1 to have l2, eth0, crypto. + * + * Please note to add "-b 1" for core1's dts compiling. + * + * Copyright 2012 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/ "p1020rdb-pc_32b.dts" + +/ { + model = "fsl,P1020RDB-PC"; + compatible = "fsl,P1020RDB-PC"; + + aliases { + ethernet0 = &enet0; + serial0 = &serial1; + }; + + cpus { + PowerPC,P1020@0 { + status = "disabled"; + }; + }; + + memory { + device_type = "memory"; + }; + + localbus@ffe05000 { + status = "disabled"; + }; + + soc@ffe00000 { + ecm-law@0 { + status = "disabled"; + }; + + ecm@1000 { + status = "disabled"; + }; + + memory-controller@2000 { + status = "disabled"; + }; + + i2c@3000 { + status = "disabled"; + }; + + i2c@3100 { + status = "disabled"; + }; + + serial0: serial@4500 { + status = "disabled"; + }; + + spi@7000 { + status = "disabled"; + }; + + gpio: gpio-controller@f000 { + status = "disabled"; + }; + + dma@21300 { + status = "disabled"; + }; + + mdio@24000 { + status = "disabled"; + }; + + mdio@25000 { + status = "disabled"; + }; + + enet1: ethernet@b1000 { + status = "disabled"; + }; + + enet2: ethernet@b2000 { + status = "disabled"; + }; + + usb@22000 { + status = "disabled"; + }; + + sdhci@2e000 { + status = "disabled"; + }; + + mpic: pic@40000 { + protected-sources = < + 16 /* ecm, mem, L2, pci0, pci1 */ + 43 42 59 /* i2c, serial0, spi */ + 47 63 62 /* gpio, tdm */ + 20 21 22 23 /* dma */ + 03 02 /* mdio */ + 35 36 40 /* enet1-queue-group0 */ + 51 52 67 /* enet1-queue-group1 */ + 31 32 33 /* enet2-queue-group0 */ + 25 26 27 /* enet2-queue-group1 */ + 28 72 58 /* usb, sdhci, crypto */ + 0xb0 0xb1 0xb2 /* message */ + 0xb3 0xb4 0xb5 + 0xb6 0xb7 + 0xe0 0xe1 0xe2 /* msi */ + 0xe3 0xe4 0xe5 + 0xe6 0xe7 /* sdhci, crypto , pci */ + >; + pic-no-reset; + }; + + msi@41600 { + status = "disabled"; + }; + + global-utilities@e0000 { //global utilities block + status = "disabled"; + }; + }; + + pci0: pcie@ffe09000 { + status = "disabled"; + }; + + pci1: pcie@ffe0a000 { + status = "disabled"; + }; +}; -- cgit v0.10.2 From 330bbf485447c0cf127750eb7d68d43a73f59356 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Mon, 12 Mar 2012 17:12:57 +0000 Subject: powerpc: Add GE FPGA config option This patch adds the GE_FPGA configuration option. This is being carried out as ground work to allow the PIC and GPIO drivers to be move from the powerpc 86xx platform directory to more general locations to allow them to be used on non-86xx boards and to reduce churn when further boards using these drivers are added. Signed-off-by: Martyn Welch Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index 8d6599d..abe2c4f 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -39,6 +39,7 @@ config GEF_PPC9A select MMIO_NVRAM select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB + select GE_FPGA help This option enables support for the GE PPC9A. @@ -48,6 +49,7 @@ config GEF_SBC310 select MMIO_NVRAM select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB + select GE_FPGA help This option enables support for the GE SBC310. @@ -57,10 +59,15 @@ config GEF_SBC610 select MMIO_NVRAM select GENERIC_GPIO select ARCH_REQUIRE_GPIOLIB + select GE_FPGA select HAS_RAPIDIO help This option enables support for the GE SBC610. +config GE_FPGA + bool + default n + endif config MPC8641 diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile index 4b0d7b1..ac6a50f 100644 --- a/arch/powerpc/platforms/86xx/Makefile +++ b/arch/powerpc/platforms/86xx/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MPC8641_HPCN) += mpc86xx_hpcn.o obj-$(CONFIG_SBC8641D) += sbc8641d.o obj-$(CONFIG_MPC8610_HPCD) += mpc8610_hpcd.o gef-gpio-$(CONFIG_GPIOLIB) += gef_gpio.o -obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o gef_pic.o $(gef-gpio-y) -obj-$(CONFIG_GEF_SBC310) += gef_sbc310.o gef_pic.o $(gef-gpio-y) -obj-$(CONFIG_GEF_PPC9A) += gef_ppc9a.o gef_pic.o $(gef-gpio-y) +obj-$(CONFIG_GE_FPGA) += gef_pic.o $(gef-gpio-y) +obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o +obj-$(CONFIG_GEF_SBC310) += gef_sbc310.o +obj-$(CONFIG_GEF_PPC9A) += gef_ppc9a.o diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 877b107..2955c3f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1039,7 +1039,7 @@ config LANTIQ_WDT config GEF_WDT tristate "GE Watchdog Timer" - depends on GEF_SBC610 || GEF_SBC310 || GEF_PPC9A + depends on GE_FPGA ---help--- Watchdog timer found in a number of GE single board computers. -- cgit v0.10.2 From 6518bb69f463446a1552f52093cc699497f18fe0 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Mon, 12 Mar 2012 17:12:58 +0000 Subject: gpio: Move GE GPIO driver to reside within GPIO subsystem The GE GPIO driver provides basic support (set direction, read/write state) for the GPIO provided on some GE single board computers. This patch moves the driver from the 86xx specific platform directrory to the GPIO subsystem so that it can be used on non-86xx boards. Signed-off-by: Martyn Welch Signed-off-by: Kumar Gala diff --git a/arch/powerpc/configs/86xx/gef_ppc9a_defconfig b/arch/powerpc/configs/86xx/gef_ppc9a_defconfig index d41857a..da731c2 100644 --- a/arch/powerpc/configs/86xx/gef_ppc9a_defconfig +++ b/arch/powerpc/configs/86xx/gef_ppc9a_defconfig @@ -131,6 +131,7 @@ CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MPC=y CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GE_FPGA=y CONFIG_SENSORS_LM90=y CONFIG_SENSORS_LM92=y CONFIG_WATCHDOG=y diff --git a/arch/powerpc/configs/86xx/gef_sbc310_defconfig b/arch/powerpc/configs/86xx/gef_sbc310_defconfig index 38303ec..2149360 100644 --- a/arch/powerpc/configs/86xx/gef_sbc310_defconfig +++ b/arch/powerpc/configs/86xx/gef_sbc310_defconfig @@ -132,6 +132,7 @@ CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MPC=y CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GE_FPGA=y CONFIG_SENSORS_LM90=y CONFIG_SENSORS_LM92=y CONFIG_WATCHDOG=y diff --git a/arch/powerpc/configs/86xx/gef_sbc610_defconfig b/arch/powerpc/configs/86xx/gef_sbc610_defconfig index 9853397..af2e8e1 100644 --- a/arch/powerpc/configs/86xx/gef_sbc610_defconfig +++ b/arch/powerpc/configs/86xx/gef_sbc610_defconfig @@ -183,6 +183,8 @@ CONFIG_NVRAM=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MPC=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GE_FPGA=y CONFIG_SENSORS_LM90=y CONFIG_SENSORS_LM92=y CONFIG_WATCHDOG=y diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile index ac6a50f..1ee6ca8 100644 --- a/arch/powerpc/platforms/86xx/Makefile +++ b/arch/powerpc/platforms/86xx/Makefile @@ -7,8 +7,7 @@ obj-$(CONFIG_SMP) += mpc86xx_smp.o obj-$(CONFIG_MPC8641_HPCN) += mpc86xx_hpcn.o obj-$(CONFIG_SBC8641D) += sbc8641d.o obj-$(CONFIG_MPC8610_HPCD) += mpc8610_hpcd.o -gef-gpio-$(CONFIG_GPIOLIB) += gef_gpio.o -obj-$(CONFIG_GE_FPGA) += gef_pic.o $(gef-gpio-y) +obj-$(CONFIG_GE_FPGA) += gef_pic.o obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o obj-$(CONFIG_GEF_SBC310) += gef_sbc310.o obj-$(CONFIG_GEF_PPC9A) += gef_ppc9a.o diff --git a/arch/powerpc/platforms/86xx/gef_gpio.c b/arch/powerpc/platforms/86xx/gef_gpio.c deleted file mode 100644 index 2a70336..0000000 --- a/arch/powerpc/platforms/86xx/gef_gpio.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Driver for GE FPGA based GPIO - * - * Author: Martyn Welch - * - * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -/* TODO - * - * Configuration of output modes (totem-pole/open-drain) - * Interrupt configuration - interrupts are always generated the FPGA relies on - * the I/O interrupt controllers mask to stop them propergating - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GEF_GPIO_DIRECT 0x00 -#define GEF_GPIO_IN 0x04 -#define GEF_GPIO_OUT 0x08 -#define GEF_GPIO_TRIG 0x0C -#define GEF_GPIO_POLAR_A 0x10 -#define GEF_GPIO_POLAR_B 0x14 -#define GEF_GPIO_INT_STAT 0x18 -#define GEF_GPIO_OVERRUN 0x1C -#define GEF_GPIO_MODE 0x20 - -static void _gef_gpio_set(void __iomem *reg, unsigned int offset, int value) -{ - unsigned int data; - - data = ioread32be(reg); - /* value: 0=low; 1=high */ - if (value & 0x1) - data = data | (0x1 << offset); - else - data = data & ~(0x1 << offset); - - iowrite32be(data, reg); -} - - -static int gef_gpio_dir_in(struct gpio_chip *chip, unsigned offset) -{ - unsigned int data; - struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); - - data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); - data = data | (0x1 << offset); - iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); - - return 0; -} - -static int gef_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value) -{ - unsigned int data; - struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); - - /* Set direction before switching to input */ - _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); - - data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); - data = data & ~(0x1 << offset); - iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); - - return 0; -} - -static int gef_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - unsigned int data; - int state = 0; - struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); - - data = ioread32be(mmchip->regs + GEF_GPIO_IN); - state = (int)((data >> offset) & 0x1); - - return state; -} - -static void gef_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); - - _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); -} - -static int __init gef_gpio_init(void) -{ - struct device_node *np; - int retval; - struct of_mm_gpio_chip *gef_gpio_chip; - - for_each_compatible_node(np, NULL, "gef,sbc610-gpio") { - - pr_debug("%s: Initialising GEF GPIO\n", np->full_name); - - /* Allocate chip structure */ - gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); - if (!gef_gpio_chip) { - pr_err("%s: Unable to allocate structure\n", - np->full_name); - continue; - } - - /* Setup pointers to chip functions */ - gef_gpio_chip->gc.of_gpio_n_cells = 2; - gef_gpio_chip->gc.ngpio = 19; - gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; - gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; - gef_gpio_chip->gc.get = gef_gpio_get; - gef_gpio_chip->gc.set = gef_gpio_set; - - /* This function adds a memory mapped GPIO chip */ - retval = of_mm_gpiochip_add(np, gef_gpio_chip); - if (retval) { - kfree(gef_gpio_chip); - pr_err("%s: Unable to add GPIO\n", np->full_name); - } - } - - for_each_compatible_node(np, NULL, "gef,sbc310-gpio") { - - pr_debug("%s: Initialising GEF GPIO\n", np->full_name); - - /* Allocate chip structure */ - gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); - if (!gef_gpio_chip) { - pr_err("%s: Unable to allocate structure\n", - np->full_name); - continue; - } - - /* Setup pointers to chip functions */ - gef_gpio_chip->gc.of_gpio_n_cells = 2; - gef_gpio_chip->gc.ngpio = 6; - gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; - gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; - gef_gpio_chip->gc.get = gef_gpio_get; - gef_gpio_chip->gc.set = gef_gpio_set; - - /* This function adds a memory mapped GPIO chip */ - retval = of_mm_gpiochip_add(np, gef_gpio_chip); - if (retval) { - kfree(gef_gpio_chip); - pr_err("%s: Unable to add GPIO\n", np->full_name); - } - } - - return 0; -}; -arch_initcall(gef_gpio_init); - -MODULE_DESCRIPTION("GE I/O FPGA GPIO driver"); -MODULE_AUTHOR("Martyn Welch + * + * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/* TODO + * + * Configuration of output modes (totem-pole/open-drain) + * Interrupt configuration - interrupts are always generated the FPGA relies on + * the I/O interrupt controllers mask to stop them propergating + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GEF_GPIO_DIRECT 0x00 +#define GEF_GPIO_IN 0x04 +#define GEF_GPIO_OUT 0x08 +#define GEF_GPIO_TRIG 0x0C +#define GEF_GPIO_POLAR_A 0x10 +#define GEF_GPIO_POLAR_B 0x14 +#define GEF_GPIO_INT_STAT 0x18 +#define GEF_GPIO_OVERRUN 0x1C +#define GEF_GPIO_MODE 0x20 + +static void _gef_gpio_set(void __iomem *reg, unsigned int offset, int value) +{ + unsigned int data; + + data = ioread32be(reg); + /* value: 0=low; 1=high */ + if (value & 0x1) + data = data | (0x1 << offset); + else + data = data & ~(0x1 << offset); + + iowrite32be(data, reg); +} + + +static int gef_gpio_dir_in(struct gpio_chip *chip, unsigned offset) +{ + unsigned int data; + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); + data = data | (0x1 << offset); + iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); + + return 0; +} + +static int gef_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned int data; + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + /* Set direction before switching to input */ + _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); + + data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); + data = data & ~(0x1 << offset); + iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); + + return 0; +} + +static int gef_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int data; + int state = 0; + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + data = ioread32be(mmchip->regs + GEF_GPIO_IN); + state = (int)((data >> offset) & 0x1); + + return state; +} + +static void gef_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + + _gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); +} + +static int __init gef_gpio_init(void) +{ + struct device_node *np; + int retval; + struct of_mm_gpio_chip *gef_gpio_chip; + + for_each_compatible_node(np, NULL, "gef,sbc610-gpio") { + + pr_debug("%s: Initialising GEF GPIO\n", np->full_name); + + /* Allocate chip structure */ + gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); + if (!gef_gpio_chip) { + pr_err("%s: Unable to allocate structure\n", + np->full_name); + continue; + } + + /* Setup pointers to chip functions */ + gef_gpio_chip->gc.of_gpio_n_cells = 2; + gef_gpio_chip->gc.ngpio = 19; + gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; + gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; + gef_gpio_chip->gc.get = gef_gpio_get; + gef_gpio_chip->gc.set = gef_gpio_set; + + /* This function adds a memory mapped GPIO chip */ + retval = of_mm_gpiochip_add(np, gef_gpio_chip); + if (retval) { + kfree(gef_gpio_chip); + pr_err("%s: Unable to add GPIO\n", np->full_name); + } + } + + for_each_compatible_node(np, NULL, "gef,sbc310-gpio") { + + pr_debug("%s: Initialising GEF GPIO\n", np->full_name); + + /* Allocate chip structure */ + gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); + if (!gef_gpio_chip) { + pr_err("%s: Unable to allocate structure\n", + np->full_name); + continue; + } + + /* Setup pointers to chip functions */ + gef_gpio_chip->gc.of_gpio_n_cells = 2; + gef_gpio_chip->gc.ngpio = 6; + gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; + gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; + gef_gpio_chip->gc.get = gef_gpio_get; + gef_gpio_chip->gc.set = gef_gpio_set; + + /* This function adds a memory mapped GPIO chip */ + retval = of_mm_gpiochip_add(np, gef_gpio_chip); + if (retval) { + kfree(gef_gpio_chip); + pr_err("%s: Unable to add GPIO\n", np->full_name); + } + } + + return 0; +}; +arch_initcall(gef_gpio_init); + +MODULE_DESCRIPTION("GE I/O FPGA GPIO driver"); +MODULE_AUTHOR("Martyn Welch Date: Mon, 12 Mar 2012 17:12:59 +0000 Subject: powerpc: Move GE PIC drivers Move the GE PIC drivers to allow these to be used by non-86xx boards. Signed-off-by: Martyn Welch Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index abe2c4f..7a6279e 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -64,10 +64,6 @@ config GEF_SBC610 help This option enables support for the GE SBC610. -config GE_FPGA - bool - default n - endif config MPC8641 diff --git a/arch/powerpc/platforms/86xx/Makefile b/arch/powerpc/platforms/86xx/Makefile index 1ee6ca8..ede815d 100644 --- a/arch/powerpc/platforms/86xx/Makefile +++ b/arch/powerpc/platforms/86xx/Makefile @@ -7,7 +7,6 @@ obj-$(CONFIG_SMP) += mpc86xx_smp.o obj-$(CONFIG_MPC8641_HPCN) += mpc86xx_hpcn.o obj-$(CONFIG_SBC8641D) += sbc8641d.o obj-$(CONFIG_MPC8610_HPCD) += mpc8610_hpcd.o -obj-$(CONFIG_GE_FPGA) += gef_pic.o obj-$(CONFIG_GEF_SBC610) += gef_sbc610.o obj-$(CONFIG_GEF_SBC310) += gef_sbc310.o obj-$(CONFIG_GEF_PPC9A) += gef_ppc9a.o diff --git a/arch/powerpc/platforms/86xx/gef_pic.c b/arch/powerpc/platforms/86xx/gef_pic.c deleted file mode 100644 index 94594e5..0000000 --- a/arch/powerpc/platforms/86xx/gef_pic.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Interrupt handling for GE FPGA based PIC - * - * Author: Martyn Welch - * - * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "gef_pic.h" - -#define DEBUG -#undef DEBUG - -#ifdef DEBUG -#define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0) -#else -#define DBG(fmt...) do { } while (0) -#endif - -#define GEF_PIC_NUM_IRQS 32 - -/* Interrupt Controller Interface Registers */ -#define GEF_PIC_INTR_STATUS 0x0000 - -#define GEF_PIC_INTR_MASK(cpu) (0x0010 + (0x4 * cpu)) -#define GEF_PIC_CPU0_INTR_MASK GEF_PIC_INTR_MASK(0) -#define GEF_PIC_CPU1_INTR_MASK GEF_PIC_INTR_MASK(1) - -#define GEF_PIC_MCP_MASK(cpu) (0x0018 + (0x4 * cpu)) -#define GEF_PIC_CPU0_MCP_MASK GEF_PIC_MCP_MASK(0) -#define GEF_PIC_CPU1_MCP_MASK GEF_PIC_MCP_MASK(1) - - -static DEFINE_RAW_SPINLOCK(gef_pic_lock); - -static void __iomem *gef_pic_irq_reg_base; -static struct irq_host *gef_pic_irq_host; -static int gef_pic_cascade_irq; - -/* - * Interrupt Controller Handling - * - * The interrupt controller handles interrupts for most on board interrupts, - * apart from PCI interrupts. For example on SBC610: - * - * 17:31 RO Reserved - * 16 RO PCI Express Doorbell 3 Status - * 15 RO PCI Express Doorbell 2 Status - * 14 RO PCI Express Doorbell 1 Status - * 13 RO PCI Express Doorbell 0 Status - * 12 RO Real Time Clock Interrupt Status - * 11 RO Temperature Interrupt Status - * 10 RO Temperature Critical Interrupt Status - * 9 RO Ethernet PHY1 Interrupt Status - * 8 RO Ethernet PHY3 Interrupt Status - * 7 RO PEX8548 Interrupt Status - * 6 RO Reserved - * 5 RO Watchdog 0 Interrupt Status - * 4 RO Watchdog 1 Interrupt Status - * 3 RO AXIS Message FIFO A Interrupt Status - * 2 RO AXIS Message FIFO B Interrupt Status - * 1 RO AXIS Message FIFO C Interrupt Status - * 0 RO AXIS Message FIFO D Interrupt Status - * - * Interrupts can be forwarded to one of two output lines. Nothing - * clever is done, so if the masks are incorrectly set, a single input - * interrupt could generate interrupts on both output lines! - * - * The dual lines are there to allow the chained interrupts to be easily - * passed into two different cores. We currently do not use this functionality - * in this driver. - * - * Controller can also be configured to generate Machine checks (MCP), again on - * two lines, to be attached to two different cores. It is suggested that these - * should be masked out. - */ - -void gef_pic_cascade(unsigned int irq, struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - unsigned int cascade_irq; - - /* - * See if we actually have an interrupt, call generic handling code if - * we do. - */ - cascade_irq = gef_pic_get_irq(); - - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq); - - chip->irq_eoi(&desc->irq_data); -} - -static void gef_pic_mask(struct irq_data *d) -{ - unsigned long flags; - unsigned int hwirq = irqd_to_hwirq(d); - u32 mask; - - raw_spin_lock_irqsave(&gef_pic_lock, flags); - mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); - mask &= ~(1 << hwirq); - out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); - raw_spin_unlock_irqrestore(&gef_pic_lock, flags); -} - -static void gef_pic_mask_ack(struct irq_data *d) -{ - /* Don't think we actually have to do anything to ack an interrupt, - * we just need to clear down the devices interrupt and it will go away - */ - gef_pic_mask(d); -} - -static void gef_pic_unmask(struct irq_data *d) -{ - unsigned long flags; - unsigned int hwirq = irqd_to_hwirq(d); - u32 mask; - - raw_spin_lock_irqsave(&gef_pic_lock, flags); - mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); - mask |= (1 << hwirq); - out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); - raw_spin_unlock_irqrestore(&gef_pic_lock, flags); -} - -static struct irq_chip gef_pic_chip = { - .name = "gefp", - .irq_mask = gef_pic_mask, - .irq_mask_ack = gef_pic_mask_ack, - .irq_unmask = gef_pic_unmask, -}; - - -/* When an interrupt is being configured, this call allows some flexibilty - * in deciding which irq_chip structure is used - */ -static int gef_pic_host_map(struct irq_host *h, unsigned int virq, - irq_hw_number_t hwirq) -{ - /* All interrupts are LEVEL sensitive */ - irq_set_status_flags(virq, IRQ_LEVEL); - irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq); - - return 0; -} - -static int gef_pic_host_xlate(struct irq_host *h, struct device_node *ct, - const u32 *intspec, unsigned int intsize, - irq_hw_number_t *out_hwirq, unsigned int *out_flags) -{ - - *out_hwirq = intspec[0]; - if (intsize > 1) - *out_flags = intspec[1]; - else - *out_flags = IRQ_TYPE_LEVEL_HIGH; - - return 0; -} - -static struct irq_host_ops gef_pic_host_ops = { - .map = gef_pic_host_map, - .xlate = gef_pic_host_xlate, -}; - - -/* - * Initialisation of PIC, this should be called in BSP - */ -void __init gef_pic_init(struct device_node *np) -{ - unsigned long flags; - - /* Map the devices registers into memory */ - gef_pic_irq_reg_base = of_iomap(np, 0); - - raw_spin_lock_irqsave(&gef_pic_lock, flags); - - /* Initialise everything as masked. */ - out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0); - out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0); - - out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0); - out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0); - - raw_spin_unlock_irqrestore(&gef_pic_lock, flags); - - /* Map controller */ - gef_pic_cascade_irq = irq_of_parse_and_map(np, 0); - if (gef_pic_cascade_irq == NO_IRQ) { - printk(KERN_ERR "SBC610: failed to map cascade interrupt"); - return; - } - - /* Setup an irq_host structure */ - gef_pic_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, - GEF_PIC_NUM_IRQS, - &gef_pic_host_ops, NO_IRQ); - if (gef_pic_irq_host == NULL) - return; - - /* Chain with parent controller */ - irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade); -} - -/* - * This is called when we receive an interrupt with apparently comes from this - * chip - check, returning the highest interrupt generated or return NO_IRQ - */ -unsigned int gef_pic_get_irq(void) -{ - u32 cause, mask, active; - unsigned int virq = NO_IRQ; - int hwirq; - - cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS); - - mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); - - active = cause & mask; - - if (active) { - for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) { - if (active & (0x1 << hwirq)) - break; - } - virq = irq_linear_revmap(gef_pic_irq_host, - (irq_hw_number_t)hwirq); - } - - return virq; -} - diff --git a/arch/powerpc/platforms/86xx/gef_pic.h b/arch/powerpc/platforms/86xx/gef_pic.h deleted file mode 100644 index 6149916..0000000 --- a/arch/powerpc/platforms/86xx/gef_pic.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __GEF_PIC_H__ -#define __GEF_PIC_H__ - -#include - -void gef_pic_cascade(unsigned int, struct irq_desc *); -unsigned int gef_pic_get_irq(void); -void gef_pic_init(struct device_node *); - -#endif /* __GEF_PIC_H__ */ - diff --git a/arch/powerpc/platforms/86xx/gef_ppc9a.c b/arch/powerpc/platforms/86xx/gef_ppc9a.c index 60ce07e..ed58b6c 100644 --- a/arch/powerpc/platforms/86xx/gef_ppc9a.c +++ b/arch/powerpc/platforms/86xx/gef_ppc9a.c @@ -37,9 +37,9 @@ #include #include +#include #include "mpc86xx.h" -#include "gef_pic.h" #undef DEBUG diff --git a/arch/powerpc/platforms/86xx/gef_sbc310.c b/arch/powerpc/platforms/86xx/gef_sbc310.c index 3ecee25..710db69 100644 --- a/arch/powerpc/platforms/86xx/gef_sbc310.c +++ b/arch/powerpc/platforms/86xx/gef_sbc310.c @@ -37,9 +37,9 @@ #include #include +#include #include "mpc86xx.h" -#include "gef_pic.h" #undef DEBUG diff --git a/arch/powerpc/platforms/86xx/gef_sbc610.c b/arch/powerpc/platforms/86xx/gef_sbc610.c index 5090d60..4a13d2f 100644 --- a/arch/powerpc/platforms/86xx/gef_sbc610.c +++ b/arch/powerpc/platforms/86xx/gef_sbc610.c @@ -37,9 +37,9 @@ #include #include +#include #include "mpc86xx.h" -#include "gef_pic.h" #undef DEBUG diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig index 7b4df37..a84fecf 100644 --- a/arch/powerpc/sysdev/Kconfig +++ b/arch/powerpc/sysdev/Kconfig @@ -29,3 +29,7 @@ config SCOM_DEBUGFS bool "Expose SCOM controllers via debugfs" depends on PPC_SCOM default n + +config GE_FPGA + bool + default n diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 5e37b47..f80ff9f 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -65,3 +65,5 @@ obj-$(CONFIG_PPC_SCOM) += scom.o subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror obj-$(CONFIG_PPC_XICS) += xics/ + +obj-$(CONFIG_GE_FPGA) += ge/ diff --git a/arch/powerpc/sysdev/ge/Makefile b/arch/powerpc/sysdev/ge/Makefile new file mode 100644 index 0000000..8731ffc --- /dev/null +++ b/arch/powerpc/sysdev/ge/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_GE_FPGA) += ge_pic.o diff --git a/arch/powerpc/sysdev/ge/ge_pic.c b/arch/powerpc/sysdev/ge/ge_pic.c new file mode 100644 index 0000000..002a562 --- /dev/null +++ b/arch/powerpc/sysdev/ge/ge_pic.c @@ -0,0 +1,252 @@ +/* + * Interrupt handling for GE FPGA based PIC + * + * Author: Martyn Welch + * + * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ge_pic.h" + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0) +#else +#define DBG(fmt...) do { } while (0) +#endif + +#define GEF_PIC_NUM_IRQS 32 + +/* Interrupt Controller Interface Registers */ +#define GEF_PIC_INTR_STATUS 0x0000 + +#define GEF_PIC_INTR_MASK(cpu) (0x0010 + (0x4 * cpu)) +#define GEF_PIC_CPU0_INTR_MASK GEF_PIC_INTR_MASK(0) +#define GEF_PIC_CPU1_INTR_MASK GEF_PIC_INTR_MASK(1) + +#define GEF_PIC_MCP_MASK(cpu) (0x0018 + (0x4 * cpu)) +#define GEF_PIC_CPU0_MCP_MASK GEF_PIC_MCP_MASK(0) +#define GEF_PIC_CPU1_MCP_MASK GEF_PIC_MCP_MASK(1) + + +static DEFINE_RAW_SPINLOCK(gef_pic_lock); + +static void __iomem *gef_pic_irq_reg_base; +static struct irq_host *gef_pic_irq_host; +static int gef_pic_cascade_irq; + +/* + * Interrupt Controller Handling + * + * The interrupt controller handles interrupts for most on board interrupts, + * apart from PCI interrupts. For example on SBC610: + * + * 17:31 RO Reserved + * 16 RO PCI Express Doorbell 3 Status + * 15 RO PCI Express Doorbell 2 Status + * 14 RO PCI Express Doorbell 1 Status + * 13 RO PCI Express Doorbell 0 Status + * 12 RO Real Time Clock Interrupt Status + * 11 RO Temperature Interrupt Status + * 10 RO Temperature Critical Interrupt Status + * 9 RO Ethernet PHY1 Interrupt Status + * 8 RO Ethernet PHY3 Interrupt Status + * 7 RO PEX8548 Interrupt Status + * 6 RO Reserved + * 5 RO Watchdog 0 Interrupt Status + * 4 RO Watchdog 1 Interrupt Status + * 3 RO AXIS Message FIFO A Interrupt Status + * 2 RO AXIS Message FIFO B Interrupt Status + * 1 RO AXIS Message FIFO C Interrupt Status + * 0 RO AXIS Message FIFO D Interrupt Status + * + * Interrupts can be forwarded to one of two output lines. Nothing + * clever is done, so if the masks are incorrectly set, a single input + * interrupt could generate interrupts on both output lines! + * + * The dual lines are there to allow the chained interrupts to be easily + * passed into two different cores. We currently do not use this functionality + * in this driver. + * + * Controller can also be configured to generate Machine checks (MCP), again on + * two lines, to be attached to two different cores. It is suggested that these + * should be masked out. + */ + +void gef_pic_cascade(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int cascade_irq; + + /* + * See if we actually have an interrupt, call generic handling code if + * we do. + */ + cascade_irq = gef_pic_get_irq(); + + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq); + + chip->irq_eoi(&desc->irq_data); +} + +static void gef_pic_mask(struct irq_data *d) +{ + unsigned long flags; + unsigned int hwirq = irqd_to_hwirq(d); + u32 mask; + + raw_spin_lock_irqsave(&gef_pic_lock, flags); + mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); + mask &= ~(1 << hwirq); + out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); + raw_spin_unlock_irqrestore(&gef_pic_lock, flags); +} + +static void gef_pic_mask_ack(struct irq_data *d) +{ + /* Don't think we actually have to do anything to ack an interrupt, + * we just need to clear down the devices interrupt and it will go away + */ + gef_pic_mask(d); +} + +static void gef_pic_unmask(struct irq_data *d) +{ + unsigned long flags; + unsigned int hwirq = irqd_to_hwirq(d); + u32 mask; + + raw_spin_lock_irqsave(&gef_pic_lock, flags); + mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); + mask |= (1 << hwirq); + out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); + raw_spin_unlock_irqrestore(&gef_pic_lock, flags); +} + +static struct irq_chip gef_pic_chip = { + .name = "gefp", + .irq_mask = gef_pic_mask, + .irq_mask_ack = gef_pic_mask_ack, + .irq_unmask = gef_pic_unmask, +}; + + +/* When an interrupt is being configured, this call allows some flexibilty + * in deciding which irq_chip structure is used + */ +static int gef_pic_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hwirq) +{ + /* All interrupts are LEVEL sensitive */ + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq); + + return 0; +} + +static int gef_pic_host_xlate(struct irq_host *h, struct device_node *ct, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_flags) +{ + + *out_hwirq = intspec[0]; + if (intsize > 1) + *out_flags = intspec[1]; + else + *out_flags = IRQ_TYPE_LEVEL_HIGH; + + return 0; +} + +static struct irq_host_ops gef_pic_host_ops = { + .map = gef_pic_host_map, + .xlate = gef_pic_host_xlate, +}; + + +/* + * Initialisation of PIC, this should be called in BSP + */ +void __init gef_pic_init(struct device_node *np) +{ + unsigned long flags; + + /* Map the devices registers into memory */ + gef_pic_irq_reg_base = of_iomap(np, 0); + + raw_spin_lock_irqsave(&gef_pic_lock, flags); + + /* Initialise everything as masked. */ + out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0); + out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0); + + out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0); + out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0); + + raw_spin_unlock_irqrestore(&gef_pic_lock, flags); + + /* Map controller */ + gef_pic_cascade_irq = irq_of_parse_and_map(np, 0); + if (gef_pic_cascade_irq == NO_IRQ) { + printk(KERN_ERR "SBC610: failed to map cascade interrupt"); + return; + } + + /* Setup an irq_host structure */ + gef_pic_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, + GEF_PIC_NUM_IRQS, + &gef_pic_host_ops, NO_IRQ); + if (gef_pic_irq_host == NULL) + return; + + /* Chain with parent controller */ + irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade); +} + +/* + * This is called when we receive an interrupt with apparently comes from this + * chip - check, returning the highest interrupt generated or return NO_IRQ + */ +unsigned int gef_pic_get_irq(void) +{ + u32 cause, mask, active; + unsigned int virq = NO_IRQ; + int hwirq; + + cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS); + + mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); + + active = cause & mask; + + if (active) { + for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) { + if (active & (0x1 << hwirq)) + break; + } + virq = irq_linear_revmap(gef_pic_irq_host, + (irq_hw_number_t)hwirq); + } + + return virq; +} + diff --git a/arch/powerpc/sysdev/ge/ge_pic.h b/arch/powerpc/sysdev/ge/ge_pic.h new file mode 100644 index 0000000..6149916 --- /dev/null +++ b/arch/powerpc/sysdev/ge/ge_pic.h @@ -0,0 +1,11 @@ +#ifndef __GEF_PIC_H__ +#define __GEF_PIC_H__ + +#include + +void gef_pic_cascade(unsigned int, struct irq_desc *); +unsigned int gef_pic_get_irq(void); +void gef_pic_init(struct device_node *); + +#endif /* __GEF_PIC_H__ */ + -- cgit v0.10.2 From e041013ac0df7fc7dea73c9ca73a33ab5b48d155 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Mon, 12 Mar 2012 17:13:00 +0000 Subject: powerpc/85xx: Board support for GE IMP3A Initial board support for the GE IMP3A, a 3U compactPCI card with a p2020 processor. Signed-off-by: Martyn Welch Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/ge_imp3a.dts b/arch/powerpc/boot/dts/ge_imp3a.dts new file mode 100644 index 0000000..fefae41 --- /dev/null +++ b/arch/powerpc/boot/dts/ge_imp3a.dts @@ -0,0 +1,255 @@ +/* + * GE IMP3A Device Tree Source + * + * Copyright 2010-2011 GE Intelligent Platforms Embedded Systems, 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. + * + * Based on: P2020 DS Device Tree Source + * Copyright 2009 Freescale Semiconductor Inc. + */ + +/include/ "fsl/p2020si-pre.dtsi" + +/ { + model = "GE_IMP3A"; + compatible = "ge,imp3a"; + + memory { + device_type = "memory"; + }; + + lbc: localbus@fef05000 { + reg = <0 0xfef05000 0 0x1000>; + + ranges = <0x0 0x0 0x0 0xff000000 0x01000000 + 0x1 0x0 0x0 0xe0000000 0x08000000 + 0x2 0x0 0x0 0xe8000000 0x08000000 + 0x3 0x0 0x0 0xfc100000 0x00020000 + 0x4 0x0 0x0 0xfc000000 0x00008000 + 0x5 0x0 0x0 0xfc008000 0x00008000 + 0x6 0x0 0x0 0xfee00000 0x00040000 + 0x7 0x0 0x0 0xfee80000 0x00040000>; + + /* nor@0,0 is a mirror of part of the memory in nor@1,0 + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ge,imp3a-firmware-mirror", "cfi-flash"; + reg = <0x0 0x0 0x1000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + label = "firmware"; + reg = <0x0 0x1000000>; + read-only; + }; + }; + */ + + nor@1,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ge,imp3a-paged-flash", "cfi-flash"; + reg = <0x1 0x0 0x8000000>; + bank-width = <2>; + device-width = <1>; + + partition@0 { + label = "user"; + reg = <0x0 0x7800000>; + }; + + partition@7800000 { + label = "firmware"; + reg = <0x7800000 0x800000>; + read-only; + }; + }; + + nvram@3,0 { + device_type = "nvram"; + compatible = "simtek,stk14ca8"; + reg = <0x3 0x0 0x20000>; + }; + + fpga@4,0 { + compatible = "ge,imp3a-fpga-regs"; + reg = <0x4 0x0 0x20>; + }; + + gef_pic: pic@4,20 { + #interrupt-cells = <1>; + interrupt-controller; + device_type = "interrupt-controller"; + compatible = "ge,imp3a-fpga-pic", "gef,fpga-pic-1.00"; + reg = <0x4 0x20 0x20>; + interrupts = <6 7 0 0>; + }; + + gef_gpio: gpio@4,400 { + #gpio-cells = <2>; + compatible = "ge,imp3a-gpio"; + reg = <0x4 0x400 0x24>; + gpio-controller; + }; + + wdt@4,800 { + compatible = "ge,imp3a-fpga-wdt", "gef,fpga-wdt-1.00", + "gef,fpga-wdt"; + reg = <0x4 0x800 0x8>; + interrupts = <10 4>; + interrupt-parent = <&gef_pic>; + }; + + /* Second watchdog available, driver currently supports one. + wdt@4,808 { + compatible = "gef,imp3a-fpga-wdt", "gef,fpga-wdt-1.00", + "gef,fpga-wdt"; + reg = <0x4 0x808 0x8>; + interrupts = <9 4>; + interrupt-parent = <&gef_pic>; + }; + */ + + nand@6,0 { + compatible = "fsl,elbc-fcm-nand"; + reg = <0x6 0x0 0x40000>; + }; + + nand@7,0 { + compatible = "fsl,elbc-fcm-nand"; + reg = <0x7 0x0 0x40000>; + }; + }; + + soc: soc@fef00000 { + ranges = <0x0 0 0xfef00000 0x100000>; + + i2c@3000 { + hwmon@48 { + compatible = "national,lm92"; + reg = <0x48>; + }; + + hwmon@4c { + compatible = "adi,adt7461"; + reg = <0x4c>; + }; + + rtc@51 { + compatible = "epson,rx8581"; + reg = <0x51>; + }; + + eti@6b { + compatible = "dallas,ds1682"; + reg = <0x6b>; + }; + }; + + usb@22000 { + phy_type = "ulpi"; + dr_mode = "host"; + }; + + mdio@24520 { + phy0: ethernet-phy@0 { + interrupt-parent = <&gef_pic>; + interrupts = <0xc 0x4>; + reg = <0x1>; + }; + phy1: ethernet-phy@1 { + interrupt-parent = <&gef_pic>; + interrupts = <0xb 0x4>; + reg = <0x2>; + }; + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@25520 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + mdio@26520 { + status = "disabled"; + }; + + enet0: ethernet@24000 { + tbi-handle = <&tbi0>; + phy-handle = <&phy0>; + phy-connection-type = "gmii"; + }; + + enet1: ethernet@25000 { + tbi-handle = <&tbi1>; + phy-handle = <&phy1>; + phy-connection-type = "gmii"; + }; + + enet2: ethernet@26000 { + status = "disabled"; + }; + }; + + pci0: pcie@fef08000 { + ranges = <0x2000000 0x0 0xc0000000 0 0xc0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xfe020000 0x0 0x10000>; + reg = <0 0xfef08000 0 0x1000>; + + pcie@0 { + ranges = <0x2000000 0x0 0xc0000000 + 0x2000000 0x0 0xc0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x10000>; + }; + }; + + pci1: pcie@fef09000 { + reg = <0 0xfef09000 0 0x1000>; + ranges = <0x2000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xfe010000 0x0 0x10000>; + + pcie@0 { + ranges = <0x2000000 0x0 0xa0000000 + 0x2000000 0x0 0xa0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x10000>; + }; + + }; + + pci2: pcie@fef0a000 { + reg = <0 0xfef0a000 0 0x1000>; + ranges = <0x2000000 0x0 0x80000000 0 0x80000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xfe000000 0x0 0x10000>; + + pcie@0 { + ranges = <0x2000000 0x0 0x80000000 + 0x2000000 0x0 0x80000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x10000>; + }; + }; +}; + +/include/ "fsl/p2020si-post.dtsi" diff --git a/arch/powerpc/configs/85xx/ge_imp3a_defconfig b/arch/powerpc/configs/85xx/ge_imp3a_defconfig new file mode 100644 index 0000000..f8c51a4 --- /dev/null +++ b/arch/powerpc/configs/85xx/ge_imp3a_defconfig @@ -0,0 +1,257 @@ +CONFIG_PPC_85xx=y +CONFIG_SMP=y +CONFIG_NR_CPUS=2 +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_SPARSE_IRQ=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_PERF_EVENTS=y +CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_GE_IMP3A=y +CONFIG_QUICC_ENGINE=y +CONFIG_QE_GPIO=y +CONFIG_CPM2=y +CONFIG_HIGHMEM=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HZ_1000=y +CONFIG_PREEMPT=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=m +CONFIG_MATH_EMULATION=y +CONFIG_IRQ_ALL_CPUS=y +CONFIG_FORCE_MAX_ZONEORDER=17 +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCI_MSI=y +CONFIG_PCCARD=y +# CONFIG_PCMCIA_LOAD_CIS is not set +CONFIG_YENTA=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=m +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET6_AH=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NET_PKTGEN=m +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_MTD=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_FSL_ELBC=y +CONFIG_PROC_DEVICETREE=y +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_CRYPTOLOOP=m +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=131072 +CONFIG_MISC_DEVICES=y +CONFIG_DS1682=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_SIL24=y +# CONFIG_ATA_SFF is not set +CONFIG_NETDEVICES=y +CONFIG_BONDING=m +CONFIG_DUMMY=m +CONFIG_NETCONSOLE=y +CONFIG_NETPOLL_TRAP=y +CONFIG_TUN=m +# CONFIG_NET_VENDOR_3COM is not set +CONFIG_FS_ENET=y +CONFIG_UCC_GETH=y +CONFIG_GIANFAR=y +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=2 +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_QE=m +CONFIG_NVRAM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_CPM=m +CONFIG_I2C_MPC=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GE_FPGA=y +CONFIG_SENSORS_LM90=y +CONFIG_SENSORS_LM92=y +CONFIG_WATCHDOG=y +CONFIG_GEF_WDT=y +CONFIG_VIDEO_OUTPUT_CONTROL=m +CONFIG_HID_DRAGONRISE=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_ZEROPLUS=y +CONFIG_USB=y +CONFIG_USB_DEVICEFS=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_FSL=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PPC_OF_BE=y +CONFIG_USB_OHCI_HCD_PPC_OF_LE=y +CONFIG_USB_STORAGE=y +CONFIG_EDAC=y +CONFIG_EDAC_MM_EDAC=y +CONFIG_EDAC_MPC85XX=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_INTF_PROC is not set +CONFIG_RTC_DRV_RX8581=y +CONFIG_DMADEVICES=y +CONFIG_FSL_DMA=y +# CONFIG_NET_DMA is not set +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=850 +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +CONFIG_NTFS_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NFSD=y +CONFIG_NFSD_V4=y +CONFIG_CIFS=m +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=y +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=y +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_T10DIF=y +CONFIG_LIBCRC32C=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_TALITOS=y diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index b221236..4c91ea9 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -172,6 +172,21 @@ config SBC8560 help This option enables support for the Wind River SBC8560 board +config GE_IMP3A + bool "GE Intelligent Platforms IMP3A" + select DEFAULT_UIMAGE + select SWIOTLB + select MMIO_NVRAM + select GENERIC_GPIO + select ARCH_REQUIRE_GPIOLIB + select GE_FPGA + help + This option enables support for the GE Intelligent Platforms IMP3A + board. + + This board is a 3U CompactPCI Single Board Computer with a Freescale + P2020 processor. + config P2041_RDB bool "Freescale P2041 RDB" select DEFAULT_UIMAGE diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 9cb2d43..2125d4c 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_SBC8548) += sbc8548.o obj-$(CONFIG_SOCRATES) += socrates.o socrates_fpga_pic.o obj-$(CONFIG_KSI8560) += ksi8560.o obj-$(CONFIG_XES_MPC85xx) += xes_mpc85xx.o +obj-$(CONFIG_GE_IMP3A) += ge_imp3a.o diff --git a/arch/powerpc/platforms/85xx/ge_imp3a.c b/arch/powerpc/platforms/85xx/ge_imp3a.c new file mode 100644 index 0000000..d50056f --- /dev/null +++ b/arch/powerpc/platforms/85xx/ge_imp3a.c @@ -0,0 +1,246 @@ +/* + * GE IMP3A Board Setup + * + * Author Martyn Welch + * + * Copyright 2010 GE Intelligent Platforms Embedded Systems, 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. + * + * Based on: mpc85xx_ds.c (MPC85xx DS Board Setup) + * Copyright 2007 Freescale Semiconductor Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "smp.h" + +#include "mpc85xx.h" +#include + +void __iomem *imp3a_regs; + +void __init ge_imp3a_pic_init(void) +{ + struct mpic *mpic; + struct device_node *np; + struct device_node *cascade_node = NULL; + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS-CAMP")) { + mpic = mpic_alloc(NULL, 0, + MPIC_NO_RESET | + MPIC_BIG_ENDIAN | + MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + } else { + mpic = mpic_alloc(NULL, 0, + MPIC_BIG_ENDIAN | + MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + } + + BUG_ON(mpic == NULL); + mpic_init(mpic); + /* + * There is a simple interrupt handler in the main FPGA, this needs + * to be cascaded into the MPIC + */ + for_each_node_by_type(np, "interrupt-controller") + if (of_device_is_compatible(np, "gef,fpga-pic-1.00")) { + cascade_node = np; + break; + } + + if (cascade_node == NULL) { + printk(KERN_WARNING "IMP3A: No FPGA PIC\n"); + return; + } + + gef_pic_init(cascade_node); + of_node_put(cascade_node); +} + +#ifdef CONFIG_PCI +static int primary_phb_addr; +#endif /* CONFIG_PCI */ + +/* + * Setup the architecture + */ +static void __init ge_imp3a_setup_arch(void) +{ + struct device_node *regs; +#ifdef CONFIG_PCI + struct device_node *np; + struct pci_controller *hose; +#endif + dma_addr_t max = 0xffffffff; + + if (ppc_md.progress) + ppc_md.progress("ge_imp3a_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,mpc8540-pci") || + of_device_is_compatible(np, "fsl,mpc8548-pcie") || + of_device_is_compatible(np, "fsl,p2020-pcie")) { + struct resource rsrc; + of_address_to_resource(np, 0, &rsrc); + if ((rsrc.start & 0xfffff) == primary_phb_addr) + fsl_add_bridge(np, 1); + else + fsl_add_bridge(np, 0); + + hose = pci_find_hose_for_OF_device(np); + max = min(max, hose->dma_window_base_cur + + hose->dma_window_size); + } + } +#endif + + mpc85xx_smp_init(); + +#ifdef CONFIG_SWIOTLB + if (memblock_end_of_DRAM() > max) { + ppc_swiotlb_enable = 1; + set_pci_dma_ops(&swiotlb_dma_ops); + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; + } +#endif + + /* Remap basic board registers */ + regs = of_find_compatible_node(NULL, NULL, "ge,imp3a-fpga-regs"); + if (regs) { + imp3a_regs = of_iomap(regs, 0); + if (imp3a_regs == NULL) + printk(KERN_WARNING "Unable to map board registers\n"); + of_node_put(regs); + } + +#if defined(CONFIG_MMIO_NVRAM) + mmio_nvram_init(); +#endif + + printk(KERN_INFO "GE Intelligent Platforms IMP3A 3U cPCI SBC\n"); +} + +/* Return the PCB revision */ +static unsigned int ge_imp3a_get_pcb_rev(void) +{ + unsigned int reg; + + reg = ioread16(imp3a_regs); + return (reg >> 8) & 0xff; +} + +/* Return the board (software) revision */ +static unsigned int ge_imp3a_get_board_rev(void) +{ + unsigned int reg; + + reg = ioread16(imp3a_regs + 0x2); + return reg & 0xff; +} + +/* Return the FPGA revision */ +static unsigned int ge_imp3a_get_fpga_rev(void) +{ + unsigned int reg; + + reg = ioread16(imp3a_regs + 0x2); + return (reg >> 8) & 0xff; +} + +/* Return compactPCI Geographical Address */ +static unsigned int ge_imp3a_get_cpci_geo_addr(void) +{ + unsigned int reg; + + reg = ioread16(imp3a_regs + 0x6); + return (reg & 0x0f00) >> 8; +} + +/* Return compactPCI System Controller Status */ +static unsigned int ge_imp3a_get_cpci_is_syscon(void) +{ + unsigned int reg; + + reg = ioread16(imp3a_regs + 0x6); + return reg & (1 << 12); +} + +static void ge_imp3a_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "Vendor\t\t: GE Intelligent Platforms\n"); + + seq_printf(m, "Revision\t: %u%c\n", ge_imp3a_get_pcb_rev(), + ('A' + ge_imp3a_get_board_rev() - 1)); + + seq_printf(m, "FPGA Revision\t: %u\n", ge_imp3a_get_fpga_rev()); + + seq_printf(m, "cPCI geo. addr\t: %u\n", ge_imp3a_get_cpci_geo_addr()); + + seq_printf(m, "cPCI syscon\t: %s\n", + ge_imp3a_get_cpci_is_syscon() ? "yes" : "no"); +} + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init ge_imp3a_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "ge,IMP3A")) { +#ifdef CONFIG_PCI + primary_phb_addr = 0x9000; +#endif + return 1; + } + + return 0; +} + +machine_device_initcall(ge_imp3a, mpc85xx_common_publish_devices); + +machine_arch_initcall(ge_imp3a, swiotlb_setup_bus_notifier); + +define_machine(ge_imp3a) { + .name = "GE_IMP3A", + .probe = ge_imp3a_probe, + .setup_arch = ge_imp3a_setup_arch, + .init_IRQ = ge_imp3a_pic_init, + .show_cpuinfo = ge_imp3a_show_cpuinfo, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c index f8e6289..7b95a4a 100644 --- a/drivers/gpio/gpio-ge.c +++ b/drivers/gpio/gpio-ge.c @@ -162,6 +162,34 @@ static int __init gef_gpio_init(void) } } + for_each_compatible_node(np, NULL, "ge,imp3a-gpio") { + + pr_debug("%s: Initialising GE GPIO\n", np->full_name); + + /* Allocate chip structure */ + gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); + if (!gef_gpio_chip) { + pr_err("%s: Unable to allocate structure\n", + np->full_name); + continue; + } + + /* Setup pointers to chip functions */ + gef_gpio_chip->gc.of_gpio_n_cells = 2; + gef_gpio_chip->gc.ngpio = 16; + gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; + gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; + gef_gpio_chip->gc.get = gef_gpio_get; + gef_gpio_chip->gc.set = gef_gpio_set; + + /* This function adds a memory mapped GPIO chip */ + retval = of_mm_gpiochip_add(np, gef_gpio_chip); + if (retval) { + kfree(gef_gpio_chip); + pr_err("%s: Unable to add GPIO\n", np->full_name); + } + } + return 0; }; arch_initcall(gef_gpio_init); -- cgit v0.10.2 From 6597c713b7a8a77ea8696a1df4bbb673f3de22aa Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 18 Nov 2011 07:50:01 +0000 Subject: powerpc/85xx: p1022ds: enable monitor switching via pixis indirect mode When the P1022's DIU video controller is active, the pixis must be accessed in "indirect" mode, which uses localbus chip select addresses. Switching between the DVI and LVDS monitor ports is handled by the pixis, so that switching needs to be done via indirect mode. This has the side-effect of no longer requiring U-Boot to enable the DIU. Now Linux can enable the DIU all by itself. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index e211b0d..0fe88e3 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -33,6 +33,10 @@ #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) +#define PMUXCR_ELBCDIU_MASK 0xc0000000 +#define PMUXCR_ELBCDIU_NOR16 0x80000000 +#define PMUXCR_ELBCDIU_DIU 0x40000000 + /* * Board-specific initialization of the DIU. This code should probably be * executed when the DIU is opened, rather than in arch code, but the DIU @@ -50,11 +54,22 @@ #define CLKDVDR_PXCLK_MASK 0x00FF0000 /* Some ngPIXIS register definitions */ +#define PX_CTL 3 +#define PX_BRDCFG0 8 +#define PX_BRDCFG1 9 + +#define PX_BRDCFG0_ELBC_SPI_MASK 0xc0 +#define PX_BRDCFG0_ELBC_SPI_ELBC 0x00 +#define PX_BRDCFG0_ELBC_SPI_NULL 0xc0 +#define PX_BRDCFG0_ELBC_DIU 0x02 + #define PX_BRDCFG1_DVIEN 0x80 #define PX_BRDCFG1_DFPEN 0x40 #define PX_BRDCFG1_BACKLIGHT 0x20 #define PX_BRDCFG1_DDCEN 0x10 +#define PX_CTL_ALTACC 0x80 + /* * DIU Area Descriptor * @@ -133,44 +148,117 @@ static void p1022ds_set_gamma_table(enum fsl_diu_monitor_port port, */ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) { - struct device_node *np; - void __iomem *pixis; - u8 __iomem *brdcfg1; - - np = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-fpga"); - if (!np) - /* older device trees used "fsl,p1022ds-pixis" */ - np = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-pixis"); - if (!np) { - pr_err("p1022ds: missing ngPIXIS node\n"); + struct device_node *guts_node; + struct device_node *indirect_node = NULL; + struct ccsr_guts_85xx __iomem *guts; + u8 __iomem *lbc_lcs0_ba = NULL; + u8 __iomem *lbc_lcs1_ba = NULL; + u8 b; + + /* Map the global utilities registers. */ + guts_node = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts"); + if (!guts_node) { + pr_err("p1022ds: missing global utilties device node\n"); return; } - pixis = of_iomap(np, 0); - if (!pixis) { - pr_err("p1022ds: could not map ngPIXIS registers\n"); - return; + guts = of_iomap(guts_node, 0); + if (!guts) { + pr_err("p1022ds: could not map global utilties device\n"); + goto exit; + } + + indirect_node = of_find_compatible_node(NULL, NULL, + "fsl,p1022ds-indirect-pixis"); + if (!indirect_node) { + pr_err("p1022ds: missing pixis indirect mode node\n"); + goto exit; } - brdcfg1 = pixis + 9; /* BRDCFG1 is at offset 9 in the ngPIXIS */ + + lbc_lcs0_ba = of_iomap(indirect_node, 0); + if (!lbc_lcs0_ba) { + pr_err("p1022ds: could not map localbus chip select 0\n"); + goto exit; + } + + lbc_lcs1_ba = of_iomap(indirect_node, 1); + if (!lbc_lcs1_ba) { + pr_err("p1022ds: could not map localbus chip select 1\n"); + goto exit; + } + + /* Make sure we're in indirect mode first. */ + if ((in_be32(&guts->pmuxcr) & PMUXCR_ELBCDIU_MASK) != + PMUXCR_ELBCDIU_DIU) { + struct device_node *pixis_node; + void __iomem *pixis; + + pixis_node = + of_find_compatible_node(NULL, NULL, "fsl,p1022ds-fpga"); + if (!pixis_node) { + pr_err("p1022ds: missing pixis node\n"); + goto exit; + } + + pixis = of_iomap(pixis_node, 0); + of_node_put(pixis_node); + if (!pixis) { + pr_err("p1022ds: could not map pixis registers\n"); + goto exit; + } + + /* Enable indirect PIXIS mode. */ + setbits8(pixis + PX_CTL, PX_CTL_ALTACC); + iounmap(pixis); + + /* Switch the board mux to the DIU */ + out_8(lbc_lcs0_ba, PX_BRDCFG0); /* BRDCFG0 */ + b = in_8(lbc_lcs1_ba); + b |= PX_BRDCFG0_ELBC_DIU; + out_8(lbc_lcs1_ba, b); + + /* Set the chip mux to DIU mode. */ + clrsetbits_be32(&guts->pmuxcr, PMUXCR_ELBCDIU_MASK, + PMUXCR_ELBCDIU_DIU); + in_be32(&guts->pmuxcr); + } + switch (port) { case FSL_DIU_PORT_DVI: - printk(KERN_INFO "%s:%u\n", __func__, __LINE__); /* Enable the DVI port, disable the DFP and the backlight */ - clrsetbits_8(brdcfg1, PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT, - PX_BRDCFG1_DVIEN); + out_8(lbc_lcs0_ba, PX_BRDCFG1); + b = in_8(lbc_lcs1_ba); + b &= ~(PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT); + b |= PX_BRDCFG1_DVIEN; + out_8(lbc_lcs1_ba, b); break; case FSL_DIU_PORT_LVDS: - printk(KERN_INFO "%s:%u\n", __func__, __LINE__); + /* + * LVDS also needs backlight enabled, otherwise the display + * will be blank. + */ /* Enable the DFP port, disable the DVI and the backlight */ - clrsetbits_8(brdcfg1, PX_BRDCFG1_DVIEN | PX_BRDCFG1_BACKLIGHT, - PX_BRDCFG1_DFPEN); + out_8(lbc_lcs0_ba, PX_BRDCFG1); + b = in_8(lbc_lcs1_ba); + b &= ~PX_BRDCFG1_DVIEN; + b |= PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT; + out_8(lbc_lcs1_ba, b); break; default: pr_err("p1022ds: unsupported monitor port %i\n", port); } - iounmap(pixis); +exit: + if (lbc_lcs1_ba) + iounmap(lbc_lcs1_ba); + if (lbc_lcs0_ba) + iounmap(lbc_lcs0_ba); + if (guts) + iounmap(guts); + + of_node_put(indirect_node); + of_node_put(guts_node); } /** -- cgit v0.10.2 From 1ee4af86fa16d636dfd1a6f2e7fc70f34b5eb9b1 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 27 Feb 2012 07:25:01 -0500 Subject: powerpc/83xx: mpc836x - fix failed phy detection for ucc ethernet on MDS The mpc836x_mds platform has been broken since the commit 6fe3264945ee63292cdfb27b6e95bc52c603bb09 "netdev/phy: Use mdiobus_read() so that proper locks are taken" which caused the fsl_pq_mdio TBI autoprobe to oops. The oops was "fixed" in commit 28d8ea2d568534026ccda3e8936f5ea1e04a86a1 "fsl_pq_mdio: Clean up tbi address configuration" by simply removing the the autoscan code, and making tbi nodes mandatory. Some of the newer reference platforms were updated to have tbi nodes in 220669495bf8b68130a8218607147c7b74c28d2b "powerpc: Add TBI PHY node to first MDIO bus" but the older mpc836x didn't get one and hence was just failing with -EBUSY as follows: fsl-pq_mdio: probe of e0102120.mdio failed with error -16 ... net eth0: Could not attach to PHY eth0: Cannot initialize PHY, aborting. Add a TBI node and use the 1st free address for it. Signed-off-by: Paul Gortmaker Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts index c0e450a..81dd513 100644 --- a/arch/powerpc/boot/dts/mpc836x_mds.dts +++ b/arch/powerpc/boot/dts/mpc836x_mds.dts @@ -405,6 +405,10 @@ reg = <0x1>; device_type = "ethernet-phy"; }; + tbi-phy@2 { + device_type = "tbi-phy"; + reg = <0x2>; + }; }; qeic: interrupt-controller@80 { -- cgit v0.10.2 From e4399461bdf4d230129c99f785ebe6814269427b Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Wed, 1 Feb 2012 19:05:15 +0200 Subject: powerpc/85xx: Fix compiler error with THIS_MODULE and related CC arch/powerpc/sysdev/fsl_85xx_l2ctlr.o arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:209:13: error: 'THIS_MODULE' undeclared here (not in a function) arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:229:20: error: expected declaration specifiers or '...' before string constant cc1: warnings being treated as errors arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:229:1: error: data definition has no type or storage class arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:229:1: error: type defaults to 'int' in declaration of 'MODULE_DESCRIPTION' arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:229:20: error: function declaration isn't a prototype arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:230:16: error: expected declaration specifiers or '...' before string constant arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:230:1: error: data definition has no type or storage class arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:230:1: error: type defaults to 'int' in declaration of 'MODULE_LICENSE' arch/powerpc/sysdev/fsl_85xx_l2ctlr.c:230:16: error: function declaration isn't a prototype make[1]: *** [arch/powerpc/sysdev/fsl_85xx_l2ctlr.o] Error 1 ... CC arch/powerpc/sysdev/fsl_85xx_cache_sram.o cc1: warnings being treated as errors arch/powerpc/sysdev/fsl_85xx_cache_sram.c:69:1: error: data definition has no type or storage class arch/powerpc/sysdev/fsl_85xx_cache_sram.c:69:1: error: type defaults to 'int' in declaration of 'EXPORT_SYMBOL' arch/powerpc/sysdev/fsl_85xx_cache_sram.c:69:1: error: parameter names (without types) in function declaration arch/powerpc/sysdev/fsl_85xx_cache_sram.c:80:1: error: data definition has no type or storage class arch/powerpc/sysdev/fsl_85xx_cache_sram.c:80:1: error: type defaults to 'int' in declaration of 'EXPORT_SYMBOL' arch/powerpc/sysdev/fsl_85xx_cache_sram.c:80:1: error: parameter names (without types) in function declaration make[1]: *** [arch/powerpc/sysdev/fsl_85xx_cache_sram.o] Error 1 Signed-off-by: Claudiu Manoil Acked-by: Paul Gortmaker Signed-off-by: Kumar Gala diff --git a/arch/powerpc/sysdev/fsl_85xx_cache_sram.c b/arch/powerpc/sysdev/fsl_85xx_cache_sram.c index 1164158..37a6909 100644 --- a/arch/powerpc/sysdev/fsl_85xx_cache_sram.c +++ b/arch/powerpc/sysdev/fsl_85xx_cache_sram.c @@ -24,6 +24,7 @@ */ #include +#include #include #include #include diff --git a/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c index 5f88797..1957e532 100644 --- a/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c +++ b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c @@ -21,6 +21,7 @@ */ #include +#include #include #include -- cgit v0.10.2 From f7bba2aaff8d034dcac3dab6df2fe6e8a3b3e81d Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Tue, 31 Jan 2012 12:15:20 +0200 Subject: powerpc/85xx: Add missing config option for CACHE SRAM code fsl_85xx_l2ctlr.o and fsl_85xx_cache_sram.o are built only if CONFIG_FSL_85XX_CACHE_SRAM is defined. The driver that qualifies and wants to make use of the CACHE SRAM's exported API (i.e. a freescale net driver) should (be able to) select this config option. Signed-off-by: Claudiu Manoil Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 4c91ea9..fd6f3c0 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -14,6 +14,15 @@ if FSL_SOC_BOOKE if PPC32 +config FSL_85XX_CACHE_SRAM + bool + select PPC_LIB_RHEAP + help + When selected, this option enables cache-sram support + for memory allocation on P1/P2 QorIQ platforms. + cache-sram-size and cache-sram-offset kernel boot + parameters should be passed when this option is enabled. + config MPC8540_ADS bool "Freescale MPC8540 ADS" select DEFAULT_UIMAGE -- cgit v0.10.2 From 5d40433ee66d860f119ce2bf3181764b1fd88e2e Mon Sep 17 00:00:00 2001 From: Liu Shuo Date: Wed, 7 Mar 2012 13:20:06 +0800 Subject: powerpc/dts: fix the compatible string of sec 4.0 Fix the compatible string of sec 4.0 to match with CAAM driver according to Documentation/devicetree/bindings/crypto/fsl-sec4.txt Signed-off-by: Liu Shuo Signed-off-by: Shengzhou Liu Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi index bf957a7..d4c9d5d 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi @@ -33,32 +33,32 @@ */ crypto@30000 { - compatible = "fsl,sec4.4", "fsl,sec4.0"; + compatible = "fsl,sec-v4.4", "fsl,sec-v4.0"; #address-cells = <1>; #size-cells = <1>; reg = <0x30000 0x10000>; interrupts = <58 2 0 0>; sec_jr0: jr@1000 { - compatible = "fsl,sec4.4-job-ring", "fsl,sec4.0-job-ring"; + compatible = "fsl,sec-v4.4-job-ring", "fsl,sec-v4.0-job-ring"; reg = <0x1000 0x1000>; interrupts = <45 2 0 0>; }; sec_jr1: jr@2000 { - compatible = "fsl,sec4.4-job-ring", "fsl,sec4.0-job-ring"; + compatible = "fsl,sec-v4.4-job-ring", "fsl,sec-v4.0-job-ring"; reg = <0x2000 0x1000>; interrupts = <45 2 0 0>; }; sec_jr2: jr@3000 { - compatible = "fsl,sec4.4-job-ring", "fsl,sec4.0-job-ring"; + compatible = "fsl,sec-v4.4-job-ring", "fsl,sec-v4.0-job-ring"; reg = <0x3000 0x1000>; interrupts = <45 2 0 0>; }; sec_jr3: jr@4000 { - compatible = "fsl,sec4.4-job-ring", "fsl,sec4.0-job-ring"; + compatible = "fsl,sec-v4.4-job-ring", "fsl,sec-v4.0-job-ring"; reg = <0x4000 0x1000>; interrupts = <45 2 0 0>; }; -- cgit v0.10.2 From 401a376e94021f2040e6cedd4cc858e0eeec5854 Mon Sep 17 00:00:00 2001 From: chenhui zhao Date: Wed, 22 Feb 2012 18:20:13 +0800 Subject: powerpc/85xx: l2sram - Add compatible entry for mpc8548 L2 controller Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c index 1957e532..cedabd0 100644 --- a/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c +++ b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c @@ -201,6 +201,9 @@ static struct of_device_id mpc85xx_l2ctlr_of_match[] = { { .compatible = "fsl,p1022-l2-cache-controller", }, + { + .compatible = "fsl,mpc8548-l2-cache-controller", + }, {}, }; -- cgit v0.10.2 From 0c350a9a5cf01db3875ceb8b01121d8ce6a7f287 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 21 Feb 2012 13:53:15 -0600 Subject: powerpc/85xx: allow CONFIG_PHYS_64BIT to be selectable Remove the "select PHYS_64BIT" from the Kconfig entry for the P1022DS, so that large physical address support is a selectable option for non-CoreNet reference boards. The option is enabled in mpc85xx_[smp_]defconfig so that the default is unchanged. However, now it can be deselected. The P1022DS had this option defined because the default device tree for this board uses 36-bit addresses. This had the side-effect of forcing this option on for all boards that use mpc85xx_[smp_]defconfig. Some users may want to disable this feature to create an optimized configuration for boards with <= 2GB of RAM. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index f37a2ab..5fb0c8a 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -1,4 +1,5 @@ CONFIG_PPC_85xx=y +CONFIG_PHYS_64BIT=y CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index abdcd31..fb51bc9 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -1,4 +1,5 @@ CONFIG_PPC_85xx=y +CONFIG_PHYS_64BIT=y CONFIG_SMP=y CONFIG_NR_CPUS=8 CONFIG_EXPERIMENTAL=y diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index fd6f3c0..ce6142c 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -90,7 +90,6 @@ config P1010_RDB config P1022_DS bool "Freescale P1022 DS" select DEFAULT_UIMAGE - select PHYS_64BIT # The DTS has 36-bit addresses select SWIOTLB help This option enables support for the Freescale P1022DS reference board. -- cgit v0.10.2 From c141b38f8643b46701c9db819909120c5b667a26 Mon Sep 17 00:00:00 2001 From: Zhicheng Fan Date: Wed, 22 Feb 2012 13:44:07 +0800 Subject: powerpc/85xx: Abstract common define of signal multiplex control for qe The mpc85xx_rdb and mpc85xx_mds have commom define of signal multiplex for qe, so they need to go in common header, the patch abstract them to fsl_guts.h Signed-off-by: Zhicheng Fan Signed-off-by: Kumar Gala diff --git a/arch/powerpc/include/asm/fsl_guts.h b/arch/powerpc/include/asm/fsl_guts.h index bebd124..ce04530 100644 --- a/arch/powerpc/include/asm/fsl_guts.h +++ b/arch/powerpc/include/asm/fsl_guts.h @@ -4,7 +4,7 @@ * Authors: Jeff Brown * Timur Tabi * - * Copyright 2004,2007 Freescale Semiconductor, Inc + * Copyright 2004,2007,2012 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 @@ -114,6 +114,10 @@ struct ccsr_guts_86xx { __be32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ } __attribute__ ((packed)); + +/* Alternate function signal multiplex control */ +#define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) + #ifdef CONFIG_PPC_86xx #define CCSR_GUTS_DMACR_DEV_SSI 0 /* DMA controller/channel set to SSI */ diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index 57aceb5..f33662b 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -1,5 +1,6 @@ /* - * Copyright (C) Freescale Semicondutor, Inc. 2006-2010. All rights reserved. + * Copyright (C) 2006-2010, 2012 Freescale Semicondutor, Inc. + * All rights reserved. * * Author: Andy Fleming * @@ -51,6 +52,7 @@ #include #include #include +#include #include "smp.h" #include "mpc85xx.h" @@ -268,34 +270,27 @@ static void __init mpc85xx_mds_qe_init(void) mpc85xx_mds_reset_ucc_phys(); if (machine_is(p1021_mds)) { -#define MPC85xx_PMUXCR_OFFSET 0x60 -#define MPC85xx_PMUXCR_QE0 0x00008000 -#define MPC85xx_PMUXCR_QE3 0x00001000 -#define MPC85xx_PMUXCR_QE9 0x00000040 -#define MPC85xx_PMUXCR_QE12 0x00000008 - static __be32 __iomem *pmuxcr; - np = of_find_node_by_name(NULL, "global-utilities"); + struct ccsr_guts_85xx __iomem *guts; + np = of_find_node_by_name(NULL, "global-utilities"); if (np) { - pmuxcr = of_iomap(np, 0) + MPC85xx_PMUXCR_OFFSET; - - if (!pmuxcr) - printk(KERN_EMERG "Error: Alternate function" - " signal multiplex control register not" - " mapped!\n"); - else + guts = of_iomap(np, 0); + if (!guts) + pr_err("mpc85xx-rdb: could not map global utilities register\n"); + else{ /* P1021 has pins muxed for QE and other functions. To * enable QE UEC mode, we need to set bit QE0 for UCC1 * in Eth mode, QE0 and QE3 for UCC5 in Eth mode, QE9 * and QE12 for QE MII management signals in PMUXCR * register. */ - setbits32(pmuxcr, MPC85xx_PMUXCR_QE0 | - MPC85xx_PMUXCR_QE3 | - MPC85xx_PMUXCR_QE9 | - MPC85xx_PMUXCR_QE12); - + setbits32(&guts->pmuxcr, MPC85xx_PMUXCR_QE(0) | + MPC85xx_PMUXCR_QE(3) | + MPC85xx_PMUXCR_QE(9) | + MPC85xx_PMUXCR_QE(12)); + iounmap(guts); + } of_node_put(np); } -- cgit v0.10.2 From 04e358d896a7351c6cb453bcaa62cbbe99ae6c7c Mon Sep 17 00:00:00 2001 From: Zhicheng Fan Date: Wed, 22 Feb 2012 13:44:06 +0800 Subject: powerpc/85xx: Add Quicc Engine support for p1025rdb Signed-off-by: Zhicheng Fan Acked-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c index e5b260c..db214cd 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include @@ -47,6 +50,10 @@ void __init mpc85xx_rdb_pic_init(void) struct mpic *mpic; unsigned long root = of_get_flat_dt_root(); +#ifdef CONFIG_QUICC_ENGINE + struct device_node *np; +#endif + if (of_flat_dt_is_compatible(root, "fsl,MPC85XXRDB-CAMP")) { mpic = mpic_alloc(NULL, 0, MPIC_NO_RESET | MPIC_BIG_ENDIAN | @@ -61,6 +68,18 @@ void __init mpc85xx_rdb_pic_init(void) BUG_ON(mpic == NULL); mpic_init(mpic); + +#ifdef CONFIG_QUICC_ENGINE + np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic"); + if (np) { + qe_ic_init(np, 0, qe_ic_cascade_low_mpic, + qe_ic_cascade_high_mpic); + of_node_put(np); + + } else + pr_err("%s: Could not find qe-ic node\n", __func__); +#endif + } /* @@ -68,7 +87,7 @@ void __init mpc85xx_rdb_pic_init(void) */ static void __init mpc85xx_rdb_setup_arch(void) { -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) || defined(CONFIG_QUICC_ENGINE) struct device_node *np; #endif @@ -84,6 +103,62 @@ static void __init mpc85xx_rdb_setup_arch(void) #endif mpc85xx_smp_init(); + +#ifdef CONFIG_QUICC_ENGINE + np = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!np) { + pr_err("%s: Could not find Quicc Engine node\n", __func__); + goto qe_fail; + } + + qe_reset(); + of_node_put(np); + + np = of_find_node_by_name(NULL, "par_io"); + if (np) { + struct device_node *ucc; + + par_io_init(np); + of_node_put(np); + + for_each_node_by_name(ucc, "ucc") + par_io_of_config(ucc); + + } +#if defined(CONFIG_UCC_GETH) || defined(CONFIG_SERIAL_QE) + if (machine_is(p1025_rdb)) { + + struct ccsr_guts_85xx __iomem *guts; + + np = of_find_node_by_name(NULL, "global-utilities"); + if (np) { + guts = of_iomap(np, 0); + if (!guts) { + + pr_err("mpc85xx-rdb: could not map global utilities register\n"); + + } else { + /* P1025 has pins muxed for QE and other functions. To + * enable QE UEC mode, we need to set bit QE0 for UCC1 + * in Eth mode, QE0 and QE3 for UCC5 in Eth mode, QE9 + * and QE12 for QE MII management singals in PMUXCR + * register. + */ + setbits32(&guts->pmuxcr, MPC85xx_PMUXCR_QE(0) | + MPC85xx_PMUXCR_QE(3) | + MPC85xx_PMUXCR_QE(9) | + MPC85xx_PMUXCR_QE(12)); + iounmap(guts); + } + of_node_put(np); + } + + } +#endif + +qe_fail: +#endif /* CONFIG_QUICC_ENGINE */ + printk(KERN_INFO "MPC85xx RDB board from Freescale Semiconductor\n"); } -- cgit v0.10.2 From ad68ee016d928975161f82619410296af8eba2e8 Mon Sep 17 00:00:00 2001 From: chenhui zhao Date: Wed, 14 Mar 2012 18:15:27 +0800 Subject: powerpc/85xx: mpc85xxcds - Fix PCI I/O space resource of PCI bridge There is a PCI bridge(Tsi310) between the MPC8548 and a VIA southbridge chip. The bootloader sets the PCI bridge to open a window from 0x0000 to 0x1fff on the PCI I/O space. But the kernel can't set the I/O resource. In the routine pci_read_bridge_io(), if the base which is read from PCI_IO_BASE is equal to zero, the routine don't set the I/O resource of the child bus. To allow the legacy I/O space on the VIA southbridge to be accessed, use the fixup to fix the PCI I/O space of the PCI bridge. Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 02d97e3..766b215 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -3,7 +3,7 @@ * * Maintained by Kumar Gala (see MAINTAINERS for contact information) * - * Copyright 2005 Freescale Semiconductor Inc. + * Copyright 2005, 2011-2012 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 @@ -158,6 +158,33 @@ DECLARE_PCI_FIXUP_EARLY(0x1957, 0x3fff, skip_fake_bridge); DECLARE_PCI_FIXUP_EARLY(0x3fff, 0x1957, skip_fake_bridge); DECLARE_PCI_FIXUP_EARLY(0xff3f, 0x5719, skip_fake_bridge); +#define PCI_DEVICE_ID_IDT_TSI310 0x01a7 + +/* + * Fix Tsi310 PCI-X bridge resource. + * Force the bridge to open a window from 0x0000-0x1fff in PCI I/O space. + * This allows legacy I/O(i8259, etc) on the VIA southbridge to be accessed. + */ +void mpc85xx_cds_fixup_bus(struct pci_bus *bus) +{ + struct pci_dev *dev = bus->self; + struct resource *res = bus->resource[0]; + + if (dev != NULL && + dev->vendor == PCI_VENDOR_ID_IBM && + dev->device == PCI_DEVICE_ID_IDT_TSI310) { + if (res) { + res->start = 0; + res->end = 0x1fff; + res->flags = IORESOURCE_IO; + pr_info("mpc85xx_cds: PCI bridge resource fixup applied\n"); + pr_info("mpc85xx_cds: %pR\n", res); + } + } + + fsl_pcibios_fixup_bus(bus); +} + #ifdef CONFIG_PPC_I8259 static void mpc85xx_8259_cascade_handler(unsigned int irq, struct irq_desc *desc) @@ -322,7 +349,7 @@ define_machine(mpc85xx_cds) { .get_irq = mpic_get_irq, #ifdef CONFIG_PCI .restart = mpc85xx_cds_restart, - .pcibios_fixup_bus = fsl_pcibios_fixup_bus, + .pcibios_fixup_bus = mpc85xx_cds_fixup_bus, #else .restart = fsl_rstcr_restart, #endif -- cgit v0.10.2 From bcf3302c3c5cee587e5662d7f597f99c1a93cdd1 Mon Sep 17 00:00:00 2001 From: chenhui zhao Date: Tue, 6 Mar 2012 17:06:40 +0800 Subject: powerpc/85xx: mpc8548cds - Add NOR flash node to dts Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts index 07b8dae..c3c8741 100644 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ b/arch/powerpc/boot/dts/mpc8548cds.dts @@ -1,7 +1,7 @@ /* * MPC8548 CDS Device Tree Source * - * Copyright 2006, 2008 Freescale Semiconductor Inc. + * Copyright 2006, 2008, 2011 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 @@ -34,6 +34,44 @@ lbc: localbus@e0005000 { reg = <0 0xe0005000 0 0x1000>; + + ranges = <0x0 0x0 0x0 0xff000000 0x01000000>; + + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x01000000>; + bank-width = <2>; + device-width = <2>; + + partition@0 { + reg = <0x0 0x0b00000>; + label = "ramdisk-nor"; + }; + + partition@300000 { + reg = <0x0b00000 0x0400000>; + label = "kernel-nor"; + }; + + partition@700000 { + reg = <0x0f00000 0x060000>; + label = "dtb-nor"; + }; + + partition@760000 { + reg = <0x0f60000 0x020000>; + label = "env-nor"; + read-only; + }; + + partition@780000 { + reg = <0x0f80000 0x080000>; + label = "u-boot-nor"; + read-only; + }; + }; }; soc: soc8548@e0000000 { -- cgit v0.10.2 From 8232a4de6131542058cb47a627c5e7445ad61a3b Mon Sep 17 00:00:00 2001 From: chenhui zhao Date: Tue, 6 Mar 2012 17:06:41 +0800 Subject: powerpc/85xx: mpc8548cds - Add RapidIO node to dts Enable RapidIO and add rapidio and rmu nodes to dts. Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/mpc8548si-post.dtsi b/arch/powerpc/boot/dts/fsl/mpc8548si-post.dtsi index 9d8023a..579d76c 100644 --- a/arch/powerpc/boot/dts/fsl/mpc8548si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/mpc8548si-post.dtsi @@ -89,6 +89,21 @@ }; }; +&rio { + compatible = "fsl,srio"; + interrupts = <48 2 0 0>; + #address-cells = <2>; + #size-cells = <2>; + fsl,srio-rmu-handle = <&rmu>; + ranges; + + port1 { + #address-cells = <2>; + #size-cells = <2>; + cell-index = <1>; + }; +}; + &soc { #address-cells = <1>; #size-cells = <1>; @@ -134,6 +149,7 @@ /include/ "pq3-sec2.1-0.dtsi" /include/ "pq3-mpic.dtsi" +/include/ "pq3-rmu-0.dtsi" global-utilities@e0000 { compatible = "fsl,mpc8548-guts"; diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts index c3c8741..8d4df8e 100644 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ b/arch/powerpc/boot/dts/mpc8548cds.dts @@ -339,6 +339,13 @@ 0x0 0x100000>; }; }; + + rio: rapidio@e00c0000 { + reg = <0x0 0xe00c0000 0x0 0x20000>; + port1 { + ranges = <0x0 0x0 0x0 0xc0000000 0x0 0x20000000>; + }; + }; }; /include/ "fsl/mpc8548si-post.dtsi" diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index ce6142c..f000d81 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -40,6 +40,7 @@ config MPC85xx_CDS bool "Freescale MPC85xx CDS" select DEFAULT_UIMAGE select PPC_I8259 + select HAS_RAPIDIO help This option enables support for the MPC85xx CDS board -- cgit v0.10.2 From 96939e79b0d8571f557bf4d7f0f933282401342b Mon Sep 17 00:00:00 2001 From: Zhao Chenhui Date: Tue, 6 Mar 2012 17:06:43 +0800 Subject: powerpc/85xx: mpc8548cds - fix alias in mpc8548si-pre.dtsi Correct ethernet1 and add ethernet2 and ethernet3. Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/fsl/mpc8548si-pre.dtsi b/arch/powerpc/boot/dts/fsl/mpc8548si-pre.dtsi index 289f121..720422d 100644 --- a/arch/powerpc/boot/dts/fsl/mpc8548si-pre.dtsi +++ b/arch/powerpc/boot/dts/fsl/mpc8548si-pre.dtsi @@ -43,7 +43,9 @@ serial0 = &serial0; serial1 = &serial1; ethernet0 = &enet0; - ethernet1 = &enet2; + ethernet1 = &enet1; + ethernet2 = &enet2; + ethernet3 = &enet3; pci0 = &pci0; pci1 = &pci1; pci2 = &pci2; -- cgit v0.10.2 From 992608ff56b9c2e987f706da94ceca991b7886a4 Mon Sep 17 00:00:00 2001 From: chenhui zhao Date: Tue, 6 Mar 2012 17:06:42 +0800 Subject: powerpc/85xx: mpc8548cds - Add FPGA node to dts Remove FPGA(CADMUS) macros in code. Move it to dts. Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Acked-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts index 8d4df8e..0683983 100644 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ b/arch/powerpc/boot/dts/mpc8548cds.dts @@ -35,7 +35,8 @@ lbc: localbus@e0005000 { reg = <0 0xe0005000 0 0x1000>; - ranges = <0x0 0x0 0x0 0xff000000 0x01000000>; + ranges = <0x0 0x0 0x0 0xff000000 0x01000000 + 0x1 0x0 0x0 0xf8004000 0x00001000>; nor@0,0 { #address-cells = <1>; @@ -72,6 +73,11 @@ read-only; }; }; + + board-control@1,0 { + compatible = "fsl,mpc8548cds-fpga"; + reg = <0x1 0x0 0x1000>; + }; }; soc: soc8548@e0000000 { diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c index 766b215..ab5f0bf1 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_cds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -48,17 +48,24 @@ #include "mpc85xx.h" -/* CADMUS info */ -/* xxx - galak, move into device tree */ -#define CADMUS_BASE (0xf8004000) -#define CADMUS_SIZE (256) -#define CM_VER (0) -#define CM_CSR (1) -#define CM_RST (2) - +/* + * The CDS board contains an FPGA/CPLD called "Cadmus", which collects + * various logic and performs system control functions. + * Here is the FPGA/CPLD register map. + */ +struct cadmus_reg { + u8 cm_ver; /* Board version */ + u8 cm_csr; /* General control/status */ + u8 cm_rst; /* Reset control */ + u8 cm_hsclk; /* High speed clock */ + u8 cm_hsxclk; /* High speed clock extended */ + u8 cm_led; /* LED data */ + u8 cm_pci; /* PCI control/status */ + u8 cm_dma; /* DMA control */ + u8 res[248]; /* Total 256 bytes */ +}; -static int cds_pci_slot = 2; -static volatile u8 *cadmus; +static struct cadmus_reg *cadmus; #ifdef CONFIG_PCI @@ -275,20 +282,30 @@ machine_device_initcall(mpc85xx_cds, mpc85xx_cds_8259_attach); */ static void __init mpc85xx_cds_setup_arch(void) { -#ifdef CONFIG_PCI struct device_node *np; -#endif + int cds_pci_slot; if (ppc_md.progress) ppc_md.progress("mpc85xx_cds_setup_arch()", 0); - cadmus = ioremap(CADMUS_BASE, CADMUS_SIZE); - cds_pci_slot = ((cadmus[CM_CSR] >> 6) & 0x3) + 1; + np = of_find_compatible_node(NULL, NULL, "fsl,mpc8548cds-fpga"); + if (!np) { + pr_err("Could not find FPGA node.\n"); + return; + } + + cadmus = of_iomap(np, 0); + of_node_put(np); + if (!cadmus) { + pr_err("Fail to map FPGA area.\n"); + return; + } if (ppc_md.progress) { char buf[40]; + cds_pci_slot = ((in_8(&cadmus->cm_csr) >> 6) & 0x3) + 1; snprintf(buf, 40, "CDS Version = 0x%x in slot %d\n", - cadmus[CM_VER], cds_pci_slot); + in_8(&cadmus->cm_ver), cds_pci_slot); ppc_md.progress(buf, 0); } @@ -318,7 +335,8 @@ static void mpc85xx_cds_show_cpuinfo(struct seq_file *m) svid = mfspr(SPRN_SVR); seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n"); - seq_printf(m, "Machine\t\t: MPC85xx CDS (0x%x)\n", cadmus[CM_VER]); + seq_printf(m, "Machine\t\t: MPC85xx CDS (0x%x)\n", + in_8(&cadmus->cm_ver)); seq_printf(m, "PVR\t\t: 0x%x\n", pvid); seq_printf(m, "SVR\t\t: 0x%x\n", svid); -- cgit v0.10.2 From 0d4fdd321c5a4450622841c3bd8f2e370f033813 Mon Sep 17 00:00:00 2001 From: Zhao Chenhui Date: Tue, 6 Mar 2012 17:06:44 +0800 Subject: powerpc/85xx: Refactor mpc8548cds device tree * Create mpc8548cds.dtsi * Move lbc, soc and pci0 nodes to mpc8548cds_32b.dtsi * Change cuImage.mpc8548cds to cuImage.mpc8548cds_32b * Rename mpc8548cds.dts to mpc8548cds_32b.dts Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 8844a17..f6622e0 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -247,7 +247,7 @@ image-$(CONFIG_ASP834x) += dtbImage.asp834x-redboot image-$(CONFIG_MPC8540_ADS) += cuImage.mpc8540ads image-$(CONFIG_MPC8560_ADS) += cuImage.mpc8560ads image-$(CONFIG_MPC85xx_CDS) += cuImage.mpc8541cds \ - cuImage.mpc8548cds \ + cuImage.mpc8548cds_32b \ cuImage.mpc8555cds image-$(CONFIG_MPC85xx_MDS) += cuImage.mpc8568mds image-$(CONFIG_MPC85xx_DS) += cuImage.mpc8544ds \ diff --git a/arch/powerpc/boot/dts/mpc8548cds.dts b/arch/powerpc/boot/dts/mpc8548cds.dts deleted file mode 100644 index 0683983..0000000 --- a/arch/powerpc/boot/dts/mpc8548cds.dts +++ /dev/null @@ -1,357 +0,0 @@ -/* - * MPC8548 CDS Device Tree Source - * - * Copyright 2006, 2008, 2011 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/ "fsl/mpc8548si-pre.dtsi" - -/ { - model = "MPC8548CDS"; - compatible = "MPC8548CDS", "MPC85xxCDS"; - - aliases { - ethernet0 = &enet0; - ethernet1 = &enet1; - ethernet2 = &enet2; - ethernet3 = &enet3; - serial0 = &serial0; - serial1 = &serial1; - pci0 = &pci0; - pci1 = &pci1; - pci2 = &pci2; - }; - - memory { - device_type = "memory"; - reg = <0 0 0x0 0x8000000>; // 128M at 0x0 - }; - - lbc: localbus@e0005000 { - reg = <0 0xe0005000 0 0x1000>; - - ranges = <0x0 0x0 0x0 0xff000000 0x01000000 - 0x1 0x0 0x0 0xf8004000 0x00001000>; - - nor@0,0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "cfi-flash"; - reg = <0x0 0x0 0x01000000>; - bank-width = <2>; - device-width = <2>; - - partition@0 { - reg = <0x0 0x0b00000>; - label = "ramdisk-nor"; - }; - - partition@300000 { - reg = <0x0b00000 0x0400000>; - label = "kernel-nor"; - }; - - partition@700000 { - reg = <0x0f00000 0x060000>; - label = "dtb-nor"; - }; - - partition@760000 { - reg = <0x0f60000 0x020000>; - label = "env-nor"; - read-only; - }; - - partition@780000 { - reg = <0x0f80000 0x080000>; - label = "u-boot-nor"; - read-only; - }; - }; - - board-control@1,0 { - compatible = "fsl,mpc8548cds-fpga"; - reg = <0x1 0x0 0x1000>; - }; - }; - - soc: soc8548@e0000000 { - ranges = <0 0x0 0xe0000000 0x100000>; - - i2c@3000 { - eeprom@50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; - - eeprom@56 { - compatible = "atmel,24c64"; - reg = <0x56>; - }; - - eeprom@57 { - compatible = "atmel,24c64"; - reg = <0x57>; - }; - }; - - i2c@3100 { - eeprom@50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; - }; - - enet0: ethernet@24000 { - tbi-handle = <&tbi0>; - phy-handle = <&phy0>; - }; - - mdio@24520 { - phy0: ethernet-phy@0 { - interrupts = <5 1 0 0>; - reg = <0x0>; - device_type = "ethernet-phy"; - }; - phy1: ethernet-phy@1 { - interrupts = <5 1 0 0>; - reg = <0x1>; - device_type = "ethernet-phy"; - }; - phy2: ethernet-phy@2 { - interrupts = <5 1 0 0>; - reg = <0x2>; - device_type = "ethernet-phy"; - }; - phy3: ethernet-phy@3 { - interrupts = <5 1 0 0>; - reg = <0x3>; - device_type = "ethernet-phy"; - }; - tbi0: tbi-phy@11 { - reg = <0x11>; - device_type = "tbi-phy"; - }; - }; - - enet1: ethernet@25000 { - tbi-handle = <&tbi1>; - phy-handle = <&phy1>; - }; - - mdio@25520 { - tbi1: tbi-phy@11 { - reg = <0x11>; - device_type = "tbi-phy"; - }; - }; - - enet2: ethernet@26000 { - tbi-handle = <&tbi2>; - phy-handle = <&phy2>; - }; - - mdio@26520 { - tbi2: tbi-phy@11 { - reg = <0x11>; - device_type = "tbi-phy"; - }; - }; - - enet3: ethernet@27000 { - tbi-handle = <&tbi3>; - phy-handle = <&phy3>; - }; - - mdio@27520 { - tbi3: tbi-phy@11 { - reg = <0x11>; - device_type = "tbi-phy"; - }; - }; - }; - - pci0: pci@e0008000 { - reg = <0 0xe0008000 0 0x1000>; - ranges = <0x2000000 0x0 0x80000000 0 0x80000000 0x0 0x10000000 - 0x1000000 0x0 0x00000000 0 0xe2000000 0x0 0x800000>; - clock-frequency = <66666666>; - interrupt-map-mask = <0xf800 0x0 0x0 0x7>; - interrupt-map = < - /* IDSEL 0x4 (PCIX Slot 2) */ - 0x2000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0x2000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0x2000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0x2000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 - - /* IDSEL 0x5 (PCIX Slot 3) */ - 0x2800 0x0 0x0 0x1 &mpic 0x1 0x1 0 0 - 0x2800 0x0 0x0 0x2 &mpic 0x2 0x1 0 0 - 0x2800 0x0 0x0 0x3 &mpic 0x3 0x1 0 0 - 0x2800 0x0 0x0 0x4 &mpic 0x0 0x1 0 0 - - /* IDSEL 0x6 (PCIX Slot 4) */ - 0x3000 0x0 0x0 0x1 &mpic 0x2 0x1 0 0 - 0x3000 0x0 0x0 0x2 &mpic 0x3 0x1 0 0 - 0x3000 0x0 0x0 0x3 &mpic 0x0 0x1 0 0 - 0x3000 0x0 0x0 0x4 &mpic 0x1 0x1 0 0 - - /* IDSEL 0x8 (PCIX Slot 5) */ - 0x4000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0x4000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0x4000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0x4000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 - - /* IDSEL 0xC (Tsi310 bridge) */ - 0x6000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0x6000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0x6000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0x6000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 - - /* IDSEL 0x14 (Slot 2) */ - 0xa000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0xa000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0xa000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0xa000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 - - /* IDSEL 0x15 (Slot 3) */ - 0xa800 0x0 0x0 0x1 &mpic 0x1 0x1 0 0 - 0xa800 0x0 0x0 0x2 &mpic 0x2 0x1 0 0 - 0xa800 0x0 0x0 0x3 &mpic 0x3 0x1 0 0 - 0xa800 0x0 0x0 0x4 &mpic 0x0 0x1 0 0 - - /* IDSEL 0x16 (Slot 4) */ - 0xb000 0x0 0x0 0x1 &mpic 0x2 0x1 0 0 - 0xb000 0x0 0x0 0x2 &mpic 0x3 0x1 0 0 - 0xb000 0x0 0x0 0x3 &mpic 0x0 0x1 0 0 - 0xb000 0x0 0x0 0x4 &mpic 0x1 0x1 0 0 - - /* IDSEL 0x18 (Slot 5) */ - 0xc000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0xc000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0xc000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0xc000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 - - /* IDSEL 0x1C (Tsi310 bridge PCI primary) */ - 0xe000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0xe000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0xe000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0xe000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0>; - - pci_bridge@1c { - interrupt-map-mask = <0xf800 0x0 0x0 0x7>; - interrupt-map = < - - /* IDSEL 0x00 (PrPMC Site) */ - 0000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 - - /* IDSEL 0x04 (VIA chip) */ - 0x2000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 - 0x2000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0x2000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0x2000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 - - /* IDSEL 0x05 (8139) */ - 0x2800 0x0 0x0 0x1 &mpic 0x1 0x1 0 0 - - /* IDSEL 0x06 (Slot 6) */ - 0x3000 0x0 0x0 0x1 &mpic 0x2 0x1 0 0 - 0x3000 0x0 0x0 0x2 &mpic 0x3 0x1 0 0 - 0x3000 0x0 0x0 0x3 &mpic 0x0 0x1 0 0 - 0x3000 0x0 0x0 0x4 &mpic 0x1 0x1 0 0 - - /* IDESL 0x07 (Slot 7) */ - 0x3800 0x0 0x0 0x1 &mpic 0x3 0x1 0 0 - 0x3800 0x0 0x0 0x2 &mpic 0x0 0x1 0 0 - 0x3800 0x0 0x0 0x3 &mpic 0x1 0x1 0 0 - 0x3800 0x0 0x0 0x4 &mpic 0x2 0x1 0 0>; - - reg = <0xe000 0x0 0x0 0x0 0x0>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - ranges = <0x2000000 0x0 0x80000000 - 0x2000000 0x0 0x80000000 - 0x0 0x20000000 - 0x1000000 0x0 0x0 - 0x1000000 0x0 0x0 - 0x0 0x80000>; - clock-frequency = <33333333>; - - isa@4 { - device_type = "isa"; - #interrupt-cells = <2>; - #size-cells = <1>; - #address-cells = <2>; - reg = <0x2000 0x0 0x0 0x0 0x0>; - ranges = <0x1 0x0 0x1000000 0x0 0x0 0x1000>; - interrupt-parent = <&i8259>; - - i8259: interrupt-controller@20 { - interrupt-controller; - device_type = "interrupt-controller"; - reg = <0x1 0x20 0x2 - 0x1 0xa0 0x2 - 0x1 0x4d0 0x2>; - #address-cells = <0>; - #interrupt-cells = <2>; - compatible = "chrp,iic"; - interrupts = <0 1 0 0>; - interrupt-parent = <&mpic>; - }; - - rtc@70 { - compatible = "pnpPNP,b00"; - reg = <0x1 0x70 0x2>; - }; - }; - }; - }; - - pci1: pci@e0009000 { - reg = <0 0xe0009000 0 0x1000>; - ranges = <0x2000000 0x0 0x90000000 0 0x90000000 0x0 0x10000000 - 0x1000000 0x0 0x00000000 0 0xe2800000 0x0 0x800000>; - clock-frequency = <66666666>; - interrupt-map-mask = <0xf800 0x0 0x0 0x7>; - interrupt-map = < - - /* IDSEL 0x15 */ - 0xa800 0x0 0x0 0x1 &mpic 0xb 0x1 0 0 - 0xa800 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 - 0xa800 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 - 0xa800 0x0 0x0 0x4 &mpic 0x3 0x1 0 0>; - }; - - pci2: pcie@e000a000 { - reg = <0 0xe000a000 0 0x1000>; - ranges = <0x2000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000 - 0x1000000 0x0 0x00000000 0 0xe3000000 0x0 0x100000>; - pcie@0 { - ranges = <0x2000000 0x0 0xa0000000 - 0x2000000 0x0 0xa0000000 - 0x0 0x20000000 - - 0x1000000 0x0 0x0 - 0x1000000 0x0 0x0 - 0x0 0x100000>; - }; - }; - - rio: rapidio@e00c0000 { - reg = <0x0 0xe00c0000 0x0 0x20000>; - port1 { - ranges = <0x0 0x0 0x0 0xc0000000 0x0 0x20000000>; - }; - }; -}; - -/include/ "fsl/mpc8548si-post.dtsi" diff --git a/arch/powerpc/boot/dts/mpc8548cds.dtsi b/arch/powerpc/boot/dts/mpc8548cds.dtsi new file mode 100644 index 0000000..c61f525 --- /dev/null +++ b/arch/powerpc/boot/dts/mpc8548cds.dtsi @@ -0,0 +1,306 @@ +/* + * MPC8548CDS Device Tree Source stub (no addresses or top-level ranges) + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +&board_lbc { + nor@0,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x01000000>; + bank-width = <2>; + device-width = <2>; + + partition@0 { + reg = <0x0 0x0b00000>; + label = "ramdisk-nor"; + }; + + partition@300000 { + reg = <0x0b00000 0x0400000>; + label = "kernel-nor"; + }; + + partition@700000 { + reg = <0x0f00000 0x060000>; + label = "dtb-nor"; + }; + + partition@760000 { + reg = <0x0f60000 0x020000>; + label = "env-nor"; + read-only; + }; + + partition@780000 { + reg = <0x0f80000 0x080000>; + label = "u-boot-nor"; + read-only; + }; + }; + + board-control@1,0 { + compatible = "fsl,mpc8548cds-fpga"; + reg = <0x1 0x0 0x1000>; + }; +}; + +&board_soc { + i2c@3000 { + eeprom@50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + + eeprom@56 { + compatible = "atmel,24c64"; + reg = <0x56>; + }; + + eeprom@57 { + compatible = "atmel,24c64"; + reg = <0x57>; + }; + }; + + i2c@3100 { + eeprom@50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + enet0: ethernet@24000 { + tbi-handle = <&tbi0>; + phy-handle = <&phy0>; + }; + + mdio@24520 { + phy0: ethernet-phy@0 { + interrupts = <5 1 0 0>; + reg = <0x0>; + device_type = "ethernet-phy"; + }; + phy1: ethernet-phy@1 { + interrupts = <5 1 0 0>; + reg = <0x1>; + device_type = "ethernet-phy"; + }; + phy2: ethernet-phy@2 { + interrupts = <5 1 0 0>; + reg = <0x2>; + device_type = "ethernet-phy"; + }; + phy3: ethernet-phy@3 { + interrupts = <5 1 0 0>; + reg = <0x3>; + device_type = "ethernet-phy"; + }; + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet1: ethernet@25000 { + tbi-handle = <&tbi1>; + phy-handle = <&phy1>; + }; + + mdio@25520 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet2: ethernet@26000 { + tbi-handle = <&tbi2>; + phy-handle = <&phy2>; + }; + + mdio@26520 { + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet3: ethernet@27000 { + tbi-handle = <&tbi3>; + phy-handle = <&phy3>; + }; + + mdio@27520 { + tbi3: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; +}; + +&board_pci0 { + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + /* IDSEL 0x4 (PCIX Slot 2) */ + 0x2000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0x2000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0x2000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0x2000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 + + /* IDSEL 0x5 (PCIX Slot 3) */ + 0x2800 0x0 0x0 0x1 &mpic 0x1 0x1 0 0 + 0x2800 0x0 0x0 0x2 &mpic 0x2 0x1 0 0 + 0x2800 0x0 0x0 0x3 &mpic 0x3 0x1 0 0 + 0x2800 0x0 0x0 0x4 &mpic 0x0 0x1 0 0 + + /* IDSEL 0x6 (PCIX Slot 4) */ + 0x3000 0x0 0x0 0x1 &mpic 0x2 0x1 0 0 + 0x3000 0x0 0x0 0x2 &mpic 0x3 0x1 0 0 + 0x3000 0x0 0x0 0x3 &mpic 0x0 0x1 0 0 + 0x3000 0x0 0x0 0x4 &mpic 0x1 0x1 0 0 + + /* IDSEL 0x8 (PCIX Slot 5) */ + 0x4000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0x4000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0x4000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0x4000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 + + /* IDSEL 0xC (Tsi310 bridge) */ + 0x6000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0x6000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0x6000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0x6000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 + + /* IDSEL 0x14 (Slot 2) */ + 0xa000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0xa000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0xa000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0xa000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 + + /* IDSEL 0x15 (Slot 3) */ + 0xa800 0x0 0x0 0x1 &mpic 0x1 0x1 0 0 + 0xa800 0x0 0x0 0x2 &mpic 0x2 0x1 0 0 + 0xa800 0x0 0x0 0x3 &mpic 0x3 0x1 0 0 + 0xa800 0x0 0x0 0x4 &mpic 0x0 0x1 0 0 + + /* IDSEL 0x16 (Slot 4) */ + 0xb000 0x0 0x0 0x1 &mpic 0x2 0x1 0 0 + 0xb000 0x0 0x0 0x2 &mpic 0x3 0x1 0 0 + 0xb000 0x0 0x0 0x3 &mpic 0x0 0x1 0 0 + 0xb000 0x0 0x0 0x4 &mpic 0x1 0x1 0 0 + + /* IDSEL 0x18 (Slot 5) */ + 0xc000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0xc000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0xc000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0xc000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 + + /* IDSEL 0x1C (Tsi310 bridge PCI primary) */ + 0xe000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0xe000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0xe000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0xe000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0>; + + pci_bridge@1c { + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + + /* IDSEL 0x00 (PrPMC Site) */ + 0000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 + + /* IDSEL 0x04 (VIA chip) */ + 0x2000 0x0 0x0 0x1 &mpic 0x0 0x1 0 0 + 0x2000 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0x2000 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0x2000 0x0 0x0 0x4 &mpic 0x3 0x1 0 0 + + /* IDSEL 0x05 (8139) */ + 0x2800 0x0 0x0 0x1 &mpic 0x1 0x1 0 0 + + /* IDSEL 0x06 (Slot 6) */ + 0x3000 0x0 0x0 0x1 &mpic 0x2 0x1 0 0 + 0x3000 0x0 0x0 0x2 &mpic 0x3 0x1 0 0 + 0x3000 0x0 0x0 0x3 &mpic 0x0 0x1 0 0 + 0x3000 0x0 0x0 0x4 &mpic 0x1 0x1 0 0 + + /* IDESL 0x07 (Slot 7) */ + 0x3800 0x0 0x0 0x1 &mpic 0x3 0x1 0 0 + 0x3800 0x0 0x0 0x2 &mpic 0x0 0x1 0 0 + 0x3800 0x0 0x0 0x3 &mpic 0x1 0x1 0 0 + 0x3800 0x0 0x0 0x4 &mpic 0x2 0x1 0 0>; + + reg = <0xe000 0x0 0x0 0x0 0x0>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + ranges = <0x2000000 0x0 0x80000000 + 0x2000000 0x0 0x80000000 + 0x0 0x20000000 + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x80000>; + clock-frequency = <33333333>; + + isa@4 { + device_type = "isa"; + #interrupt-cells = <2>; + #size-cells = <1>; + #address-cells = <2>; + reg = <0x2000 0x0 0x0 0x0 0x0>; + ranges = <0x1 0x0 0x1000000 0x0 0x0 0x1000>; + interrupt-parent = <&i8259>; + + i8259: interrupt-controller@20 { + interrupt-controller; + device_type = "interrupt-controller"; + reg = <0x1 0x20 0x2 + 0x1 0xa0 0x2 + 0x1 0x4d0 0x2>; + #address-cells = <0>; + #interrupt-cells = <2>; + compatible = "chrp,iic"; + interrupts = <0 1 0 0>; + interrupt-parent = <&mpic>; + }; + + rtc@70 { + compatible = "pnpPNP,b00"; + reg = <0x1 0x70 0x2>; + }; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc8548cds_32b.dts b/arch/powerpc/boot/dts/mpc8548cds_32b.dts new file mode 100644 index 0000000..6fd6316 --- /dev/null +++ b/arch/powerpc/boot/dts/mpc8548cds_32b.dts @@ -0,0 +1,86 @@ +/* + * MPC8548 CDS Device Tree Source (32-bit address map) + * + * Copyright 2006, 2008, 2011-2012 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/ "fsl/mpc8548si-pre.dtsi" + +/ { + model = "MPC8548CDS"; + compatible = "MPC8548CDS", "MPC85xxCDS"; + + memory { + device_type = "memory"; + reg = <0 0 0x0 0x8000000>; // 128M at 0x0 + }; + + board_lbc: lbc: localbus@e0005000 { + reg = <0 0xe0005000 0 0x1000>; + + ranges = <0x0 0x0 0x0 0xff000000 0x01000000 + 0x1 0x0 0x0 0xf8004000 0x00001000>; + + }; + + board_soc: soc: soc8548@e0000000 { + ranges = <0 0x0 0xe0000000 0x100000>; + }; + + board_pci0: pci0: pci@e0008000 { + reg = <0 0xe0008000 0 0x1000>; + ranges = <0x2000000 0x0 0x80000000 0 0x80000000 0x0 0x10000000 + 0x1000000 0x0 0x00000000 0 0xe2000000 0x0 0x800000>; + clock-frequency = <66666666>; + }; + + pci1: pci@e0009000 { + reg = <0 0xe0009000 0 0x1000>; + ranges = <0x2000000 0x0 0x90000000 0 0x90000000 0x0 0x10000000 + 0x1000000 0x0 0x00000000 0 0xe2800000 0x0 0x800000>; + clock-frequency = <66666666>; + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + + /* IDSEL 0x15 */ + 0xa800 0x0 0x0 0x1 &mpic 0xb 0x1 0 0 + 0xa800 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0xa800 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0xa800 0x0 0x0 0x4 &mpic 0x3 0x1 0 0>; + }; + + pci2: pcie@e000a000 { + reg = <0 0xe000a000 0 0x1000>; + ranges = <0x2000000 0x0 0xa0000000 0 0xa0000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0 0xe3000000 0x0 0x100000>; + pcie@0 { + ranges = <0x2000000 0x0 0xa0000000 + 0x2000000 0x0 0xa0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + rio: rapidio@e00c0000 { + reg = <0x0 0xe00c0000 0x0 0x20000>; + port1 { + ranges = <0x0 0x0 0x0 0xc0000000 0x0 0x20000000>; + }; + }; +}; + +/* + * mpc8548cds.dtsi must be last to ensure board_pci0 overrides pci0 settings + * for interrupt-map & interrupt-map-mask. + */ + +/include/ "fsl/mpc8548si-post.dtsi" +/include/ "mpc8548cds.dtsi" -- cgit v0.10.2 From a2279e3fe484e89f744e03421377d5c0fca9f77d Mon Sep 17 00:00:00 2001 From: Zhao Chenhui Date: Tue, 6 Mar 2012 17:06:45 +0800 Subject: powerpc/85xx: mpc8548cds - add 36-bit dts Create mpc8548cds_36b.dts. Support 36-bit mode. Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/boot/dts/mpc8548cds_36b.dts b/arch/powerpc/boot/dts/mpc8548cds_36b.dts new file mode 100644 index 0000000..10e551b --- /dev/null +++ b/arch/powerpc/boot/dts/mpc8548cds_36b.dts @@ -0,0 +1,86 @@ +/* + * MPC8548 CDS Device Tree Source (36-bit address map) + * + * Copyright 2012 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/ "fsl/mpc8548si-pre.dtsi" + +/ { + model = "MPC8548CDS"; + compatible = "MPC8548CDS", "MPC85xxCDS"; + + memory { + device_type = "memory"; + reg = <0 0 0x0 0x8000000>; // 128M at 0x0 + }; + + board_lbc: lbc: localbus@fe0005000 { + reg = <0xf 0xe0005000 0 0x1000>; + + ranges = <0x0 0x0 0xf 0xff000000 0x01000000 + 0x1 0x0 0xf 0xf8004000 0x00001000>; + + }; + + board_soc: soc: soc8548@fe0000000 { + ranges = <0 0xf 0xe0000000 0x100000>; + }; + + board_pci0: pci0: pci@fe0008000 { + reg = <0xf 0xe0008000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x00000000 0x0 0x10000000 + 0x1000000 0x0 0x00000000 0xf 0xe2000000 0x0 0x800000>; + clock-frequency = <66666666>; + }; + + pci1: pci@fe0009000 { + reg = <0xf 0xe0009000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x10000000 0x0 0x10000000 + 0x1000000 0x0 0x00000000 0xf 0xe2800000 0x0 0x800000>; + clock-frequency = <66666666>; + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + + /* IDSEL 0x15 */ + 0xa800 0x0 0x0 0x1 &mpic 0xb 0x1 0 0 + 0xa800 0x0 0x0 0x2 &mpic 0x1 0x1 0 0 + 0xa800 0x0 0x0 0x3 &mpic 0x2 0x1 0 0 + 0xa800 0x0 0x0 0x4 &mpic 0x3 0x1 0 0>; + }; + + pci2: pcie@fe000a000 { + reg = <0xf 0xe000a000 0 0x1000>; + ranges = <0x2000000 0x0 0xe0000000 0xc 0x20000000 0x0 0x20000000 + 0x1000000 0x0 0x00000000 0xf 0xe3000000 0x0 0x100000>; + pcie@0 { + ranges = <0x2000000 0x0 0xa0000000 + 0x2000000 0x0 0xa0000000 + 0x0 0x20000000 + + 0x1000000 0x0 0x0 + 0x1000000 0x0 0x0 + 0x0 0x100000>; + }; + }; + + rio: rapidio@fe00c0000 { + reg = <0xf 0xe00c0000 0x0 0x20000>; + port1 { + ranges = <0x0 0x0 0xc 0x40000000 0x0 0x20000000>; + }; + }; +}; + +/* + * mpc8548cds.dtsi must be last to ensure board_pci0 overrides pci0 settings + * for interrupt-map & interrupt-map-mask. + */ + +/include/ "fsl/mpc8548si-post.dtsi" +/include/ "mpc8548cds.dtsi" -- cgit v0.10.2 From da3b6c0534c76bc08ce5524342586138687fd106 Mon Sep 17 00:00:00 2001 From: Diana CRACIUN Date: Wed, 1 Feb 2012 17:50:34 +0200 Subject: powerpc/fsl: Added aliased MSIIR register address to MSI node in dts The MSIIR register for each MSI bank is aliased to a different address. The MSI node reg property was updated to contain this address: e.g. reg = <0x41600 0x200 0x44140 4>; The first region contains the address and length of the MSI register set and the second region contains the address of the aliased MSIIR register at 0x44140. Signed-off-by: Diana CRACIUN Signed-off-by: Kumar Gala diff --git a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt index 5d586e1..5693877 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/msi-pic.txt @@ -6,8 +6,10 @@ Required properties: etc.) and the second is "fsl,mpic-msi" or "fsl,ipic-msi" depending on the parent type. -- reg : should contain the address and the length of the shared message - interrupt register set. +- reg : It may contain one or two regions. The first region should contain + the address and the length of the shared message interrupt register set. + The second region should contain the address of aliased MSIIR register for + platforms that have such an alias. - msi-available-ranges: use style section to define which msi interrupt can be used in the 256 msi interrupts. This property is diff --git a/arch/powerpc/boot/dts/fsl/qoriq-mpic.dtsi b/arch/powerpc/boot/dts/fsl/qoriq-mpic.dtsi index b9bada6..08f4227 100644 --- a/arch/powerpc/boot/dts/fsl/qoriq-mpic.dtsi +++ b/arch/powerpc/boot/dts/fsl/qoriq-mpic.dtsi @@ -53,7 +53,7 @@ timer@41100 { msi0: msi@41600 { compatible = "fsl,mpic-msi"; - reg = <0x41600 0x200>; + reg = <0x41600 0x200 0x44140 4>; msi-available-ranges = <0 0x100>; interrupts = < 0xe0 0 0 0 @@ -68,7 +68,7 @@ msi0: msi@41600 { msi1: msi@41800 { compatible = "fsl,mpic-msi"; - reg = <0x41800 0x200>; + reg = <0x41800 0x200 0x45140 4>; msi-available-ranges = <0 0x100>; interrupts = < 0xe8 0 0 0 @@ -83,7 +83,7 @@ msi1: msi@41800 { msi2: msi@41a00 { compatible = "fsl,mpic-msi"; - reg = <0x41a00 0x200>; + reg = <0x41a00 0x200 0x46140 4>; msi-available-ranges = <0 0x100>; interrupts = < 0xf0 0 0 0 -- cgit v0.10.2 From 8626816e905e8e131663dfcae27d307df8c220d0 Mon Sep 17 00:00:00 2001 From: Jia Hongtao Date: Fri, 17 Feb 2012 10:49:03 +0800 Subject: powerpc: add support for MPIC message register API Some MPIC implementations contain one or more blocks of message registers that are used to send messages between cores via IPIs. A simple API has been added to access (get/put, read, write, etc ...) these message registers. The available message registers are initially discovered via nodes in the device tree. A separate commit contains a binding for the message register nodes. Signed-off-by: Meador Inge Signed-off-by: Jia Hongtao Signed-off-by: Li Yang Signed-off-by: Kumar Gala diff --git a/arch/powerpc/include/asm/mpic_msgr.h b/arch/powerpc/include/asm/mpic_msgr.h new file mode 100644 index 0000000..3ec37dc --- /dev/null +++ b/arch/powerpc/include/asm/mpic_msgr.h @@ -0,0 +1,132 @@ +/* + * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. + * + * 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; version 2 of the + * License. + * + */ + +#ifndef _ASM_MPIC_MSGR_H +#define _ASM_MPIC_MSGR_H + +#include +#include + +struct mpic_msgr { + u32 __iomem *base; + u32 __iomem *mer; + int irq; + unsigned char in_use; + raw_spinlock_t lock; + int num; +}; + +/* Get a message register + * + * @reg_num: the MPIC message register to get + * + * A pointer to the message register is returned. If + * the message register asked for is already in use, then + * EBUSY is returned. If the number given is not associated + * with an actual message register, then ENODEV is returned. + * Successfully getting the register marks it as in use. + */ +extern struct mpic_msgr *mpic_msgr_get(unsigned int reg_num); + +/* Relinquish a message register + * + * @msgr: the message register to return + * + * Disables the given message register and marks it as free. + * After this call has completed successully the message + * register is available to be acquired by a call to + * mpic_msgr_get. + */ +extern void mpic_msgr_put(struct mpic_msgr *msgr); + +/* Enable a message register + * + * @msgr: the message register to enable + * + * The given message register is enabled for sending + * messages. + */ +extern void mpic_msgr_enable(struct mpic_msgr *msgr); + +/* Disable a message register + * + * @msgr: the message register to disable + * + * The given message register is disabled for sending + * messages. + */ +extern void mpic_msgr_disable(struct mpic_msgr *msgr); + +/* Write a message to a message register + * + * @msgr: the message register to write to + * @message: the message to write + * + * The given 32-bit message is written to the given message + * register. Writing to an enabled message registers fires + * an interrupt. + */ +static inline void mpic_msgr_write(struct mpic_msgr *msgr, u32 message) +{ + out_be32(msgr->base, message); +} + +/* Read a message from a message register + * + * @msgr: the message register to read from + * + * Returns the 32-bit value currently in the given message register. + * Upon reading the register any interrupts for that register are + * cleared. + */ +static inline u32 mpic_msgr_read(struct mpic_msgr *msgr) +{ + return in_be32(msgr->base); +} + +/* Clear a message register + * + * @msgr: the message register to clear + * + * Clears any interrupts associated with the given message register. + */ +static inline void mpic_msgr_clear(struct mpic_msgr *msgr) +{ + (void) mpic_msgr_read(msgr); +} + +/* Set the destination CPU for the message register + * + * @msgr: the message register whose destination is to be set + * @cpu_num: the Linux CPU number to bind the message register to + * + * Note that the CPU number given is the CPU number used by the kernel + * and *not* the actual hardware CPU number. + */ +static inline void mpic_msgr_set_destination(struct mpic_msgr *msgr, + u32 cpu_num) +{ + out_be32(msgr->base, 1 << get_hard_smp_processor_id(cpu_num)); +} + +/* Get the IRQ number for the message register + * @msgr: the message register whose IRQ is to be returned + * + * Returns the IRQ number associated with the given message register. + * NO_IRQ is returned if this message register is not capable of + * receiving interrupts. What message register can and cannot receive + * interrupts is specified in the device tree for the system. + */ +static inline int mpic_msgr_get_irq(struct mpic_msgr *msgr) +{ + return msgr->irq; +} + +#endif diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 236ab67..a35ca44 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -86,6 +86,14 @@ config MPIC_WEIRD bool default n +config MPIC_MSGR + bool "MPIC message register support" + depends on MPIC + default n + help + Enables support for the MPIC message registers. These + registers are used for inter-processor communication. + config PPC_I8259 bool default n diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index f80ff9f..1bd7ecb 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -4,6 +4,8 @@ ccflags-$(CONFIG_PPC64) := -mno-minimal-toc mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) +mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o +obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y) obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o fsl-msi-obj-$(CONFIG_PCI_MSI) += fsl_msi.o obj-$(CONFIG_PPC_MSI_BITMAP) += msi_bitmap.o diff --git a/arch/powerpc/sysdev/mpic_msgr.c b/arch/powerpc/sysdev/mpic_msgr.c new file mode 100644 index 0000000..6e7fa38 --- /dev/null +++ b/arch/powerpc/sysdev/mpic_msgr.c @@ -0,0 +1,282 @@ +/* + * Copyright 2011-2012, Meador Inge, Mentor Graphics Corporation. + * + * Some ideas based on un-pushed work done by Vivek Mahajan, Jason Jin, and + * Mingkai Hu from 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; version 2 of the + * License. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MPIC_MSGR_REGISTERS_PER_BLOCK 4 +#define MPIC_MSGR_STRIDE 0x10 +#define MPIC_MSGR_MER_OFFSET 0x100 +#define MSGR_INUSE 0 +#define MSGR_FREE 1 + +static struct mpic_msgr **mpic_msgrs; +static unsigned int mpic_msgr_count; + +static inline void _mpic_msgr_mer_write(struct mpic_msgr *msgr, u32 value) +{ + out_be32(msgr->mer, value); +} + +static inline u32 _mpic_msgr_mer_read(struct mpic_msgr *msgr) +{ + return in_be32(msgr->mer); +} + +static inline void _mpic_msgr_disable(struct mpic_msgr *msgr) +{ + u32 mer = _mpic_msgr_mer_read(msgr); + + _mpic_msgr_mer_write(msgr, mer & ~(1 << msgr->num)); +} + +struct mpic_msgr *mpic_msgr_get(unsigned int reg_num) +{ + unsigned long flags; + struct mpic_msgr *msgr; + + /* Assume busy until proven otherwise. */ + msgr = ERR_PTR(-EBUSY); + + if (reg_num >= mpic_msgr_count) + return ERR_PTR(-ENODEV); + + raw_spin_lock_irqsave(&msgr->lock, flags); + if (mpic_msgrs[reg_num]->in_use == MSGR_FREE) { + msgr = mpic_msgrs[reg_num]; + msgr->in_use = MSGR_INUSE; + } + raw_spin_unlock_irqrestore(&msgr->lock, flags); + + return msgr; +} +EXPORT_SYMBOL_GPL(mpic_msgr_get); + +void mpic_msgr_put(struct mpic_msgr *msgr) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&msgr->lock, flags); + msgr->in_use = MSGR_FREE; + _mpic_msgr_disable(msgr); + raw_spin_unlock_irqrestore(&msgr->lock, flags); +} +EXPORT_SYMBOL_GPL(mpic_msgr_put); + +void mpic_msgr_enable(struct mpic_msgr *msgr) +{ + unsigned long flags; + u32 mer; + + raw_spin_lock_irqsave(&msgr->lock, flags); + mer = _mpic_msgr_mer_read(msgr); + _mpic_msgr_mer_write(msgr, mer | (1 << msgr->num)); + raw_spin_unlock_irqrestore(&msgr->lock, flags); +} +EXPORT_SYMBOL_GPL(mpic_msgr_enable); + +void mpic_msgr_disable(struct mpic_msgr *msgr) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&msgr->lock, flags); + _mpic_msgr_disable(msgr); + raw_spin_unlock_irqrestore(&msgr->lock, flags); +} +EXPORT_SYMBOL_GPL(mpic_msgr_disable); + +/* The following three functions are used to compute the order and number of + * the message register blocks. They are clearly very inefficent. However, + * they are called *only* a few times during device initialization. + */ +static unsigned int mpic_msgr_number_of_blocks(void) +{ + unsigned int count; + struct device_node *aliases; + + count = 0; + aliases = of_find_node_by_name(NULL, "aliases"); + + if (aliases) { + char buf[32]; + + for (;;) { + snprintf(buf, sizeof(buf), "mpic-msgr-block%d", count); + if (!of_find_property(aliases, buf, NULL)) + break; + + count += 1; + } + } + + return count; +} + +static unsigned int mpic_msgr_number_of_registers(void) +{ + return mpic_msgr_number_of_blocks() * MPIC_MSGR_REGISTERS_PER_BLOCK; +} + +static int mpic_msgr_block_number(struct device_node *node) +{ + struct device_node *aliases; + unsigned int index, number_of_blocks; + char buf[64]; + + number_of_blocks = mpic_msgr_number_of_blocks(); + aliases = of_find_node_by_name(NULL, "aliases"); + if (!aliases) + return -1; + + for (index = 0; index < number_of_blocks; ++index) { + struct property *prop; + + snprintf(buf, sizeof(buf), "mpic-msgr-block%d", index); + prop = of_find_property(aliases, buf, NULL); + if (node == of_find_node_by_path(prop->value)) + break; + } + + return index == number_of_blocks ? -1 : index; +} + +/* The probe function for a single message register block. + */ +static __devinit int mpic_msgr_probe(struct platform_device *dev) +{ + void __iomem *msgr_block_addr; + int block_number; + struct resource rsrc; + unsigned int i; + unsigned int irq_index; + struct device_node *np = dev->dev.of_node; + unsigned int receive_mask; + const unsigned int *prop; + + if (!np) { + dev_err(&dev->dev, "Device OF-Node is NULL"); + return -EFAULT; + } + + /* Allocate the message register array upon the first device + * registered. + */ + if (!mpic_msgrs) { + mpic_msgr_count = mpic_msgr_number_of_registers(); + dev_info(&dev->dev, "Found %d message registers\n", + mpic_msgr_count); + + mpic_msgrs = kzalloc(sizeof(struct mpic_msgr) * mpic_msgr_count, + GFP_KERNEL); + if (!mpic_msgrs) { + dev_err(&dev->dev, + "No memory for message register blocks\n"); + return -ENOMEM; + } + } + dev_info(&dev->dev, "Of-device full name %s\n", np->full_name); + + /* IO map the message register block. */ + of_address_to_resource(np, 0, &rsrc); + msgr_block_addr = ioremap(rsrc.start, rsrc.end - rsrc.start); + if (!msgr_block_addr) { + dev_err(&dev->dev, "Failed to iomap MPIC message registers"); + return -EFAULT; + } + + /* Ensure the block has a defined order. */ + block_number = mpic_msgr_block_number(np); + if (block_number < 0) { + dev_err(&dev->dev, + "Failed to find message register block alias\n"); + return -ENODEV; + } + dev_info(&dev->dev, "Setting up message register block %d\n", + block_number); + + /* Grab the receive mask which specifies what registers can receive + * interrupts. + */ + prop = of_get_property(np, "mpic-msgr-receive-mask", NULL); + receive_mask = (prop) ? *prop : 0xF; + + /* Build up the appropriate message register data structures. */ + for (i = 0, irq_index = 0; i < MPIC_MSGR_REGISTERS_PER_BLOCK; ++i) { + struct mpic_msgr *msgr; + unsigned int reg_number; + + msgr = kzalloc(sizeof(struct mpic_msgr), GFP_KERNEL); + if (!msgr) { + dev_err(&dev->dev, "No memory for message register\n"); + return -ENOMEM; + } + + reg_number = block_number * MPIC_MSGR_REGISTERS_PER_BLOCK + i; + msgr->base = msgr_block_addr + i * MPIC_MSGR_STRIDE; + msgr->mer = msgr->base + MPIC_MSGR_MER_OFFSET; + msgr->in_use = MSGR_FREE; + msgr->num = i; + raw_spin_lock_init(&msgr->lock); + + if (receive_mask & (1 << i)) { + struct resource irq; + + if (of_irq_to_resource(np, irq_index, &irq) == NO_IRQ) { + dev_err(&dev->dev, + "Missing interrupt specifier"); + kfree(msgr); + return -EFAULT; + } + msgr->irq = irq.start; + irq_index += 1; + } else { + msgr->irq = NO_IRQ; + } + + mpic_msgrs[reg_number] = msgr; + mpic_msgr_disable(msgr); + dev_info(&dev->dev, "Register %d initialized: irq %d\n", + reg_number, msgr->irq); + + } + + return 0; +} + +static const struct of_device_id mpic_msgr_ids[] = { + { + .compatible = "fsl,mpic-v3.1-msgr", + .data = NULL, + }, + {} +}; + +static struct platform_driver mpic_msgr_driver = { + .driver = { + .name = "mpic-msgr", + .owner = THIS_MODULE, + .of_match_table = mpic_msgr_ids, + }, + .probe = mpic_msgr_probe, +}; + +static __init int mpic_msgr_init(void) +{ + return platform_driver_register(&mpic_msgr_driver); +} +subsys_initcall(mpic_msgr_init); -- cgit v0.10.2 From e96dde2b5edbc0d385ccced05fb5db68c070b0d4 Mon Sep 17 00:00:00 2001 From: Jia Hongtao Date: Thu, 1 Mar 2012 17:32:34 +0800 Subject: powerpc: document the FSL MPIC message register binding This binding documents how the message register blocks found in some FSL MPIC implementations shall be represented in a device tree. Signed-off-by: Meador Inge Signed-off-by: Jia Hongtao Signed-off-by: Kumar Gala diff --git a/Documentation/devicetree/bindings/powerpc/fsl/mpic-msgr.txt b/Documentation/devicetree/bindings/powerpc/fsl/mpic-msgr.txt new file mode 100644 index 0000000..bc8ded6 --- /dev/null +++ b/Documentation/devicetree/bindings/powerpc/fsl/mpic-msgr.txt @@ -0,0 +1,63 @@ +* FSL MPIC Message Registers + +This binding specifies what properties must be available in the device tree +representation of the message register blocks found in some FSL MPIC +implementations. + +Required properties: + + - compatible: Specifies the compatibility list for the message register + block. The type shall be and the value shall be of the form + "fsl,mpic-v-msgr", where is the version number of + the MPIC containing the message registers. + + - reg: Specifies the base physical address(s) and size(s) of the + message register block's addressable register space. The type shall be + . + + - interrupts: Specifies a list of interrupt-specifiers which are available + for receiving interrupts. Interrupt-specifier consists of two cells: first + cell is interrupt-number and second cell is level-sense. The type shall be + . + +Optional properties: + + - mpic-msgr-receive-mask: Specifies what registers in the containing block + are allowed to receive interrupts. The value is a bit mask where a set + bit at bit 'n' indicates that message register 'n' can receive interrupts. + Note that "bit 'n'" is numbered from LSB for PPC hardware. The type shall + be . If not present, then all of the message registers in the block + are available. + +Aliases: + + An alias should be created for every message register block. They are not + required, though. However, a particular implementation of this binding + may require aliases to be present. Aliases are of the form + 'mpic-msgr-block', where is an integer specifying the block's number. + Numbers shall start at 0. + +Example: + + aliases { + mpic-msgr-block0 = &mpic_msgr_block0; + mpic-msgr-block1 = &mpic_msgr_block1; + }; + + mpic_msgr_block0: mpic-msgr-block@41400 { + compatible = "fsl,mpic-v3.1-msgr"; + reg = <0x41400 0x200>; + // Message registers 0 and 2 in this block can receive interrupts on + // sources 0xb0 and 0xb2, respectively. + interrupts = <0xb0 2 0xb2 2>; + mpic-msgr-receive-mask = <0x5>; + }; + + mpic_msgr_block1: mpic-msgr-block@42400 { + compatible = "fsl,mpic-v3.1-msgr"; + reg = <0x42400 0x200>; + // Message registers 0 and 2 in this block can receive interrupts on + // sources 0xb4 and 0xb6, respectively. + interrupts = <0xb4 2 0xb6 2>; + mpic-msgr-receive-mask = <0x5>; + }; -- cgit v0.10.2 From 7c801160be0adf826b7b792ee4eaf6a3ae47569d Mon Sep 17 00:00:00 2001 From: Vinh Nguyen Huu Tuong Date: Tue, 20 Dec 2011 02:43:34 +0000 Subject: powerpc/44x: The bug fixed support for APM821xx SoC and Bluestone board This patch consists of: - Fix the pvr mask for checking pvr in cputable.c - Fix the cpu name as consistent with cpu name is describled in dts file Signed-off-by: Vinh Nguyen Huu Tuong Signed-off-by: Josh Boyer diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index 81db9e2..87353ba 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -1816,7 +1816,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .platform = "ppc440", }, { /* 464 in APM821xx */ - .pvr_mask = 0xffffff00, + .pvr_mask = 0xfffffff0, .pvr_value = 0x12C41C80, .cpu_name = "APM821XX", .cpu_features = CPU_FTRS_44X, diff --git a/arch/powerpc/platforms/44x/ppc44x_simple.c b/arch/powerpc/platforms/44x/ppc44x_simple.c index 8d22027..3ffb915 100644 --- a/arch/powerpc/platforms/44x/ppc44x_simple.c +++ b/arch/powerpc/platforms/44x/ppc44x_simple.c @@ -52,7 +52,7 @@ machine_device_initcall(ppc44x_simple, ppc44x_device_probe); static char *board[] __initdata = { "amcc,arches", "amcc,bamboo", - "amcc,bluestone", + "apm,bluestone", "amcc,glacier", "ibm,ebony", "amcc,eiger", -- cgit v0.10.2 From 645609c0d9e1fa0febc882f3605457d28484ed2a Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 1 Mar 2012 09:07:54 -0500 Subject: MAINTAINERS: Update PowerPC 4xx tree Move the powerpc-4xx.git tree back to kernel.org Signed-off-by: Josh Boyer diff --git a/MAINTAINERS b/MAINTAINERS index 75a9a5f..93de670 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4034,7 +4034,7 @@ M: Josh Boyer M: Matt Porter W: http://www.penguinppc.org/ L: linuxppc-dev@lists.ozlabs.org -T: git git://git.infradead.org/users/jwboyer/powerpc-4xx.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jwboyer/powerpc-4xx.git S: Maintained F: arch/powerpc/platforms/40x/ F: arch/powerpc/platforms/44x/ -- cgit v0.10.2 From b6bb23b923048be159265004f4cd6aa272da2409 Mon Sep 17 00:00:00 2001 From: Vinh Nguyen Huu Tuong Date: Thu, 15 Mar 2012 00:57:19 +0000 Subject: powerpc/44x: Add support PCI-E for APM821xx SoC and Bluestone board This patch extends PCI-E driver to support PCI-E for APM821xx SoC on Bluestone board. Signed-off-by: Vinh Nguyen Huu Tuong Signed-off-by: Josh Boyer diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index fcf6bf2..2e4e64a 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -23,6 +23,7 @@ config BLUESTONE default n select PPC44x_SIMPLE select APM821xx + select PPC4xx_PCI_EXPRESS select IBM_EMAC_RGMII help This option enables support for the APM APM821xx Evaluation board. diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index 4f05f75..56e8b3c 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -1050,6 +1050,74 @@ static struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata = .check_link = ppc4xx_pciex_check_link_sdr, }; +static int __init apm821xx_pciex_core_init(struct device_node *np) +{ + /* Return the number of pcie port */ + return 1; +} + +static int apm821xx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +{ + u32 val; + + /* + * Do a software reset on PCIe ports. + * This code is to fix the issue that pci drivers doesn't re-assign + * bus number for PCIE devices after Uboot + * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 + * PT quad port, SAS LSI 1064E) + */ + + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); + mdelay(10); + + if (port->endpoint) + val = PTYPE_LEGACY_ENDPOINT << 20; + else + val = PTYPE_ROOT_PORT << 20; + + val |= LNKW_X1 << 12; + + mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val); + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x00000000); + mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000); + + mtdcri(SDR0, PESDR0_460EX_L0CDRCTL, 0x00003230); + mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); + mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); + + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); + mdelay(50); + mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); + + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, + mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | + (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTPYN)); + + /* Poll for PHY reset */ + val = PESDR0_460EX_RSTSTA - port->sdr_base; + if (ppc4xx_pciex_wait_on_sdr(port, val, 0x1, 1, 100)) { + printk(KERN_WARNING "%s: PCIE: Can't reset PHY\n", __func__); + return -EBUSY; + } else { + mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, + (mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) & + ~(PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL)) | + PESDRx_RCSSET_RSTPYN); + + port->has_ibpre = 1; + return 0; + } +} + +static struct ppc4xx_pciex_hwops apm821xx_pcie_hwops __initdata = { + .want_sdr = true, + .core_init = apm821xx_pciex_core_init, + .port_init_hw = apm821xx_pciex_init_port_hw, + .setup_utl = ppc460ex_pciex_init_utl, + .check_link = ppc4xx_pciex_check_link_sdr, +}; + static int __init ppc460sx_pciex_core_init(struct device_node *np) { /* HSS drive amplitude */ @@ -1362,6 +1430,8 @@ static int __init ppc4xx_pciex_check_core_init(struct device_node *np) ppc4xx_pciex_hwops = &ppc460ex_pcie_hwops; if (of_device_is_compatible(np, "ibm,plb-pciex-460sx")) ppc4xx_pciex_hwops = &ppc460sx_pcie_hwops; + if (of_device_is_compatible(np, "ibm,plb-pciex-apm821xx")) + ppc4xx_pciex_hwops = &apm821xx_pcie_hwops; #endif /* CONFIG_44x */ #ifdef CONFIG_40x if (of_device_is_compatible(np, "ibm,plb-pciex-405ex")) -- cgit v0.10.2 From b5594a7760fa048730db64c501cf4534df06b3b3 Mon Sep 17 00:00:00 2001 From: Vinh Nguyen Huu Tuong Date: Thu, 15 Mar 2012 00:56:32 +0000 Subject: powerpc/44x: Add additional device support for APM821xx SoC and Bluestone board This patch updates the dts file for bluestone board with support: - UART1 - L2 cache - NAND with NDFC - PCI-E Signed-off-by: Vinh Nguyen Huu Tuong Signed-off-by: Josh Boyer diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts index 2a56a0d..cfa23bf 100644 --- a/arch/powerpc/boot/dts/bluestone.dts +++ b/arch/powerpc/boot/dts/bluestone.dts @@ -33,7 +33,7 @@ aliases { ethernet0 = &EMAC0; serial0 = &UART0; - //serial1 = &UART1; --gcl missing UART1 label + serial1 = &UART1; }; cpus { @@ -52,7 +52,7 @@ d-cache-size = <32768>; dcr-controller; dcr-access-method = "native"; - //next-level-cache = <&L2C0>; --gcl missing L2C0 label + next-level-cache = <&L2C0>; }; }; @@ -117,6 +117,16 @@ dcr-reg = <0x00c 0x002>; }; + L2C0: l2c { + compatible = "ibm,l2-cache-apm82181", "ibm,l2-cache"; + dcr-reg = <0x020 0x008 + 0x030 0x008>; + cache-line-size = <32>; + cache-size = <262144>; + interrupt-parent = <&UIC1>; + interrupts = <11 1>; + }; + plb { compatible = "ibm,plb4"; #address-cells = <2>; @@ -182,6 +192,53 @@ reg = <0x001a0000 0x00060000>; }; }; + + ndfc@1,0 { + compatible = "ibm,ndfc"; + reg = <0x00000003 0x00000000 0x00002000>; + ccr = <0x00001000>; + bank-settings = <0x80002222>; + #address-cells = <1>; + #size-cells = <1>; + /* 2Gb Nand Flash */ + nand { + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "firmware"; + reg = <0x00000000 0x00C00000>; + }; + partition@c00000 { + label = "environment"; + reg = <0x00C00000 0x00B00000>; + }; + partition@1700000 { + label = "kernel"; + reg = <0x01700000 0x00E00000>; + }; + partition@2500000 { + label = "root"; + reg = <0x02500000 0x08200000>; + }; + partition@a700000 { + label = "device-tree"; + reg = <0x0A700000 0x00B00000>; + }; + partition@b200000 { + label = "config"; + reg = <0x0B200000 0x00D00000>; + }; + partition@bf00000 { + label = "diag"; + reg = <0x0BF00000 0x00C00000>; + }; + partition@cb00000 { + label = "vendor"; + reg = <0x0CB00000 0x3500000>; + }; + }; + }; }; UART0: serial@ef600300 { @@ -195,11 +252,36 @@ interrupts = <0x1 0x4>; }; + UART1: serial@ef600400 { + device_type = "serial"; + compatible = "ns16550"; + reg = <0xef600400 0x00000008>; + virtual-reg = <0xef600400>; + clock-frequency = <0>; /* Filled in by U-Boot */ + current-speed = <0>; /* Filled in by U-Boot */ + interrupt-parent = <&UIC0>; + interrupts = <0x1 0x4>; + }; + IIC0: i2c@ef600700 { compatible = "ibm,iic"; reg = <0xef600700 0x00000014>; interrupt-parent = <&UIC0>; interrupts = <0x2 0x4>; + #address-cells = <1>; + #size-cells = <0>; + rtc@68 { + compatible = "stm,m41t80"; + reg = <0x68>; + interrupt-parent = <&UIC0>; + interrupts = <0x9 0x8>; + }; + sttm@4C { + compatible = "adm,adm1032"; + reg = <0x4C>; + interrupt-parent = <&UIC1>; + interrupts = <0x1E 0x8>; /* CPU_THERNAL_L */ + }; }; IIC1: i2c@ef600800 { @@ -250,5 +332,46 @@ }; }; + PCIE0: pciex@d00000000 { + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + compatible = "ibm,plb-pciex-apm821xx", "ibm,plb-pciex"; + primary; + port = <0x0>; /* port number */ + reg = <0x0000000d 0x00000000 0x20000000 /* Config space access */ + 0x0000000c 0x08010000 0x00001000>; /* Registers */ + dcr-reg = <0x100 0x020>; + sdr-base = <0x300>; + + /* Outbound ranges, one memory and one IO, + * later cannot be changed + */ + ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000 + 0x02000000 0x00000000 0x00000000 0x0000000f 0x00000000 0x00000000 0x00100000 + 0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>; + + /* Inbound 2GB range starting at 0 */ + dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>; + + /* This drives busses 40 to 0x7f */ + bus-range = <0x40 0x7f>; + + /* Legacy interrupts (note the weird polarity, the bridge seems + * to invert PCIe legacy interrupts). + * We are de-swizzling here because the numbers are actually for + * port of the root complex virtual P2P bridge. But I want + * to avoid putting a node for it in the tree, so the numbers + * below are basically de-swizzled numbers. + * The real slot is on idsel 0, so the swizzling is 1:1 + */ + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = < + 0x0 0x0 0x0 0x1 &UIC3 0xc 0x4 /* swizzled int A */ + 0x0 0x0 0x0 0x2 &UIC3 0xd 0x4 /* swizzled int B */ + 0x0 0x0 0x0 0x3 &UIC3 0xe 0x4 /* swizzled int C */ + 0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>; + }; }; }; -- cgit v0.10.2 From 976af684bb7d2eb9e7dae7ae31d43843f0aa580b Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Sat, 17 Mar 2012 01:33:23 +0100 Subject: Documentation/powerpc/mpc52xx.txt: Checkpatch cleanup Fix all trailing whitespace errors reported by checkpatch. Signed-off-by: Andrea Gelmini Signed-off-by: Anatolij Gustschin diff --git a/Documentation/powerpc/mpc52xx.txt b/Documentation/powerpc/mpc52xx.txt index 10dd4ab..0d540a3 100644 --- a/Documentation/powerpc/mpc52xx.txt +++ b/Documentation/powerpc/mpc52xx.txt @@ -2,7 +2,7 @@ Linux 2.6.x on MPC52xx family ----------------------------- For the latest info, go to http://www.246tNt.com/mpc52xx/ - + To compile/use : - U-Boot: @@ -10,23 +10,23 @@ To compile/use : if you wish to ). # make lite5200_defconfig # make uImage - + then, on U-boot: => tftpboot 200000 uImage => tftpboot 400000 pRamdisk => bootm 200000 400000 - + - DBug: # dn -i zImage.initrd.lite5200 - + Some remarks : - The port is named mpc52xxx, and config options are PPC_MPC52xx. The MGT5100 -- cgit v0.10.2 From dd831916250d5d5fb536e44d3101f2c61e49f2a6 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sat, 17 Mar 2012 10:30:30 +0100 Subject: powerpc/mpc5200: update mpc5200_defconfig to fit for charon board Add following options to mpc5200_defconfig: - CONFIG_MTD_PLATRAM=y (selects CONFIG_MTD_RAM, so this is removed) - CONFIG_FIXED_PHY=y - CONFIG_SENSORS_LM80=y - CONFIG_FB_FOREIGN_ENDIAN=y - CONFIG_FB_SM501=m - CONFIG_RTC_DRV_DS1374=y Signed-off-by: Heiko Schocher cc: Grant Likely cc: Wolfgang Denk Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/configs/mpc5200_defconfig b/arch/powerpc/configs/mpc5200_defconfig index 2a1320f..730a2ff 100644 --- a/arch/powerpc/configs/mpc5200_defconfig +++ b/arch/powerpc/configs/mpc5200_defconfig @@ -1,8 +1,8 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y +CONFIG_SPARSE_IRQ=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set @@ -13,15 +13,12 @@ CONFIG_PPC_EFIKA=y CONFIG_PPC_LITE5200=y CONFIG_PPC_MEDIA5200=y CONFIG_PPC_MPC5200_BUGFIX=y -CONFIG_PPC_MPC5200_GPIO=y CONFIG_PPC_MPC5200_LPBFIFO=m # CONFIG_PPC_PMAC is not set CONFIG_PPC_BESTCOMM=y CONFIG_SIMPLE_GPIO=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y -CONFIG_SPARSE_IRQ=y -CONFIG_PM=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -36,23 +33,20 @@ CONFIG_SYN_COOKIES=y # CONFIG_IPV6 is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_MTD=y -CONFIG_MTD_CONCAT=y -CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_OF_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_RAM=y CONFIG_MTD_ROM=y CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_PLATRAM=y CONFIG_MTD_UBI=m CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=32768 -CONFIG_MISC_DEVICES=y CONFIG_EEPROM_AT24=y CONFIG_SCSI_TGT=y CONFIG_BLK_DEV_SD=y @@ -61,11 +55,9 @@ CONFIG_ATA=y CONFIG_PATA_MPC52xx=y CONFIG_PATA_PLATFORM=y CONFIG_NETDEVICES=y -CONFIG_LXT_PHY=y -CONFIG_NET_ETHERNET=y CONFIG_FEC_MPC52xx=y -# CONFIG_NETDEV_1000 is not set -# CONFIG_NETDEV_10000 is not set +CONFIG_LXT_PHY=y +CONFIG_FIXED_PHY=y # CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_SERIO is not set @@ -80,11 +72,15 @@ CONFIG_SPI_GPIO=m CONFIG_SPI_MPC52xx=m CONFIG_SPI_MPC52xx_PSC=m CONFIG_SPI_SPIDEV=m +CONFIG_SENSORS_LM80=y CONFIG_WATCHDOG=y +CONFIG_MFD_SM501=m CONFIG_DRM=y CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y +CONFIG_FB_FOREIGN_ENDIAN=y CONFIG_FB_RADEON=y +CONFIG_FB_SM501=m # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y @@ -124,10 +120,10 @@ CONFIG_USB_STORAGE=y CONFIG_NEW_LEDS=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_DS1374=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -CONFIG_INOTIFY=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_PROC_KCORE=y @@ -145,5 +141,4 @@ CONFIG_PRINTK_TIME=y CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_INFO=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set # CONFIG_CRYPTO_ANSI_CPRNG is not set -- cgit v0.10.2 From 7eb64c0f25574abeb02b79088258f94ba9339075 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sun, 18 Mar 2012 23:00:44 +0100 Subject: powerpc/mpc52xx: add a4m072 board support Add DTS file for a4m072 board and add its name to the list of the supported boards. Signed-off-by: Heiko Schocher cc: Grant Likely cc: devicetree-discuss@ozlabs.org cc: Wolfgang Denk cc: Wolfram Sang Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/boot/dts/a4m072.dts b/arch/powerpc/boot/dts/a4m072.dts new file mode 100644 index 0000000..fabe7b7 --- /dev/null +++ b/arch/powerpc/boot/dts/a4m072.dts @@ -0,0 +1,168 @@ +/* + * a4m072 board Device Tree Source + * + * Copyright (C) 2011 DENX Software Engineering GmbH + * Heiko Schocher + * + * Copyright (C) 2007 Semihalf + * Marian Balakowicz + * + * 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/ "mpc5200b.dtsi" + +/ { + model = "anonymous,a4m072"; + compatible = "anonymous,a4m072"; + + soc5200@f0000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc5200b-immr"; + ranges = <0 0xf0000000 0x0000c000>; + reg = <0xf0000000 0x00000100>; + bus-frequency = <0>; /* From boot loader */ + system-frequency = <0>; /* From boot loader */ + + cdm@200 { + fsl,init-ext-48mhz-en = <0x0>; + fsl,init-fd-enable = <0x01>; + fsl,init-fd-counters = <0x3333>; + }; + + timer@600 { + fsl,has-wdt; + }; + + gpt3: timer@630 { /* General Purpose Timer in GPIO mode */ + compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + + gpt4: timer@640 { /* General Purpose Timer in GPIO mode */ + compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + + gpt5: timer@650 { /* General Purpose Timer in GPIO mode */ + compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + + spi@f00 { + status = "disabled"; + }; + + psc@2000 { + compatible = "fsl,mpc5200b-psc-uart","fsl,mpc5200-psc-uart"; + reg = <0x2000 0x100>; + interrupts = <2 1 0>; + }; + + psc@2200 { + compatible = "fsl,mpc5200b-psc-uart","fsl,mpc5200-psc-uart"; + reg = <0x2200 0x100>; + interrupts = <2 2 0>; + }; + + psc@2400 { + compatible = "fsl,mpc5200b-psc-uart","fsl,mpc5200-psc-uart"; + reg = <0x2400 0x100>; + interrupts = <2 3 0>; + }; + + psc@2600 { + status = "disabled"; + }; + + psc@2800 { + status = "disabled"; + }; + + psc@2c00 { + compatible = "fsl,mpc5200b-psc-uart","fsl,mpc5200-psc-uart"; + reg = <0x2c00 0x100>; + interrupts = <2 4 0>; + }; + + ethernet@3000 { + phy-handle = <&phy0>; + }; + + mdio@3000 { + phy0: ethernet-phy@1f { + reg = <0x1f>; + interrupts = <1 2 0>; /* IRQ 2 active low */ + }; + }; + + i2c@3d00 { + status = "disabled"; + }; + + i2c@3d40 { + hwmon@2e { + compatible = "nsc,lm87"; + reg = <0x2e>; + }; + rtc@51 { + compatible = "nxp,rtc8564"; + reg = <0x51>; + }; + }; + }; + + localbus { + compatible = "fsl,mpc5200b-lpb","simple-bus"; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0xfe000000 0x02000000 + 1 0 0x62000000 0x00400000 + 2 0 0x64000000 0x00200000 + 3 0 0x66000000 0x01000000 + 6 0 0x68000000 0x01000000 + 7 0 0x6a000000 0x00000004>; + + flash@0,0 { + compatible = "cfi-flash"; + reg = <0 0 0x02000000>; + bank-width = <2>; + #size-cells = <1>; + #address-cells = <1>; + }; + sram0@1,0 { + compatible = "mtd-ram"; + reg = <1 0x00000 0x00400000>; + bank-width = <2>; + }; + }; + + pci@f0000d00 { + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + compatible = "fsl,mpc5200-pci"; + reg = <0xf0000d00 0x100>; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = < + /* IDSEL 0x16 */ + 0xc000 0 0 1 &mpc5200_pic 1 3 3 + 0xc000 0 0 2 &mpc5200_pic 1 3 3 + 0xc000 0 0 3 &mpc5200_pic 1 3 3 + 0xc000 0 0 4 &mpc5200_pic 1 3 3>; + clock-frequency = <0>; /* From boot loader */ + interrupts = <2 8 0 2 9 0 2 10 0>; + bus-range = <0 0>; + ranges = <0x42000000 0 0x80000000 0x80000000 0 0x10000000 + 0x02000000 0 0x90000000 0x90000000 0 0x10000000 + 0x01000000 0 0x00000000 0xa0000000 0 0x01000000>; + }; +}; diff --git a/arch/powerpc/platforms/52xx/mpc5200_simple.c b/arch/powerpc/platforms/52xx/mpc5200_simple.c index 846b789..c0aa040 100644 --- a/arch/powerpc/platforms/52xx/mpc5200_simple.c +++ b/arch/powerpc/platforms/52xx/mpc5200_simple.c @@ -50,6 +50,7 @@ static void __init mpc5200_simple_setup_arch(void) /* list of the supported boards */ static const char *board[] __initdata = { + "anonymous,a4m072", "anon,charon", "intercontrol,digsy-mtc", "manroland,mucmc52", -- cgit v0.10.2 From 70a3e5a029eed803b3ce27791018db5d27d5787e Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Sun, 18 Mar 2012 23:20:06 +0100 Subject: powerpc/mpc5200: add options to mpc5200_defconfig Add the following options to the mpc5200_defconfig, needed for the a4m072 board support: CONFIG_AMD_PHY=y CONFIG_GPIO_SYSFS=y CONFIG_SENSORS_LM87=m CONFIG_RTC_DRV_PCF8563=m Signed-off-by: Heiko Schocher cc: Wolfgang Denk Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/configs/mpc5200_defconfig b/arch/powerpc/configs/mpc5200_defconfig index 730a2ff..6640a35 100644 --- a/arch/powerpc/configs/mpc5200_defconfig +++ b/arch/powerpc/configs/mpc5200_defconfig @@ -56,6 +56,7 @@ CONFIG_PATA_MPC52xx=y CONFIG_PATA_PLATFORM=y CONFIG_NETDEVICES=y CONFIG_FEC_MPC52xx=y +CONFIG_AMD_PHY=y CONFIG_LXT_PHY=y CONFIG_FIXED_PHY=y # CONFIG_INPUT_KEYBOARD is not set @@ -72,7 +73,9 @@ CONFIG_SPI_GPIO=m CONFIG_SPI_MPC52xx=m CONFIG_SPI_MPC52xx_PSC=m CONFIG_SPI_SPIDEV=m +CONFIG_GPIO_SYSFS=y CONFIG_SENSORS_LM80=y +CONFIG_SENSORS_LM87=m CONFIG_WATCHDOG=y CONFIG_MFD_SM501=m CONFIG_DRM=y @@ -121,6 +124,7 @@ CONFIG_NEW_LEDS=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=y CONFIG_RTC_DRV_DS1374=y +CONFIG_RTC_DRV_PCF8563=m CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -- cgit v0.10.2 From ff651516683421d773802707652207d4036244cc Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 21 Jun 2011 08:45:13 +0000 Subject: powerpc/5200: convert mpc5200 to use of_platform_populate() of_platform_populate() also handles nodes at the root of the tree, which is wanted for things like describing the sound complex. This patch converts mpc5200 support to use of_platform_populate() instead of of_platform_bus_probe(). Signed-off-by: Grant Likely Signed-off-by: Anatolij Gustschin diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c index 369fd54..d7e94f4 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_common.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c @@ -98,13 +98,11 @@ struct mpc52xx_gpio_wkup __iomem *wkup_gpio; * of the localplus bus to the of_platform * bus. */ -void __init -mpc52xx_declare_of_platform_devices(void) +void __init mpc52xx_declare_of_platform_devices(void) { - /* Find every child of the SOC node and add it to of_platform */ - if (of_platform_bus_probe(NULL, mpc52xx_bus_ids, NULL)) - printk(KERN_ERR __FILE__ ": " - "Error while probing of_platform bus\n"); + /* Find all the 'platform' devices and register them. */ + if (of_platform_populate(NULL, mpc52xx_bus_ids, NULL, NULL)) + pr_err(__FILE__ ": Error while populating devices from DT\n"); } /* -- cgit v0.10.2 From fb700d3653acbec8fd1f81496ce4da029e12d557 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 7 Mar 2012 11:01:35 +0000 Subject: powerpc/spufs: Fix double unlocks spufs return path has a bug where it could end up trying to unlock an inode mutex twice. Fix it. Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index d4a094c..114ab14 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -646,6 +646,7 @@ long spufs_create(struct path *path, struct dentry *dentry, out: mutex_unlock(&path->dentry->d_inode->i_mutex); + dput(dentry); return ret; } diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c index 8591bb6..5665dcc 100644 --- a/arch/powerpc/platforms/cell/spufs/syscalls.c +++ b/arch/powerpc/platforms/cell/spufs/syscalls.c @@ -70,8 +70,6 @@ static long do_spu_create(const char __user *pathname, unsigned int flags, ret = PTR_ERR(dentry); if (!IS_ERR(dentry)) { ret = spufs_create(&path, dentry, flags, mode, neighbor); - mutex_unlock(&path.dentry->d_inode->i_mutex); - dput(dentry); path_put(&path); } -- cgit v0.10.2 From ec86b45af464d2d3c00d1125b220d6c3b6ca93d8 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 15 Mar 2012 18:16:54 +0000 Subject: tty/hvc_vio: FW_FEATURE_ISERIES is no longer selectable so remove the code that tests for it. Cc: Greg Kroah-Hartman Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index fc3c3ad..3a0d53d 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include @@ -322,9 +321,6 @@ static int __init hvc_vio_init(void) { int rc; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - /* Register as a vio device to receive callbacks */ rc = vio_register_driver(&hvc_vio_driver); -- cgit v0.10.2 From f5339277eb8d3aed37f12a27988366f68ab68930 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 15 Mar 2012 18:18:00 +0000 Subject: powerpc: Remove FW_FEATURE ISERIES from arch code This is no longer selectable, so just remove all the dependent code. Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/abs_addr.h b/arch/powerpc/include/asm/abs_addr.h index 5ab0b71..9d92ba0 100644 --- a/arch/powerpc/include/asm/abs_addr.h +++ b/arch/powerpc/include/asm/abs_addr.h @@ -17,7 +17,6 @@ #include #include #include -#include struct mschunks_map { unsigned long num_chunks; @@ -46,30 +45,12 @@ static inline unsigned long addr_to_chunk(unsigned long addr) static inline unsigned long phys_to_abs(unsigned long pa) { - unsigned long chunk; - - /* This is a no-op on non-iSeries */ - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return pa; - - chunk = addr_to_chunk(pa); - - if (chunk < mschunks_map.num_chunks) - chunk = mschunks_map.mapping[chunk]; - - return chunk_to_addr(chunk) + (pa & MSCHUNKS_OFFSET_MASK); + return pa; } /* Convenience macros */ #define virt_to_abs(va) phys_to_abs(__pa(va)) #define abs_to_virt(aa) __va(aa) -/* - * Converts Virtual Address to Real Address for - * Legacy iSeries Hypervisor calls - */ -#define iseries_hv_addr(virtaddr) \ - (0x8000000000000000UL | virt_to_abs(virtaddr)) - #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_ABS_ADDR_H */ diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h index 14db29b..ad0b751 100644 --- a/arch/powerpc/include/asm/firmware.h +++ b/arch/powerpc/include/asm/firmware.h @@ -41,7 +41,6 @@ #define FW_FEATURE_XDABR ASM_CONST(0x0000000000040000) #define FW_FEATURE_MULTITCE ASM_CONST(0x0000000000080000) #define FW_FEATURE_SPLPAR ASM_CONST(0x0000000000100000) -#define FW_FEATURE_ISERIES ASM_CONST(0x0000000000200000) #define FW_FEATURE_LPAR ASM_CONST(0x0000000000400000) #define FW_FEATURE_PS3_LV1 ASM_CONST(0x0000000000800000) #define FW_FEATURE_BEAT ASM_CONST(0x0000000001000000) @@ -65,8 +64,6 @@ enum { FW_FEATURE_MULTITCE | FW_FEATURE_SPLPAR | FW_FEATURE_LPAR | FW_FEATURE_CMO | FW_FEATURE_VPHN | FW_FEATURE_XCMO, FW_FEATURE_PSERIES_ALWAYS = 0, - FW_FEATURE_ISERIES_POSSIBLE = FW_FEATURE_ISERIES | FW_FEATURE_LPAR, - FW_FEATURE_ISERIES_ALWAYS = FW_FEATURE_ISERIES | FW_FEATURE_LPAR, FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL | FW_FEATURE_OPALv2, FW_FEATURE_POWERNV_ALWAYS = 0, FW_FEATURE_PS3_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1, @@ -79,9 +76,6 @@ enum { #ifdef CONFIG_PPC_PSERIES FW_FEATURE_PSERIES_POSSIBLE | #endif -#ifdef CONFIG_PPC_ISERIES - FW_FEATURE_ISERIES_POSSIBLE | -#endif #ifdef CONFIG_PPC_POWERNV FW_FEATURE_POWERNV_POSSIBLE | #endif @@ -99,9 +93,6 @@ enum { #ifdef CONFIG_PPC_PSERIES FW_FEATURE_PSERIES_ALWAYS & #endif -#ifdef CONFIG_PPC_ISERIES - FW_FEATURE_ISERIES_ALWAYS & -#endif #ifdef CONFIG_PPC_POWERNV FW_FEATURE_POWERNV_ALWAYS & #endif diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h index 7eb10fb..2136f58 100644 --- a/arch/powerpc/include/asm/time.h +++ b/arch/powerpc/include/asm/time.h @@ -18,11 +18,6 @@ #include #include -#ifdef CONFIG_PPC_ISERIES -#include -#include -#include -#endif /* time.c */ extern unsigned long tb_ticks_per_jiffy; @@ -167,15 +162,6 @@ static inline void set_dec(int val) #ifndef CONFIG_BOOKE --val; #endif -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES) && - get_lppaca()->shared_proc) { - get_lppaca()->virtual_decr = val; - if (get_dec() > val) - HvCall_setVirtualDecr(); - return; - } -#endif mtspr(SPRN_DEC, val); #endif /* not 40x or 8xx_CPU6 */ } @@ -217,7 +203,6 @@ DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array); #endif extern void secondary_cpu_time_init(void); -extern void iSeries_time_init_early(void); DECLARE_PER_CPU(u64, decrementers_next_tb); diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index eb804e1..45b367c 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -211,11 +211,6 @@ notrace void arch_local_irq_restore(unsigned long en) * External interrupt events on non-iseries will have caused * interrupts to be hard-disabled, so there is no problem, we * cannot have preempted. - * - * That leaves us with EEs on iSeries or decrementer interrupts, - * which I decided to safely ignore. The preemption would have - * itself been the result of an interrupt, upon which return we - * will have checked for pending events on the old CPU. */ irq_happened = get_irq_happened(); if (!irq_happened) @@ -458,15 +453,6 @@ void do_IRQ(struct pt_regs *regs) irq_exit(); set_irq_regs(old_regs); -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES) && - get_lppaca()->int_dword.fields.decr_int) { - get_lppaca()->int_dword.fields.decr_int = 0; - /* Signal a fake decrementer interrupt */ - timer_interrupt(regs); - } -#endif - trace_irq_exit(regs); } diff --git a/arch/powerpc/kernel/isa-bridge.c b/arch/powerpc/kernel/isa-bridge.c index 4797529..d45ec58 100644 --- a/arch/powerpc/kernel/isa-bridge.c +++ b/arch/powerpc/kernel/isa-bridge.c @@ -29,7 +29,6 @@ #include #include #include -#include unsigned long isa_io_base; /* NULL if no ISA bus */ EXPORT_SYMBOL(isa_io_base); @@ -261,8 +260,6 @@ static struct notifier_block isa_bridge_notifier = { */ static int __init isa_bridge_init(void) { - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; bus_register_notifier(&pci_bus_type, &isa_bridge_notifier); return 0; } diff --git a/arch/powerpc/kernel/lparcfg.c b/arch/powerpc/kernel/lparcfg.c index 578f35f..ac12bd8 100644 --- a/arch/powerpc/kernel/lparcfg.c +++ b/arch/powerpc/kernel/lparcfg.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -55,80 +54,14 @@ static unsigned long get_purr(void) int cpu; for_each_possible_cpu(cpu) { - if (firmware_has_feature(FW_FEATURE_ISERIES)) - sum_purr += lppaca_of(cpu).emulated_time_base; - else { - struct cpu_usage *cu; + struct cpu_usage *cu; - cu = &per_cpu(cpu_usage_array, cpu); - sum_purr += cu->current_tb; - } + cu = &per_cpu(cpu_usage_array, cpu); + sum_purr += cu->current_tb; } return sum_purr; } -#ifdef CONFIG_PPC_ISERIES - -/* - * Methods used to fetch LPAR data when running on an iSeries platform. - */ -static int iseries_lparcfg_data(struct seq_file *m, void *v) -{ - unsigned long pool_id; - int shared, entitled_capacity, max_entitled_capacity; - int processors, max_processors; - unsigned long purr = get_purr(); - - shared = (int)(local_paca->lppaca_ptr->shared_proc); - - seq_printf(m, "system_active_processors=%d\n", - (int)HvLpConfig_getSystemPhysicalProcessors()); - - seq_printf(m, "system_potential_processors=%d\n", - (int)HvLpConfig_getSystemPhysicalProcessors()); - - processors = (int)HvLpConfig_getPhysicalProcessors(); - seq_printf(m, "partition_active_processors=%d\n", processors); - - max_processors = (int)HvLpConfig_getMaxPhysicalProcessors(); - seq_printf(m, "partition_potential_processors=%d\n", max_processors); - - if (shared) { - entitled_capacity = HvLpConfig_getSharedProcUnits(); - max_entitled_capacity = HvLpConfig_getMaxSharedProcUnits(); - } else { - entitled_capacity = processors * 100; - max_entitled_capacity = max_processors * 100; - } - seq_printf(m, "partition_entitled_capacity=%d\n", entitled_capacity); - - seq_printf(m, "partition_max_entitled_capacity=%d\n", - max_entitled_capacity); - - if (shared) { - pool_id = HvLpConfig_getSharedPoolIndex(); - seq_printf(m, "pool=%d\n", (int)pool_id); - seq_printf(m, "pool_capacity=%d\n", - (int)(HvLpConfig_getNumProcsInSharedPool(pool_id) * - 100)); - seq_printf(m, "purr=%ld\n", purr); - } - - seq_printf(m, "shared_processor_mode=%d\n", shared); - - return 0; -} - -#else /* CONFIG_PPC_ISERIES */ - -static int iseries_lparcfg_data(struct seq_file *m, void *v) -{ - return 0; -} - -#endif /* CONFIG_PPC_ISERIES */ - -#ifdef CONFIG_PPC_PSERIES /* * Methods used to fetch LPAR data when running on a pSeries platform. */ @@ -648,8 +581,7 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf, u8 new_weight, *new_weight_ptr = &new_weight; ssize_t retval; - if (!firmware_has_feature(FW_FEATURE_SPLPAR) || - firmware_has_feature(FW_FEATURE_ISERIES)) + if (!firmware_has_feature(FW_FEATURE_SPLPAR)) return -EINVAL; if (count > kbuf_sz) @@ -709,21 +641,6 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf, return retval; } -#else /* CONFIG_PPC_PSERIES */ - -static int pseries_lparcfg_data(struct seq_file *m, void *v) -{ - return 0; -} - -static ssize_t lparcfg_write(struct file *file, const char __user * buf, - size_t count, loff_t * off) -{ - return -EINVAL; -} - -#endif /* CONFIG_PPC_PSERIES */ - static int lparcfg_data(struct seq_file *m, void *v) { struct device_node *rootdn; @@ -738,19 +655,11 @@ static int lparcfg_data(struct seq_file *m, void *v) rootdn = of_find_node_by_path("/"); if (rootdn) { tmp = of_get_property(rootdn, "model", NULL); - if (tmp) { + if (tmp) model = tmp; - /* Skip "IBM," - see platforms/iseries/dt.c */ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - model += 4; - } tmp = of_get_property(rootdn, "system-id", NULL); - if (tmp) { + if (tmp) system_id = tmp; - /* Skip "IBM," - see platforms/iseries/dt.c */ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - system_id += 4; - } lp_index_ptr = of_get_property(rootdn, "ibm,partition-no", NULL); if (lp_index_ptr) @@ -761,8 +670,6 @@ static int lparcfg_data(struct seq_file *m, void *v) seq_printf(m, "system_type=%s\n", model); seq_printf(m, "partition_id=%d\n", (int)lp_index); - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return iseries_lparcfg_data(m, v); return pseries_lparcfg_data(m, v); } @@ -786,8 +693,7 @@ static int __init lparcfg_init(void) umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; /* Allow writing if we have FW_FEATURE_SPLPAR */ - if (firmware_has_feature(FW_FEATURE_SPLPAR) && - !firmware_has_feature(FW_FEATURE_ISERIES)) + if (firmware_has_feature(FW_FEATURE_SPLPAR)) mode |= S_IWUSR; ent = proc_create("powerpc/lparcfg", mode, NULL, &lparcfg_fops); diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index 41456ff..0bb1f98 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -11,13 +11,10 @@ #include #include -#include #include #include #include #include -#include -#include #include /* This symbol is provided by the linker - let it fill in the paca @@ -30,8 +27,8 @@ extern unsigned long __toc_start; * The structure which the hypervisor knows about - this structure * should not cross a page boundary. The vpa_init/register_vpa call * is now known to fail if the lppaca structure crosses a page - * boundary. The lppaca is also used on legacy iSeries and POWER5 - * pSeries boxes. The lppaca is 640 bytes long, and cannot readily + * boundary. The lppaca is also used on POWER5 pSeries boxes. + * The lppaca is 640 bytes long, and cannot readily * change since the hypervisor knows its layout, so a 1kB alignment * will suffice to ensure that it doesn't cross a page boundary. */ @@ -183,12 +180,9 @@ void __init allocate_pacas(void) /* * We can't take SLB misses on the paca, and we want to access them * in real mode, so allocate them within the RMA and also within - * the first segment. On iSeries they must be within the area mapped - * by the HV, which is HvPagesToMap * HVPAGESIZE bytes. + * the first segment. */ limit = min(0x10000000ULL, ppc64_rma_size); - if (firmware_has_feature(FW_FEATURE_ISERIES)) - limit = min(limit, HvPagesToMap * HVPAGESIZE); paca_size = PAGE_ALIGN(sizeof(struct paca_struct) * nr_cpu_ids); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index cce98d7..d0373bc 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -38,7 +38,6 @@ #include #include #include -#include #include static DEFINE_SPINLOCK(hose_spinlock); @@ -219,20 +218,6 @@ static int pci_read_irq_line(struct pci_dev *pci_dev) struct of_irq oirq; unsigned int virq; - /* The current device-tree that iSeries generates from the HV - * PCI informations doesn't contain proper interrupt routing, - * and all the fallback would do is print out crap, so we - * don't attempt to resolve the interrupts here at all, some - * iSeries specific fixup does it. - * - * In the long run, we will hopefully fix the generated device-tree - * instead. - */ -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return -1; -#endif - pr_debug("PCI: Try to map irq for %s...\n", pci_name(pci_dev)); #ifdef DEBUG diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 883e74c..0c683d3 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -341,8 +340,7 @@ static void __cpuinit register_cpu_online(unsigned int cpu) int i, nattrs; #ifdef CONFIG_PPC64 - if (!firmware_has_feature(FW_FEATURE_ISERIES) && - cpu_has_feature(CPU_FTR_SMT)) + if (cpu_has_feature(CPU_FTR_SMT)) device_create_file(s, &dev_attr_smt_snooze_delay); #endif @@ -414,8 +412,7 @@ static void unregister_cpu_online(unsigned int cpu) BUG_ON(!c->hotpluggable); #ifdef CONFIG_PPC64 - if (!firmware_has_feature(FW_FEATURE_ISERIES) && - cpu_has_feature(CPU_FTR_SMT)) + if (cpu_has_feature(CPU_FTR_SMT)) device_remove_file(s, &dev_attr_smt_snooze_delay); #endif diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index f81c81b..2c42cd7 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -17,8 +17,7 @@ * * TODO (not necessarily in this file): * - improve precision and reproducibility of timebase frequency - * measurement at boot time. (for iSeries, we calibrate the timebase - * against the Titan chip's clock.) + * measurement at boot time. * - for astronomical applications: add a new function to get * non ambiguous timestamps even around leap seconds. This needs * a new timestamp format and a good name. @@ -70,10 +69,6 @@ #include #include #include -#ifdef CONFIG_PPC_ISERIES -#include -#include -#endif /* powerpc clocksource/clockevent code */ @@ -117,14 +112,6 @@ static struct clock_event_device decrementer_clockevent = { DEFINE_PER_CPU(u64, decrementers_next_tb); static DEFINE_PER_CPU(struct clock_event_device, decrementers); -#ifdef CONFIG_PPC_ISERIES -static unsigned long __initdata iSeries_recal_titan; -static signed long __initdata iSeries_recal_tb; - -/* Forward declaration is only needed for iSereis compiles */ -static void __init clocksource_init(void); -#endif - #define XSEC_PER_SEC (1024*1024) #ifdef CONFIG_PPC64 @@ -423,74 +410,6 @@ unsigned long profile_pc(struct pt_regs *regs) EXPORT_SYMBOL(profile_pc); #endif -#ifdef CONFIG_PPC_ISERIES - -/* - * This function recalibrates the timebase based on the 49-bit time-of-day - * value in the Titan chip. The Titan is much more accurate than the value - * returned by the service processor for the timebase frequency. - */ - -static int __init iSeries_tb_recal(void) -{ - unsigned long titan, tb; - - /* Make sure we only run on iSeries */ - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - - tb = get_tb(); - titan = HvCallXm_loadTod(); - if ( iSeries_recal_titan ) { - unsigned long tb_ticks = tb - iSeries_recal_tb; - unsigned long titan_usec = (titan - iSeries_recal_titan) >> 12; - unsigned long new_tb_ticks_per_sec = (tb_ticks * USEC_PER_SEC)/titan_usec; - unsigned long new_tb_ticks_per_jiffy = - DIV_ROUND_CLOSEST(new_tb_ticks_per_sec, HZ); - long tick_diff = new_tb_ticks_per_jiffy - tb_ticks_per_jiffy; - char sign = '+'; - /* make sure tb_ticks_per_sec and tb_ticks_per_jiffy are consistent */ - new_tb_ticks_per_sec = new_tb_ticks_per_jiffy * HZ; - - if ( tick_diff < 0 ) { - tick_diff = -tick_diff; - sign = '-'; - } - if ( tick_diff ) { - if ( tick_diff < tb_ticks_per_jiffy/25 ) { - printk( "Titan recalibrate: new tb_ticks_per_jiffy = %lu (%c%ld)\n", - new_tb_ticks_per_jiffy, sign, tick_diff ); - tb_ticks_per_jiffy = new_tb_ticks_per_jiffy; - tb_ticks_per_sec = new_tb_ticks_per_sec; - calc_cputime_factors(); - vdso_data->tb_ticks_per_sec = tb_ticks_per_sec; - setup_cputime_one_jiffy(); - } - else { - printk( "Titan recalibrate: FAILED (difference > 4 percent)\n" - " new tb_ticks_per_jiffy = %lu\n" - " old tb_ticks_per_jiffy = %lu\n", - new_tb_ticks_per_jiffy, tb_ticks_per_jiffy ); - } - } - } - iSeries_recal_titan = titan; - iSeries_recal_tb = tb; - - /* Called here as now we know accurate values for the timebase */ - clocksource_init(); - return 0; -} -late_initcall(iSeries_tb_recal); - -/* Called from platform early init */ -void __init iSeries_time_init_early(void) -{ - iSeries_recal_tb = get_tb(); - iSeries_recal_titan = HvCallXm_loadTod(); -} -#endif /* CONFIG_PPC_ISERIES */ - #ifdef CONFIG_IRQ_WORK /* @@ -547,16 +466,6 @@ void arch_irq_work_raise(void) #endif /* CONFIG_IRQ_WORK */ /* - * For iSeries shared processors, we have to let the hypervisor - * set the hardware decrementer. We set a virtual decrementer - * in the lppaca and call the hypervisor if the virtual - * decrementer is less than the current value in the hardware - * decrementer. (almost always the new decrementer value will - * be greater than the current hardware decementer so the hypervisor - * call will not be needed) - */ - -/* * timer_interrupt - gets called when the decrementer overflows, * with interrupts disabled. */ @@ -599,20 +508,10 @@ void timer_interrupt(struct pt_regs * regs) irq_work_run(); } -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES)) - get_lppaca()->int_dword.fields.decr_int = 0; -#endif - *next_tb = ~(u64)0; if (evt->event_handler) evt->event_handler(evt); -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES) && hvlpevent_is_pending()) - process_hvlpevents(); -#endif - #ifdef CONFIG_PPC64 /* collect purr register values often, for accurate calculations */ if (firmware_has_feature(FW_FEATURE_SPLPAR)) { @@ -984,9 +883,8 @@ void __init time_init(void) */ start_cpu_decrementer(); - /* Register the clocksource, if we're not running on iSeries */ - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - clocksource_init(); + /* Register the clocksource */ + clocksource_init(); init_decrementer_clockevent(); } diff --git a/arch/powerpc/lib/locks.c b/arch/powerpc/lib/locks.c index a6ebba5..bb7cfec 100644 --- a/arch/powerpc/lib/locks.c +++ b/arch/powerpc/lib/locks.c @@ -19,11 +19,9 @@ #include /* waiting for a spinlock... */ -#if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES) +#if defined(CONFIG_PPC_SPLPAR) #include -#include #include -#include void __spin_yield(arch_spinlock_t *lock) { @@ -40,14 +38,8 @@ void __spin_yield(arch_spinlock_t *lock) rmb(); if (lock->slock != lock_value) return; /* something has changed */ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, - ((u64)holder_cpu << 32) | yield_count); -#ifdef CONFIG_PPC_SPLPAR - else - plpar_hcall_norets(H_CONFER, - get_hard_smp_processor_id(holder_cpu), yield_count); -#endif + plpar_hcall_norets(H_CONFER, + get_hard_smp_processor_id(holder_cpu), yield_count); } /* @@ -71,14 +63,8 @@ void __rw_yield(arch_rwlock_t *rw) rmb(); if (rw->lock != lock_value) return; /* something has changed */ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - HvCall2(HvCallBaseYieldProcessor, HvCall_YieldToProc, - ((u64)holder_cpu << 32) | yield_count); -#ifdef CONFIG_PPC_SPLPAR - else - plpar_hcall_norets(H_CONFER, - get_hard_smp_processor_id(holder_cpu), yield_count); -#endif + plpar_hcall_norets(H_CONFER, + get_hard_smp_processor_id(holder_cpu), yield_count); } #endif diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index b534bba..3e8c37a 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -56,6 +56,7 @@ #include #include #include +#include #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -756,12 +757,9 @@ void __init early_init_mmu(void) */ htab_initialize(); - /* Initialize stab / SLB management except on iSeries - */ + /* Initialize stab / SLB management */ if (mmu_has_feature(MMU_FTR_SLB)) slb_initialize(); - else if (!firmware_has_feature(FW_FEATURE_ISERIES)) - stab_initialize(get_paca()->stab_real); } #ifdef CONFIG_SMP @@ -772,8 +770,7 @@ void __cpuinit early_init_mmu_secondary(void) mtspr(SPRN_SDR1, _SDR1); /* Initialize STAB/SLB. We use a virtual address as it works - * in real mode on pSeries and we want a virtual address on - * iSeries anyway + * in real mode on pSeries. */ if (mmu_has_feature(MMU_FTR_SLB)) slb_initialize(); diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c index e22276c..a538c80 100644 --- a/arch/powerpc/mm/slb.c +++ b/arch/powerpc/mm/slb.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -307,11 +306,6 @@ void slb_initialize(void) get_paca()->stab_rr = SLB_NUM_BOLTED; - /* On iSeries the bolted entries have already been set up by - * the hypervisor from the lparMap data in head.S */ - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return; - lflags = SLB_VSID_KERNEL | linear_llp; vflags = SLB_VSID_KERNEL | vmalloc_llp; diff --git a/arch/powerpc/mm/stab.c b/arch/powerpc/mm/stab.c index 41e3164..9106ebb 100644 --- a/arch/powerpc/mm/stab.c +++ b/arch/powerpc/mm/stab.c @@ -21,8 +21,6 @@ #include #include #include -#include -#include struct stab_entry { unsigned long esid_data; @@ -285,12 +283,5 @@ void stab_initialize(unsigned long stab) /* Set ASR */ stabreal = get_paca()->stab_real | 0x1ul; -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES)) { - HvCall1(HvCallBaseSetASR, stabreal); - return; - } -#endif /* CONFIG_PPC_ISERIES */ - mtspr(SPRN_ASR, stabreal); } diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c index d65e68f..6f01624 100644 --- a/arch/powerpc/oprofile/common.c +++ b/arch/powerpc/oprofile/common.c @@ -195,9 +195,6 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) if (!cur_cpu_spec->oprofile_cpu_type) return -ENODEV; - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return -ENODEV; - switch (cur_cpu_spec->oprofile_type) { #ifdef CONFIG_PPC_BOOK3S_64 #ifdef CONFIG_OPROFILE_CELL diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index f92b9ef..214478d 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "powernv.h" #include "pci.h" diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 7bc73af..5f3ef87 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "plpar_wrappers.h" #include "pseries.h" diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 974a47b..68a9cbb 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -1635,25 +1634,6 @@ static void super_regs(void) mfspr(SPRN_DEC), mfspr(SPRN_SPRG2)); printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3)); printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR)); -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES)) { - struct paca_struct *ptrPaca; - struct lppaca *ptrLpPaca; - - /* Dump out relevant Paca data areas. */ - printf("Paca: \n"); - ptrPaca = local_paca; - - printf(" Local Processor Control Area (LpPaca): \n"); - ptrLpPaca = ptrPaca->lppaca_ptr; - printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n", - ptrLpPaca->saved_srr0, ptrLpPaca->saved_srr1); - printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n", - ptrLpPaca->saved_gpr3, ptrLpPaca->saved_gpr4); - printf(" Saved Gpr5=%.16lx \n", - ptrLpPaca->gpr5_dword.saved_gpr5); - } -#endif return; } @@ -2856,10 +2836,6 @@ static void dump_tlb_book3e(void) static void xmon_init(int enable) { -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return; -#endif if (enable) { __debugger = xmon; __debugger_ipi = xmon_ipi; @@ -2896,10 +2872,6 @@ static struct sysrq_key_op sysrq_xmon_op = { static int __init setup_xmon_sysrq(void) { -#ifdef CONFIG_PPC_ISERIES - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return 0; -#endif register_sysrq_key('x', &sysrq_xmon_op); return 0; } -- cgit v0.10.2 From bc58450b023c5815e5bc54e6d43edbd1e3576fe6 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 15 Mar 2012 18:19:08 +0000 Subject: init: Remove CONFIG_PPC_ISERIES It is no longer selectable, so remove the check for it. Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c index 887629e..01f1306 100644 --- a/init/do_mounts_rd.c +++ b/init/do_mounts_rd.c @@ -178,7 +178,7 @@ int __init rd_load_image(char *from) char *buf = NULL; unsigned short rotate = 0; decompress_fn decompressor = NULL; -#if !defined(CONFIG_S390) && !defined(CONFIG_PPC_ISERIES) +#if !defined(CONFIG_S390) char rotator[4] = { '|' , '/' , '-' , '\\' }; #endif @@ -264,7 +264,7 @@ int __init rd_load_image(char *from) } sys_read(in_fd, buf, BLOCK_SIZE); sys_write(out_fd, buf, BLOCK_SIZE); -#if !defined(CONFIG_S390) && !defined(CONFIG_PPC_ISERIES) +#if !defined(CONFIG_S390) if (!(i % 16)) { printk("%c\b", rotator[rotate & 0x3]); rotate++; -- cgit v0.10.2 From 1b041885ae1d9938440fc2cf6a444b70ec0a86c9 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 15 Mar 2012 18:20:13 +0000 Subject: powerpc: Remove the remaining CONFIG_PPC_ISERIES pieces Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index f6622e0..e8461cb 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -184,7 +184,6 @@ image-$(CONFIG_PPC_EFIKA) += zImage.chrp image-$(CONFIG_PPC_PMAC) += zImage.pmac image-$(CONFIG_PPC_HOLLY) += dtbImage.holly image-$(CONFIG_PPC_PRPMC2800) += dtbImage.prpmc2800 -image-$(CONFIG_PPC_ISERIES) += zImage.iseries image-$(CONFIG_DEFAULT_UIMAGE) += uImage image-$(CONFIG_EPAPR_BOOT) += zImage.epapr @@ -311,12 +310,6 @@ $(obj)/dtbImage.%: vmlinux $(wrapperbits) $(obj)/%.dtb $(obj)/vmlinux.strip: vmlinux $(STRIP) -s -R .comment $< -o $@ -# The iseries hypervisor won't take an ET_DYN executable, so this -# changes the type (byte 17) in the file to ET_EXEC (2). -$(obj)/zImage.iseries: vmlinux - $(STRIP) -s -R .comment $< -o $@ - printf "\x02" | dd of=$@ conv=notrunc bs=1 seek=17 - $(obj)/uImage: vmlinux $(wrapperbits) $(call if_changed,wrap,uboot) @@ -364,7 +357,7 @@ install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y)) # anything not in $(targets) clean-files += $(image-) $(initrd-) cuImage.* dtbImage.* treeImage.* \ zImage zImage.initrd zImage.chrp zImage.coff zImage.holly \ - zImage.iseries zImage.miboot zImage.pmac zImage.pseries \ + zImage.miboot zImage.pmac zImage.pseries \ zImage.maple simpleImage.* otheros.bld *.dtb # clean up files cached by wrapper diff --git a/arch/powerpc/include/asm/dma.h b/arch/powerpc/include/asm/dma.h index a7e06e2..adadb99 100644 --- a/arch/powerpc/include/asm/dma.h +++ b/arch/powerpc/include/asm/dma.h @@ -34,8 +34,6 @@ /* Doesn't really apply... */ #define MAX_DMA_ADDRESS (~0UL) -#if !defined(CONFIG_PPC_ISERIES) || defined(CONFIG_PCI) - #ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER #define dma_outb outb_p #else @@ -354,7 +352,5 @@ extern int isa_dma_bridge_buggy; #define isa_dma_bridge_buggy (0) #endif -#endif /* !defined(CONFIG_PPC_ISERIES) || defined(CONFIG_PCI) */ - #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_DMA_H */ diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h index e0298d2..a76254a 100644 --- a/arch/powerpc/include/asm/lppaca.h +++ b/arch/powerpc/include/asm/lppaca.h @@ -41,15 +41,7 @@ * We only have to have statically allocated lppaca structs on * legacy iSeries, which supports at most 64 cpus. */ -#ifdef CONFIG_PPC_ISERIES -#if NR_CPUS < 64 -#define NR_LPPACAS NR_CPUS -#else -#define NR_LPPACAS 64 -#endif -#else /* not iSeries */ #define NR_LPPACAS 1 -#endif /* The Hypervisor barfs if the lppaca crosses a page boundary. A 1k diff --git a/arch/powerpc/include/asm/spinlock.h b/arch/powerpc/include/asm/spinlock.h index f9611bd..7124fc0 100644 --- a/arch/powerpc/include/asm/spinlock.h +++ b/arch/powerpc/include/asm/spinlock.h @@ -23,7 +23,6 @@ #ifdef CONFIG_PPC64 #include #include -#include #endif #include #include @@ -95,12 +94,12 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock) * value. */ -#if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES) +#if defined(CONFIG_PPC_SPLPAR) /* We only yield to the hypervisor if we are in shared processor mode */ #define SHARED_PROCESSOR (get_lppaca()->shared_proc) extern void __spin_yield(arch_spinlock_t *lock); extern void __rw_yield(arch_rwlock_t *lock); -#else /* SPLPAR || ISERIES */ +#else /* SPLPAR */ #define __spin_yield(x) barrier() #define __rw_yield(x) barrier() #define SHARED_PROCESSOR 0 diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index cdd0d26..cc492e4 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -46,9 +46,6 @@ #include #include #endif -#ifdef CONFIG_PPC_ISERIES -#include -#endif #ifdef CONFIG_PPC_POWERNV #include #endif @@ -384,17 +381,6 @@ int main(void) DEFINE(BUG_ENTRY_SIZE, sizeof(struct bug_entry)); #endif -#ifdef CONFIG_PPC_ISERIES - /* the assembler miscalculates the VSID values */ - DEFINE(PAGE_OFFSET_ESID, GET_ESID(PAGE_OFFSET)); - DEFINE(PAGE_OFFSET_VSID, KERNEL_VSID(PAGE_OFFSET)); - DEFINE(VMALLOC_START_ESID, GET_ESID(VMALLOC_START)); - DEFINE(VMALLOC_START_VSID, KERNEL_VSID(VMALLOC_START)); - - /* alpaca */ - DEFINE(ALPACA_SIZE, sizeof(struct alpaca)); -#endif - DEFINE(PGD_TABLE_SIZE, PGD_TABLE_SIZE); DEFINE(PTE_SIZE, sizeof(pte_t)); -- cgit v0.10.2 From a6626ffe09d379bad4d0e49885f9195946ac875d Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 15 Mar 2012 18:22:08 +0000 Subject: powerpc: Remove the rest of the legacy iSeries include files since they are not referenced any more. Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/include/asm/iseries/alpaca.h b/arch/powerpc/include/asm/iseries/alpaca.h deleted file mode 100644 index c0cce67..0000000 --- a/arch/powerpc/include/asm/iseries/alpaca.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2008 Stephen Rothwell IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ASM_POWERPC_ISERIES_ALPACA_H -#define _ASM_POWERPC_ISERIES_ALPACA_H - -/* - * This is the part of the paca that the iSeries hypervisor - * needs to be statically initialised. Immediately after boot - * we switch to the normal Linux paca. - */ -struct alpaca { - struct lppaca *lppaca_ptr; /* Pointer to LpPaca for PLIC */ - const void *reg_save_ptr; /* Pointer to LpRegSave for PLIC */ -}; - -#endif /* _ASM_POWERPC_ISERIES_ALPACA_H */ diff --git a/arch/powerpc/include/asm/iseries/hv_call.h b/arch/powerpc/include/asm/iseries/hv_call.h deleted file mode 100644 index 162d653..0000000 --- a/arch/powerpc/include/asm/iseries/hv_call.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - * - * This file contains the "hypervisor call" interface which is used to - * drive the hypervisor from the OS. - */ -#ifndef _ASM_POWERPC_ISERIES_HV_CALL_H -#define _ASM_POWERPC_ISERIES_HV_CALL_H - -#include -#include -#include - -/* Type of yield for HvCallBaseYieldProcessor */ -#define HvCall_YieldTimed 0 /* Yield until specified time (tb) */ -#define HvCall_YieldToActive 1 /* Yield until all active procs have run */ -#define HvCall_YieldToProc 2 /* Yield until the specified processor has run */ - -/* interrupt masks for setEnabledInterrupts */ -#define HvCall_MaskIPI 0x00000001 -#define HvCall_MaskLpEvent 0x00000002 -#define HvCall_MaskLpProd 0x00000004 -#define HvCall_MaskTimeout 0x00000008 - -/* Log buffer formats */ -#define HvCall_LogBuffer_ASCII 0 -#define HvCall_LogBuffer_EBCDIC 1 - -#define HvCallBaseAckDeferredInts HvCallBase + 0 -#define HvCallBaseCpmPowerOff HvCallBase + 1 -#define HvCallBaseGetHwPatch HvCallBase + 2 -#define HvCallBaseReIplSpAttn HvCallBase + 3 -#define HvCallBaseSetASR HvCallBase + 4 -#define HvCallBaseSetASRAndRfi HvCallBase + 5 -#define HvCallBaseSetIMR HvCallBase + 6 -#define HvCallBaseSendIPI HvCallBase + 7 -#define HvCallBaseTerminateMachine HvCallBase + 8 -#define HvCallBaseTerminateMachineSrc HvCallBase + 9 -#define HvCallBaseProcessPlicInterrupts HvCallBase + 10 -#define HvCallBaseIsPrimaryCpmOrMsdIpl HvCallBase + 11 -#define HvCallBaseSetVirtualSIT HvCallBase + 12 -#define HvCallBaseVaryOffThisProcessor HvCallBase + 13 -#define HvCallBaseVaryOffMemoryChunk HvCallBase + 14 -#define HvCallBaseVaryOffInteractivePercentage HvCallBase + 15 -#define HvCallBaseSendLpProd HvCallBase + 16 -#define HvCallBaseSetEnabledInterrupts HvCallBase + 17 -#define HvCallBaseYieldProcessor HvCallBase + 18 -#define HvCallBaseVaryOffSharedProcUnits HvCallBase + 19 -#define HvCallBaseSetVirtualDecr HvCallBase + 20 -#define HvCallBaseClearLogBuffer HvCallBase + 21 -#define HvCallBaseGetLogBufferCodePage HvCallBase + 22 -#define HvCallBaseGetLogBufferFormat HvCallBase + 23 -#define HvCallBaseGetLogBufferLength HvCallBase + 24 -#define HvCallBaseReadLogBuffer HvCallBase + 25 -#define HvCallBaseSetLogBufferFormatAndCodePage HvCallBase + 26 -#define HvCallBaseWriteLogBuffer HvCallBase + 27 -#define HvCallBaseRouter28 HvCallBase + 28 -#define HvCallBaseRouter29 HvCallBase + 29 -#define HvCallBaseRouter30 HvCallBase + 30 -#define HvCallBaseSetDebugBus HvCallBase + 31 - -#define HvCallCcSetDABR HvCallCc + 7 - -static inline void HvCall_setVirtualDecr(void) -{ - /* - * Ignore any error return codes - most likely means that the - * target value for the LP has been increased and this vary off - * would bring us below the new target. - */ - HvCall0(HvCallBaseSetVirtualDecr); -} - -static inline void HvCall_yieldProcessor(unsigned typeOfYield, u64 yieldParm) -{ - HvCall2(HvCallBaseYieldProcessor, typeOfYield, yieldParm); -} - -static inline void HvCall_setEnabledInterrupts(u64 enabledInterrupts) -{ - HvCall1(HvCallBaseSetEnabledInterrupts, enabledInterrupts); -} - -static inline void HvCall_setLogBufferFormatAndCodepage(int format, - u32 codePage) -{ - HvCall2(HvCallBaseSetLogBufferFormatAndCodePage, format, codePage); -} - -extern void HvCall_writeLogBuffer(const void *buffer, u64 bufLen); - -static inline void HvCall_sendIPI(struct paca_struct *targetPaca) -{ - HvCall1(HvCallBaseSendIPI, targetPaca->paca_index); -} - -#endif /* _ASM_POWERPC_ISERIES_HV_CALL_H */ diff --git a/arch/powerpc/include/asm/iseries/hv_call_sc.h b/arch/powerpc/include/asm/iseries/hv_call_sc.h deleted file mode 100644 index f5d2109..0000000 --- a/arch/powerpc/include/asm/iseries/hv_call_sc.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ASM_POWERPC_ISERIES_HV_CALL_SC_H -#define _ASM_POWERPC_ISERIES_HV_CALL_SC_H - -#include - -#define HvCallBase 0x8000000000000000ul -#define HvCallCc 0x8001000000000000ul -#define HvCallCfg 0x8002000000000000ul -#define HvCallEvent 0x8003000000000000ul -#define HvCallHpt 0x8004000000000000ul -#define HvCallPci 0x8005000000000000ul -#define HvCallSm 0x8007000000000000ul -#define HvCallXm 0x8009000000000000ul - -extern u64 HvCall0(u64); -extern u64 HvCall1(u64, u64); -extern u64 HvCall2(u64, u64, u64); -extern u64 HvCall3(u64, u64, u64, u64); -extern u64 HvCall4(u64, u64, u64, u64, u64); -extern u64 HvCall5(u64, u64, u64, u64, u64, u64); -extern u64 HvCall6(u64, u64, u64, u64, u64, u64, u64); -extern u64 HvCall7(u64, u64, u64, u64, u64, u64, u64, u64); - -extern u64 HvCall0Ret16(u64, void *); -extern u64 HvCall1Ret16(u64, void *, u64); -extern u64 HvCall2Ret16(u64, void *, u64, u64); -extern u64 HvCall3Ret16(u64, void *, u64, u64, u64); -extern u64 HvCall4Ret16(u64, void *, u64, u64, u64, u64); -extern u64 HvCall5Ret16(u64, void *, u64, u64, u64, u64, u64); -extern u64 HvCall6Ret16(u64, void *, u64, u64, u64, u64, u64, u64); -extern u64 HvCall7Ret16(u64, void *, u64, u64 ,u64 ,u64 ,u64 ,u64 ,u64); - -#endif /* _ASM_POWERPC_ISERIES_HV_CALL_SC_H */ diff --git a/arch/powerpc/include/asm/iseries/hv_call_xm.h b/arch/powerpc/include/asm/iseries/hv_call_xm.h deleted file mode 100644 index 392ac3f..0000000 --- a/arch/powerpc/include/asm/iseries/hv_call_xm.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file contains the "hypervisor call" interface which is used to - * drive the hypervisor from SLIC. - */ -#ifndef _ASM_POWERPC_ISERIES_HV_CALL_XM_H -#define _ASM_POWERPC_ISERIES_HV_CALL_XM_H - -#include -#include - -#define HvCallXmGetTceTableParms HvCallXm + 0 -#define HvCallXmTestBus HvCallXm + 1 -#define HvCallXmConnectBusUnit HvCallXm + 2 -#define HvCallXmLoadTod HvCallXm + 8 -#define HvCallXmTestBusUnit HvCallXm + 9 -#define HvCallXmSetTce HvCallXm + 11 -#define HvCallXmSetTces HvCallXm + 13 - -static inline void HvCallXm_getTceTableParms(u64 cb) -{ - HvCall1(HvCallXmGetTceTableParms, cb); -} - -static inline u64 HvCallXm_setTce(u64 tceTableToken, u64 tceOffset, u64 tce) -{ - return HvCall3(HvCallXmSetTce, tceTableToken, tceOffset, tce); -} - -static inline u64 HvCallXm_setTces(u64 tceTableToken, u64 tceOffset, - u64 numTces, u64 tce1, u64 tce2, u64 tce3, u64 tce4) -{ - return HvCall7(HvCallXmSetTces, tceTableToken, tceOffset, numTces, - tce1, tce2, tce3, tce4); -} - -static inline u64 HvCallXm_testBus(u16 busNumber) -{ - return HvCall1(HvCallXmTestBus, busNumber); -} - -static inline u64 HvCallXm_testBusUnit(u16 busNumber, u8 subBusNumber, - u8 deviceId) -{ - return HvCall2(HvCallXmTestBusUnit, busNumber, - (subBusNumber << 8) | deviceId); -} - -static inline u64 HvCallXm_connectBusUnit(u16 busNumber, u8 subBusNumber, - u8 deviceId, u64 interruptToken) -{ - return HvCall5(HvCallXmConnectBusUnit, busNumber, - (subBusNumber << 8) | deviceId, interruptToken, 0, - 0 /* HvLpConfig::mapDsaToQueueIndex(HvLpDSA(busNumber, xBoard, xCard)) */); -} - -static inline u64 HvCallXm_loadTod(void) -{ - return HvCall0(HvCallXmLoadTod); -} - -#endif /* _ASM_POWERPC_ISERIES_HV_CALL_XM_H */ diff --git a/arch/powerpc/include/asm/iseries/hv_lp_config.h b/arch/powerpc/include/asm/iseries/hv_lp_config.h deleted file mode 100644 index a006fd1..0000000 --- a/arch/powerpc/include/asm/iseries/hv_lp_config.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ASM_POWERPC_ISERIES_HV_LP_CONFIG_H -#define _ASM_POWERPC_ISERIES_HV_LP_CONFIG_H - -/* - * This file contains the interface to the LPAR configuration data - * to determine which resources should be allocated to each partition. - */ - -#include -#include - -enum { - HvCallCfg_Cur = 0, - HvCallCfg_Init = 1, - HvCallCfg_Max = 2, - HvCallCfg_Min = 3 -}; - -#define HvCallCfgGetSystemPhysicalProcessors HvCallCfg + 6 -#define HvCallCfgGetPhysicalProcessors HvCallCfg + 7 -#define HvCallCfgGetMsChunks HvCallCfg + 9 -#define HvCallCfgGetSharedPoolIndex HvCallCfg + 20 -#define HvCallCfgGetSharedProcUnits HvCallCfg + 21 -#define HvCallCfgGetNumProcsInSharedPool HvCallCfg + 22 -#define HvCallCfgGetVirtualLanIndexMap HvCallCfg + 30 -#define HvCallCfgGetHostingLpIndex HvCallCfg + 32 - -extern HvLpIndex HvLpConfig_getLpIndex_outline(void); -extern HvLpIndex HvLpConfig_getLpIndex(void); -extern HvLpIndex HvLpConfig_getPrimaryLpIndex(void); - -static inline u64 HvLpConfig_getMsChunks(void) -{ - return HvCall2(HvCallCfgGetMsChunks, HvLpConfig_getLpIndex(), - HvCallCfg_Cur); -} - -static inline u64 HvLpConfig_getSystemPhysicalProcessors(void) -{ - return HvCall0(HvCallCfgGetSystemPhysicalProcessors); -} - -static inline u64 HvLpConfig_getNumProcsInSharedPool(HvLpSharedPoolIndex sPI) -{ - return (u16)HvCall1(HvCallCfgGetNumProcsInSharedPool, sPI); -} - -static inline u64 HvLpConfig_getPhysicalProcessors(void) -{ - return HvCall2(HvCallCfgGetPhysicalProcessors, HvLpConfig_getLpIndex(), - HvCallCfg_Cur); -} - -static inline HvLpSharedPoolIndex HvLpConfig_getSharedPoolIndex(void) -{ - return HvCall1(HvCallCfgGetSharedPoolIndex, HvLpConfig_getLpIndex()); -} - -static inline u64 HvLpConfig_getSharedProcUnits(void) -{ - return HvCall2(HvCallCfgGetSharedProcUnits, HvLpConfig_getLpIndex(), - HvCallCfg_Cur); -} - -static inline u64 HvLpConfig_getMaxSharedProcUnits(void) -{ - return HvCall2(HvCallCfgGetSharedProcUnits, HvLpConfig_getLpIndex(), - HvCallCfg_Max); -} - -static inline u64 HvLpConfig_getMaxPhysicalProcessors(void) -{ - return HvCall2(HvCallCfgGetPhysicalProcessors, HvLpConfig_getLpIndex(), - HvCallCfg_Max); -} - -static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMapForLp( - HvLpIndex lp) -{ - /* - * This is a new function in V5R1 so calls to this on older - * hypervisors will return -1 - */ - u64 retVal = HvCall1(HvCallCfgGetVirtualLanIndexMap, lp); - if (retVal == -1) - retVal = 0; - return retVal; -} - -static inline HvLpVirtualLanIndexMap HvLpConfig_getVirtualLanIndexMap(void) -{ - return HvLpConfig_getVirtualLanIndexMapForLp( - HvLpConfig_getLpIndex_outline()); -} - -static inline int HvLpConfig_doLpsCommunicateOnVirtualLan(HvLpIndex lp1, - HvLpIndex lp2) -{ - HvLpVirtualLanIndexMap virtualLanIndexMap1 = - HvLpConfig_getVirtualLanIndexMapForLp(lp1); - HvLpVirtualLanIndexMap virtualLanIndexMap2 = - HvLpConfig_getVirtualLanIndexMapForLp(lp2); - return ((virtualLanIndexMap1 & virtualLanIndexMap2) != 0); -} - -static inline HvLpIndex HvLpConfig_getHostingLpIndex(HvLpIndex lp) -{ - return HvCall1(HvCallCfgGetHostingLpIndex, lp); -} - -#endif /* _ASM_POWERPC_ISERIES_HV_LP_CONFIG_H */ diff --git a/arch/powerpc/include/asm/iseries/hv_types.h b/arch/powerpc/include/asm/iseries/hv_types.h deleted file mode 100644 index c3e6d2a..0000000 --- a/arch/powerpc/include/asm/iseries/hv_types.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ASM_POWERPC_ISERIES_HV_TYPES_H -#define _ASM_POWERPC_ISERIES_HV_TYPES_H - -/* - * General typedefs for the hypervisor. - */ - -#include - -typedef u8 HvLpIndex; -typedef u16 HvLpInstanceId; -typedef u64 HvLpTOD; -typedef u64 HvLpSystemSerialNum; -typedef u8 HvLpDeviceSerialNum[12]; -typedef u16 HvLpSanHwSet; -typedef u16 HvLpBus; -typedef u16 HvLpBoard; -typedef u16 HvLpCard; -typedef u8 HvLpDeviceType[4]; -typedef u8 HvLpDeviceModel[3]; -typedef u64 HvIoToken; -typedef u8 HvLpName[8]; -typedef u32 HvIoId; -typedef u64 HvRealMemoryIndex; -typedef u32 HvLpIndexMap; /* Must hold HVMAXARCHITECTEDLPS bits!!! */ -typedef u16 HvLpVrmIndex; -typedef u32 HvXmGenerationId; -typedef u8 HvLpBusPool; -typedef u8 HvLpSharedPoolIndex; -typedef u16 HvLpSharedProcUnitsX100; -typedef u8 HvLpVirtualLanIndex; -typedef u16 HvLpVirtualLanIndexMap; /* Must hold HVMAXARCHITECTEDVIRTUALLANS bits!!! */ -typedef u16 HvBusNumber; /* Hypervisor Bus Number */ -typedef u8 HvSubBusNumber; /* Hypervisor SubBus Number */ -typedef u8 HvAgentId; /* Hypervisor DevFn */ - - -#define HVMAXARCHITECTEDLPS 32 -#define HVMAXARCHITECTEDVIRTUALLANS 16 -#define HVMAXARCHITECTEDVIRTUALDISKS 32 -#define HVMAXARCHITECTEDVIRTUALCDROMS 8 -#define HVMAXARCHITECTEDVIRTUALTAPES 8 -#define HVCHUNKSIZE (256 * 1024) -#define HVPAGESIZE (4 * 1024) -#define HVLPMINMEGSPRIMARY 256 -#define HVLPMINMEGSSECONDARY 64 -#define HVCHUNKSPERMEG 4 -#define HVPAGESPERMEG 256 -#define HVPAGESPERCHUNK 64 - -#define HvLpIndexInvalid ((HvLpIndex)0xff) - -/* - * Enums for the sub-components under PLIC - * Used in HvCall and HvPrimaryCall - */ -enum { - HvCallCompId = 0, - HvCallCpuCtlsCompId = 1, - HvCallCfgCompId = 2, - HvCallEventCompId = 3, - HvCallHptCompId = 4, - HvCallPciCompId = 5, - HvCallSlmCompId = 6, - HvCallSmCompId = 7, - HvCallSpdCompId = 8, - HvCallXmCompId = 9, - HvCallRioCompId = 10, - HvCallRsvd3CompId = 11, - HvCallRsvd2CompId = 12, - HvCallRsvd1CompId = 13, - HvCallMaxCompId = 14, - HvPrimaryCallCompId = 0, - HvPrimaryCallCfgCompId = 1, - HvPrimaryCallPciCompId = 2, - HvPrimaryCallSmCompId = 3, - HvPrimaryCallSpdCompId = 4, - HvPrimaryCallXmCompId = 5, - HvPrimaryCallRioCompId = 6, - HvPrimaryCallRsvd7CompId = 7, - HvPrimaryCallRsvd6CompId = 8, - HvPrimaryCallRsvd5CompId = 9, - HvPrimaryCallRsvd4CompId = 10, - HvPrimaryCallRsvd3CompId = 11, - HvPrimaryCallRsvd2CompId = 12, - HvPrimaryCallRsvd1CompId = 13, - HvPrimaryCallMaxCompId = HvCallMaxCompId -}; - -struct HvLpBufferList { - u64 addr; - u64 len; -}; - -#endif /* _ASM_POWERPC_ISERIES_HV_TYPES_H */ diff --git a/arch/powerpc/include/asm/iseries/it_lp_queue.h b/arch/powerpc/include/asm/iseries/it_lp_queue.h deleted file mode 100644 index 4282788..0000000 --- a/arch/powerpc/include/asm/iseries/it_lp_queue.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ASM_POWERPC_ISERIES_IT_LP_QUEUE_H -#define _ASM_POWERPC_ISERIES_IT_LP_QUEUE_H - -/* - * This control block defines the simple LP queue structure that is - * shared between the hypervisor (PLIC) and the OS in order to send - * events to an LP. - */ - -#include -#include - -#define IT_LP_MAX_QUEUES 8 - -#define IT_LP_NOT_USED 0 /* Queue will not be used by PLIC */ -#define IT_LP_DEDICATED_IO 1 /* Queue dedicated to IO processor specified */ -#define IT_LP_DEDICATED_LP 2 /* Queue dedicated to LP specified */ -#define IT_LP_SHARED 3 /* Queue shared for both IO and LP */ - -#define IT_LP_EVENT_STACK_SIZE 4096 -#define IT_LP_EVENT_MAX_SIZE 256 -#define IT_LP_EVENT_ALIGN 64 - -struct hvlpevent_queue { -/* - * The hq_current_event is the pointer to the next event stack entry - * that will become valid. The OS must peek at this entry to determine - * if it is valid. PLIC will set the valid indicator as the very last - * store into that entry. - * - * When the OS has completed processing of the event then it will mark - * the event as invalid so that PLIC knows it can store into that event - * location again. - * - * If the event stack fills and there are overflow events, then PLIC - * will set the hq_overflow_pending flag in which case the OS will - * have to fetch the additional LP events once they have drained the - * event stack. - * - * The first 16-bytes are known by both the OS and PLIC. The remainder - * of the cache line is for use by the OS. - */ - u8 hq_overflow_pending; /* 0x00 Overflow events are pending */ - u8 hq_status; /* 0x01 DedicatedIo or DedicatedLp or NotUsed */ - u16 hq_proc_index; /* 0x02 Logical Proc Index for correlation */ - u8 hq_reserved1[12]; /* 0x04 */ - char *hq_current_event; /* 0x10 */ - char *hq_last_event; /* 0x18 */ - char *hq_event_stack; /* 0x20 */ - u8 hq_index; /* 0x28 unique sequential index. */ - u8 hq_reserved2[3]; /* 0x29-2b */ - spinlock_t hq_lock; -}; - -extern struct hvlpevent_queue hvlpevent_queue; - -extern int hvlpevent_is_pending(void); -extern void process_hvlpevents(void); -extern void setup_hvlpevent_queue(void); - -#endif /* _ASM_POWERPC_ISERIES_IT_LP_QUEUE_H */ diff --git a/arch/powerpc/include/asm/iseries/lpar_map.h b/arch/powerpc/include/asm/iseries/lpar_map.h deleted file mode 100644 index 5e9f3e1..0000000 --- a/arch/powerpc/include/asm/iseries/lpar_map.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * 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. - * - * 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 - */ -#ifndef _ASM_POWERPC_ISERIES_LPAR_MAP_H -#define _ASM_POWERPC_ISERIES_LPAR_MAP_H - -#ifndef __ASSEMBLY__ - -#include - -#endif - -/* - * The iSeries hypervisor will set up mapping for one or more - * ESID/VSID pairs (in SLB/segment registers) and will set up - * mappings of one or more ranges of pages to VAs. - * We will have the hypervisor set up the ESID->VSID mapping - * for the four kernel segments (C-F). With shared processors, - * the hypervisor will clear all segment registers and reload - * these four whenever the processor is switched from one - * partition to another. - */ - -/* The Vsid and Esid identified below will be used by the hypervisor - * to set up a memory mapping for part of the load area before giving - * control to the Linux kernel. The load area is 64 MB, but this must - * not attempt to map the whole load area. The Hashed Page Table may - * need to be located within the load area (if the total partition size - * is 64 MB), but cannot be mapped. Typically, this should specify - * to map half (32 MB) of the load area. - * - * The hypervisor will set up page table entries for the number of - * pages specified. - * - * In 32-bit mode, the hypervisor will load all four of the - * segment registers (identified by the low-order four bits of the - * Esid field. In 64-bit mode, the hypervisor will load one SLB - * entry to map the Esid to the Vsid. -*/ - -#define HvEsidsToMap 2 -#define HvRangesToMap 1 - -/* Hypervisor initially maps 32MB of the load area */ -#define HvPagesToMap 8192 - -#ifndef __ASSEMBLY__ -struct LparMap { - u64 xNumberEsids; // Number of ESID/VSID pairs - u64 xNumberRanges; // Number of VA ranges to map - u64 xSegmentTableOffs; // Page number within load area of seg table - u64 xRsvd[5]; - struct { - u64 xKernelEsid; // Esid used to map kernel load - u64 xKernelVsid; // Vsid used to map kernel load - } xEsids[HvEsidsToMap]; - struct { - u64 xPages; // Number of pages to be mapped - u64 xOffset; // Offset from start of load area - u64 xVPN; // Virtual Page Number - } xRanges[HvRangesToMap]; -}; - -extern const struct LparMap xLparMap; - -#endif /* __ASSEMBLY__ */ - -/* the fixed address where the LparMap exists */ -#define LPARMAP_PHYS 0x7000 - -#endif /* _ASM_POWERPC_ISERIES_LPAR_MAP_H */ diff --git a/arch/powerpc/include/asm/iseries/mf.h b/arch/powerpc/include/asm/iseries/mf.h deleted file mode 100644 index eb851a9..0000000 --- a/arch/powerpc/include/asm/iseries/mf.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2001 Troy D. Armstrong IBM Corporation - * Copyright (C) 2004 Stephen Rothwell IBM Corporation - * - * This modules exists as an interface between a Linux secondary partition - * running on an iSeries and the primary partition's Virtual Service - * Processor (VSP) object. The VSP has final authority over powering on/off - * all partitions in the iSeries. It also provides miscellaneous low-level - * machine facility type operations. - * - * 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. - * - * 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 - */ -#ifndef _ASM_POWERPC_ISERIES_MF_H -#define _ASM_POWERPC_ISERIES_MF_H - -#include - -#include -#include - -struct rtc_time; - -typedef void (*MFCompleteHandler)(void *clientToken, int returnCode); - -extern void mf_allocate_lp_events(HvLpIndex targetLp, HvLpEvent_Type type, - unsigned size, unsigned amount, MFCompleteHandler hdlr, - void *userToken); -extern void mf_deallocate_lp_events(HvLpIndex targetLp, HvLpEvent_Type type, - unsigned count, MFCompleteHandler hdlr, void *userToken); - -extern void mf_power_off(void); -extern void mf_reboot(char *cmd); - -extern void mf_display_src(u32 word); -extern void mf_display_progress(u16 value); - -extern void mf_init(void); - -#endif /* _ASM_POWERPC_ISERIES_MF_H */ -- cgit v0.10.2 From dfbc2d75c1bd47c3186fa91f1655ea2f3825b0ec Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 20 Mar 2012 14:13:51 +1100 Subject: powerpc/ps3: Do not adjust the wrapper load address Commit c55aef0e5bc6 "powerpc/boot: Change the load address for the wrapper to fit the kernel" adjusted the laod address if the uncompressed kernel was too large. Ps3 does not compress the kernel and uses a different linker script, so do not adjust the load address in that case. fixes this build error: powerpc64-linux-ld: section .text loaded at [0000000000e00000,0000000000e0721b] overlaps section .kernel:dtb loaded at [0000000000e00000,0000000000e0066f] Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index f090e6d..6761c74 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -144,6 +144,7 @@ tmp=$tmpdir/zImage.$$.o ksection=.kernel:vmlinux.strip isection=.kernel:initrd link_address='0x400000' +make_space=y case "$platform" in pseries) @@ -210,6 +211,7 @@ ps3) ksection=.kernel:vmlinux.bin isection=.kernel:initrd link_address='' + make_space=n pie= ;; ep88xc|ep405|ep8248e) @@ -278,17 +280,19 @@ else rm -f $vmz.$$ fi -# Round the size to next higher MB limit -round_size=$(((strip_size + 0xfffff) & 0xfff00000)) +if [ "$make_space" = "y" ]; then + # Round the size to next higher MB limit + round_size=$(((strip_size + 0xfffff) & 0xfff00000)) -round_size=0x$(printf "%x" $round_size) -link_addr=$(printf "%d" $link_address) + round_size=0x$(printf "%x" $round_size) + link_addr=$(printf "%d" $link_address) -if [ $link_addr -lt $strip_size ]; then - echo "INFO: Uncompressed kernel (size 0x$(printf "%x\n" $strip_size))" \ - "overlaps the address of the wrapper($link_address)" - echo "INFO: Fixing the link_address of wrapper to ($round_size)" - link_address=$round_size + if [ $link_addr -lt $strip_size ]; then + echo "INFO: Uncompressed kernel (size 0x$(printf "%x\n" $strip_size))" \ + "overlaps the address of the wrapper($link_address)" + echo "INFO: Fixing the link_address of wrapper to ($round_size)" + link_address=$round_size + fi fi vmz="$vmz$gzip" -- cgit v0.10.2