From: Mikael Pettersson <mikpe@csd.uu.se>

- Implement perfctr_cpu_control_write()/read() in-kernel API.
  Only handle PERFCTR_DOMAIN_CPU_REGS, as the other domains will be
  handled in generic code.
- Implement get_reg_offset() via a static table. The ppc32 SPR numbers
  we use don't form a nice dense range, alas.

This depends on the physical-indexing patch for ppc32, and on the
common part of the cpu_control access patch.

Signed-off-by: Mikael Pettersson <mikpe@csd.uu.se>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/perfctr/ppc.c     |   70 ++++++++++++++++++++++++++++++++++++++
 25-akpm/include/asm-ppc/perfctr.h |   11 +++++
 2 files changed, 81 insertions(+)

diff -puN drivers/perfctr/ppc.c~perfctr-api-update-8-9-cpu_control-access-ppc32 drivers/perfctr/ppc.c
--- 25/drivers/perfctr/ppc.c~perfctr-api-update-8-9-cpu_control-access-ppc32	2005-03-13 13:23:25.000000000 -0800
+++ 25-akpm/drivers/perfctr/ppc.c	2005-03-13 13:23:25.000000000 -0800
@@ -636,6 +636,76 @@ int perfctr_cpu_update_control(struct pe
 	return 0;
 }
 
+/*
+ * get_reg_offset() maps SPR numbers to offsets into struct perfctr_cpu_control,
+ * suitable for accessing control data of type unsigned int.
+ */
+static const struct {
+	unsigned int spr;
+	unsigned int offset;
+} reg_offsets[] = {
+	{ SPRN_MMCR0, offsetof(struct perfctr_cpu_control, mmcr0) },
+	{ SPRN_MMCR1, offsetof(struct perfctr_cpu_control, mmcr1) },
+	{ SPRN_MMCR2, offsetof(struct perfctr_cpu_control, mmcr2) },
+	{ SPRN_PMC1,  offsetof(struct perfctr_cpu_control, ireset[1-1]) },
+	{ SPRN_PMC2,  offsetof(struct perfctr_cpu_control, ireset[2-1]) },
+	{ SPRN_PMC3,  offsetof(struct perfctr_cpu_control, ireset[3-1]) },
+	{ SPRN_PMC4,  offsetof(struct perfctr_cpu_control, ireset[4-1]) },
+	{ SPRN_PMC5,  offsetof(struct perfctr_cpu_control, ireset[5-1]) },
+	{ SPRN_PMC6,  offsetof(struct perfctr_cpu_control, ireset[6-1]) },
+};
+
+static int get_reg_offset(unsigned int spr)
+{
+	unsigned int i;
+
+	for(i = 0; i < ARRAY_SIZE(reg_offsets); ++i)
+		if (spr == reg_offsets[i].spr)
+			return reg_offsets[i].offset;
+	return -1;
+}
+
+static int access_regs(struct perfctr_cpu_control *control,
+		       void *argp, unsigned int argbytes, int do_write)
+{
+	struct perfctr_cpu_reg *regs;
+	unsigned int i, nr_regs, *where;
+	int offset;
+
+	if (argbytes & (sizeof(struct perfctr_cpu_reg) - 1))
+		return -EINVAL;
+	regs = (struct perfctr_cpu_reg*)argp;
+	nr_regs = argbytes / sizeof(struct perfctr_cpu_reg);
+
+	for(i = 0; i < nr_regs; ++i) {
+		offset = get_reg_offset(regs[i].nr);
+		if (offset < 0)
+			return -EINVAL;
+		where = (unsigned int*)((char*)control + offset);
+		if (do_write)
+			*where = regs[i].value;
+		else
+			regs[i].value = *where;
+	}
+	return argbytes;
+}
+
+int perfctr_cpu_control_write(struct perfctr_cpu_control *control, unsigned int domain,
+			      const void *srcp, unsigned int srcbytes)
+{
+	if (domain != PERFCTR_DOMAIN_CPU_REGS)
+		return -EINVAL;
+	return access_regs(control, (void*)srcp, srcbytes, 1);
+}
+
+int perfctr_cpu_control_read(const struct perfctr_cpu_control *control, unsigned int domain,
+			     void *dstp, unsigned int dstbytes)
+{
+	if (domain != PERFCTR_DOMAIN_CPU_REGS)
+		return -EINVAL;
+	return access_regs((struct perfctr_cpu_control*)control, dstp, dstbytes, 0);
+}
+
 void perfctr_cpu_suspend(struct perfctr_cpu_state *state)
 {
 	unsigned int i, cstatus, nractrs;
diff -puN include/asm-ppc/perfctr.h~perfctr-api-update-8-9-cpu_control-access-ppc32 include/asm-ppc/perfctr.h
--- 25/include/asm-ppc/perfctr.h~perfctr-api-update-8-9-cpu_control-access-ppc32	2005-03-13 13:23:25.000000000 -0800
+++ 25-akpm/include/asm-ppc/perfctr.h	2005-03-13 13:23:25.000000000 -0800
@@ -133,6 +133,17 @@ extern void perfctr_cpu_release(const ch
    Returns a negative error code if the control data is invalid. */
 extern int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global);
 
+/* Parse and update control for the given domain. */
+extern int perfctr_cpu_control_write(struct perfctr_cpu_control *control,
+				     unsigned int domain,
+				     const void *srcp, unsigned int srcbytes);
+
+/* Retrieve and format control for the given domain.
+   Returns number of bytes written. */
+extern int perfctr_cpu_control_read(const struct perfctr_cpu_control *control,
+				    unsigned int domain,
+				    void *dstp, unsigned int dstbytes);
+
 /* Read a-mode counters. Subtract from start and accumulate into sums.
    Must be called with preemption disabled. */
 extern void perfctr_cpu_suspend(struct perfctr_cpu_state *state);
_