* [PATCH] [1/5] pasemi: allow 8KB config space for I/O bridge
@ 2007-04-16 6:26 Olof Johansson
2007-04-16 6:27 ` [PATCH] [2/5] pasemi: enable one more hid bit Olof Johansson
` (3 more replies)
0 siblings, 4 replies; 13+ messages in thread
From: Olof Johansson @ 2007-04-16 6:26 UTC (permalink / raw)
To: paulus; +Cc: linuxppc-dev
Device 0 function 0 on the root bus is really a two-function bus agent,
but only the first function is visible. Because of this, we need to
allow config accesses into the second range. Modify the check for valid
offsets accordingly.
Signed-off-by: Olof Johansson <olof@lixom.net>
Index: linux-2.6/arch/powerpc/platforms/pasemi/pci.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pasemi/pci.c
+++ linux-2.6/arch/powerpc/platforms/pasemi/pci.c
@@ -33,7 +33,17 @@
#define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
-#define CONFIG_OFFSET_VALID(off) ((off) < 4096)
+static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset)
+{
+ /* Device 0 Function 0 is special: It's config space spans function 1 as
+ * well, so allow larger offset. It's really a two-function device but the
+ * second function does not probe.
+ */
+ if (bus == 0 && devfn == 0)
+ return offset < 8192;
+ else
+ return offset < 4096;
+}
static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose,
u8 bus, u8 devfn, int offset)
@@ -51,7 +61,7 @@ static int pa_pxp_read_config(struct pci
if (!hose)
return PCIBIOS_DEVICE_NOT_FOUND;
- if (!CONFIG_OFFSET_VALID(offset))
+ if (!pa_pxp_offset_valid(bus->number, devfn, offset))
return PCIBIOS_BAD_REGISTER_NUMBER;
addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
@@ -85,7 +95,7 @@ static int pa_pxp_write_config(struct pc
if (!hose)
return PCIBIOS_DEVICE_NOT_FOUND;
- if (!CONFIG_OFFSET_VALID(offset))
+ if (!pa_pxp_offset_valid(bus->number, devfn, offset))
return PCIBIOS_BAD_REGISTER_NUMBER;
addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH] [2/5] pasemi: enable one more hid bit 2007-04-16 6:26 [PATCH] [1/5] pasemi: allow 8KB config space for I/O bridge Olof Johansson @ 2007-04-16 6:27 ` Olof Johansson 2007-04-24 0:27 ` Benjamin Herrenschmidt 2007-04-16 6:28 ` [PATCH] [3/5] pasemi: reset mpic on boot Olof Johansson ` (2 subsequent siblings) 3 siblings, 1 reply; 13+ messages in thread From: Olof Johansson @ 2007-04-16 6:27 UTC (permalink / raw) To: paulus; +Cc: linuxppc-dev Minor HID change. Firmware can't know that we want this set so we have to set it in the kernel. Signed-off-by: Olof Johansson <olof@lixom.net> Index: linux-2.6/arch/powerpc/kernel/cpu_setup_pa6t.S =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/cpu_setup_pa6t.S +++ linux-2.6/arch/powerpc/kernel/cpu_setup_pa6t.S @@ -34,7 +34,7 @@ _GLOBAL(__setup_cpu_pa6t) beqlr mfspr r0,SPRN_HID5 - ori r0,r0,0x30 + ori r0,r0,0x38 mtspr SPRN_HID5,r0 mfspr r0,SPRN_LPCR ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] [2/5] pasemi: enable one more hid bit 2007-04-16 6:27 ` [PATCH] [2/5] pasemi: enable one more hid bit Olof Johansson @ 2007-04-24 0:27 ` Benjamin Herrenschmidt 2007-04-24 1:04 ` Olof Johansson 0 siblings, 1 reply; 13+ messages in thread From: Benjamin Herrenschmidt @ 2007-04-24 0:27 UTC (permalink / raw) To: Olof Johansson; +Cc: linuxppc-dev, paulus On Mon, 2007-04-16 at 01:27 -0500, Olof Johansson wrote: > Minor HID change. Firmware can't know that we want this set so we have > to set it in the kernel. Care to document/name these bits ? > > Signed-off-by: Olof Johansson <olof@lixom.net> > > Index: linux-2.6/arch/powerpc/kernel/cpu_setup_pa6t.S > =================================================================== > --- linux-2.6.orig/arch/powerpc/kernel/cpu_setup_pa6t.S > +++ linux-2.6/arch/powerpc/kernel/cpu_setup_pa6t.S > @@ -34,7 +34,7 @@ _GLOBAL(__setup_cpu_pa6t) > beqlr > > mfspr r0,SPRN_HID5 > - ori r0,r0,0x30 > + ori r0,r0,0x38 > mtspr SPRN_HID5,r0 > > mfspr r0,SPRN_LPCR > _______________________________________________ > Linuxppc-dev mailing list > Linuxppc-dev@ozlabs.org > https://ozlabs.org/mailman/listinfo/linuxppc-dev ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] [2/5] pasemi: enable one more hid bit 2007-04-24 0:27 ` Benjamin Herrenschmidt @ 2007-04-24 1:04 ` Olof Johansson 0 siblings, 0 replies; 13+ messages in thread From: Olof Johansson @ 2007-04-24 1:04 UTC (permalink / raw) To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, paulus On Tue, Apr 24, 2007 at 10:27:20AM +1000, Benjamin Herrenschmidt wrote: > On Mon, 2007-04-16 at 01:27 -0500, Olof Johansson wrote: > > Minor HID change. Firmware can't know that we want this set so we have > > to set it in the kernel. > > Care to document/name these bits ? Not at this time, sorry. -Olof ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] [3/5] pasemi: reset mpic on boot 2007-04-16 6:26 [PATCH] [1/5] pasemi: allow 8KB config space for I/O bridge Olof Johansson 2007-04-16 6:27 ` [PATCH] [2/5] pasemi: enable one more hid bit Olof Johansson @ 2007-04-16 6:28 ` Olof Johansson 2007-04-16 6:30 ` [PATCH] [4/5] pasemi: PA6T oprofile support Olof Johansson 2007-04-16 6:31 ` [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver Olof Johansson 3 siblings, 0 replies; 13+ messages in thread From: Olof Johansson @ 2007-04-16 6:28 UTC (permalink / raw) To: paulus; +Cc: linuxppc-dev Reset MPIC on boot to clear some timer state that firmware might leave configured. Signed-off-by: Olof Johansson <olof@lixom.net> Index: linux-2.6/arch/powerpc/platforms/pasemi/setup.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/pasemi/setup.c +++ linux-2.6/arch/powerpc/platforms/pasemi/setup.c @@ -147,7 +147,7 @@ static __init void pas_init_IRQ(void) printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); mpic = mpic_alloc(mpic_node, openpic_addr, - MPIC_PRIMARY|MPIC_LARGE_VECTORS, + MPIC_PRIMARY|MPIC_LARGE_VECTORS|MPIC_WANTS_RESET, 0, 0, " PAS-OPIC "); BUG_ON(!mpic); ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] [4/5] pasemi: PA6T oprofile support 2007-04-16 6:26 [PATCH] [1/5] pasemi: allow 8KB config space for I/O bridge Olof Johansson 2007-04-16 6:27 ` [PATCH] [2/5] pasemi: enable one more hid bit Olof Johansson 2007-04-16 6:28 ` [PATCH] [3/5] pasemi: reset mpic on boot Olof Johansson @ 2007-04-16 6:30 ` Olof Johansson 2007-04-18 6:38 ` [PATCH] [4/5] [v2] " Olof Johansson 2007-04-16 6:31 ` [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver Olof Johansson 3 siblings, 1 reply; 13+ messages in thread From: Olof Johansson @ 2007-04-16 6:30 UTC (permalink / raw) To: paulus; +Cc: linuxppc-dev, anton Oprofile support for PA6T, kernel side. Also rename the PA6T_SPRN.* defines to SPRN_PA6T.*. Signed-off-by: Olof Johansson <olof@lixom.net> Index: linux-2.6/arch/powerpc/kernel/cputable.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/cputable.c +++ linux-2.6/arch/powerpc/kernel/cputable.c @@ -373,6 +373,8 @@ static struct cpu_spec cpu_specs[] = { .pmc_type = PPC_PMC_PA6T, .cpu_setup = __setup_cpu_pa6t, .cpu_restore = __restore_cpu_pa6t, + .oprofile_cpu_type = "ppc64/pa6t", + .oprofile_type = PPC_OPROFILE_PA6T, .platform = "pa6t", }, { /* default match */ Index: linux-2.6/arch/powerpc/kernel/sysfs.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/sysfs.c +++ linux-2.6/arch/powerpc/kernel/sysfs.c @@ -189,12 +189,12 @@ SYSFS_PMCSETUP(purr, SPRN_PURR); SYSFS_PMCSETUP(spurr, SPRN_SPURR); SYSFS_PMCSETUP(dscr, SPRN_DSCR); -SYSFS_PMCSETUP(pa6t_pmc0, PA6T_SPRN_PMC0); -SYSFS_PMCSETUP(pa6t_pmc1, PA6T_SPRN_PMC1); -SYSFS_PMCSETUP(pa6t_pmc2, PA6T_SPRN_PMC2); -SYSFS_PMCSETUP(pa6t_pmc3, PA6T_SPRN_PMC3); -SYSFS_PMCSETUP(pa6t_pmc4, PA6T_SPRN_PMC4); -SYSFS_PMCSETUP(pa6t_pmc5, PA6T_SPRN_PMC5); +SYSFS_PMCSETUP(pa6t_pmc0, SPRN_PA6T_PMC0); +SYSFS_PMCSETUP(pa6t_pmc1, SPRN_PA6T_PMC1); +SYSFS_PMCSETUP(pa6t_pmc2, SPRN_PA6T_PMC2); +SYSFS_PMCSETUP(pa6t_pmc3, SPRN_PA6T_PMC3); +SYSFS_PMCSETUP(pa6t_pmc4, SPRN_PA6T_PMC4); +SYSFS_PMCSETUP(pa6t_pmc5, SPRN_PA6T_PMC5); static SYSDEV_ATTR(mmcra, 0600, show_mmcra, store_mmcra); Index: linux-2.6/arch/powerpc/oprofile/op_model_pa6t.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/oprofile/op_model_pa6t.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Shashi Rao, PA Semi + * + * Maintained by: Olof Johansson <olof@lixom.net> + * + * Based on arch/powerpc/oprofile/op_model_power4.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/oprofile.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/percpu.h> +#include <asm/processor.h> +#include <asm/cputable.h> +#include <asm/oprofile_impl.h> +#include <asm/reg.h> + +static unsigned char oprofile_running; + +/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */ +static u64 mmcr0_val; +static u64 mmcr1_val; + +/* inited in pa6t_reg_setup */ +static u64 reset_value[OP_MAX_COUNTER]; + +static inline u64 ctr_read(unsigned int i) +{ + switch (i) { + case 0: + return mfspr(SPRN_PA6T_PMC0); + case 1: + return mfspr(SPRN_PA6T_PMC1); + case 2: + return mfspr(SPRN_PA6T_PMC2); + case 3: + return mfspr(SPRN_PA6T_PMC3); + case 4: + return mfspr(SPRN_PA6T_PMC4); + case 5: + return mfspr(SPRN_PA6T_PMC5); + default: + printk(KERN_ERR "ctr_read called with bad arg %u\n", i); + return 0; + } +} + +static inline void ctr_write(unsigned int i, u64 val) +{ + switch (i) { + case 0: + mtspr(SPRN_PA6T_PMC0, val); + break; + case 1: + mtspr(SPRN_PA6T_PMC1, val); + break; + case 2: + mtspr(SPRN_PA6T_PMC2, val); + break; + case 3: + mtspr(SPRN_PA6T_PMC3, val); + break; + case 4: + mtspr(SPRN_PA6T_PMC4, val); + break; + case 5: + mtspr(SPRN_PA6T_PMC5, val); + break; + default: + printk(KERN_ERR "ctr_write called with bad arg %u\n", i); + break; + } +} + + +/* precompute the values to stuff in the hardware registers */ +static void pa6t_reg_setup(struct op_counter_config *ctr, + struct op_system_config *sys, + int num_ctrs) +{ + int pmc; + + /* + * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the + * event_mappings file by turning off the counters that the user doesn't + * care about + * + * setup user and kernel profiling + */ + for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) + if (!ctr[pmc].enabled) { + sys->mmcr0 &= ~(0x1UL << pmc); + sys->mmcr0 &= ~(0x1UL << (pmc+12)); + pr_debug("turned off counter %u\n", pmc); + } + + if (sys->enable_kernel) + sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN; + else + sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN); + + if (sys->enable_user) + sys->mmcr0 |= PA6T_MMCR0_PREN; + else + sys->mmcr0 &= ~PA6T_MMCR0_PREN; + + /* + * The performance counter event settings are given in the mmcr0 and + * mmcr1 values passed from the user in the op_system_config + * structure (sys variable). + */ + mmcr0_val = sys->mmcr0; + mmcr1_val = sys->mmcr1; + pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0); + pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1); + + for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) { + /* counters are 40 bit. Move to cputable at some point? */ + reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count; + pr_debug("reset_value for pmc%u inited to 0x%lx\n", + pmc, reset_value[pmc]); + } +} + +/* configure registers on this cpu */ +static void pa6t_cpu_setup(struct op_counter_config *ctr) +{ + u64 mmcr0 = mmcr0_val; + u64 mmcr1 = mmcr1_val; + + /* Default is all PMCs off */ + mmcr0 &= ~(0x3FUL); + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + /* program selected programmable events in */ + mtspr(SPRN_PA6T_MMCR1, mmcr1); + + pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(), + mfspr(SPRN_PA6T_MMCR0)); + pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(), + mfspr(SPRN_PA6T_MMCR1)); +} + +static void pa6t_start(struct op_counter_config *ctr) +{ + int i; + + /* Hold off event counting until rfid */ + u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; + + for (i = 0; i < cur_cpu_spec->num_pmcs; i++) + if (ctr[i].enabled) + ctr_write(i, reset_value[i]); + else + ctr_write(i, 0UL); + + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + oprofile_running = 1; + + pr_debug("start on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0); +} + +static void pa6t_stop(void) +{ + u64 mmcr0; + + /* freeze counters */ + mmcr0 = mfspr(SPRN_PA6T_MMCR0); + mmcr0 |= PA6T_MMCR0_FCM0; + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + oprofile_running = 0; + + pr_debug("stop on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0); +} + +/* handle the perfmon overflow vector */ +static void pa6t_handle_interrupt(struct pt_regs *regs, + struct op_counter_config *ctr) +{ + unsigned long pc = mfspr(SPRN_PA6T_SIAR); + int is_kernel = is_kernel_addr(pc); + u64 val; + int i; + u64 mmcr0; + + /* disable perfmon counting until rfid */ + mmcr0 = mfspr(SPRN_PA6T_MMCR0); + mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS); + + /* Record samples. We've got one global bit for whether a sample + * was taken, so add it for any counter that triggered overflow. + */ + for (i = 0; i < cur_cpu_spec->num_pmcs; i++) { + val = ctr_read(i); + if (val & (0x1UL << 39)) { /* Overflow bit set */ + if (oprofile_running && ctr[i].enabled) { + if (mmcr0 & PA6T_MMCR0_SIARLOG) + oprofile_add_ext_sample(pc, regs, i, is_kernel); + ctr_write(i, reset_value[i]); + } else { + ctr_write(i, 0UL); + } + } + } + + /* Restore mmcr0 to a good known value since the PMI changes it */ + mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; + mtspr(SPRN_PA6T_MMCR0, mmcr0); +} + +struct op_powerpc_model op_model_pa6t = { + .reg_setup = pa6t_reg_setup, + .cpu_setup = pa6t_cpu_setup, + .start = pa6t_start, + .stop = pa6t_stop, + .handle_interrupt = pa6t_handle_interrupt, +}; Index: linux-2.6/arch/powerpc/platforms/pasemi/setup.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/pasemi/setup.c +++ linux-2.6/arch/powerpc/platforms/pasemi/setup.c @@ -28,6 +28,7 @@ #include <linux/console.h> #include <linux/pci.h> +#include <asm/pmc.h> #include <asm/prom.h> #include <asm/system.h> #include <asm/iommu.h> Index: linux-2.6/include/asm-powerpc/cputable.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/cputable.h +++ linux-2.6/include/asm-powerpc/cputable.h @@ -48,6 +48,7 @@ enum powerpc_oprofile_type { PPC_OPROFILE_G4 = 3, PPC_OPROFILE_BOOKE = 4, PPC_OPROFILE_CELL = 5, + PPC_OPROFILE_PA6T = 6, }; enum powerpc_pmc_type { Index: linux-2.6/include/asm-powerpc/reg.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/reg.h +++ linux-2.6/include/asm-powerpc/reg.h @@ -469,12 +469,77 @@ #define SPRN_SIAR 780 #define SPRN_SDAR 781 -#define PA6T_SPRN_PMC0 787 -#define PA6T_SPRN_PMC1 788 -#define PA6T_SPRN_PMC2 789 -#define PA6T_SPRN_PMC3 790 -#define PA6T_SPRN_PMC4 791 -#define PA6T_SPRN_PMC5 792 +#define SPRN_PA6T_PMC0 787 +#define SPRN_PA6T_PMC1 788 +#define SPRN_PA6T_PMC2 789 +#define SPRN_PA6T_PMC3 790 +#define SPRN_PA6T_PMC4 791 +#define SPRN_PA6T_PMC5 792 + +#define PA6T_BITPOS(x) (0x1UL << (63 - x)) + +#define SPRN_PA6T_MMCR0 795 +#define PA6T_MMCR0_EN0 0x0000000000000001UL +#define PA6T_MMCR0_EN1 0x0000000000000002UL +#define PA6T_MMCR0_EN2 0x0000000000000004UL +#define PA6T_MMCR0_EN3 0x0000000000000008UL +#define PA6T_MMCR0_EN4 0x0000000000000010UL +#define PA6T_MMCR0_EN5 0x0000000000000020UL +#define PA6T_MMCR0_SUPEN 0x0000000000000040UL +#define PA6T_MMCR0_PREN 0x0000000000000080UL +#define PA6T_MMCR0_HYPEN 0x0000000000000100UL +#define PA6T_MMCR0_FCM0 0x0000000000000200UL +#define PA6T_MMCR0_FCM1 0x0000000000000400UL +#define PA6T_MMCR0_INTGEN 0x0000000000000800UL +#define PA6T_MMCR0_INTEN0 0x0000000000001000UL +#define PA6T_MMCR0_INTEN1 0x0000000000002000UL +#define PA6T_MMCR0_INTEN2 0x0000000000004000UL +#define PA6T_MMCR0_INTEN3 0x0000000000008000UL +#define PA6T_MMCR0_INTEN4 0x0000000000010000UL +#define PA6T_MMCR0_INTEN5 0x0000000000020000UL +#define PA6T_MMCR0_DISCNT 0x0000000000040000UL +#define PA6T_MMCR0_UOP 0x0000000000080000UL +#define PA6T_MMCR0_TRG 0x0000000000100000UL +#define PA6T_MMCR0_TRGEN 0x0000000000200000UL +#define PA6T_MMCR0_TRGREG 0x0000000001600000UL +#define PA6T_MMCR0_SIARLOG 0x0000000002000000UL +#define PA6T_MMCR0_SDARLOG 0x0000000004000000UL +#define PA6T_MMCR0_PROEN 0x0000000008000000UL +#define PA6T_MMCR0_PROLOG 0x0000000010000000UL +#define PA6T_MMCR0_DAMEN2 0x0000000020000000UL +#define PA6T_MMCR0_DAMEN3 0x0000000040000000UL +#define PA6T_MMCR0_DAMEN4 0x0000000080000000UL +#define PA6T_MMCR0_DAMEN5 0x0000000100000000UL +#define PA6T_MMCR0_DAMSEL2 0x0000000200000000UL +#define PA6T_MMCR0_DAMSEL3 0x0000000400000000UL +#define PA6T_MMCR0_DAMSEL4 0x0000000800000000UL +#define PA6T_MMCR0_DAMSEL5 0x0000001000000000UL +#define PA6T_MMCR0_HANDDIS 0x0000002000000000UL +#define PA6T_MMCR0_PCTEN 0x0000004000000000UL +#define PA6T_MMCR0_SOCEN 0x0000008000000000UL +#define PA6T_MMCR0_SOCMOD 0x0000010000000000UL + +#define SPRN_PA6T_MMCR1 798 +#define PA6T_MMCR1_ES2 0x00000000000000ffUL +#define PA6T_MMCR1_ES3 0x000000000000ff00UL +#define PA6T_MMCR1_ES4 0x0000000000ff0000UL +#define PA6T_MMCR1_ES5 0x00000000ff000000UL + +#define SPRN_PA6T_SIAR 780 +#define SPRN_PA6T_UPMC0 771 +#define SPRN_PA6T_UPMC1 772 +#define SPRN_PA6T_UPMC2 773 +#define SPRN_PA6T_UPMC3 774 +#define SPRN_PA6T_UPMC4 775 +#define SPRN_PA6T_UPMC5 776 +#define SPRN_PA6T_UMMCR0 779 +#define SPRN_PA6T_UMMCR1 782 +#define SPRN_PA6T_PMC0 787 +#define SPRN_PA6T_PMC1 788 +#define SPRN_PA6T_PMC2 789 +#define SPRN_PA6T_PMC3 790 +#define SPRN_PA6T_PMC4 791 +#define SPRN_PA6T_PMC5 792 #else /* 32-bit */ #define SPRN_MMCR0 952 /* Monitor Mode Control Register 0 */ Index: linux-2.6/include/asm-powerpc/pmc.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/pmc.h +++ linux-2.6/include/asm-powerpc/pmc.h @@ -30,6 +30,7 @@ void release_pmc_hardware(void); #ifdef CONFIG_PPC64 void power4_enable_pmcs(void); +void pasemi_enable_pmcs(void); #endif #endif /* __KERNEL__ */ Index: linux-2.6/arch/powerpc/oprofile/common.c =================================================================== --- linux-2.6.orig/arch/powerpc/oprofile/common.c +++ linux-2.6/arch/powerpc/oprofile/common.c @@ -160,6 +160,9 @@ int __init oprofile_arch_init(struct opr case PPC_OPROFILE_POWER4: model = &op_model_power4; break; + case PPC_OPROFILE_PA6T: + model = &op_model_pa6t; + break; #endif #ifdef CONFIG_6xx case PPC_OPROFILE_G4: Index: linux-2.6/arch/powerpc/oprofile/Makefile =================================================================== --- linux-2.6.orig/arch/powerpc/oprofile/Makefile +++ linux-2.6/arch/powerpc/oprofile/Makefile @@ -12,6 +12,6 @@ DRIVER_OBJS := $(addprefix ../../../driv oprofile-y := $(DRIVER_OBJS) common.o backtrace.o oprofile-$(CONFIG_PPC_CELL_NATIVE) += op_model_cell.o -oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o +oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o oprofile-$(CONFIG_6xx) += op_model_7450.o Index: linux-2.6/include/asm-powerpc/oprofile_impl.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/oprofile_impl.h +++ linux-2.6/include/asm-powerpc/oprofile_impl.h @@ -57,6 +57,8 @@ extern struct op_powerpc_model op_model_ extern struct op_powerpc_model op_model_power4; extern struct op_powerpc_model op_model_7450; extern struct op_powerpc_model op_model_cell; +extern struct op_powerpc_model op_model_pa6t; + /* All the classic PPC parts use these */ static inline unsigned int classic_ctr_read(unsigned int i) ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] [4/5] [v2] pasemi: PA6T oprofile support 2007-04-16 6:30 ` [PATCH] [4/5] pasemi: PA6T oprofile support Olof Johansson @ 2007-04-18 6:38 ` Olof Johansson 0 siblings, 0 replies; 13+ messages in thread From: Olof Johansson @ 2007-04-18 6:38 UTC (permalink / raw) To: paulus; +Cc: linuxppc-dev, anton Oprofile support for PA6T, kernel side. Also rename the PA6T_SPRN.* defines to SPRN_PA6T.*. Signed-off-by: Olof Johansson <olof@lixom.net> -- Changed: Fixed some redundant defines and unneeded include file. Index: powerpc/arch/powerpc/kernel/cputable.c =================================================================== --- powerpc.orig/arch/powerpc/kernel/cputable.c +++ powerpc/arch/powerpc/kernel/cputable.c @@ -389,6 +389,8 @@ static struct cpu_spec cpu_specs[] = { .pmc_type = PPC_PMC_PA6T, .cpu_setup = __setup_cpu_pa6t, .cpu_restore = __restore_cpu_pa6t, + .oprofile_cpu_type = "ppc64/pa6t", + .oprofile_type = PPC_OPROFILE_PA6T, .platform = "pa6t", }, { /* default match */ Index: powerpc/arch/powerpc/kernel/sysfs.c =================================================================== --- powerpc.orig/arch/powerpc/kernel/sysfs.c +++ powerpc/arch/powerpc/kernel/sysfs.c @@ -189,12 +189,12 @@ SYSFS_PMCSETUP(purr, SPRN_PURR); SYSFS_PMCSETUP(spurr, SPRN_SPURR); SYSFS_PMCSETUP(dscr, SPRN_DSCR); -SYSFS_PMCSETUP(pa6t_pmc0, PA6T_SPRN_PMC0); -SYSFS_PMCSETUP(pa6t_pmc1, PA6T_SPRN_PMC1); -SYSFS_PMCSETUP(pa6t_pmc2, PA6T_SPRN_PMC2); -SYSFS_PMCSETUP(pa6t_pmc3, PA6T_SPRN_PMC3); -SYSFS_PMCSETUP(pa6t_pmc4, PA6T_SPRN_PMC4); -SYSFS_PMCSETUP(pa6t_pmc5, PA6T_SPRN_PMC5); +SYSFS_PMCSETUP(pa6t_pmc0, SPRN_PA6T_PMC0); +SYSFS_PMCSETUP(pa6t_pmc1, SPRN_PA6T_PMC1); +SYSFS_PMCSETUP(pa6t_pmc2, SPRN_PA6T_PMC2); +SYSFS_PMCSETUP(pa6t_pmc3, SPRN_PA6T_PMC3); +SYSFS_PMCSETUP(pa6t_pmc4, SPRN_PA6T_PMC4); +SYSFS_PMCSETUP(pa6t_pmc5, SPRN_PA6T_PMC5); static SYSDEV_ATTR(mmcra, 0600, show_mmcra, store_mmcra); Index: powerpc/arch/powerpc/oprofile/op_model_pa6t.c =================================================================== --- /dev/null +++ powerpc/arch/powerpc/oprofile/op_model_pa6t.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Shashi Rao, PA Semi + * + * Maintained by: Olof Johansson <olof@lixom.net> + * + * Based on arch/powerpc/oprofile/op_model_power4.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/oprofile.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/percpu.h> +#include <asm/processor.h> +#include <asm/cputable.h> +#include <asm/oprofile_impl.h> +#include <asm/reg.h> + +static unsigned char oprofile_running; + +/* mmcr values are set in pa6t_reg_setup, used in pa6t_cpu_setup */ +static u64 mmcr0_val; +static u64 mmcr1_val; + +/* inited in pa6t_reg_setup */ +static u64 reset_value[OP_MAX_COUNTER]; + +static inline u64 ctr_read(unsigned int i) +{ + switch (i) { + case 0: + return mfspr(SPRN_PA6T_PMC0); + case 1: + return mfspr(SPRN_PA6T_PMC1); + case 2: + return mfspr(SPRN_PA6T_PMC2); + case 3: + return mfspr(SPRN_PA6T_PMC3); + case 4: + return mfspr(SPRN_PA6T_PMC4); + case 5: + return mfspr(SPRN_PA6T_PMC5); + default: + printk(KERN_ERR "ctr_read called with bad arg %u\n", i); + return 0; + } +} + +static inline void ctr_write(unsigned int i, u64 val) +{ + switch (i) { + case 0: + mtspr(SPRN_PA6T_PMC0, val); + break; + case 1: + mtspr(SPRN_PA6T_PMC1, val); + break; + case 2: + mtspr(SPRN_PA6T_PMC2, val); + break; + case 3: + mtspr(SPRN_PA6T_PMC3, val); + break; + case 4: + mtspr(SPRN_PA6T_PMC4, val); + break; + case 5: + mtspr(SPRN_PA6T_PMC5, val); + break; + default: + printk(KERN_ERR "ctr_write called with bad arg %u\n", i); + break; + } +} + + +/* precompute the values to stuff in the hardware registers */ +static void pa6t_reg_setup(struct op_counter_config *ctr, + struct op_system_config *sys, + int num_ctrs) +{ + int pmc; + + /* + * adjust the mmcr0.en[0-5] and mmcr0.inten[0-5] values obtained from the + * event_mappings file by turning off the counters that the user doesn't + * care about + * + * setup user and kernel profiling + */ + for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) + if (!ctr[pmc].enabled) { + sys->mmcr0 &= ~(0x1UL << pmc); + sys->mmcr0 &= ~(0x1UL << (pmc+12)); + pr_debug("turned off counter %u\n", pmc); + } + + if (sys->enable_kernel) + sys->mmcr0 |= PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN; + else + sys->mmcr0 &= ~(PA6T_MMCR0_SUPEN | PA6T_MMCR0_HYPEN); + + if (sys->enable_user) + sys->mmcr0 |= PA6T_MMCR0_PREN; + else + sys->mmcr0 &= ~PA6T_MMCR0_PREN; + + /* + * The performance counter event settings are given in the mmcr0 and + * mmcr1 values passed from the user in the op_system_config + * structure (sys variable). + */ + mmcr0_val = sys->mmcr0; + mmcr1_val = sys->mmcr1; + pr_debug("mmcr0_val inited to %016lx\n", sys->mmcr0); + pr_debug("mmcr1_val inited to %016lx\n", sys->mmcr1); + + for (pmc = 0; pmc < cur_cpu_spec->num_pmcs; pmc++) { + /* counters are 40 bit. Move to cputable at some point? */ + reset_value[pmc] = (0x1UL << 39) - ctr[pmc].count; + pr_debug("reset_value for pmc%u inited to 0x%lx\n", + pmc, reset_value[pmc]); + } +} + +/* configure registers on this cpu */ +static void pa6t_cpu_setup(struct op_counter_config *ctr) +{ + u64 mmcr0 = mmcr0_val; + u64 mmcr1 = mmcr1_val; + + /* Default is all PMCs off */ + mmcr0 &= ~(0x3FUL); + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + /* program selected programmable events in */ + mtspr(SPRN_PA6T_MMCR1, mmcr1); + + pr_debug("setup on cpu %d, mmcr0 %016lx\n", smp_processor_id(), + mfspr(SPRN_PA6T_MMCR0)); + pr_debug("setup on cpu %d, mmcr1 %016lx\n", smp_processor_id(), + mfspr(SPRN_PA6T_MMCR1)); +} + +static void pa6t_start(struct op_counter_config *ctr) +{ + int i; + + /* Hold off event counting until rfid */ + u64 mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; + + for (i = 0; i < cur_cpu_spec->num_pmcs; i++) + if (ctr[i].enabled) + ctr_write(i, reset_value[i]); + else + ctr_write(i, 0UL); + + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + oprofile_running = 1; + + pr_debug("start on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0); +} + +static void pa6t_stop(void) +{ + u64 mmcr0; + + /* freeze counters */ + mmcr0 = mfspr(SPRN_PA6T_MMCR0); + mmcr0 |= PA6T_MMCR0_FCM0; + mtspr(SPRN_PA6T_MMCR0, mmcr0); + + oprofile_running = 0; + + pr_debug("stop on cpu %d, mmcr0 %lx\n", smp_processor_id(), mmcr0); +} + +/* handle the perfmon overflow vector */ +static void pa6t_handle_interrupt(struct pt_regs *regs, + struct op_counter_config *ctr) +{ + unsigned long pc = mfspr(SPRN_PA6T_SIAR); + int is_kernel = is_kernel_addr(pc); + u64 val; + int i; + u64 mmcr0; + + /* disable perfmon counting until rfid */ + mmcr0 = mfspr(SPRN_PA6T_MMCR0); + mtspr(SPRN_PA6T_MMCR0, mmcr0 | PA6T_MMCR0_HANDDIS); + + /* Record samples. We've got one global bit for whether a sample + * was taken, so add it for any counter that triggered overflow. + */ + for (i = 0; i < cur_cpu_spec->num_pmcs; i++) { + val = ctr_read(i); + if (val & (0x1UL << 39)) { /* Overflow bit set */ + if (oprofile_running && ctr[i].enabled) { + if (mmcr0 & PA6T_MMCR0_SIARLOG) + oprofile_add_ext_sample(pc, regs, i, is_kernel); + ctr_write(i, reset_value[i]); + } else { + ctr_write(i, 0UL); + } + } + } + + /* Restore mmcr0 to a good known value since the PMI changes it */ + mmcr0 = mmcr0_val | PA6T_MMCR0_HANDDIS; + mtspr(SPRN_PA6T_MMCR0, mmcr0); +} + +struct op_powerpc_model op_model_pa6t = { + .reg_setup = pa6t_reg_setup, + .cpu_setup = pa6t_cpu_setup, + .start = pa6t_start, + .stop = pa6t_stop, + .handle_interrupt = pa6t_handle_interrupt, +}; Index: powerpc/include/asm-powerpc/cputable.h =================================================================== --- powerpc.orig/include/asm-powerpc/cputable.h +++ powerpc/include/asm-powerpc/cputable.h @@ -48,6 +48,7 @@ enum powerpc_oprofile_type { PPC_OPROFILE_G4 = 3, PPC_OPROFILE_BOOKE = 4, PPC_OPROFILE_CELL = 5, + PPC_OPROFILE_PA6T = 6, }; enum powerpc_pmc_type { Index: powerpc/include/asm-powerpc/reg.h =================================================================== --- powerpc.orig/include/asm-powerpc/reg.h +++ powerpc/include/asm-powerpc/reg.h @@ -469,12 +469,68 @@ #define SPRN_SIAR 780 #define SPRN_SDAR 781 -#define PA6T_SPRN_PMC0 787 -#define PA6T_SPRN_PMC1 788 -#define PA6T_SPRN_PMC2 789 -#define PA6T_SPRN_PMC3 790 -#define PA6T_SPRN_PMC4 791 -#define PA6T_SPRN_PMC5 792 +#define SPRN_PA6T_MMCR0 795 +#define PA6T_MMCR0_EN0 0x0000000000000001UL +#define PA6T_MMCR0_EN1 0x0000000000000002UL +#define PA6T_MMCR0_EN2 0x0000000000000004UL +#define PA6T_MMCR0_EN3 0x0000000000000008UL +#define PA6T_MMCR0_EN4 0x0000000000000010UL +#define PA6T_MMCR0_EN5 0x0000000000000020UL +#define PA6T_MMCR0_SUPEN 0x0000000000000040UL +#define PA6T_MMCR0_PREN 0x0000000000000080UL +#define PA6T_MMCR0_HYPEN 0x0000000000000100UL +#define PA6T_MMCR0_FCM0 0x0000000000000200UL +#define PA6T_MMCR0_FCM1 0x0000000000000400UL +#define PA6T_MMCR0_INTGEN 0x0000000000000800UL +#define PA6T_MMCR0_INTEN0 0x0000000000001000UL +#define PA6T_MMCR0_INTEN1 0x0000000000002000UL +#define PA6T_MMCR0_INTEN2 0x0000000000004000UL +#define PA6T_MMCR0_INTEN3 0x0000000000008000UL +#define PA6T_MMCR0_INTEN4 0x0000000000010000UL +#define PA6T_MMCR0_INTEN5 0x0000000000020000UL +#define PA6T_MMCR0_DISCNT 0x0000000000040000UL +#define PA6T_MMCR0_UOP 0x0000000000080000UL +#define PA6T_MMCR0_TRG 0x0000000000100000UL +#define PA6T_MMCR0_TRGEN 0x0000000000200000UL +#define PA6T_MMCR0_TRGREG 0x0000000001600000UL +#define PA6T_MMCR0_SIARLOG 0x0000000002000000UL +#define PA6T_MMCR0_SDARLOG 0x0000000004000000UL +#define PA6T_MMCR0_PROEN 0x0000000008000000UL +#define PA6T_MMCR0_PROLOG 0x0000000010000000UL +#define PA6T_MMCR0_DAMEN2 0x0000000020000000UL +#define PA6T_MMCR0_DAMEN3 0x0000000040000000UL +#define PA6T_MMCR0_DAMEN4 0x0000000080000000UL +#define PA6T_MMCR0_DAMEN5 0x0000000100000000UL +#define PA6T_MMCR0_DAMSEL2 0x0000000200000000UL +#define PA6T_MMCR0_DAMSEL3 0x0000000400000000UL +#define PA6T_MMCR0_DAMSEL4 0x0000000800000000UL +#define PA6T_MMCR0_DAMSEL5 0x0000001000000000UL +#define PA6T_MMCR0_HANDDIS 0x0000002000000000UL +#define PA6T_MMCR0_PCTEN 0x0000004000000000UL +#define PA6T_MMCR0_SOCEN 0x0000008000000000UL +#define PA6T_MMCR0_SOCMOD 0x0000010000000000UL + +#define SPRN_PA6T_MMCR1 798 +#define PA6T_MMCR1_ES2 0x00000000000000ffUL +#define PA6T_MMCR1_ES3 0x000000000000ff00UL +#define PA6T_MMCR1_ES4 0x0000000000ff0000UL +#define PA6T_MMCR1_ES5 0x00000000ff000000UL + +#define SPRN_PA6T_SIAR 780 +#define SPRN_PA6T_UPMC0 771 +#define SPRN_PA6T_UPMC1 772 +#define SPRN_PA6T_UPMC2 773 +#define SPRN_PA6T_UPMC3 774 +#define SPRN_PA6T_UPMC4 775 +#define SPRN_PA6T_UPMC5 776 +#define SPRN_PA6T_UMMCR0 779 +#define SPRN_PA6T_UMMCR1 782 +#define SPRN_PA6T_PMC0 787 +#define SPRN_PA6T_PMC1 788 +#define SPRN_PA6T_PMC2 789 +#define SPRN_PA6T_PMC3 790 +#define SPRN_PA6T_PMC4 791 +#define SPRN_PA6T_PMC5 792 #else /* 32-bit */ #define SPRN_MMCR0 952 /* Monitor Mode Control Register 0 */ Index: powerpc/include/asm-powerpc/pmc.h =================================================================== --- powerpc.orig/include/asm-powerpc/pmc.h +++ powerpc/include/asm-powerpc/pmc.h @@ -30,6 +30,7 @@ void release_pmc_hardware(void); #ifdef CONFIG_PPC64 void power4_enable_pmcs(void); +void pasemi_enable_pmcs(void); #endif #endif /* __KERNEL__ */ Index: powerpc/arch/powerpc/oprofile/common.c =================================================================== --- powerpc.orig/arch/powerpc/oprofile/common.c +++ powerpc/arch/powerpc/oprofile/common.c @@ -160,6 +160,9 @@ int __init oprofile_arch_init(struct opr case PPC_OPROFILE_POWER4: model = &op_model_power4; break; + case PPC_OPROFILE_PA6T: + model = &op_model_pa6t; + break; #endif #ifdef CONFIG_6xx case PPC_OPROFILE_G4: Index: powerpc/arch/powerpc/oprofile/Makefile =================================================================== --- powerpc.orig/arch/powerpc/oprofile/Makefile +++ powerpc/arch/powerpc/oprofile/Makefile @@ -12,6 +12,6 @@ DRIVER_OBJS := $(addprefix ../../../driv oprofile-y := $(DRIVER_OBJS) common.o backtrace.o oprofile-$(CONFIG_PPC_CELL_NATIVE) += op_model_cell.o -oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o +oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o oprofile-$(CONFIG_6xx) += op_model_7450.o Index: powerpc/include/asm-powerpc/oprofile_impl.h =================================================================== --- powerpc.orig/include/asm-powerpc/oprofile_impl.h +++ powerpc/include/asm-powerpc/oprofile_impl.h @@ -57,6 +57,8 @@ extern struct op_powerpc_model op_model_ extern struct op_powerpc_model op_model_power4; extern struct op_powerpc_model op_model_7450; extern struct op_powerpc_model op_model_cell; +extern struct op_powerpc_model op_model_pa6t; + /* All the classic PPC parts use these */ static inline unsigned int classic_ctr_read(unsigned int i) ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver 2007-04-16 6:26 [PATCH] [1/5] pasemi: allow 8KB config space for I/O bridge Olof Johansson ` (2 preceding siblings ...) 2007-04-16 6:30 ` [PATCH] [4/5] pasemi: PA6T oprofile support Olof Johansson @ 2007-04-16 6:31 ` Olof Johansson 2007-04-16 23:07 ` Arnd Bergmann 2007-04-18 6:39 ` [PATCH] [5/5] [v2] " Olof Johansson 3 siblings, 2 replies; 13+ messages in thread From: Olof Johansson @ 2007-04-16 6:31 UTC (permalink / raw) To: paulus; +Cc: linuxppc-dev MDIO driver for PHY's connected via GPIO as on the PA Semi Electra eval board. Signed-off-by: Olof Johansson <olof@lixom.net> Index: powerpc/arch/powerpc/platforms/pasemi/gpio_mdio.c =================================================================== --- /dev/null +++ powerpc/arch/powerpc/platforms/pasemi/gpio_mdio.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Olof Johansson, PA Semi + * + * Maintained by: Olof Johansson <olof@lixom.net> + * + * Based on drivers/net/fs_enet/mii-bitbang.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <asm/of_platform.h> + +#define DELAY 1 + +static void __iomem *gpio_regs; + +struct gpio_priv { + int mdc_pin; + int mdio_pin; +}; + +#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin) +#define MDIO_PIN(bus) (((struct gpio_priv *)bus->priv)->mdio_pin) + +static inline void mdio_lo(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus)); +} + +static inline void mdio_hi(struct mii_bus *bus) +{ + out_le32(gpio_regs, 1 << MDIO_PIN(bus)); +} + +static inline void mdc_lo(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus)); +} + +static inline void mdc_hi(struct mii_bus *bus) +{ + out_le32(gpio_regs, 1 << MDC_PIN(bus)); +} + +static inline void mdio_active(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus))); +} + +static inline void mdio_tristate(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus))); +} + +static inline int mdio_read(struct mii_bus *bus) +{ + return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus))); +} + +static void clock_out(struct mii_bus *bus, int bit) +{ + if (bit) + mdio_hi(bus); + else + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); +} + +/* Utility to send the preamble, address, and register (common to read and write). */ +static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg) +{ + int i; + + /* CFE uses a really long preamble (40 bits). We'll do the same. */ + mdio_active(bus); + for (i = 0; i < 40; i++) { + clock_out(bus, 1); + } + + /* send the start bit (01) and the read opcode (10) or write (10) */ + clock_out(bus, 0); + clock_out(bus, 1); + + clock_out(bus, read); + clock_out(bus, !read); + + /* send the PHY address */ + for (i = 0; i < 5; i++) { + clock_out(bus, (addr & 0x10) != 0); + addr <<= 1; + } + + /* send the register address */ + for (i = 0; i < 5; i++) { + clock_out(bus, (reg & 0x10) != 0); + reg <<= 1; + } +} + +static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location) +{ + u16 rdreg; + int ret, i; + u8 addr = phy_id & 0xff; + u8 reg = location & 0xff; + + bitbang_pre(bus, 1, addr, reg); + + /* tri-state our MDIO I/O pin so we can read */ + mdio_tristate(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + + /* read 16 bits of register data, MSB first */ + rdreg = 0; + for (i = 0; i < 16; i++) { + mdc_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + udelay(DELAY); + rdreg <<= 1; + rdreg |= mdio_read(bus); + } + + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + udelay(DELAY); + + ret = rdreg; + + return ret; +} + +static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val) +{ + int i; + + u8 addr = phy_id & 0xff; + u8 reg = location & 0xff; + u16 value = val & 0xffff; + + bitbang_pre(bus, 0, addr, reg); + + /* send the turnaround (10) */ + mdc_lo(bus); + mdio_hi(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + + /* write 16 bits of register data, MSB first */ + for (i = 0; i < 16; i++) { + mdc_lo(bus); + if (value & 0x8000) + mdio_hi(bus); + else + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + value <<= 1; + } + + /* + * Tri-state the MDIO line. + */ + mdio_tristate(bus); + mdc_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + return 0; +} + +static int gpio_mdio_reset(struct mii_bus *bus) +{ + /*nothing here - dunno how to reset it*/ + return 0; +} + + +static int __devinit gpio_mdio_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->node; + struct device_node *gpio_np; + struct mii_bus *new_bus; + struct resource res; + struct gpio_priv *priv; + const unsigned int *prop; + int err = 0; + int i; + + gpio_np = of_find_compatible_node(NULL, "gpio", "1682m-gpio"); + + if (!gpio_np) + return -ENODEV; + + err = of_address_to_resource(gpio_np, 0, &res); + of_node_put(gpio_np); + + if (err) + return -EINVAL; + + if (!gpio_regs) + gpio_regs = ioremap(res.start, 0x100); + + if (!gpio_regs) + return -EPERM; + + priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + + if (new_bus == NULL) + return -ENOMEM; + + new_bus->name = "pasemi gpio mdio bus", + new_bus->read = &gpio_mdio_read, + new_bus->write = &gpio_mdio_write, + new_bus->reset = &gpio_mdio_reset, + + prop = get_property(np, "reg", NULL); + new_bus->id = *prop; + new_bus->priv = priv; + + new_bus->phy_mask = 0; + + new_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + for(i = 0; i < PHY_MAX_ADDR; ++i) + new_bus->irq[i] = irq_create_mapping(NULL, 10); + + + prop = get_property(np, "mdc-pin", NULL); + priv->mdc_pin = *prop; + + prop = get_property(np, "mdio-pin", NULL); + priv->mdio_pin = *prop; + + new_bus->dev = dev; + dev_set_drvdata(dev, new_bus); + + err = mdiobus_register(new_bus); + + if (0 != err) { + printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n", + new_bus->name, err); + goto bus_register_fail; + } + + return 0; + +bus_register_fail: + kfree(new_bus); + + return err; +} + + +static int gpio_mdio_remove(struct of_device *dev) +{ + struct mii_bus *bus = dev_get_drvdata(&dev->dev); + + mdiobus_unregister(bus); + + dev_set_drvdata(&dev->dev, NULL); + + kfree(bus->priv); + bus->priv = NULL; + kfree(bus); + + return 0; +} + +static struct of_device_id gpio_mdio_match[] = +{ + { + .compatible = "gpio-mdio", + }, + {}, +}; + +static struct of_platform_driver gpio_mdio_driver = +{ + .name = "gpio-mdio-bitbang", + .match_table = gpio_mdio_match, + .probe = gpio_mdio_probe, + .remove = gpio_mdio_remove, +}; + +int gpio_mdio_init(void) +{ + return of_register_platform_driver(&gpio_mdio_driver); +} + +void gpio_mdio_exit(void) +{ + of_unregister_platform_driver(&gpio_mdio_driver); +} + +device_initcall(gpio_mdio_init); + +static int __devinit hack_init(void) +{ + struct platform_device *pdev; + + pdev = platform_device_register_simple("gpio-mdio-bitbang", 0, NULL, 0); + + return 0; +} +late_initcall(hack_init); Index: powerpc/arch/powerpc/platforms/pasemi/Makefile =================================================================== --- powerpc.orig/arch/powerpc/platforms/pasemi/Makefile +++ powerpc/arch/powerpc/platforms/pasemi/Makefile @@ -1,3 +1,3 @@ -obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o +obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o gpio_mdio.o obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver 2007-04-16 6:31 ` [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver Olof Johansson @ 2007-04-16 23:07 ` Arnd Bergmann 2007-04-17 0:47 ` Olof Johansson 2007-04-18 6:39 ` [PATCH] [5/5] [v2] " Olof Johansson 1 sibling, 1 reply; 13+ messages in thread From: Arnd Bergmann @ 2007-04-16 23:07 UTC (permalink / raw) To: linuxppc-dev; +Cc: Olof Johansson, paulus On Monday 16 April 2007, Olof Johansson wrote: > +static int __devinit hack_init(void) > +{ > +=A0=A0=A0=A0=A0=A0=A0struct platform_device *pdev; > + > +=A0=A0=A0=A0=A0=A0=A0pdev =3D platform_device_register_simple("gpio-mdio= =2Dbitbang", 0, NULL, 0); > + > +=A0=A0=A0=A0=A0=A0=A0return 0; > +} > +late_initcall(hack_init); Why do you need this? The name already suggests that you are aware that this should be autoprobed, but it would be good to have a comment explaining why you did it this way. Arnd <>< ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver 2007-04-16 23:07 ` Arnd Bergmann @ 2007-04-17 0:47 ` Olof Johansson 0 siblings, 0 replies; 13+ messages in thread From: Olof Johansson @ 2007-04-17 0:47 UTC (permalink / raw) To: Arnd Bergmann; +Cc: linuxppc-dev, paulus On Tue, Apr 17, 2007 at 01:07:12AM +0200, Arnd Bergmann wrote: > On Monday 16 April 2007, Olof Johansson wrote: > > +static int __devinit hack_init(void) > > +{ > > +???????struct platform_device *pdev; > > + > > +???????pdev = platform_device_register_simple("gpio-mdio-bitbang", 0, NULL, 0); > > + > > +???????return 0; > > +} > > +late_initcall(hack_init); > > Why do you need this? The name already suggests that you are aware that this > should be autoprobed, but it would be good to have a comment explaining > why you did it this way. Because I'm an idiot and forgot to take it away before posting. :-) Thanks, will clean it up. -Olof ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH] [5/5] [v2] pasemi: GPIO MDIO of_platform driver 2007-04-16 6:31 ` [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver Olof Johansson 2007-04-16 23:07 ` Arnd Bergmann @ 2007-04-18 6:39 ` Olof Johansson 2007-05-01 4:35 ` Paul Mackerras 1 sibling, 1 reply; 13+ messages in thread From: Olof Johansson @ 2007-04-18 6:39 UTC (permalink / raw) To: paulus; +Cc: linuxppc-dev, arnd MDIO driver for PHY's connected via GPIO as on the PA Semi Electra eval board. Signed-off-by: Olof Johansson <olof@lixom.net> -- Changed: Removed hack to probe the device, and added the compatible = "sdc" of platform probe. Index: powerpc/arch/powerpc/platforms/pasemi/gpio_mdio.c =================================================================== --- /dev/null +++ powerpc/arch/powerpc/platforms/pasemi/gpio_mdio.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Olof Johansson, PA Semi + * + * Maintained by: Olof Johansson <olof@lixom.net> + * + * Based on drivers/net/fs_enet/mii-bitbang.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <asm/of_platform.h> + +#define DELAY 1 + +static void __iomem *gpio_regs; + +struct gpio_priv { + int mdc_pin; + int mdio_pin; +}; + +#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin) +#define MDIO_PIN(bus) (((struct gpio_priv *)bus->priv)->mdio_pin) + +static inline void mdio_lo(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus)); +} + +static inline void mdio_hi(struct mii_bus *bus) +{ + out_le32(gpio_regs, 1 << MDIO_PIN(bus)); +} + +static inline void mdc_lo(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus)); +} + +static inline void mdc_hi(struct mii_bus *bus) +{ + out_le32(gpio_regs, 1 << MDC_PIN(bus)); +} + +static inline void mdio_active(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus))); +} + +static inline void mdio_tristate(struct mii_bus *bus) +{ + out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus))); +} + +static inline int mdio_read(struct mii_bus *bus) +{ + return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus))); +} + +static void clock_out(struct mii_bus *bus, int bit) +{ + if (bit) + mdio_hi(bus); + else + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); +} + +/* Utility to send the preamble, address, and register (common to read and write). */ +static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg) +{ + int i; + + /* CFE uses a really long preamble (40 bits). We'll do the same. */ + mdio_active(bus); + for (i = 0; i < 40; i++) { + clock_out(bus, 1); + } + + /* send the start bit (01) and the read opcode (10) or write (10) */ + clock_out(bus, 0); + clock_out(bus, 1); + + clock_out(bus, read); + clock_out(bus, !read); + + /* send the PHY address */ + for (i = 0; i < 5; i++) { + clock_out(bus, (addr & 0x10) != 0); + addr <<= 1; + } + + /* send the register address */ + for (i = 0; i < 5; i++) { + clock_out(bus, (reg & 0x10) != 0); + reg <<= 1; + } +} + +static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location) +{ + u16 rdreg; + int ret, i; + u8 addr = phy_id & 0xff; + u8 reg = location & 0xff; + + bitbang_pre(bus, 1, addr, reg); + + /* tri-state our MDIO I/O pin so we can read */ + mdio_tristate(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + + /* read 16 bits of register data, MSB first */ + rdreg = 0; + for (i = 0; i < 16; i++) { + mdc_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + udelay(DELAY); + rdreg <<= 1; + rdreg |= mdio_read(bus); + } + + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + udelay(DELAY); + + ret = rdreg; + + return ret; +} + +static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val) +{ + int i; + + u8 addr = phy_id & 0xff; + u8 reg = location & 0xff; + u16 value = val & 0xffff; + + bitbang_pre(bus, 0, addr, reg); + + /* send the turnaround (10) */ + mdc_lo(bus); + mdio_hi(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + mdc_lo(bus); + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + + /* write 16 bits of register data, MSB first */ + for (i = 0; i < 16; i++) { + mdc_lo(bus); + if (value & 0x8000) + mdio_hi(bus); + else + mdio_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + value <<= 1; + } + + /* + * Tri-state the MDIO line. + */ + mdio_tristate(bus); + mdc_lo(bus); + udelay(DELAY); + mdc_hi(bus); + udelay(DELAY); + return 0; +} + +static int gpio_mdio_reset(struct mii_bus *bus) +{ + /*nothing here - dunno how to reset it*/ + return 0; +} + + +static int __devinit gpio_mdio_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->node; + struct device_node *gpio_np; + struct mii_bus *new_bus; + struct resource res; + struct gpio_priv *priv; + const unsigned int *prop; + int err = 0; + int i; + + gpio_np = of_find_compatible_node(NULL, "gpio", "1682m-gpio"); + + if (!gpio_np) + return -ENODEV; + + err = of_address_to_resource(gpio_np, 0, &res); + of_node_put(gpio_np); + + if (err) + return -EINVAL; + + if (!gpio_regs) + gpio_regs = ioremap(res.start, 0x100); + + if (!gpio_regs) + return -EPERM; + + priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + + if (new_bus == NULL) + return -ENOMEM; + + new_bus->name = "pasemi gpio mdio bus", + new_bus->read = &gpio_mdio_read, + new_bus->write = &gpio_mdio_write, + new_bus->reset = &gpio_mdio_reset, + + prop = get_property(np, "reg", NULL); + new_bus->id = *prop; + new_bus->priv = priv; + + new_bus->phy_mask = 0; + + new_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + for(i = 0; i < PHY_MAX_ADDR; ++i) + new_bus->irq[i] = irq_create_mapping(NULL, 10); + + + prop = get_property(np, "mdc-pin", NULL); + priv->mdc_pin = *prop; + + prop = get_property(np, "mdio-pin", NULL); + priv->mdio_pin = *prop; + + new_bus->dev = dev; + dev_set_drvdata(dev, new_bus); + + err = mdiobus_register(new_bus); + + if (0 != err) { + printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n", + new_bus->name, err); + goto bus_register_fail; + } + + return 0; + +bus_register_fail: + kfree(new_bus); + + return err; +} + + +static int gpio_mdio_remove(struct of_device *dev) +{ + struct mii_bus *bus = dev_get_drvdata(&dev->dev); + + mdiobus_unregister(bus); + + dev_set_drvdata(&dev->dev, NULL); + + kfree(bus->priv); + bus->priv = NULL; + kfree(bus); + + return 0; +} + +static struct of_device_id gpio_mdio_match[] = +{ + { + .compatible = "gpio-mdio", + }, + {}, +}; + +static struct of_platform_driver gpio_mdio_driver = +{ + .name = "gpio-mdio-bitbang", + .match_table = gpio_mdio_match, + .probe = gpio_mdio_probe, + .remove = gpio_mdio_remove, +}; + +int gpio_mdio_init(void) +{ + return of_register_platform_driver(&gpio_mdio_driver); +} + +void gpio_mdio_exit(void) +{ + of_unregister_platform_driver(&gpio_mdio_driver); +} +device_initcall(gpio_mdio_init); + Index: powerpc/arch/powerpc/platforms/pasemi/Makefile =================================================================== --- powerpc.orig/arch/powerpc/platforms/pasemi/Makefile +++ powerpc/arch/powerpc/platforms/pasemi/Makefile @@ -1,3 +1,3 @@ -obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o +obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o gpio_mdio.o obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o Index: powerpc/arch/powerpc/platforms/pasemi/setup.c =================================================================== --- powerpc.orig/arch/powerpc/platforms/pasemi/setup.c +++ powerpc/arch/powerpc/platforms/pasemi/setup.c @@ -209,6 +209,20 @@ static void __init pas_init_early(void) iommu_init_early_pasemi(); } +static struct of_device_id pasemi_bus_ids[] = { + { .type = "sdc", }, + {}, +}; + +static int __init pasemi_publish_devices(void) +{ + /* Publish OF platform devices for southbridge IOs */ + of_platform_bus_probe(NULL, pasemi_bus_ids, NULL); + + return 0; +} +device_initcall(pasemi_publish_devices); + /* * Called very early, MMU is off, device-tree isn't unflattened ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] [5/5] [v2] pasemi: GPIO MDIO of_platform driver 2007-04-18 6:39 ` [PATCH] [5/5] [v2] " Olof Johansson @ 2007-05-01 4:35 ` Paul Mackerras 2007-05-01 4:45 ` Olof Johansson 0 siblings, 1 reply; 13+ messages in thread From: Paul Mackerras @ 2007-05-01 4:35 UTC (permalink / raw) To: Olof Johansson; +Cc: linuxppc-dev, arnd I just noticed this in arch/powerpc/platforms/pasemi/setup.c: > --- powerpc.orig/arch/powerpc/platforms/pasemi/setup.c > +++ powerpc/arch/powerpc/platforms/pasemi/setup.c > @@ -209,6 +209,20 @@ static void __init pas_init_early(void) > iommu_init_early_pasemi(); > } > > +static struct of_device_id pasemi_bus_ids[] = { > + { .type = "sdc", }, > + {}, > +}; > + > +static int __init pasemi_publish_devices(void) > +{ > + /* Publish OF platform devices for southbridge IOs */ > + of_platform_bus_probe(NULL, pasemi_bus_ids, NULL); > + > + return 0; > +} > +device_initcall(pasemi_publish_devices); Oof. Please make sure you only call of_platform_bus_probe if you are actually on a pasemi platform... :) Paul. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] [5/5] [v2] pasemi: GPIO MDIO of_platform driver 2007-05-01 4:35 ` Paul Mackerras @ 2007-05-01 4:45 ` Olof Johansson 0 siblings, 0 replies; 13+ messages in thread From: Olof Johansson @ 2007-05-01 4:45 UTC (permalink / raw) To: Paul Mackerras; +Cc: linuxppc-dev, arnd On Tue, May 01, 2007 at 02:35:58PM +1000, Paul Mackerras wrote: > I just noticed this in arch/powerpc/platforms/pasemi/setup.c: > > Oof. Please make sure you only call of_platform_bus_probe if you are > actually on a pasemi platform... :) Check your inbox. :-) Benh just pinged me about it on IRC as well. -Olof ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2007-05-01 4:44 UTC | newest] Thread overview: 13+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-04-16 6:26 [PATCH] [1/5] pasemi: allow 8KB config space for I/O bridge Olof Johansson 2007-04-16 6:27 ` [PATCH] [2/5] pasemi: enable one more hid bit Olof Johansson 2007-04-24 0:27 ` Benjamin Herrenschmidt 2007-04-24 1:04 ` Olof Johansson 2007-04-16 6:28 ` [PATCH] [3/5] pasemi: reset mpic on boot Olof Johansson 2007-04-16 6:30 ` [PATCH] [4/5] pasemi: PA6T oprofile support Olof Johansson 2007-04-18 6:38 ` [PATCH] [4/5] [v2] " Olof Johansson 2007-04-16 6:31 ` [PATCH] [5/5] pasemi: GPIO MDIO of_platform driver Olof Johansson 2007-04-16 23:07 ` Arnd Bergmann 2007-04-17 0:47 ` Olof Johansson 2007-04-18 6:39 ` [PATCH] [5/5] [v2] " Olof Johansson 2007-05-01 4:35 ` Paul Mackerras 2007-05-01 4:45 ` Olof Johansson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).