LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Fixed some missing files to be deleted when running make clean
From: Matthew McClintock @ 2006-10-23 19:26 UTC (permalink / raw)
  To: Mark A. Greer; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <20061012013223.GA9143@mag.az.mvista.com>

Fixed some missing files to be deleted when running make clean

Signed-off-by: Matthew McClintock <msm@freescale.com>

---
These patches apply against the latest series of Mark's bootwrapper
patches. They fix some missing files that need to be cleaned.

commit b05c2cae79ba02edd7263e19fe5f722c67c3c5cb
tree 3016e7e0f0b84d36ded2ec1c1ff5efe98cc94d4c
parent 44ae8267b3597965b485669885ff68384640b95a
author Matthew McClintock <msm@freescale.com> Mon, 23 Oct 2006 14:09:47
-0500
committer Matthew McClintock <msm@freescale.com> Mon, 23 Oct 2006
14:09:47 -0500

 arch/powerpc/boot/Makefile |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 8e33890..43507ca 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -75,7 +75,7 @@ quiet_cmd_copy_zliblinuxheader = COPY   
 	@cp $< $@
 
 clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \
-		$(obj)/empty.c
+		empty.c zImage.coff.lds zImage.lds zImage.sandpoint
 
 quiet_cmd_bootcc = BOOTCC  $@
       cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o
$@ $<

^ permalink raw reply related

* Re: MPC8xx CPM I2C interface driver
From: Christopher Murch @ 2006-10-23 20:17 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <20061023025001.45e821c3@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 731 bytes --]

Here's a simple driver that we wrote for 8xx i2c master-only usage.  It
only supports the I2C_RDWR ioctl to send and receive reads/writes to
various devices.

To integrate the patch, it will be necessary to modify your
linux/drivers/i2c/Makefile to build in the i2c-cpm support.

We've used this driver successfully with LM77 temp sensors, DS1337 RTC
and some EEPROMs.


On Sun, 2006-10-22 at 18:50 -0400, Vitaly Bordug wrote:
> _______________________________________________ 
> Linuxppc-embedded mailing list 
> Linuxppc-embedded@ozlabs.org 
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
> 
> email message attachment
> > -------- Forwarded Message --------
> > Subject: 
> > Date: Sun, 22 Oct 2006 18:57:11 -0400
> > 

[-- Attachment #2: i2c-cpm.patch --]
[-- Type: text/x-patch, Size: 28109 bytes --]

--- nothing	1969-12-31 19:00:00.000000000 -0500
+++ i2c-cpm.c	2006-10-23 16:00:56.000000000 -0400
@@ -0,0 +1,999 @@
+/*
+ *	ASP Watchdog   0.05:	In-Reach ASP Watchdog Device
+ *
+ *	(c) Copyright 2002 Ken Poole <kpoole@mrv.com>, All Rights Reserved.
+ *				http://www.mrv.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.
+ *	
+ *	Neither Ken Poole nor MRV Communications, Inc. admit liability
+ *      nor provide warranty for any of this software. This material is
+ *      provided "AS-IS" and at no charge.	
+ *
+ */
+ 
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c-dev.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/dma-mapping.h>
+#include <asm/commproc.h>
+#include <asm/8xx_immap.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define CPM_MAX_READ   513
+#define CPM_TIMEOUT    1000
+#define CPM_NUM_BDS    4
+#define CPM_WRT_BUF_SZ 128
+
+#define BD_SC_CLEARALL  (BD_SC_LAST | BD_SC_INTRPT | BD_IIC_START \
+			 | BD_SC_READY | BD_IIC_NAK | BD_IIC_UN | BD_IIC_CL)
+
+#define LOG_INT        1
+#define LOG_WRITE_ENT  2
+#define LOG_WRITE_CMD  3
+#define LOG_READ_ENT   4
+#define LOG_READ_CMD   5
+#define LOG_WRITE_DONE 6
+#define LOG_READ_DONE  7
+#define LOG_SIZE 128
+#define LOG_DISP  32
+
+struct i2c_datalog {
+  char type;          
+  char displayed;
+  char pad[2];
+  unsigned long msecs;
+  cbd_t *tbdf;
+  cbd_t *rbdf;
+  u16 tx_sc;
+  u16 rx_sc;
+  u16 tbptr;
+  u16 rbptr;
+};
+
+int i2c_logindex = 0;
+
+struct i2c_datalog i2c_log[LOG_SIZE];
+
+struct i2c_8xx {
+	uint dp_addr;
+	u16 reloc;
+	volatile i2c8xx_t *i2c;
+	volatile iic_t	*iip;
+	volatile cpm8xx_t *cp;
+        spinlock_t lock;
+};
+
+struct i2c_8xx cpm_i2c_8xx;
+
+#ifdef DECLARE_WAITQUEUE
+static wait_queue_head_t iic_rd_wait;
+static wait_queue_head_t iic_wr_wait;
+static wait_queue_head_t iic_cbd_wait;
+#else	
+static struct wait_queue *iic_rd_wait;
+static struct wait_queue *iic_wr_wait;
+static struct wait_queue *iic_cbd_wait;
+#endif	
+
+static ushort r_tbase, r_rbase;
+volatile static cbd_t *tbdf_cur, *rbdf_cur, *tbdf_base, *rbdf_base;
+#ifdef DEBUG_SHORT
+static char spbuf[256]; /* TEST TEST TEST */
+#endif
+
+#ifdef DEBUG_TIMEOUT
+unsigned char save_cer[32];
+int save_idx = 0;
+#endif
+
+unsigned char in_use;
+
+int cpm_scan = 0;
+int cpm_debug;
+
+static int cpm_iic_release(struct inode *inode, struct file *file);
+
+static unsigned long last_jifs = 0;
+
+static void make_log (char type, volatile cbd_t *tbdf, volatile cbd_t *rbdf)
+{
+  volatile iic_t *iip = cpm_i2c_8xx.iip;
+
+  i2c_log[i2c_logindex].type = type;
+  i2c_log[i2c_logindex].displayed = 0;
+  i2c_log[i2c_logindex].tbdf = (cbd_t *)tbdf;
+  i2c_log[i2c_logindex].rbdf = (cbd_t *)rbdf;
+  i2c_log[i2c_logindex].tx_sc = tbdf->cbd_sc;
+  i2c_log[i2c_logindex].rx_sc = rbdf->cbd_sc;
+  i2c_log[i2c_logindex].tbptr = iip->iic_tbptr;
+  i2c_log[i2c_logindex].rbptr = iip->iic_rbptr;
+  i2c_log[i2c_logindex].msecs = jiffies - last_jifs;
+
+  last_jifs = jiffies;
+
+  if (++i2c_logindex >= sizeof(i2c_log)/sizeof(struct i2c_datalog))
+    i2c_logindex = 0;
+}
+
+static void show_log (void)
+{
+  int index;
+  int count = 0;
+
+  index = i2c_logindex - LOG_DISP;
+  if (index < 0)
+    index = LOG_SIZE + index;
+
+  printk(KERN_DEBUG "logindex is %d\n", i2c_logindex);
+  while(1) {
+    if (i2c_log[index].displayed == 0) {
+      printk(KERN_DEBUG "%03d ", index);
+      printk(KERN_DEBUG "%04ld ", i2c_log[index].msecs);
+      switch (i2c_log[index].type) {
+      case LOG_INT:
+	printk(KERN_DEBUG "intrpt     "); break;
+      case LOG_WRITE_ENT:
+	printk(KERN_DEBUG "write ent  "); break;
+      case LOG_WRITE_CMD:
+	printk(KERN_DEBUG "write cmd  "); break;
+      case LOG_READ_ENT:
+	printk(KERN_DEBUG "read ent   "); break;
+      case LOG_READ_CMD:
+	printk(KERN_DEBUG "read cmd   "); break;
+      case LOG_WRITE_DONE:
+	printk(KERN_DEBUG "write done "); break;
+      case LOG_READ_DONE:
+	printk(KERN_DEBUG "read done  "); break;
+      }
+      printk(KERN_DEBUG "tbdf=%p rbdf=%p tx_sc=%04x rx_sc=%04x tbptr=%04x rbptr=%04x\n",
+	   i2c_log[index].tbdf, i2c_log[index].rbdf, i2c_log[index].tx_sc, i2c_log[index].rx_sc,
+	   i2c_log[index].tbptr, i2c_log[index].rbptr);
+      i2c_log[index].displayed = 1;
+    }
+    if (++index >= LOG_SIZE)
+      index = 0;
+    if (++count >= LOG_DISP)
+      break;
+  }
+  return;
+}
+
+static irqreturn_t
+cpm_iic_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	volatile i2c8xx_t *i2c = (i2c8xx_t *)dev_id;
+	unsigned char local_cer;
+	volatile iic_t *iip = cpm_i2c_8xx.iip;
+	volatile cpm8xx_t *cp = cpm_i2c_8xx.cp;
+
+	if (cpm_debug > 1)
+		printk("cpm_iic_interrupt(dev_id=%p)\n", dev_id);
+
+	/* Save status */
+	local_cer = i2c->i2c_i2cer;
+	/* Clear interrupt */
+	i2c->i2c_i2cer = local_cer;
+
+	if ((local_cer & 0x14) && cpm_debug)
+	  printk("cpm_iic_int: error int %02x\n", local_cer);
+
+	/* Chip errata, clear enable.
+	*/
+	/* i2c->i2c_i2mod = 0; */
+
+#ifdef DEBUG_TIMEOUT
+	/* Record interrupt status */
+	save_cer[++save_idx] = local_cer;
+	if (save_idx >= 32)
+	  save_idx = 0;
+#endif
+	/* printk(KERN_ERR "cpm_iic_int: iip=%p tbdf=%p rbdf=%p\n",
+	       iip, &cp->cp_dpmem[iip->iic_tbptr],
+	       &cp->cp_dpmem[iip->iic_rbptr]); */
+        make_log(LOG_INT, (cbd_t *)&cp->cp_dpmem[iip->iic_tbptr], (cbd_t *)&cp->cp_dpmem[iip->iic_rbptr]);
+
+	/* Get 'me going again.
+	*/
+	if (local_cer & 0x01) {
+	  wake_up_interruptible(&iic_rd_wait);
+	}
+	if (local_cer & 0x02) {
+	  wake_up_interruptible(&iic_wr_wait);
+	}
+	if (local_cer & 0x14) {
+	  wake_up_interruptible(&iic_rd_wait);
+	  wake_up_interruptible(&iic_wr_wait);
+	}	  
+	return IRQ_HANDLED;
+}
+
+#ifdef DEBUG_TIMEOUT
+static void
+show_last_ints(void)
+{
+  int i, j;
+
+	  j = save_idx - 7;
+	  if (j < 0)
+	    j = save_idx + 32;
+
+	  printk("last 8 ints (idx=%d): ", save_idx);
+	  for ( i = 0;  i < 8; i++) {
+	    printk(" %02x", save_cer[j]);
+	    if (j++ >= 32)
+	      j = 0;
+	  }
+	  printk("\n");
+}
+#endif
+
+
+static void 
+cpm_reset_iic_params(volatile iic_t *iip)
+{
+	iip->iic_tbase = r_tbase;
+	iip->iic_rbase = r_rbase;
+
+	tbdf_cur = tbdf_base;
+	rbdf_cur = rbdf_base;
+
+	iip->iic_tfcr = SMC_EB;
+	iip->iic_rfcr = SMC_EB;
+
+	iip->iic_mrblr = CPM_MAX_READ;
+
+	iip->iic_rstate = 0;
+	iip->iic_rdp = 0;
+	iip->iic_rbptr = 0;
+	iip->iic_rbc = 0;
+	iip->iic_rxtmp = 0;
+	iip->iic_tstate = 0;
+	iip->iic_tdp = 0;
+	iip->iic_tbptr = 0;
+	iip->iic_tbc = 0;
+	iip->iic_txtmp = 0;
+}
+
+#define BD_SC_NAK		((ushort)0x0004) /* NAK - did not respond */
+
+static void force_close(struct i2c_8xx *cpm)
+{
+	if (cpm->reloc == 0) {
+		volatile cpm8xx_t *cp = cpm->cp;
+
+		if (cpm_debug) printk("force_close()\n");
+		cp->cp_cpcr =
+			mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RX_BD) |
+			CPM_CR_FLG;
+
+		while (cp->cp_cpcr & CPM_CR_FLG);
+	}
+}
+
+/*
+ */
+static int cpm_iic_open(struct inode *inode, struct file *file)
+{
+  /* Ok, so what do we do with the open? */
+
+	return 0;
+}
+
+ssize_t cpm_iic_read(u_char abyte, dma_addr_t buf, int count)
+{
+	DECLARE_WAITQUEUE(rdwait, current);
+	volatile iic_t *iip = cpm_i2c_8xx.iip;
+	volatile i2c8xx_t *i2c = cpm_i2c_8xx.i2c;
+	volatile cpm8xx_t *cp = cpm_i2c_8xx.cp;
+	volatile cbd_t	*tbdf, *rbdf;
+	u_char *tb;
+	dma_addr_t tx_dma;
+	unsigned long orig;
+	int i;
+#ifdef DEBUG_TIMEOUT
+	int save_save_idx;
+#endif
+	if (count >= CPM_MAX_READ)
+		return -EINVAL;
+
+	/* check for and use a microcode relocation patch */
+	if (cpm_i2c_8xx.reloc) {
+		cpm_reset_iic_params(iip);
+	}
+
+	if (cpm_debug) printk("cpm_iic_read(abyte=0x%x)\n", abyte);
+
+	tbdf = tbdf_cur;
+	rbdf = rbdf_cur;
+	
+	/* Bump current and wrap */
+	if (tbdf->cbd_sc & BD_SC_WRAP)
+	        tbdf_cur = tbdf_base;
+	else
+	        tbdf_cur = tbdf + 1;
+	
+	if (rbdf->cbd_sc & BD_SC_WRAP)
+	        rbdf_cur = rbdf_base;
+	else
+	        rbdf_cur = rbdf + 1;
+
+	/* Allocate one transmit buffer. To read, we need an empty buffer
+	 * of the proper length. All that is used is the first byte for
+	 * address, the remainder is just used for timing (and doesn't
+	 * really have to exist).
+	 */
+	tb = dma_alloc_coherent(NULL, CPM_WRT_BUF_SZ, &tx_dma, GFP_KERNEL);
+	if (tb == NULL) {
+	  printk(KERN_ERR "cpm_iic_read: allocate failed\n");
+	}
+
+	tb[0] = abyte;		/* Device address byte w/rw flag */
+
+	//spin_lock_irqsave(&cpm_i2c_8xx.lock, flags );
+	spin_lock_irq(&cpm_i2c_8xx.lock);
+
+	/* Set up tx and rx descriptors */
+	tbdf->cbd_bufaddr = tx_dma;
+	tbdf->cbd_datlen = count + 1;
+
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = buf;
+
+	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_INTRPT;
+	tbdf->cbd_sc |= BD_SC_READY | BD_SC_LAST | BD_IIC_START;
+
+	make_log(LOG_READ_CMD, tbdf, rbdf);
+
+	if (cpm_debug)
+	  printk("cpm_iic_read: tbdf=%p rbdf=%p rbptr=%x tx sc=%04x rx sc=%04x\n",
+		 tbdf, rbdf, iip->iic_rbptr, tbdf->cbd_sc, rbdf->cbd_sc);
+
+	/* Chip bug, set enable here */
+	i2c->i2c_i2cer = i2c->i2c_i2cer;
+	i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+	i2c->i2c_i2mod = 1;	/* Enable */
+	i2c->i2c_i2com = 0x81;	/* Start master */
+#ifdef DEBUG_TIMEOUT
+	save_save_idx = save_idx;
+#endif
+	orig = jiffies;         /* Remember start time */
+	add_wait_queue(&iic_rd_wait, &rdwait);
+	spin_unlock_irq(&cpm_i2c_8xx.lock);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/* Wait for IIC transfer */
+	while(1) {
+	        if (!(rbdf->cbd_sc & BD_SC_EMPTY) && !(tbdf->cbd_sc & BD_SC_READY))
+		        break;
+		if (tbdf->cbd_sc & (BD_SC_NAK | BD_SC_OV | BD_SC_CL))
+		        break;
+
+	        if ((jiffies - orig) >= CPM_TIMEOUT) {
+#ifdef DEBUG_TIMEOUT
+	                printk("cpm_iic_read: timed out tx sc %04x, rx sc %04x \n",
+			       tbdf->cbd_sc, rbdf->cbd_sc);
+			printk("cpm_iic_read: tbdf=%p rbdf=%p rbptr=%x\n",
+			       tbdf, rbdf, iip->iic_rbptr);
+			show_last_ints();
+#endif
+			/* read_int_defect: */
+			show_log();
+			printk(KERN_DEBUG "cpm_iic_read: timed out wait for complete tbdf=%p rbdf=%p tx sc=%04x, rx sc=%04x\n",
+			       tbdf, rbdf, tbdf->cbd_sc, rbdf->cbd_sc);
+			tbdf->cbd_sc &= ~BD_SC_CLEARALL;
+			rbdf->cbd_sc &= ~BD_SC_OV;
+			/* Restore BD ptrs by reading controller reg */
+			tbdf_cur = (cbd_t *)&cp->cp_dpmem[iip->iic_tbptr];
+			rbdf_cur = (cbd_t *)&cp->cp_dpmem[iip->iic_rbptr];
+			i2c->i2c_i2mod = 0;
+			__set_current_state(TASK_RUNNING);
+			remove_wait_queue(&iic_rd_wait, &rdwait);
+			dma_free_coherent(NULL, CPM_WRT_BUF_SZ, tb, tx_dma);
+			return -ETIMEDOUT;
+		}
+		schedule_timeout(CPM_TIMEOUT);
+
+		if (signal_pending(current)) {
+			i2c->i2c_i2mod = 0;
+		        __set_current_state(TASK_RUNNING);
+		        remove_wait_queue(&iic_rd_wait, &rdwait);
+			dma_free_coherent(NULL, CPM_WRT_BUF_SZ, tb, tx_dma);
+		        return -EIO;
+		}
+	}
+
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&iic_rd_wait, &rdwait);
+	dma_free_coherent(NULL, CPM_WRT_BUF_SZ, tb, tx_dma);
+
+	make_log(LOG_READ_DONE, tbdf, rbdf);
+
+	if (cpm_debug) {
+		printk("tx sc %04x, rx sc %04x\n",
+		       tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+	        if (cpm_debug)
+		    printk("IIC read; no ack tbdf %p rbdf %p\n",
+			   tbdf, rbdf);
+		force_close(&cpm_i2c_8xx);
+		/* NAK does not bump internal RBD pointer, so
+		   we have to re-use the software rbd index */
+		rbdf_cur = rbdf;
+		count = -ENODEV;
+		goto read_out;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		printk("IIC read; complete but rbuf empty\n");
+		printk("tx sc %04x, rx sc %04x\n",
+		       tbdf->cbd_sc, rbdf->cbd_sc);
+		force_close(&cpm_i2c_8xx);
+	}
+
+	if (rbdf->cbd_datlen < count) {
+	        show_log();
+		printk("IIC read; short, wanted %d got %d (%d) ",
+		       count, rbdf->cbd_datlen, tbdf->cbd_datlen);
+		//for (i = 0; i < rbdf->cbd_datlen; i++)
+		//printk(" %02x", buf[i]);
+		printk(" tx sc %04x, rx sc %04x\n",
+		       tbdf->cbd_sc, rbdf->cbd_sc);
+
+		/* To recover from this, we pulse the SCL clock line
+		   manually for 10 more cycles */
+		cp->cp_pbpar &= ~0x00000020;
+		for (i = 0; i < 10; i++) {
+		  cp->cp_pbdat |= 0x00000020;
+		  udelay(10);
+		  cp->cp_pbdat &= ~0x00000020;
+		  udelay(10);
+		}
+		cp->cp_pbpar |= 0x00000020;
+#ifdef DEBUG_SHORT
+		printk("prev write: %s", spbuf);
+#endif
+#ifdef DEBUG_TIMEOUT
+		show_last_ints();
+#endif
+		count = 0;
+		/* Fall through */
+	}
+
+read_out:
+	i2c->i2c_i2mod = 0;
+	tbdf->cbd_sc &= ~BD_SC_CLEARALL;
+	rbdf->cbd_sc &= ~BD_IIC_OV;
+	return count;
+}
+
+ssize_t cpm_iic_write(u_char abyte, dma_addr_t buf, int count)
+{
+	DECLARE_WAITQUEUE(wrwait, current);
+	volatile iic_t *iip = cpm_i2c_8xx.iip;
+	volatile i2c8xx_t *i2c = cpm_i2c_8xx.i2c;
+	volatile cpm8xx_t *cp = cpm_i2c_8xx.cp;
+	volatile cbd_t	*tbdf0, *tbdf1;
+	u_char *tb;
+	dma_addr_t tx_dma;
+	unsigned long orig;
+#ifdef DEBUG_SHORT
+	int i, len = 0;
+#endif
+#ifdef DEBUG_TIMEOUT
+	int save_save_idx;
+#endif
+	/* check for and use a microcode relocation patch */
+	if (cpm_i2c_8xx.reloc) {
+		cpm_reset_iic_params(iip);
+	}
+
+	if (cpm_debug) printk("cpm_iic_write(abyte=0x%x)\n", abyte);
+
+	/* Get a descriptor */
+	tbdf0 = tbdf_cur;
+	/* Get next descriptor */
+	if (tbdf0->cbd_sc & BD_SC_WRAP)
+	        tbdf1 = tbdf_base;
+	else
+	        tbdf1 = tbdf0 + 1;
+
+	/* Bump current and and wrap */
+	if (tbdf1->cbd_sc & BD_SC_WRAP)
+	        tbdf_cur = tbdf_base;
+	else
+	        tbdf_cur = tbdf1 + 1;
+	
+	/* Allocate one transmit message buffer */
+	tb = dma_alloc_coherent(NULL, CPM_WRT_BUF_SZ, &tx_dma, GFP_KERNEL);
+	if (tb == NULL) {
+	  printk(KERN_ERR "cpm_iic_read: allocate failed\n");
+	}
+
+	*tb = abyte;		/* Device address byte w/rw flag */
+
+	//spin_lock_irqsave(&cpm_i2c_8xx.lock, flags);
+	spin_lock_irq(&cpm_i2c_8xx.lock);
+
+	/* Set up 1st descriptor */
+	tbdf0->cbd_datlen = 1;
+	tbdf0->cbd_bufaddr = tx_dma;
+
+	/* Set up 2nd desc */
+	//tbdf1->cbd_bufaddr = __pa(buf);
+	tbdf1->cbd_bufaddr = buf;
+	tbdf1->cbd_datlen = count;
+
+	tbdf1->cbd_sc |= BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST;
+	tbdf0->cbd_sc |= BD_SC_READY | BD_IIC_START;
+
+	make_log(LOG_WRITE_CMD, tbdf0, tbdf1);
+
+	if (cpm_debug)
+	  printk("cpm_iic_write: tbdf0=%p tbdf1=%p sc=%04x %04x abyte=%02x\n",
+		 tbdf0, tbdf1, tbdf0->cbd_sc, tbdf1->cbd_sc, abyte);
+
+	orig = jiffies;         /* Remember start time */
+
+	/* Chip bug, set enable here */
+	i2c->i2c_i2cer = i2c->i2c_i2cer;
+	i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+	i2c->i2c_i2mod = 1;	/* Enable */
+	i2c->i2c_i2com = 0x81;	/* Start master */
+#ifdef DEBUG_TIMEOUT
+	save_save_idx = save_idx;
+#endif
+	add_wait_queue(&iic_wr_wait, &wrwait);
+	spin_unlock_irq(&cpm_i2c_8xx.lock);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/* Wait for IIC transfer */
+	while(1) {
+		if (!(tbdf1->cbd_sc & BD_SC_READY))
+		        break;
+
+		if ((jiffies - orig) >= CPM_TIMEOUT) {
+#ifdef DEBUG_TIMEOUT
+		        printk("cpm_iic_write: timed out tx0 sc %04x, tx1 sc %04x\n",
+			       tbdf0->cbd_sc, tbdf1->cbd_sc);
+			printk("cpm_iic_write: tbdf0=%p tbdf1=%p tbptr=%04x\n",
+			       tbdf0, tbdf1, iip->iic_tbptr);
+			show_last_ints();
+#endif
+			show_log();
+			printk(KERN_DEBUG "cpm_iic_write: timed out wait for complete tbdf0=%p tbdf1=%p tx sc=%04x, tx sc=%04x\n",
+			       tbdf0, tbdf1, tbdf0->cbd_sc, tbdf1->cbd_sc);
+			tbdf0->cbd_sc &= ~BD_SC_CLEARALL;
+			tbdf1->cbd_sc &= ~BD_SC_CLEARALL;
+			/* Restore BD ptr by reading controller reg */
+			tbdf_cur = (cbd_t *)&cp->cp_dpmem[iip->iic_tbptr];
+			i2c->i2c_i2mod = 0;
+			__set_current_state(TASK_RUNNING);
+			remove_wait_queue(&iic_wr_wait, &wrwait);
+			dma_free_coherent(NULL, CPM_WRT_BUF_SZ, tb, tx_dma);
+			return -ETIMEDOUT;
+		}
+		schedule_timeout(CPM_TIMEOUT);
+
+		if (signal_pending(current)) {
+			i2c->i2c_i2mod = 0;
+			__set_current_state(TASK_RUNNING);
+			remove_wait_queue(&iic_wr_wait, &wrwait);
+			dma_free_coherent(NULL, CPM_WRT_BUF_SZ, tb, tx_dma);
+		        return -EIO;
+		}
+	}
+
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&iic_wr_wait, &wrwait);
+	dma_free_coherent(NULL, CPM_WRT_BUF_SZ, tb, tx_dma);
+
+	make_log(LOG_WRITE_DONE, tbdf0, tbdf1);
+
+	if (cpm_debug) {
+		printk("tx0 sc %04x, tx1 sc %04x\n",
+		       tbdf0->cbd_sc, tbdf1->cbd_sc);
+	}
+
+	if (tbdf0->cbd_sc & BD_SC_NAK) {
+	        if (cpm_debug)
+		        printk("IIC write; no ack abyte=%02x\n", abyte);
+		tbdf_cur = (cbd_t *)&cp->cp_dpmem[iip->iic_tbptr];
+		count = -ENODEV;
+		goto write_out;
+	}
+	  
+	if (tbdf0->cbd_sc & BD_SC_READY) {
+		show_log();
+		printk("IIC write; complete but tbuf ready tbdf0=%p tcur=%p txsc=%04x tbptr=%04x\n", tbdf0, tbdf_cur, tbdf0->cbd_sc, iip->iic_tbptr);
+		tbdf_cur = (cbd_t *)&cp->cp_dpmem[iip->iic_tbptr];
+		count = 0;
+		goto write_out;
+	}
+
+#ifdef DEBUG_SHORT
+	/* Save status of write for later */
+	len = sprintf(spbuf, "cpm_iic_write: wrote %d bytes ", tbdf1->cbd_datlen);
+	//for (i = 0; i < tbdf1->cbd_datlen; i++)
+	//len += sprintf(spbuf+len, " %02x", ((u_char *)buf)[i]);
+	len += sprintf(spbuf+len, "  tx0 sc %04x, tx1 sc %04x idx=%d\n",
+	       tbdf0->cbd_sc, tbdf1->cbd_sc, save_save_idx);
+#endif
+write_out:
+	i2c->i2c_i2mod = 0;
+	tbdf0->cbd_sc &= ~BD_SC_CLEARALL;
+	tbdf1->cbd_sc &= ~BD_SC_CLEARALL;
+
+	return count;
+}
+
+static int cpm_xfer(struct file *file, struct i2c_msg *pmsg, u_char *virt, dma_addr_t dma)
+{
+	int j, ret;
+	u_char addr;
+    
+	if (cpm_debug)
+	        printk("cpm_xfer: "
+		       "addr=0x%x flags=0x%x len=%d virt=%p dma=%x\n",
+		       pmsg->addr, pmsg->flags, pmsg->len, virt, dma);
+
+	addr = pmsg->addr << 1;
+	if (pmsg->flags & I2C_M_RD )
+	        addr |= 1;
+	if (pmsg->flags & I2C_M_REV_DIR_ADDR )
+	        addr ^= 1;
+    
+	if (!(pmsg->flags & I2C_M_NOSTART)) {
+	}
+	if (pmsg->flags & I2C_M_RD ) {
+	        /* read bytes into buffer*/
+	        ret = cpm_iic_read(addr, dma, pmsg->len);
+		if (cpm_debug) {
+		        printk("cpm_xfer: read %d bytes ", ret);
+			for(j = 0; j < ret; j++)
+			        printk("%02x ", virt[j]);
+			printk("\n");
+		}
+		if (ret < pmsg->len ) {
+			return (ret<0)? ret : -EREMOTEIO;
+		}
+	} else {
+	        /* write bytes from buffer */
+	        ret = cpm_iic_write(addr, dma, pmsg->len);
+		if (cpm_debug) {
+		        printk("cpm_xfer: wrote %d bytes ", ret);
+			for(j = 0; j < ret; j++)
+			        printk("%02x ", virt[j]);
+			printk("\n");
+		}
+		if (ret < pmsg->len ) {
+			return (ret<0) ? ret : -EREMOTEIO;
+		}
+	}
+
+	return (ret);
+}
+
+static int cpm_iic_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	DECLARE_WAITQUEUE(cbdwait, current);
+	struct i2c_rdwr_ioctl_data rdwr_arg;
+	struct i2c_msg *rdwr_pa;
+	u8 __user *virt_ptr;
+	dma_addr_t dma_addr;
+	unsigned long orig;
+	int i, res;
+
+	switch(cmd) {
+	case I2C_RDWR:
+	  /* Wait for the controller to become available */
+	  orig = jiffies;     /* Remember start time */
+	  add_wait_queue(&iic_cbd_wait, &cbdwait);
+	  set_current_state(TASK_INTERRUPTIBLE);
+	  while(1) {
+                /* Test and set 'in use' flags, update of 'current'  must be atomic */
+         	spin_lock_irq(&cpm_i2c_8xx.lock);
+	        if (in_use == 0) {
+		        in_use = 1;
+		        spin_unlock_irq(&cpm_i2c_8xx.lock);
+		        break;
+		}
+		spin_unlock_irq(&cpm_i2c_8xx.lock);
+
+	        if ((jiffies - orig) >= CPM_TIMEOUT) {
+			__set_current_state(TASK_RUNNING);
+			remove_wait_queue(&iic_cbd_wait, &cbdwait);
+			printk(KERN_DEBUG "cpm_iic_read: timed out wait for controller\n");
+			return -ETIMEDOUT;
+		}
+		schedule_timeout(CPM_TIMEOUT);
+
+		if (signal_pending(current)) {
+		        __set_current_state(TASK_RUNNING);
+		        remove_wait_queue(&iic_cbd_wait, &cbdwait);
+		        return -EIO;
+		}
+	  }
+
+	  __set_current_state(TASK_RUNNING);
+	  remove_wait_queue(&iic_cbd_wait, &cbdwait);
+
+	  /* Local copy of all args */
+	  if (copy_from_user(&rdwr_arg, 
+			     (struct i2c_rdwr_ioctl_data __user *)arg, 
+			     sizeof(rdwr_arg))) {
+	    res = -EFAULT;
+	    goto rdwr_out;
+	  }
+
+	  /* Put an arbritrary limit on the number of messages that can
+	   * be sent at once */
+	  if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) {
+	    res = -EINVAL;
+	    goto rdwr_out;
+	  }
+		
+	  /* Buffer for local copy of msgs pointed to by args */
+	  rdwr_pa = (struct i2c_msg *)
+	    kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), 
+		    GFP_KERNEL);
+	  if (rdwr_pa == NULL) {
+	    res = -ENOMEM;
+	    goto rdwr_out;
+	  }
+
+	  /* Copy header portion of the messages into local buffer */
+	  if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
+			     rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
+	    kfree(rdwr_pa);
+	    res = -EFAULT;
+	    goto rdwr_out;
+	  }
+	  
+	  res = 0;
+	  /* For each message ... */
+	  for( i=0; i<rdwr_arg.nmsgs; i++ ) {
+	    /* Limit the size of the message to a sane amount */
+	    if (rdwr_pa[i].len > 8192) {
+	      res = -EINVAL;
+	      break;
+	    }
+	    /* Allocate a DMA buffer */
+	    virt_ptr = dma_alloc_coherent(NULL, L1_CACHE_ALIGN(rdwr_pa[i].len), &dma_addr, GFP_KERNEL);
+	    if(virt_ptr == NULL) {
+	      res = -ENOMEM;
+	      break;
+	    }
+	    /* Copy the data buffer portion of the message */
+	    if(copy_from_user(virt_ptr,
+			      rdwr_pa[i].buf,
+			      rdwr_pa[i].len)) {
+	      ++i; /* Needs to be kfreed too */
+	      res = -EFAULT;
+	      break;
+	    }
+	    /* Do the actual transfer */
+	    res = cpm_xfer(file, &rdwr_pa[i], virt_ptr, dma_addr);
+	    if (cpm_debug)
+	      printk(KERN_ERR "i2c_rdwr: cpm_xfer returned %d\n", res);
+
+	    /* If a read, copy the resulting data back */
+	    if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {
+	      if(copy_to_user(rdwr_pa[i].buf,
+			      virt_ptr,
+			      rdwr_pa[i].len)) {
+		res = -EFAULT;
+	      }
+	    }
+	    dma_free_coherent(NULL, L1_CACHE_ALIGN(rdwr_pa[i].len), virt_ptr, dma_addr);
+	  }
+
+	  kfree(rdwr_pa);
+rdwr_out:
+	  in_use = 0;
+	  wake_up_interruptible(&iic_cbd_wait);
+	  schedule();
+	  return res;
+	default:
+	  return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct file_operations cpm_iic_fops = {
+	owner:		THIS_MODULE,
+	ioctl:		cpm_iic_ioctl,
+	open:		cpm_iic_open,
+	release:	cpm_iic_release,
+};
+
+static struct miscdevice cpm_iic_miscdev = {
+	minor:		CPM_IIC_MINOR,
+	name:		"i2c-0",
+	fops:		&cpm_iic_fops,
+};
+
+static int __init cpm_iic_init(void)
+{
+	volatile iic_t		*iip;
+	volatile i2c8xx_t	*i2c;
+	volatile cbd_t          *bdp;
+	volatile cpm8xx_t       *cp;
+	volatile immap_t        *immap;
+	int j, ret;
+
+	ret = misc_register(&cpm_iic_miscdev);
+	if (ret)
+		return ret;
+
+	cp = cpmp;	/* Get pointer to Communication Processor */
+	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+
+	cpm_i2c_8xx.iip = iip = (iic_t *)&cp->cp_dparam[PROFF_IIC];
+	cpm_i2c_8xx.reloc = 0;
+
+	/* Check for and use a microcode relocation patch.
+	*/
+#if 0
+	if ((cpm_i2c_8xx.reloc = cpm_i2c_8xx.iip->iic_rpbase))
+		cpm_i2c_8xx.iip = (iic_t *)&cp->cp_dpmem[cpm_i2c_8xx.iip->iic_rpbase];
+#endif
+	cpm_debug = 0;
+		
+	cpm_i2c_8xx.i2c = i2c = (i2c8xx_t *)&(immap->im_i2c);
+	cpm_i2c_8xx.cp = cp;
+	cpm_i2c_8xx.lock = SPIN_LOCK_UNLOCKED;
+
+	/* Initialize Port B IIC pins.
+	*/
+	cp->cp_pbpar |= 0x00000030;
+	cp->cp_pbdir |= 0x00000030;
+	cp->cp_pbodr |= 0x00000030;
+
+	/* Allocate space for two transmit and two receive buffer
+	 * descriptors in the DP ram.
+	 */
+	cpm_i2c_8xx.dp_addr = cpm_dpalloc(sizeof(cbd_t) * (CPM_NUM_BDS*2), 8);
+	if (cpm_debug) printk("cpm_iic_init: dp_addr %x\n", cpm_i2c_8xx.dp_addr);
+
+	/* ptr to i2c area */
+	cpm_i2c_8xx.i2c = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c);
+
+	if (cpm_debug) printk("cpm_iic_init() - iip=%p\n",iip);
+
+	/* Initialize the parameter ram.
+	 * We need to make sure many things are initialized to zero,
+	 * especially in the case of a microcode patch.
+	 */
+	iip->iic_rstate = 0;
+	iip->iic_rdp = 0;
+	iip->iic_rbptr = 0;
+	iip->iic_rbc = 0;
+	iip->iic_rxtmp = 0;
+	iip->iic_tstate = 0;
+	iip->iic_tdp = 0;
+	iip->iic_tbptr = 0;
+	iip->iic_tbc = 0;
+	iip->iic_txtmp = 0;
+
+	/* Set up the IIC parameters in the parameter ram.
+	*/
+	iip->iic_tbase = r_tbase = cpm_i2c_8xx.dp_addr;
+	iip->iic_rbase = r_rbase = cpm_i2c_8xx.dp_addr + sizeof(cbd_t)*CPM_NUM_BDS;
+
+	tbdf_cur = tbdf_base = (cbd_t *)&cp->cp_dpmem[iip->iic_tbase];
+	rbdf_cur = rbdf_base = (cbd_t *)&cp->cp_dpmem[iip->iic_rbase];
+
+	bdp = tbdf_cur;
+	for (j=0; j<(CPM_NUM_BDS-1); j++) {
+	  bdp->cbd_sc = 0;
+	  /* bdp->cbd_sc &= ~( BD_SC_LAST | BD_SC_INTRPT | BD_IIC_START); */
+	  bdp++;
+	}
+	bdp->cbd_sc = 0;
+	/* bdp->cbd_sc &= ~( BD_SC_LAST | BD_SC_INTRPT | BD_IIC_START); */
+	bdp->cbd_sc |= BD_SC_WRAP;
+
+	bdp = rbdf_cur;
+	for (j=0; j<(CPM_NUM_BDS-1); j++) {
+	  bdp->cbd_sc = BD_SC_INTRPT;
+	  bdp->cbd_sc &= ~BD_SC_EMPTY;
+	  bdp++;
+	}
+	bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT;
+	bdp->cbd_sc &= ~BD_SC_EMPTY;
+
+	if (cpm_debug)
+	  printk("cpm_iic_init: tbdf_cur=%p rbdf_cur=%p\n", tbdf_cur, rbdf_cur);
+
+	in_use = 0;
+
+	iip->iic_tfcr = SMC_EB;
+	iip->iic_rfcr = SMC_EB;
+
+	/* Set maximum receive size.
+	*/
+	iip->iic_mrblr = CPM_MAX_READ;
+
+	/* Initialize Tx/Rx parameters.
+	*/
+	if (cpm_i2c_8xx.reloc == 0) {
+		volatile cpm8xx_t *cp = cpm_i2c_8xx.cp;
+
+		cp->cp_cpcr =
+			mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+		while (cp->cp_cpcr & CPM_CR_FLG);
+	}
+
+	/* Select an arbitrary address.  Just make sure it is unique.
+	*/
+	i2c->i2c_i2add = 0x34;
+
+	/* Make clock run maximum slow.
+	*/
+	i2c->i2c_i2brg = 7;
+
+	/* Disable interrupts.
+	*/
+	i2c->i2c_i2cmr = 0;
+	i2c->i2c_i2cer = 0xff;
+
+	init_waitqueue_head(&iic_rd_wait);
+	init_waitqueue_head(&iic_wr_wait);
+	init_waitqueue_head(&iic_cbd_wait);
+
+	/* Install interrupt handler.
+	*/
+	if (cpm_debug) {
+		printk ("%s[%d] Install ISR for IRQ %d\n",
+			__func__,__LINE__, CPMVEC_I2C);
+	}
+	request_irq(CPMVEC_I2C + CPM_IRQ_OFFSET, cpm_iic_interrupt, 0, "cpm_i2c", (void *)i2c);
+
+	//cpm_debug = 1;
+
+	return(0);
+}
+
+static int
+cpm_iic_release(struct inode *inode, struct file *file)
+{
+  /*
+   * We don't do anything in the open, so don't do anything in the close.
+   * Any operation on the physical device could affect other tasks that 
+   * might be using it.
+   */
+	return(0);
+}
+
+static void __exit cpm_iic_exit(void)
+{
+	misc_deregister(&cpm_iic_miscdev);
+}
+
+module_init(cpm_iic_init);
+module_exit(cpm_iic_exit);
+
+MODULE_AUTHOR("Ken Poole");
+MODULE_DESCRIPTION("MRV LX CPM_i2c Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(CPM_IIC_MINOR);

--- /home/cmurch/kernels/linux-2.6.18/include/linux/i2c-dev.h	2006-09-19 23:42:06.000000000 -0400
+++ i2c-dev.h	2006-10-23 16:00:55.000000000 -0400
@@ -19,6 +19,8 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+/* $Id: i2c-dev.h,v 1.2 2005/02/23 14:50:02 kpoole Exp $ */
+
 #ifndef _LINUX_I2C_DEV_H
 #define _LINUX_I2C_DEV_H
 
@@ -36,6 +38,22 @@
 	union i2c_smbus_data __user *data;
 };
 
+/*
+ * I2C Message - used for pure i2c transaction, also from /dev interface
+ */
+struct i2c_msg {
+	__u16 addr;	/* slave address			*/
+ 	__u16 flags;		
+#define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
+#define I2C_M_RD	0x01
+#define I2C_M_NOSTART	0x4000
+#define I2C_M_REV_DIR_ADDR	0x2000
+#define I2C_M_IGNORE_NAK	0x1000
+#define I2C_M_NO_RD_ACK		0x0800
+ 	__u16 len;		/* msg length				*/
+ 	__u8 *buf;		/* pointer to msg data			*/
+};
+
 /* This is the structure as used in the I2C_RDWR ioctl call */
 struct i2c_rdwr_ioctl_data {
 	struct i2c_msg __user *msgs;	/* pointers to i2c_msgs */
@@ -44,4 +62,6 @@
 
 #define  I2C_RDRW_IOCTL_MAX_MSGS	42
 
+#define I2C_RDWR      0x0707  /* Combined R/W transfer (one stop only)*/
+
 #endif /* _LINUX_I2C_DEV_H */

^ permalink raw reply

* Re: 2.6.19-rc2: known unfixed regressions (v3)
From: Adrian Bunk @ 2006-10-23 20:59 UTC (permalink / raw)
  To: Meelis Roos
  Cc: Andrew Morton, linuxppc-dev, Linus Torvalds, paulus,
	Linux Kernel Mailing List
In-Reply-To: <Pine.SOC.4.61.0610231757590.27929@math.ut.ee>

On Mon, Oct 23, 2006 at 06:20:18PM +0300, Meelis Roos wrote:
> >Subject    : ppc prep boot hang
> >References : http://lkml.org/lkml/2006/10/14/58
> >Submitter  : Meelis Roos <mroos@linux.ee>
> >Status     : unknown
> 
> Seems to be fixed in 2.6.19-rc2+git as of 20061022.

Thanks for the information, I've removed it from my list.

> Meelis Roos

cu
Adrian

-- 

       "Is there not promise of rain?" Ling Tan asked suddenly out
        of the darkness. There had been need of rain for many days.
       "Only a promise," Lao Er said.
                                       Pearl S. Buck - Dragon Seed

^ permalink raw reply

* Re: [PATCH 1/5] powerpc: consolidate feature fixup code
From: Benjamin Herrenschmidt @ 2006-10-23 21:12 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev list
In-Reply-To: <17724.39959.51600.232589@cargo.ozlabs.ibm.com>


> > Index: linux-cell/include/asm-powerpc/cputable.h
> > ===================================================================
> > --- linux-cell.orig/include/asm-powerpc/cputable.h	2006-10-13 16:00:28.000000000 +1000
> > +++ linux-cell/include/asm-powerpc/cputable.h	2006-10-13 16:19:51.000000000 +1000
> > @@ -89,8 +89,7 @@ struct cpu_spec {
> >  
> >  extern struct cpu_spec		*cur_cpu_spec;
> >  
> > -extern void identify_cpu(unsigned long offset, unsigned long cpu);
> > -extern void do_cpu_ftr_fixups(unsigned long offset);
> > +extern struct cpu_spec *identify_cpu(unsigned long offset);
> 
> ... breaks the ARCH=ppc build.

Gack... I though I tested, I must have screwed up. Should be easy enough
to fix though. I'll have a look.

Ben.

^ permalink raw reply

* Re: [PATCH] General CHRP/MPC5K2 Platform and drivers support - to comment
From: Benjamin Herrenschmidt @ 2006-10-23 21:38 UTC (permalink / raw)
  To: Matt Sealey; +Cc: linuxppc-dev
In-Reply-To: <453CD615.4050401@genesi-usa.com>

On Mon, 2006-10-23 at 16:47 +0200, Matt Sealey wrote:
> Curious question.
> 
> What are you guys going to do when the PowerPC name is defunct?

It's not really, is it ? I don't care anyway, it will stay PowerPC in
linux of course :)

> Like, last month :)
> 
> Power Architecture is where it is at. The trademark is even going to
> lapse

Who cares ? Besides, while PAPR says "Power architecture", the processor
architecture specification says PowerPC :)

> . It's a bit too late for the ppc->powerpc tree breakout now,
> but wouldn't it just confuse people to be using a "Power Architecture"
> processor or SoC of some type, using collections of definitions from
> the Power ISA 2.03 and have this "powerpc" thing pop up?

It's PowerPC ISA :)

> It confused me even before, because ppc and ppc64 have also been
> used to support real POWER (with a capital P, O, W, E and R) processors,
> and now these are lumped in with powerpc which is no better than
> ppc64 in these terms?

Historically, POWER means something else ... then with POWER3, POWER
processors became compatible with the PowerPC architecture, then IBM
played name changing game a couple of times and nobody knows what's up
anymore :)
 
> Just flexing my marketing exec muscles, see if they work, never done
> it before. Oh... *crack*.. that wasn't a good noise :]

AFAIK, the processor instruction set architecture is PowerPC an that
will not change.

Ben.

^ permalink raw reply

* xapp902 with 2.6.17 -- uartlite & ramdisk problems
From: robert corley @ 2006-10-23 22:32 UTC (permalink / raw)
  To: linux linuxppc-embedded

All;=0A=0AAs the below shows, the ramdisk problem has been circumvented by =
use of the cmdline option ramdisk_size=3D4660000.=0A=0AStill experience ter=
ribly slow uart.  Can anyone quickly point me to the appropriate interrupt =
components for the uartlite et al?=0A=0ASome other questions:=0A=0A1.   Are=
 interrupts obtained via the platform_get_resource() call?=0A2.   Are alloc=
ated interrupts issued starting at 0?=0A3.    Is there a correlation betwee=
n the xparameters_ml403.h entries below and the interrupt issued in 1, abov=
e?=0A=0A#define XPAR_PLB_TEMAC_0_IP2INTC_IRPT_MASK         0X000001=0A#defi=
ne XPAR_OPB_INTC_0_PLB_TEMAC_0_IP2INTC_IRPT_INTR     0=0A#define XPAR_XUL_U=
ART_INTERRUPT_MASK             0X000002=0A#define XPAR_OPB_INTC_0_XUL_UART_=
INTERRUPT_INTR     1=0A=0A#define XPAR_INTC_0_TEMAC_0_VEC_ID     XPAR_OPB_I=
NTC_0_PLB_TEMAC_0_IP2INTC_IRPT_INTR=0A#define XPAR_INTC_0_UARTLITE_0_VEC_ID=
     XPAR_OPB_INTC_0_XUL_UART_INTERRUPT_INTR=0A=0AThanks in advance for any=
ones help.=0A=0A-cy=0A=0A=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=0A[    0.000000] Linux version 2.6.17.1 (rdcorle@athena) (gcc version 3.4.=
2) #4 Mon Oct 23 19:47:51 UTC 2006=0A[    0.000000] Xilinx ML403 Reference =
System (Virtex-4 FX)=0A[    0.000000] Built 1 zonelists=0A[    0.000000] Ke=
rnel command line: console=3DttyUL0 single ip=3Doff ramdisk_size=3D4660000 =
root=3D/dev/ram rw=0A[    0.000000] Xilinx INTC #0 at 0xD1000FC0 mapped to =
0xFDFFFFC0=0A[    0.000000] PID hash table entries: 512 (order: 9, 2048 byt=
es)=0A[    0.000151] Console: colour dummy device 80x25=0A[    0.000560] De=
ntry cache hash table entries: 8192 (order: 3, 32768 bytes)=0A[    0.001270=
] Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)=0A[    0.013=
923] Memory: 58572k available (1348k kernel code, 344k data, 80k init, 0k h=
ighmem)=0A[    0.200437] Mount-cache hash table entries: 512=0A[    0.20311=
9] checking if image is initramfs...it isn't (no cpio magic); looks like an=
 initrd=0A[    2.505388] Freeing initrd memory: 4549k freed=0A[    2.509461=
] NET: Registered protocol family 16=0A[    2.518975] NET: Registered proto=
col family 2=0A[    2.583987] IP route cache hash table entries: 512 (order=
: -1, 2048 bytes)=0A[    2.584462] TCP established hash table entries: 2048=
 (order: 3, 32768 bytes)=0A[    2.584931] TCP bind hash table entries: 1024=
 (order: 2, 20480 bytes)=0A[    2.585217] TCP: Hash tables configured (esta=
blished 2048 bind 1024)=0A[    2.585251] TCP reno registered=0A[    2.59163=
8] io scheduler noop registered=0A[    2.591747] io scheduler anticipatory =
registered (default)=0A[    2.591902] io scheduler deadline registered=0A[ =
   2.592039] io scheduler cfq registered=0A[    2.637280] serial irq #1 obt=
ained from platform=0A[    2.637397] uartlite.0: ttyUL0 at MMIO 0xa0000000 =
(irq =3D 1) is a uartlite=0A[   42.667249] RAMDISK driver initialized: 16 R=
AM disks of 4660000K size 1024 blocksize=0A[   44.738763] eth%d: XTemac usi=
ng sgDMA mode.=0A[   45.866885] eth%d: XTemac buffer_descriptor_space: phy:=
 0xbb8000, virt: 0xff100000, size: 0x5000=0A[   48.220095] eth0: Xilinx TEM=
AC #0 at 0x60000000 mapped to 0xC5008000, irq=3D0=0A[   50.084940] mice: PS=
/2 mouse device common for all mice=0A[   51.489111] TCP bic registered=0A[=
   52.319241] RAMDISK: Compressed image found at block 0=0A[   56.598970] V=
FS: Mounted root (ext2 filesystem).=0A[   57.842088] Freeing unused kernel =
memory: 80k init=0A=0A=0ABusyBox v1.00-pre9 (2004.04.18-19s# =0A=0A=0A

^ permalink raw reply

* Re: [PATCH] General CHRP/MPC5K2 Platform and drivers support - to comment
From: Paul Mackerras @ 2006-10-23 22:57 UTC (permalink / raw)
  To: Matt Sealey; +Cc: linuxppc-dev
In-Reply-To: <453CD615.4050401@genesi-usa.com>

Matt Sealey writes:

> It confused me even before, because ppc and ppc64 have also been
> used to support real POWER (with a capital P, O, W, E and R) processors,
> and now these are lumped in with powerpc which is no better than
> ppc64 in these terms?

"PowerPC" is the name of the instruction set architecture, "POWER" is
the name of some specific implementations of the PowerPC architecture
made by a certain large multinational computer company.  Unless you're
talking about the really really old ISA that was the predecessor of
PowerPC, that is. :)

Paul.

^ permalink raw reply

* [RFC]: map 4K iommu pages even on 64K largepage systems.
From: Linas Vepstas @ 2006-10-24  0:25 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Olof Johansson


Subject: [RFC]: map 4K iommu pages even on 64K largepage systems.

The 10Gigabit ethernet device drivers appear to be able to chew
up all 256MB of TCE mappings on pSeries systems, as evidenced by
numerous error messages:

 iommu_alloc failed, tbl c0000000010d5c48 vaddr c0000000d875eff0 npages 1

Some experimentaiton indicates that this is essentially because
one 1500 byte ethernet MTU gets mapped as a 64K DMA region when
the large 64K pages are enabled. Thus, it doesn't take much to
exhaust all of the available DMA mappings for a high-speed card.

This patch changes the iommu allocator to work with its own 
unique, distinct page size. Although the patch is long, its
actually quite simple: it just #defines  distinct IOMMU_PAGE_SIZE
and then uses this in al the places tha matter.

The patch boots on pseries, untested in other places.

Haven't yet thought if this is a good long-term solution or not,
whether this kind of thing is desirable or not.  That's why its 
an RFC.  Comments?

--linas

Cc: Olof Johansson <olof@lixom.net>

----
 arch/powerpc/kernel/iommu.c            |   63 ++++++++++++++++++---------------
 arch/powerpc/kernel/vio.c              |    4 +-
 arch/powerpc/platforms/pseries/iommu.c |   13 +++---
 include/asm-powerpc/iommu.h            |   27 +++++++++++++-
 include/asm-powerpc/tce.h              |    4 +-
 5 files changed, 74 insertions(+), 37 deletions(-)

Index: linux-2.6.19-rc1-git11/arch/powerpc/kernel/iommu.c
===================================================================
--- linux-2.6.19-rc1-git11.orig/arch/powerpc/kernel/iommu.c	2006-10-13 11:54:00.000000000 -0500
+++ linux-2.6.19-rc1-git11/arch/powerpc/kernel/iommu.c	2006-10-23 18:41:38.000000000 -0500
@@ -47,6 +47,15 @@ static int novmerge = 0;
 static int novmerge = 1;
 #endif
 
+static inline unsigned long
+iommu_num_pages(unsigned long vaddr, unsigned long slen)
+{
+	unsigned long npages;
+	npages = IOMMU_PAGE_ALIGN(vaddr + slen) - (vaddr & IOMMU_PAGE_MASK);
+	npages >>= IOMMU_PAGE_SHIFT;
+	return npages;
+}
+
 static int __init setup_iommu(char *str)
 {
 	if (!strcmp(str, "novmerge"))
@@ -178,10 +187,10 @@ static dma_addr_t iommu_alloc(struct iom
 	}
 
 	entry += tbl->it_offset;	/* Offset into real TCE table */
-	ret = entry << PAGE_SHIFT;	/* Set the return dma address */
+	ret = entry << IOMMU_PAGE_SHIFT;	/* Set the return dma address */
 
 	/* Put the TCEs in the HW table */
-	ppc_md.tce_build(tbl, entry, npages, (unsigned long)page & PAGE_MASK,
+	ppc_md.tce_build(tbl, entry, npages, (unsigned long)page & IOMMU_PAGE_MASK,
 			 direction);
 
 
@@ -203,7 +212,7 @@ static void __iommu_free(struct iommu_ta
 	unsigned long entry, free_entry;
 	unsigned long i;
 
-	entry = dma_addr >> PAGE_SHIFT;
+	entry = dma_addr >> IOMMU_PAGE_SHIFT;
 	free_entry = entry - tbl->it_offset;
 
 	if (((free_entry + npages) > tbl->it_size) ||
@@ -270,7 +279,7 @@ int iommu_map_sg(struct device *dev, str
 	/* Init first segment length for backout at failure */
 	outs->dma_length = 0;
 
-	DBG("mapping %d elements:\n", nelems);
+	DBG("sg mapping %d elements:\n", nelems);
 
 	spin_lock_irqsave(&(tbl->it_lock), flags);
 
@@ -285,9 +294,8 @@ int iommu_map_sg(struct device *dev, str
 		}
 		/* Allocate iommu entries for that segment */
 		vaddr = (unsigned long)page_address(s->page) + s->offset;
-		npages = PAGE_ALIGN(vaddr + slen) - (vaddr & PAGE_MASK);
-		npages >>= PAGE_SHIFT;
-		entry = iommu_range_alloc(tbl, npages, &handle, mask >> PAGE_SHIFT, 0);
+		npages = iommu_num_pages(vaddr, slen);
+		entry = iommu_range_alloc(tbl, npages, &handle, mask >> IOMMU_PAGE_SHIFT, 0);
 
 		DBG("  - vaddr: %lx, size: %lx\n", vaddr, slen);
 
@@ -301,14 +309,14 @@ int iommu_map_sg(struct device *dev, str
 
 		/* Convert entry to a dma_addr_t */
 		entry += tbl->it_offset;
-		dma_addr = entry << PAGE_SHIFT;
-		dma_addr |= s->offset;
+		dma_addr = entry << IOMMU_PAGE_SHIFT;
+		dma_addr |= (s->offset & ~IOMMU_PAGE_MASK);
 
-		DBG("  - %lx pages, entry: %lx, dma_addr: %lx\n",
+		DBG("  - %lu pages, entry: %lx, dma_addr: %lx\n",
 			    npages, entry, dma_addr);
 
 		/* Insert into HW table */
-		ppc_md.tce_build(tbl, entry, npages, vaddr & PAGE_MASK, direction);
+		ppc_md.tce_build(tbl, entry, npages, vaddr & IOMMU_PAGE_MASK, direction);
 
 		/* If we are in an open segment, try merging */
 		if (segstart != s) {
@@ -323,7 +331,7 @@ int iommu_map_sg(struct device *dev, str
 				DBG("    can't merge, new segment.\n");
 			} else {
 				outs->dma_length += s->length;
-				DBG("    merged, new len: %lx\n", outs->dma_length);
+				DBG("    merged, new len: %ux\n", outs->dma_length);
 			}
 		}
 
@@ -367,9 +375,8 @@ int iommu_map_sg(struct device *dev, str
 		if (s->dma_length != 0) {
 			unsigned long vaddr, npages;
 
-			vaddr = s->dma_address & PAGE_MASK;
-			npages = (PAGE_ALIGN(s->dma_address + s->dma_length) - vaddr)
-				>> PAGE_SHIFT;
+			vaddr = s->dma_address & IOMMU_PAGE_MASK;
+			npages = iommu_num_pages(s->dma_address, s->dma_length);
 			__iommu_free(tbl, vaddr, npages);
 			s->dma_address = DMA_ERROR_CODE;
 			s->dma_length = 0;
@@ -398,8 +405,7 @@ void iommu_unmap_sg(struct iommu_table *
 
 		if (sglist->dma_length == 0)
 			break;
-		npages = (PAGE_ALIGN(dma_handle + sglist->dma_length)
-			  - (dma_handle & PAGE_MASK)) >> PAGE_SHIFT;
+		npages = iommu_num_pages(dma_handle,sglist->dma_length);
 		__iommu_free(tbl, dma_handle, npages);
 		sglist++;
 	}
@@ -532,12 +538,11 @@ dma_addr_t iommu_map_single(struct iommu
 	BUG_ON(direction == DMA_NONE);
 
 	uaddr = (unsigned long)vaddr;
-	npages = PAGE_ALIGN(uaddr + size) - (uaddr & PAGE_MASK);
-	npages >>= PAGE_SHIFT;
+	npages = iommu_num_pages(uaddr, size);
 
 	if (tbl) {
 		dma_handle = iommu_alloc(tbl, vaddr, npages, direction,
-					 mask >> PAGE_SHIFT, 0);
+					 mask >> IOMMU_PAGE_SHIFT, 0);
 		if (dma_handle == DMA_ERROR_CODE) {
 			if (printk_ratelimit())  {
 				printk(KERN_INFO "iommu_alloc failed, "
@@ -545,7 +550,7 @@ dma_addr_t iommu_map_single(struct iommu
 						tbl, vaddr, npages);
 			}
 		} else
-			dma_handle |= (uaddr & ~PAGE_MASK);
+			dma_handle |= (uaddr & ~IOMMU_PAGE_MASK);
 	}
 
 	return dma_handle;
@@ -557,8 +562,8 @@ void iommu_unmap_single(struct iommu_tab
 	BUG_ON(direction == DMA_NONE);
 
 	if (tbl)
-		iommu_free(tbl, dma_handle, (PAGE_ALIGN(dma_handle + size) -
-					(dma_handle & PAGE_MASK)) >> PAGE_SHIFT);
+		iommu_free(tbl, dma_handle, (IOMMU_PAGE_ALIGN(dma_handle + size) -
+					(dma_handle & IOMMU_PAGE_MASK)) >> IOMMU_PAGE_SHIFT);
 }
 
 /* Allocates a contiguous real buffer and creates mappings over it.
@@ -571,6 +576,7 @@ void *iommu_alloc_coherent(struct iommu_
 	void *ret = NULL;
 	dma_addr_t mapping;
 	unsigned int npages, order;
+	unsigned int nio_pages, io_order;
 	struct page *page;
 
 	size = PAGE_ALIGN(size);
@@ -598,8 +604,10 @@ void *iommu_alloc_coherent(struct iommu_
 	memset(ret, 0, size);
 
 	/* Set up tces to cover the allocated range */
-	mapping = iommu_alloc(tbl, ret, npages, DMA_BIDIRECTIONAL,
-			      mask >> PAGE_SHIFT, order);
+	nio_pages = size >> IOMMU_PAGE_SHIFT;
+	io_order = get_iommu_order(size);
+	mapping = iommu_alloc(tbl, ret, nio_pages, DMA_BIDIRECTIONAL,
+			      mask >> IOMMU_PAGE_SHIFT, io_order);
 	if (mapping == DMA_ERROR_CODE) {
 		free_pages((unsigned long)ret, order);
 		return NULL;
@@ -614,9 +622,10 @@ void iommu_free_coherent(struct iommu_ta
 	unsigned int npages;
 
 	if (tbl) {
-		size = PAGE_ALIGN(size);
-		npages = size >> PAGE_SHIFT;
+		size = IOMMU_PAGE_ALIGN(size);
+		npages = size >> IOMMU_PAGE_SHIFT;
 		iommu_free(tbl, dma_handle, npages);
+		size = PAGE_ALIGN(size);
 		free_pages((unsigned long)vaddr, get_order(size));
 	}
 }
Index: linux-2.6.19-rc1-git11/include/asm-powerpc/iommu.h
===================================================================
--- linux-2.6.19-rc1-git11.orig/include/asm-powerpc/iommu.h	2006-09-19 22:42:06.000000000 -0500
+++ linux-2.6.19-rc1-git11/include/asm-powerpc/iommu.h	2006-10-23 18:28:21.000000000 -0500
@@ -23,16 +23,41 @@
 #ifdef __KERNEL__
 
 #include <asm/types.h>
+#include <linux/compiler.h>
 #include <linux/spinlock.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 
+#define IOMMU_PAGE_SHIFT      12
+#define IOMMU_PAGE_SIZE       (ASM_CONST(1) << IOMMU_PAGE_SHIFT)
+#define IOMMU_PAGE_MASK       (~((1 << IOMMU_PAGE_SHIFT) - 1))
+#define IOMMU_PAGE_ALIGN(addr) _ALIGN_UP(addr, IOMMU_PAGE_SIZE)
+
+#ifndef __ASSEMBLY__
+
+/* Pure 2^n version of get_order */
+static __inline__ __attribute_const__ int get_iommu_order(unsigned long size)
+{
+	int order;
+
+	size = (size - 1) >> (IOMMU_PAGE_SHIFT - 1);
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+	return order;
+}
+
+#endif   /* __ASSEMBLY__ */
+
+
 /*
  * IOMAP_MAX_ORDER defines the largest contiguous block
  * of dma space we can get.  IOMAP_MAX_ORDER = 13
  * allows up to 2**12 pages (4096 * 4096) = 16 MB
  */
-#define IOMAP_MAX_ORDER 13
+#define IOMAP_MAX_ORDER (25 - PAGE_SHIFT)
 
 struct iommu_table {
 	unsigned long  it_busno;     /* Bus number this table belongs to */
Index: linux-2.6.19-rc1-git11/include/asm-powerpc/tce.h
===================================================================
--- linux-2.6.19-rc1-git11.orig/include/asm-powerpc/tce.h	2006-09-19 22:42:06.000000000 -0500
+++ linux-2.6.19-rc1-git11/include/asm-powerpc/tce.h	2006-10-23 15:46:57.000000000 -0500
@@ -22,6 +22,8 @@
 #define _ASM_POWERPC_TCE_H
 #ifdef __KERNEL__
 
+#include <asm/iommu.h>
+
 /*
  * Tces come in two formats, one for the virtual bus and a different
  * format for PCI
@@ -33,7 +35,7 @@
 
 #define TCE_SHIFT	12
 #define TCE_PAGE_SIZE	(1 << TCE_SHIFT)
-#define TCE_PAGE_FACTOR	(PAGE_SHIFT - TCE_SHIFT)
+#define TCE_PAGE_FACTOR	(IOMMU_PAGE_SHIFT - TCE_SHIFT)
 
 #define TCE_ENTRY_SIZE		8		/* each TCE is 64 bits */
 
Index: linux-2.6.19-rc1-git11/arch/powerpc/kernel/vio.c
===================================================================
--- linux-2.6.19-rc1-git11.orig/arch/powerpc/kernel/vio.c	2006-10-13 11:52:51.000000000 -0500
+++ linux-2.6.19-rc1-git11/arch/powerpc/kernel/vio.c	2006-10-23 16:06:43.000000000 -0500
@@ -92,9 +92,9 @@ static struct iommu_table *vio_build_iom
 				&tbl->it_index, &offset, &size);
 
 		/* TCE table size - measured in tce entries */
-		tbl->it_size = size >> PAGE_SHIFT;
+		tbl->it_size = size >> IOMMU_PAGE_SHIFT;
 		/* offset for VIO should always be 0 */
-		tbl->it_offset = offset >> PAGE_SHIFT;
+		tbl->it_offset = offset >> IOMMU_PAGE_SHIFT;
 		tbl->it_busno = 0;
 		tbl->it_type = TCE_VB;
 
Index: linux-2.6.19-rc1-git11/arch/powerpc/platforms/pseries/iommu.c
===================================================================
--- linux-2.6.19-rc1-git11.orig/arch/powerpc/platforms/pseries/iommu.c	2006-10-13 11:54:00.000000000 -0500
+++ linux-2.6.19-rc1-git11/arch/powerpc/platforms/pseries/iommu.c	2006-10-23 18:42:03.000000000 -0500
@@ -289,7 +289,7 @@ static void iommu_table_setparms(struct 
 	tbl->it_busno = phb->bus->number;
 
 	/* Units of tce entries */
-	tbl->it_offset = phb->dma_window_base_cur >> PAGE_SHIFT;
+	tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT;
 
 	/* Test if we are going over 2GB of DMA space */
 	if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) {
@@ -300,7 +300,7 @@ static void iommu_table_setparms(struct 
 	phb->dma_window_base_cur += phb->dma_window_size;
 
 	/* Set the tce table size - measured in entries */
-	tbl->it_size = phb->dma_window_size >> PAGE_SHIFT;
+	tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT;
 
 	tbl->it_index = 0;
 	tbl->it_blocksize = 16;
@@ -325,8 +325,8 @@ static void iommu_table_setparms_lpar(st
 	tbl->it_base   = 0;
 	tbl->it_blocksize  = 16;
 	tbl->it_type = TCE_PCI;
-	tbl->it_offset = offset >> PAGE_SHIFT;
-	tbl->it_size = size >> PAGE_SHIFT;
+	tbl->it_offset = offset >> IOMMU_PAGE_SHIFT;
+	tbl->it_size = size >> IOMMU_PAGE_SHIFT;
 }
 
 static void iommu_bus_setup_pSeries(struct pci_bus *bus)
@@ -522,8 +522,6 @@ static void iommu_dev_setup_pSeriesLP(st
 	const void *dma_window = NULL;
 	struct pci_dn *pci;
 
-	DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, pci_name(dev));
-
 	/* dev setup for LPAR is a little tricky, since the device tree might
 	 * contain the dma-window properties per-device and not neccesarily
 	 * for the bus. So we need to search upwards in the tree until we
@@ -532,6 +530,9 @@ static void iommu_dev_setup_pSeriesLP(st
 	 */
 	dn = pci_device_to_OF_node(dev);
 
+	DBG("iommu_dev_setup_pSeriesLP, dev %p (%s) %s\n",
+	     dev, pci_name(dev), dn->full_name);
+
 	for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
 	     pdn = pdn->parent) {
 		dma_window = get_property(pdn, "ibm,dma-window", NULL);

^ permalink raw reply

* Re: [RFC]: map 4K iommu pages even on 64K largepage systems.
From: Geoff Levand @ 2006-10-24  0:50 UTC (permalink / raw)
  To: Linas Vepstas; +Cc: Olof Johansson, linuxppc-dev
In-Reply-To: <20061024002540.GA6360@austin.ibm.com>

Linas Vepstas wrote:
> Subject: [RFC]: map 4K iommu pages even on 64K largepage systems.
...
> Some experimentaiton indicates that this is essentially because
> one 1500 byte ethernet MTU gets mapped as a 64K DMA region when
> the large 64K pages are enabled. Thus, it doesn't take much to
> exhaust all of the available DMA mappings for a high-speed card.
> 
> This patch changes the iommu allocator to work with its own 
> unique, distinct page size. Although the patch is long, its
> actually quite simple: it just #defines  distinct IOMMU_PAGE_SIZE
> and then uses this in al the places tha matter.

This is a step in the right direction.  Cell allows each device to
have its own io pagesize, from among those that the io controller
supports (4k, 64k, 1m,16m).  This limitation of the current iommu
code that you try to address here has caused me to use platform
specific dma alloc routines.

-Geoff

^ permalink raw reply

* Re: [PATCH] Preliminary MPIC MSI backend
From: Michael Ellerman @ 2006-10-24  1:46 UTC (permalink / raw)
  To: Segher Boessenkool; +Cc: linuxppc-dev, linux-kernel, Eric W. Biederman
In-Reply-To: <65FA7A1D-5D74-4ABA-9985-2DB78ABC8685@kernel.crashing.org>

[-- Attachment #1: Type: text/plain, Size: 2686 bytes --]

On Sat, 2006-09-30 at 10:43 +0200, Segher Boessenkool wrote:
> > A pretty hackish MPIC backend, just enough to flesh out the design.
> > Based on code from Segher.
> 
> It's pretty alright, and very hackish ;-)  I'll sign off on it,
> but some comments...
> 
> Signed-off-by: Segher Boessenkool <segher@kernel.crashing.org>
> > Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
> 
> > +static int msi_mpic_check(struct pci_dev *pdev, int num,
> > +			struct msix_entry *entries, int type)
> > +{
> > +	/* The irq allocator needs more work to support MSI-X/multi-MSI */
> > +	if (type == PCI_CAP_ID_MSIX || num != 1)
> > +		return 1;
> 
> I never tested any MSI-X, so maybe keep MSI-X disabled completely
> for now?

Yeah it is, that's an ||.

> > +static int msi_mpic_alloc(struct pci_dev *pdev, int num,
> > +			struct msix_entry *entries, int type)
> > +{
> > +	irq_hw_number_t hwirq;
> > +	unsigned int virq;
> > +
> > +	/* We need a smarter allocator for MSI-X/multi-MSI */
> > +	hwirq = irq_map[pdev->irq].hwirq;
> > +	hwirq += 100;
> 
> Yep, that's the main problem with this code.  A sanity check to
> make sure the number isn't >= 120 would be good, too.

Talking to Ben, we decided for the moment we'll just reuse the currently
assigned irq, in the medium term we'll come up with some way to find the
unassigned irqs on mpic and write an allocator.

> > +	set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
> 
> I also had some code to show MSI IRQs as "MSI" instead of "EDGE"
> in /proc/interrupts, maybe you want to add a generic version of
> that?  Or maybe you have, and I judt didn't see it.

I lost that along the way somewhere, I'll try and find it and resurrect
it.

> > +static int msi_mpic_setup_msi_msg(struct pci_dev *pdev,
> > +		struct msix_entry *entry, struct msi_msg *msg, int type)
> > +{
> > +	msg->address_lo = 0xfee00000;	/* XXX What is this value? */
> > +	msg->address_hi = 0;
> > +	msg->data = pdev->irq | 0x8000;
> 
> Lose the | 0x8000 part, that was an old experiment to work around
> U3/U4 MPIC brokenness (and it didn't work).

OK.

> > +static int msi_mpic_init(void)
> > +{
> > +	/* XXX Do this in mpic_init ? */
> > +	pr_debug("mpic_msi_init: Registering MPIC MSI ops.\n");
> > +	ppc_md.get_msi_ops = mpic_get_msi_ops;
> 
> It's best to do this in the platform code I think.

Yeah probably, I'll leave that up to Ben.

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 191 bytes --]

^ permalink raw reply

* Re: [RFC/PATCH 3/7] Powerpc MSI ops layer
From: Michael Ellerman @ 2006-10-24  1:51 UTC (permalink / raw)
  To: Jake Moilanen; +Cc: Eric W. Biederman, linux-kernel, linuxppc-dev
In-Reply-To: <1159912492.4997.257.camel@goblue>

[-- Attachment #1: Type: text/plain, Size: 3115 bytes --]

On Tue, 2006-10-03 at 16:54 -0500, Jake Moilanen wrote:
> On Fri, 2006-09-29 at 07:53 +1000, Michael Ellerman wrote:
> > Powerpc MSI ops layer.
> > 
> > Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
> > ---
> > 
> >  arch/powerpc/kernel/msi.c        |  347 +++++++++++++++++++++++++++++++++++++++
> >  include/asm-powerpc/machdep.h    |    6 
> >  include/asm-powerpc/msi.h        |  175 +++++++++++++++++++
> >  include/asm-powerpc/pci-bridge.h |    4 
> >  4 files changed, 532 insertions(+)
> > 
> > Index: to-merge/arch/powerpc/kernel/msi.c
> > ===================================================================
> > --- /dev/null
> > +++ to-merge/arch/powerpc/kernel/msi.c
> > @@ -0,0 +1,347 @@
> > +/*
> > + * Copyright 2006 (C), 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.
> > + */
> > +
> > +#undef DEBUG
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/slab.h>
> > +#include <asm/msi.h>
> > +#include <asm/machdep.h>
> > +
> > +static struct ppc_msi_ops *get_msi_ops(struct pci_dev *pdev)
> > +{
> > +	if (ppc_md.get_msi_ops)
> > +		return ppc_md.get_msi_ops(pdev);
> > +	return NULL;
> > +}
> > +
> > +/* Activated by pci=nomsi on the command line. */
> > +static int no_msi;
> > +
> > +void pci_no_msi(void)
> > +{
> > +	no_msi = 1;
> > +}
> > +
> > +
> > +/* msi_info helpers */
> > +
> > +static struct pci_dn *get_pdn(struct pci_dev *pdev)
> > +{
> > +	struct device_node *dn;
> > +	struct pci_dn *pdn;
> > +
> > +	dn = pci_device_to_OF_node(pdev);
> > +	if (!dn) {
> > +		pr_debug("get_pdn: no dn found for %s\n", pci_name(pdev));
> > +		return NULL;
> > +	}
> > +
> > +	pdn = PCI_DN(dn);
> > +	if (!pdn) {
> > +		pr_debug("get_pdn: no pci_dn found for %s\n", pci_name(pdev));
> > +		return NULL;
> > +	}
> > +
> > +	return pdn;
> > +}
> > +
> > +static int alloc_msi_info(struct pci_dev *pdev, int num,
> > +			struct msix_entry *entries, int type)
> > +{
> > +	struct msi_info *info;
> > +	unsigned int entries_size;
> > +	struct pci_dn *pdn;
> > +
> > +	entries_size = sizeof(struct msix_entry) * num;
> > +
> > +	info = kzalloc(sizeof(struct msi_info) + entries_size, GFP_KERNEL);
> 
> Shouldn't you do a second kzalloc for info->entries, and not just add on
> the size to the end?

We could, but I don't see why it's better. It's a little sneaky to tack
the entries on the end but I don't see a problem with it?

There is a bug in there that I don't set the entries pointer before
doing the memcpy, but I've fixed that - or is that what you meant ? :)

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 191 bytes --]

^ permalink raw reply

* Re: [RFC]: map 4K iommu pages even on 64K largepage systems.
From: Benjamin Herrenschmidt @ 2006-10-24  2:22 UTC (permalink / raw)
  To: Linas Vepstas; +Cc: Olof Johansson, linuxppc-dev
In-Reply-To: <20061024002540.GA6360@austin.ibm.com>

On Mon, 2006-10-23 at 19:25 -0500, Linas Vepstas wrote:
> Subject: [RFC]: map 4K iommu pages even on 64K largepage systems.
> 
> The 10Gigabit ethernet device drivers appear to be able to chew
> up all 256MB of TCE mappings on pSeries systems, as evidenced by
> numerous error messages:
> 
>  iommu_alloc failed, tbl c0000000010d5c48 vaddr c0000000d875eff0 npages 1
> 
> Some experimentaiton indicates that this is essentially because
> one 1500 byte ethernet MTU gets mapped as a 64K DMA region when
> the large 64K pages are enabled. Thus, it doesn't take much to
> exhaust all of the available DMA mappings for a high-speed card.

There is much to be said about using a 1500MTU and no TSO on a 10G
link :) But appart from that, I agree, we have a problem.
 
> This patch changes the iommu allocator to work with its own 
> unique, distinct page size. Although the patch is long, its
> actually quite simple: it just #defines  distinct IOMMU_PAGE_SIZE
> and then uses this in al the places tha matter.
> 
> The patch boots on pseries, untested in other places.
> 
> Haven't yet thought if this is a good long-term solution or not,
> whether this kind of thing is desirable or not.  That's why its 
> an RFC.  Comments?

It's probably a good enough solution for RHEL, but we should do
something different long term. There are a few things I have in mind:

 - We could have a page size field in the iommu_table and have the iommu
allocator use that. Thus we can have a per iommu table instance page
size. That would allow Geoff to deal with his crazy hypervisor by
basically having one iommu table instance per device. It would also
allow us to keep using large iommu page sizes on platform where the
system gives us more than a pinhole for iommu space :)

 - In the long run, I'm thinking about the interest in supporting two
page sizes for the fine and coarse allocation regions of the table. We
would need to get a bit more infos from the HW backend to do that, but
for example, on native Cell, we can have a page size per 256Mb region,
thus we could have the iommu space dividied in 4k pages for small
mappings and 64k pages for full page or more mappings.

So I reckon we should first audit and make sure your current patch works
fine on everything as a crash-fix for 2.6.19 and backportable to RHEL. 

Then, we can implement my first option for 2.6.20 and possibly debate
about the interest of my second option, unless somebody else comes up
with better ideas of course :)

Ben.

^ permalink raw reply

* Re: [RFC]: map 4K iommu pages even on 64K largepage systems.
From: Benjamin Herrenschmidt @ 2006-10-24  2:23 UTC (permalink / raw)
  To: Geoff Levand; +Cc: Olof Johansson, linuxppc-dev
In-Reply-To: <453D635B.20405@am.sony.com>


> This is a step in the right direction.  Cell allows each device to
> have its own io pagesize, from among those that the io controller
> supports (4k, 64k, 1m,16m).  This limitation of the current iommu
> code that you try to address here has caused me to use platform
> specific dma alloc routines.

Are devices -actually- using different page sizes ? AFAIK, for your
platform, you are using a 4k base page size, thus there is very little
point (in fact, it's more like a problem) to have an IO mapping using
larger page sizes.

In any case, read my reply to Linas, I'm proposing a scheme where each
iommu_table instance could have its own page size, in which case you can
just create a table instance per device.

Ben.

^ permalink raw reply

* Re: [RFC/PATCH 6/7] RTAS MSI implementation
From: Michael Ellerman @ 2006-10-24  2:49 UTC (permalink / raw)
  To: Jake Moilanen; +Cc: Eric W. Biederman, linux-kernel, linuxppc-dev
In-Reply-To: <1159912408.4997.255.camel@goblue>

[-- Attachment #1: Type: text/plain, Size: 8178 bytes --]

On Tue, 2006-10-03 at 16:53 -0500, Jake Moilanen wrote:
> On Fri, 2006-09-29 at 07:53 +1000, Michael Ellerman wrote:
> > Powerpc MSI support via RTAS. Based on Jake's code.
> > 
> > Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
> > ---
> > 
> >  arch/powerpc/kernel/Makefile   |    1 
> >  arch/powerpc/kernel/msi-rtas.c |  246 +++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 247 insertions(+)
> > 
> > Index: to-merge/arch/powerpc/kernel/Makefile
> > ===================================================================
> > --- to-merge.orig/arch/powerpc/kernel/Makefile
> > +++ to-merge/arch/powerpc/kernel/Makefile
> > @@ -69,6 +69,7 @@ pci32-$(CONFIG_PPC32)		:= pci_32.o
> >  obj-$(CONFIG_PCI)		+= $(pci64-y) $(pci32-y)
> >  
> >  msiobj-y			:= msi.o
> > +msiobj-$(CONFIG_PPC_PSERIES)	+= msi-rtas.o
> >  obj-$(CONFIG_PCI_MSI)		+= $(msiobj-y)
> >  
> >  kexec-$(CONFIG_PPC64)		:= machine_kexec_64.o
> > Index: to-merge/arch/powerpc/kernel/msi-rtas.c
> > ===================================================================
> > --- /dev/null
> > +++ to-merge/arch/powerpc/kernel/msi-rtas.c
> > @@ -0,0 +1,246 @@
> > +/*
> > + * Copyright (C) 2006 Jake Moilanen <moilanen@austin.ibm.com>, IBM Corp.
> > + * Copyright (C) 2006 Michael Ellerman, 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; version 2 of the
> > + * License.
> > + *
> > + */
> > +
> > +#define DEBUG 1
> > +
> > +#include <linux/irq.h>
> > +#include <asm/msi.h>
> > +#include <asm/rtas.h>
> > +#include <asm/hw_irq.h>
> > +#include <asm/ppc-pci.h>
> > +
> > +static int query_token, change_token;
> > +
> > +#define RTAS_QUERY_MSI_FN	0
> > +#define RTAS_CHANGE_MSI_FN	1
> > +#define RTAS_RESET_MSI_FN	2
> > +
> > +
> > +/* RTAS Helpers */
> > +
> > +static int rtas_change_msi(struct pci_dn *pdn, u32 function, u32 num_irqs)
> > +{
> > +	u32 addr, seq_num, rtas_ret[2];
> > +	unsigned long buid;
> > +	int rc;
> > +
> > +	addr = rtas_config_addr(pdn->busno, pdn->devfn, 0);
> > +	buid = pdn->phb->buid;
> > +
> > +	seq_num = 1;
> > +	do {
> > +		rc = rtas_call(change_token, 6, 3, rtas_ret, addr,
> > +				BUID_HI(buid), BUID_LO(buid),
> > +				function, num_irqs, seq_num);
> 
> This call is still currently broken in firmware.  Hopefully we'll have a
> resolution soon.

Any word on that?

> > +
> > +		seq_num = rtas_ret[1];
> > +	} while (rtas_busy_delay(rc));
> > +
> > +	if (rc) {
> > +		printk(KERN_WARNING "Error[%d]: getting the number of"
> > +			" MSI interrupts for %s\n", rc, pci_name(pdn->pcidev));
> > +		return rc;
> > +	}
> > +
> > +	return rtas_ret[0];
> > +}
> > +
> > +static int rtas_query_irq_number(struct pci_dn *pdn, int offset)
> > +{
> > +	u32 addr, rtas_ret[2];
> > +	unsigned long buid;
> > +	int rc;
> > +
> > +	addr = rtas_config_addr(pdn->busno, pdn->devfn, 0);
> > +	buid = pdn->phb->buid;
> > +
> > +	do {
> > +		rc = rtas_call(query_token, 4, 3, rtas_ret, addr,
> > +			       BUID_HI(buid), BUID_LO(buid), offset);
> > +	} while (rtas_busy_delay(rc));
> > +
> > +	if (rc) {
> > +		printk(KERN_WARNING "Error[%d]: Querying irq source number "
> > +				"for %s\n", rc, pci_name(pdn->pcidev));
> > +		return rc;
> > +	}
> > +
> > +	return rtas_ret[0];
> > +}
> > +
> > +/*
> > + * The spec gives firmware the option to enable either MSI or MSI-X,
> > + * this doesn't wash with the Linux API. For the time beinging, we
> > + * kludge around that by checking ourselves the right type is enabled.
> > + */
> > +static int check_msi_type(struct pci_dev *pdev, int type)
> > +{
> > +	int pos, msi_enabled, msix_enabled;
> > +	u16 reg;
> > +
> > +	pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
> > +	if (!pos)
> > +		return -1;
> > +
> > +	pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &reg);
> > +
> > +	msi_enabled = msix_enabled = 0;
> > +
> > +	if (reg & PCI_MSI_FLAGS_ENABLE)
> > +		msi_enabled = 1;
> > +
> 
> This is not being set correctly by firmware either. I have them looking
> into the problem.

Are you sure? I don't see how MSI could possibly work if they don't
enable it on the device itself. Or is it completely broken at the
moment?

> 
> > +	if (reg & PCI_MSIX_FLAGS_ENABLE)
> > +		msix_enabled = 1;
> > +
> > +	if (type == PCI_CAP_ID_MSI && (msix_enabled || !msi_enabled)) {
> > +		pr_debug("check_msi_type: Expected MSI but got %s.\n",
> > +			msix_enabled ? "MSI-X" : "none");
> > +		return -1;
> > +	}
> > +
> > +	if (type == PCI_CAP_ID_MSIX && (msi_enabled || !msix_enabled)) {
> > +		pr_debug("check_msi_type: Expected MSI-X but got %s.\n",
> > +			msi_enabled ? "MSI" : "none");
> > +		return -1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void msi_rtas_free(struct pci_dev *pdev, int num,
> > +			struct msix_entry *entries, int type)
> > +{
> > +	struct device_node *dn;
> > +	struct pci_dn *pdn;
> > +	int i;
> > +
> > +	dn = pci_device_to_OF_node(pdev);
> > +	if (!dn) {
> > +		pr_debug("msi_rtas_free: No OF device node for %s\n",
> > +				pci_name(pdev));
> > +		return;
> > +	}
> > +
> > +	pdn = PCI_DN(dn);
> > +	if (!pdn) {
> > +		pr_debug("msi_rtas_free: No PCI DN for %s\n",
> > +				pci_name(pdev));
> > +		return;
> > +	}
> > +
> > +	for (i = 0; i < num; i++) {
> > +		irq_dispose_mapping(entries[i].vector);
> > +	}
> > +
> > +	/* XXX can we do anything sane if this fails? */
> > +	rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, 0);
> > +}
> > +
> > +static int msi_rtas_check(struct pci_dev *pdev, int num,
> > +			struct msix_entry *entries, int type)
> > +{
> > +	struct device_node *dn;
> > +	int i;
> > +
> > +	dn = pci_device_to_OF_node(pdev);
> > +
> > +	if (!of_find_property(dn, "ibm,req#msi", NULL)) {
> > +		pr_debug("msi_rtas_check: No ibm,req#msi for %s\n",
> > +				pci_name(pdev));
> > +		return -1;
> > +	}
> > +
> > +	/*
> > +	 * Firmware gives us no control over which entries are allocated
> > +	 * for MSI-X, it seems to assume we want 0 - n. For now just insist
> > +	 * that the entries array entry members are 0 - n.
> > +	 */
> > +	for (i = 0; i < num; i++) {
> > +		if (entries[i].entry != i) {
> > +			pr_debug("msi_rtas_check: entries[i].entry != i\n");
> > +			return -1;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int msi_rtas_alloc(struct pci_dev *pdev, int num,
> > +			struct msix_entry *entries, int type)
> > +{
> > +	struct pci_dn *pdn;
> > +	int hwirq, virq, i;
> > +
> > +	pdn = PCI_DN(pci_device_to_OF_node(pdev));
> > +
> > +	/*
> > +	 * In the case of an error it's not clear whether the device is left
> > +	 * with MSI enabled or not, I think we should explicitly disable.
> > +	 */
> > +	if (rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, num) != num)
> > +		goto out_free;
> > +
> > +	if (check_msi_type(pdev, type))
> > +		goto out_free;
> > +
> > +	for (i = 0; i < num; i++) {
> > +		hwirq = rtas_query_irq_number(pdn, i);
> > +		if (hwirq < 0)
> > +			goto out_free;
> > +
> > +		virq = irq_create_mapping(NULL, hwirq);
> > +
> > +		if (virq == NO_IRQ) {
> > +			pr_debug("msi_rtas_alloc: Failed mapping hwirq %d\n",
> > +				hwirq);
> > +			goto out_free;
> > +		}
> > +
> > +		entries[i].vector = virq;
> > +	}
> > +
> > +	return 0;
> > +
> > + out_free:
> > +	msi_rtas_free(pdev, num, entries, type);
> 
> Shouldn't this be:
> 
> 	msi_rtas_free(pdev, i,.......
> 
> Otherwise you'll try freeing unallocated entries.

Yeah maybe. I think I was intending that free be written in such a way
that it could cope with tearing down a partial setup, but I never got
round to writing the patch for irq_dispose_mapping() to make that work -
I should do that.

cheers

-- 
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 191 bytes --]

^ permalink raw reply

* Re: [RFC]: map 4K iommu pages even on 64K largepage systems.
From: Geoff Levand @ 2006-10-24  3:00 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: Olof Johansson, linuxppc-dev
In-Reply-To: <1161656545.10524.524.camel@localhost.localdomain>

Benjamin Herrenschmidt wrote:
> On Mon, 2006-10-23 at 19:25 -0500, Linas Vepstas wrote:
>> Subject: [RFC]: map 4K iommu pages even on 64K largepage systems.
>> 
>> The 10Gigabit ethernet device drivers appear to be able to chew
>> up all 256MB of TCE mappings on pSeries systems, as evidenced by
>> numerous error messages:
>> 
>>  iommu_alloc failed, tbl c0000000010d5c48 vaddr c0000000d875eff0 npages 1
>> 
>> Some experimentaiton indicates that this is essentially because
>> one 1500 byte ethernet MTU gets mapped as a 64K DMA region when
>> the large 64K pages are enabled. Thus, it doesn't take much to
>> exhaust all of the available DMA mappings for a high-speed card.
> 
> There is much to be said about using a 1500MTU and no TSO on a 10G
> link :) But appart from that, I agree, we have a problem.
>  
>> This patch changes the iommu allocator to work with its own 
>> unique, distinct page size. Although the patch is long, its
>> actually quite simple: it just #defines  distinct IOMMU_PAGE_SIZE
>> and then uses this in al the places tha matter.
>> 
>> The patch boots on pseries, untested in other places.
>> 
>> Haven't yet thought if this is a good long-term solution or not,
>> whether this kind of thing is desirable or not.  That's why its 
>> an RFC.  Comments?
> 
> It's probably a good enough solution for RHEL, but we should do
> something different long term. There are a few things I have in mind:
> 
>  - We could have a page size field in the iommu_table and have the iommu
> allocator use that. Thus we can have a per iommu table instance page
> size. That would allow Geoff to deal with his crazy hypervisor by
> basically having one iommu table instance per device. It would also
> allow us to keep using large iommu page sizes on platform where the
> system gives us more than a pinhole for iommu space :)


Actually, its not so important for me, since for performance, most users
will just want to map the whole of ram for every device and essentially
not use iommu pages.

For those that want to use per-device dynamic mapping, I believe there
are enough entries to support 4K io pages.

-Geoff

^ permalink raw reply

* Re: [PATCH] IPIC: Don't call set_irq_handler with desc->lock held.
From: Benjamin Herrenschmidt @ 2006-10-24  3:12 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <20061023163522.GA15901@ld0162-tx32.am.freescale.net>


> This patch gets things working in the spinlock-debugging-enabled case,
> but I'm curious as to where the handler setting is ideally supposed to be
> done.  I don't see any documentation on set_irq_type() that clarifies
> what the semantics are supposed to be.

It's unclear... the common code calls it, I think, when request_irq() is
passed explicit type. When changing the type, some PICs need to change
the handler, some don't. It looks that none of the PICs I've dealt with
have this problem though...

Ben.

^ permalink raw reply

* [PATCH] Make irq_dispose_mapping(NO_IRQ) a nop
From: Michael Ellerman @ 2006-10-24  3:37 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

It makes for a friendlier API if irq_dispose_mapping(NO_IRQ) is a
nop, rather than triggering a WARN_ON.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
---

 arch/powerpc/kernel/irq.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

Index: git/arch/powerpc/kernel/irq.c
===================================================================
--- git.orig/arch/powerpc/kernel/irq.c
+++ git/arch/powerpc/kernel/irq.c
@@ -626,10 +626,14 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
 
 void irq_dispose_mapping(unsigned int virq)
 {
-	struct irq_host *host = irq_map[virq].host;
+	struct irq_host *host;
 	irq_hw_number_t hwirq;
 	unsigned long flags;
 
+	if (virq == NO_IRQ)
+		return;
+
+	host = irq_map[virq].host;
 	WARN_ON (host == NULL);
 	if (host == NULL)
 		return;

^ permalink raw reply

* Re: [PATCH] Make irq_dispose_mapping(NO_IRQ) a nop
From: Benjamin Herrenschmidt @ 2006-10-24  3:52 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: linuxppc-dev, Paul Mackerras
In-Reply-To: <20061024033735.AF71B67B6D@ozlabs.org>

On Tue, 2006-10-24 at 13:37 +1000, Michael Ellerman wrote:
> It makes for a friendlier API if irq_dispose_mapping(NO_IRQ) is a
> nop, rather than triggering a WARN_ON.
> 
> Signed-off-by: Michael Ellerman <michael@ellerman.id.au>

Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

> ---
> 
>  arch/powerpc/kernel/irq.c |    6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> Index: git/arch/powerpc/kernel/irq.c
> ===================================================================
> --- git.orig/arch/powerpc/kernel/irq.c
> +++ git/arch/powerpc/kernel/irq.c
> @@ -626,10 +626,14 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
>  
>  void irq_dispose_mapping(unsigned int virq)
>  {
> -	struct irq_host *host = irq_map[virq].host;
> +	struct irq_host *host;
>  	irq_hw_number_t hwirq;
>  	unsigned long flags;
>  
> +	if (virq == NO_IRQ)
> +		return;
> +
> +	host = irq_map[virq].host;
>  	WARN_ON (host == NULL);
>  	if (host == NULL)
>  		return;

^ permalink raw reply

* Re: [RFC]: map 4K iommu pages even on 64K largepage systems.
From: Benjamin Herrenschmidt @ 2006-10-24  4:43 UTC (permalink / raw)
  To: Linas Vepstas; +Cc: Olof Johansson, linuxppc-dev
In-Reply-To: <20061024002540.GA6360@austin.ibm.com>


> Haven't yet thought if this is a good long-term solution or not,
> whether this kind of thing is desirable or not.  That's why its 
> an RFC.  Comments?

Ok, after some time reading the details of the patch, it looks fairly
good, just a few nits. Also, we need to fix the DART code still :)

Ben

 
> +static inline unsigned long
> +iommu_num_pages(unsigned long vaddr, unsigned long slen)

Coding style in that file is to do:

static inline unsigned long iommu_num_pages(unsigned long vaddr,
					    unsigned long slen)


> @@ -557,8 +562,8 @@ void iommu_unmap_single(struct iommu_tab
>  	BUG_ON(direction == DMA_NONE);
>  
>  	if (tbl)
> -		iommu_free(tbl, dma_handle, (PAGE_ALIGN(dma_handle + size) -
> -					(dma_handle & PAGE_MASK)) >> PAGE_SHIFT);
> +		iommu_free(tbl, dma_handle, (IOMMU_PAGE_ALIGN(dma_handle + size) -
> +					(dma_handle & IOMMU_PAGE_MASK)) >> IOMMU_PAGE_SHIFT);
>  }

Use iommu_num_pages ?

>  /* Allocates a contiguous real buffer and creates mappings over it.
> @@ -571,6 +576,7 @@ void *iommu_alloc_coherent(struct iommu_
>  	void *ret = NULL;
>  	dma_addr_t mapping;
>  	unsigned int npages, order;
> +	unsigned int nio_pages, io_order;
>  	struct page *page;
>  
>  	size = PAGE_ALIGN(size);
> @@ -598,8 +604,10 @@ void *iommu_alloc_coherent(struct iommu_
>  	memset(ret, 0, size);
>  
>  	/* Set up tces to cover the allocated range */
> -	mapping = iommu_alloc(tbl, ret, npages, DMA_BIDIRECTIONAL,
> -			      mask >> PAGE_SHIFT, order);
> +	nio_pages = size >> IOMMU_PAGE_SHIFT;
> +	io_order = get_iommu_order(size);

Not directly related to your patch, but do we really have this
requirement of allocating with an alignement of the order of the
alloation size here ?

Also, your code assumes that PAGE_SHIFT >= IOMMU_PAGE_SHIFT... it might
be useful to not have this restriction in fact. Just maybe use
iommu_num_pages(size) instead of size >> IOMMU_PAGE_SHIFT here ...

> +	mapping = iommu_alloc(tbl, ret, nio_pages, DMA_BIDIRECTIONAL,
> +			      mask >> IOMMU_PAGE_SHIFT, io_order);
>  	if (mapping == DMA_ERROR_CODE) {
>  		free_pages((unsigned long)ret, order);
>  		return NULL;
> @@ -614,9 +622,10 @@ void iommu_free_coherent(struct iommu_ta
>  	unsigned int npages;
>  
>  	if (tbl) {
> -		size = PAGE_ALIGN(size);
> -		npages = size >> PAGE_SHIFT;
> +		size = IOMMU_PAGE_ALIGN(size);
> +		npages = size >> IOMMU_PAGE_SHIFT;
>
>  		iommu_free(tbl, dma_handle, npages);
> +		size = PAGE_ALIGN(size);
>  		free_pages((unsigned long)vaddr, get_order(size));
>  	}

All those repeated alignments on size and one loses track... Are you
sure you'll always end up with the proper values ? I'd rather use
iommu_num_pages() for the iommu_free and only align size once with
PAGE_ALIGN for free_pages().

>  }
> Index: linux-2.6.19-rc1-git11/include/asm-powerpc/iommu.h
> ===================================================================
> --- linux-2.6.19-rc1-git11.orig/include/asm-powerpc/iommu.h	2006-09-19 22:42:06.000000000 -0500
> +++ linux-2.6.19-rc1-git11/include/asm-powerpc/iommu.h	2006-10-23 18:28:21.000000000 -0500
> @@ -23,16 +23,41 @@
>  #ifdef __KERNEL__
>  
>  #include <asm/types.h>
> +#include <linux/compiler.h>
>  #include <linux/spinlock.h>
>  #include <linux/device.h>
>  #include <linux/dma-mapping.h>
>  
> +#define IOMMU_PAGE_SHIFT      12
> +#define IOMMU_PAGE_SIZE       (ASM_CONST(1) << IOMMU_PAGE_SHIFT)
> +#define IOMMU_PAGE_MASK       (~((1 << IOMMU_PAGE_SHIFT) - 1))
> +#define IOMMU_PAGE_ALIGN(addr) _ALIGN_UP(addr, IOMMU_PAGE_SIZE)
> +
> +#ifndef __ASSEMBLY__
> +
> +/* Pure 2^n version of get_order */
> +static __inline__ __attribute_const__ int get_iommu_order(unsigned long size)
> +{
> +	int order;
> +
> +	size = (size - 1) >> (IOMMU_PAGE_SHIFT - 1);
> +	order = -1;
> +	do {
> +		size >>= 1;
> +		order++;
> +	} while (size);
> +	return order;
> +}
> +
> +#endif   /* __ASSEMBLY__ */

Use cntlzw... or ilog2, which would give something like :

	return __ilog2((size - 1) >> IOMMU_PAGE_SHIFT) + 1;

I think Dave Howells has patches fixing get_order generically to be
implemented in terms of ilog2. (I just discovered that on ppc64, we
didn't use cntlzw/ilog2 for it, sucks, but let's wait for Dave patches
rather than fixing it ourselves now).

> +
>  /*
>   * IOMAP_MAX_ORDER defines the largest contiguous block
>   * of dma space we can get.  IOMAP_MAX_ORDER = 13
>   * allows up to 2**12 pages (4096 * 4096) = 16 MB
>   */
> -#define IOMAP_MAX_ORDER 13
> +#define IOMAP_MAX_ORDER (25 - PAGE_SHIFT)

I'm not completely sure we want the above to be defined as a function of
the page shift... 

>  struct iommu_table {
>  	unsigned long  it_busno;     /* Bus number this table belongs to */
> Index: linux-2.6.19-rc1-git11/include/asm-powerpc/tce.h
> ===================================================================
> --- linux-2.6.19-rc1-git11.orig/include/asm-powerpc/tce.h	2006-09-19 22:42:06.000000000 -0500
> +++ linux-2.6.19-rc1-git11/include/asm-powerpc/tce.h	2006-10-23 15:46:57.000000000 -0500
> @@ -22,6 +22,8 @@
>  #define _ASM_POWERPC_TCE_H
>  #ifdef __KERNEL__
>  
> +#include <asm/iommu.h>
> +
>  /*
>   * Tces come in two formats, one for the virtual bus and a different
>   * format for PCI
> @@ -33,7 +35,7 @@
>  
>  #define TCE_SHIFT	12
>  #define TCE_PAGE_SIZE	(1 << TCE_SHIFT)
> -#define TCE_PAGE_FACTOR	(PAGE_SHIFT - TCE_SHIFT)
> +#define TCE_PAGE_FACTOR	(IOMMU_PAGE_SHIFT - TCE_SHIFT)
>  
>  #define TCE_ENTRY_SIZE		8		/* each TCE is 64 bits */

We can actually remove TCE_PAGE_FACTOR and code using it completely.

With your patch, TCE_SHIFT == IOMMU_PAGE_SHIFT, and in a second step, we
can start having the iommu_page_shift be a member of iommu_table. Thus
there will no longer be any need for a correction factor in the backend.

> Index: linux-2.6.19-rc1-git11/arch/powerpc/kernel/vio.c
> ===================================================================
> --- linux-2.6.19-rc1-git11.orig/arch/powerpc/kernel/vio.c	2006-10-13 11:52:51.000000000 -0500
> +++ linux-2.6.19-rc1-git11/arch/powerpc/kernel/vio.c	2006-10-23 16:06:43.000000000 -0500
> @@ -92,9 +92,9 @@ static struct iommu_table *vio_build_iom
>  				&tbl->it_index, &offset, &size);
>  
>  		/* TCE table size - measured in tce entries */
> -		tbl->it_size = size >> PAGE_SHIFT;
> +		tbl->it_size = size >> IOMMU_PAGE_SHIFT;
>  		/* offset for VIO should always be 0 */
> -		tbl->it_offset = offset >> PAGE_SHIFT;
> +		tbl->it_offset = offset >> IOMMU_PAGE_SHIFT;
>  		tbl->it_busno = 0;
>  		tbl->it_type = TCE_VB;
>  
> Index: linux-2.6.19-rc1-git11/arch/powerpc/platforms/pseries/iommu.c
> ===================================================================
> --- linux-2.6.19-rc1-git11.orig/arch/powerpc/platforms/pseries/iommu.c	2006-10-13 11:54:00.000000000 -0500
> +++ linux-2.6.19-rc1-git11/arch/powerpc/platforms/pseries/iommu.c	2006-10-23 18:42:03.000000000 -0500
> @@ -289,7 +289,7 @@ static void iommu_table_setparms(struct 
>  	tbl->it_busno = phb->bus->number;
>  
>  	/* Units of tce entries */
> -	tbl->it_offset = phb->dma_window_base_cur >> PAGE_SHIFT;
> +	tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT;
>  
>  	/* Test if we are going over 2GB of DMA space */
>  	if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) {
> @@ -300,7 +300,7 @@ static void iommu_table_setparms(struct 
>  	phb->dma_window_base_cur += phb->dma_window_size;
>  
>  	/* Set the tce table size - measured in entries */
> -	tbl->it_size = phb->dma_window_size >> PAGE_SHIFT;
> +	tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT;
>  
>  	tbl->it_index = 0;
>  	tbl->it_blocksize = 16;
> @@ -325,8 +325,8 @@ static void iommu_table_setparms_lpar(st
>  	tbl->it_base   = 0;
>  	tbl->it_blocksize  = 16;
>  	tbl->it_type = TCE_PCI;
> -	tbl->it_offset = offset >> PAGE_SHIFT;
> -	tbl->it_size = size >> PAGE_SHIFT;
> +	tbl->it_offset = offset >> IOMMU_PAGE_SHIFT;
> +	tbl->it_size = size >> IOMMU_PAGE_SHIFT;
>  }
>  
>  static void iommu_bus_setup_pSeries(struct pci_bus *bus)
> @@ -522,8 +522,6 @@ static void iommu_dev_setup_pSeriesLP(st
>  	const void *dma_window = NULL;
>  	struct pci_dn *pci;
>  
> -	DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, pci_name(dev));
> -
>  	/* dev setup for LPAR is a little tricky, since the device tree might
>  	 * contain the dma-window properties per-device and not neccesarily
>  	 * for the bus. So we need to search upwards in the tree until we
> @@ -532,6 +530,9 @@ static void iommu_dev_setup_pSeriesLP(st
>  	 */
>  	dn = pci_device_to_OF_node(dev);
>  
> +	DBG("iommu_dev_setup_pSeriesLP, dev %p (%s) %s\n",
> +	     dev, pci_name(dev), dn->full_name);
> +
>  	for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
>  	     pdn = pdn->parent) {
>  		dma_window = get_property(pdn, "ibm,dma-window", NULL);
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev

^ permalink raw reply

* [PATCH] powerpc: map 4K iommu pages even on 64K largepage systems
From: Benjamin Herrenschmidt @ 2006-10-24  5:14 UTC (permalink / raw)
  To: Linas Vepstas; +Cc: Olof Johansson, linuxppc-dev, Paul Mackerras
In-Reply-To: <1161665007.10524.572.camel@localhost.localdomain>

The 10Gigabit ethernet device drivers appear to be able to chew
up all 256MB of TCE mappings on pSeries systems, as evidenced by
numerous error messages:

 iommu_alloc failed, tbl c0000000010d5c48 vaddr c0000000d875eff0 npages 1

Some experimentaiton indicates that this is essentially because
one 1500 byte ethernet MTU gets mapped as a 64K DMA region when
the large 64K pages are enabled. Thus, it doesn't take much to
exhaust all of the available DMA mappings for a high-speed card.

This patch changes the iommu allocator to work with its own 
unique, distinct page size. Although the patch is long, its
actually quite simple: it just #defines  distinct IOMMU_PAGE_SIZE
and then uses this in al the places tha matter.

The patch boots on pseries, untested in other places.

Haven't yet thought if this is a good long-term solution or not,
whether this kind of thing is desirable or not.  That's why its 
an RFC.  Comments?

Signed-off-by: Linas Vepstas <linas@austin.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
----

And here's a slightly modified version that fixes dart_iommu too and
changes things according to some of my comments.

(I tested on the G5 with and without 64K pages.

 arch/powerpc/kernel/iommu.c            |   77 +++++++++++++++++++--------------
 arch/powerpc/kernel/vio.c              |    4 -
 arch/powerpc/platforms/pseries/iommu.c |   35 +++------------
 arch/powerpc/sysdev/dart.h             |    1
 arch/powerpc/sysdev/dart_iommu.c       |    8 ---
 include/asm-powerpc/iommu.h            |   22 ++++++++-
 include/asm-powerpc/tce.h              |    3 -
 7 files changed, 78 insertions(+), 72 deletions(-)

Index: linux-cell/arch/powerpc/kernel/iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/iommu.c	2006-10-24 14:44:26.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/iommu.c	2006-10-24 14:55:59.000000000 +1000
@@ -47,6 +47,17 @@ static int novmerge = 0;
 static int novmerge = 1;
 #endif
 
+static inline unsigned long iommu_num_pages(unsigned long vaddr,
+					    unsigned long slen)
+{
+	unsigned long npages;
+
+	npages = IOMMU_PAGE_ALIGN(vaddr + slen) - (vaddr & IOMMU_PAGE_MASK);
+	npages >>= IOMMU_PAGE_SHIFT;
+
+	return npages;
+}
+
 static int __init setup_iommu(char *str)
 {
 	if (!strcmp(str, "novmerge"))
@@ -178,10 +189,10 @@ static dma_addr_t iommu_alloc(struct iom
 	}
 
 	entry += tbl->it_offset;	/* Offset into real TCE table */
-	ret = entry << PAGE_SHIFT;	/* Set the return dma address */
+	ret = entry << IOMMU_PAGE_SHIFT;	/* Set the return dma address */
 
 	/* Put the TCEs in the HW table */
-	ppc_md.tce_build(tbl, entry, npages, (unsigned long)page & PAGE_MASK,
+	ppc_md.tce_build(tbl, entry, npages, (unsigned long)page & IOMMU_PAGE_MASK,
 			 direction);
 
 
@@ -203,7 +214,7 @@ static void __iommu_free(struct iommu_ta
 	unsigned long entry, free_entry;
 	unsigned long i;
 
-	entry = dma_addr >> PAGE_SHIFT;
+	entry = dma_addr >> IOMMU_PAGE_SHIFT;
 	free_entry = entry - tbl->it_offset;
 
 	if (((free_entry + npages) > tbl->it_size) ||
@@ -270,7 +281,7 @@ int iommu_map_sg(struct device *dev, str
 	/* Init first segment length for backout at failure */
 	outs->dma_length = 0;
 
-	DBG("mapping %d elements:\n", nelems);
+	DBG("sg mapping %d elements:\n", nelems);
 
 	spin_lock_irqsave(&(tbl->it_lock), flags);
 
@@ -285,9 +296,8 @@ int iommu_map_sg(struct device *dev, str
 		}
 		/* Allocate iommu entries for that segment */
 		vaddr = (unsigned long)page_address(s->page) + s->offset;
-		npages = PAGE_ALIGN(vaddr + slen) - (vaddr & PAGE_MASK);
-		npages >>= PAGE_SHIFT;
-		entry = iommu_range_alloc(tbl, npages, &handle, mask >> PAGE_SHIFT, 0);
+		npages = iommu_num_pages(vaddr, slen);
+		entry = iommu_range_alloc(tbl, npages, &handle, mask >> IOMMU_PAGE_SHIFT, 0);
 
 		DBG("  - vaddr: %lx, size: %lx\n", vaddr, slen);
 
@@ -301,14 +311,14 @@ int iommu_map_sg(struct device *dev, str
 
 		/* Convert entry to a dma_addr_t */
 		entry += tbl->it_offset;
-		dma_addr = entry << PAGE_SHIFT;
-		dma_addr |= s->offset;
+		dma_addr = entry << IOMMU_PAGE_SHIFT;
+		dma_addr |= (s->offset & ~IOMMU_PAGE_MASK);
 
-		DBG("  - %lx pages, entry: %lx, dma_addr: %lx\n",
+		DBG("  - %lu pages, entry: %lx, dma_addr: %lx\n",
 			    npages, entry, dma_addr);
 
 		/* Insert into HW table */
-		ppc_md.tce_build(tbl, entry, npages, vaddr & PAGE_MASK, direction);
+		ppc_md.tce_build(tbl, entry, npages, vaddr & IOMMU_PAGE_MASK, direction);
 
 		/* If we are in an open segment, try merging */
 		if (segstart != s) {
@@ -323,7 +333,7 @@ int iommu_map_sg(struct device *dev, str
 				DBG("    can't merge, new segment.\n");
 			} else {
 				outs->dma_length += s->length;
-				DBG("    merged, new len: %lx\n", outs->dma_length);
+				DBG("    merged, new len: %ux\n", outs->dma_length);
 			}
 		}
 
@@ -367,9 +377,8 @@ int iommu_map_sg(struct device *dev, str
 		if (s->dma_length != 0) {
 			unsigned long vaddr, npages;
 
-			vaddr = s->dma_address & PAGE_MASK;
-			npages = (PAGE_ALIGN(s->dma_address + s->dma_length) - vaddr)
-				>> PAGE_SHIFT;
+			vaddr = s->dma_address & IOMMU_PAGE_MASK;
+			npages = iommu_num_pages(s->dma_address, s->dma_length);
 			__iommu_free(tbl, vaddr, npages);
 			s->dma_address = DMA_ERROR_CODE;
 			s->dma_length = 0;
@@ -398,8 +407,7 @@ void iommu_unmap_sg(struct iommu_table *
 
 		if (sglist->dma_length == 0)
 			break;
-		npages = (PAGE_ALIGN(dma_handle + sglist->dma_length)
-			  - (dma_handle & PAGE_MASK)) >> PAGE_SHIFT;
+		npages = iommu_num_pages(dma_handle,sglist->dma_length);
 		__iommu_free(tbl, dma_handle, npages);
 		sglist++;
 	}
@@ -532,12 +540,11 @@ dma_addr_t iommu_map_single(struct iommu
 	BUG_ON(direction == DMA_NONE);
 
 	uaddr = (unsigned long)vaddr;
-	npages = PAGE_ALIGN(uaddr + size) - (uaddr & PAGE_MASK);
-	npages >>= PAGE_SHIFT;
+	npages = iommu_num_pages(uaddr, size);
 
 	if (tbl) {
 		dma_handle = iommu_alloc(tbl, vaddr, npages, direction,
-					 mask >> PAGE_SHIFT, 0);
+					 mask >> IOMMU_PAGE_SHIFT, 0);
 		if (dma_handle == DMA_ERROR_CODE) {
 			if (printk_ratelimit())  {
 				printk(KERN_INFO "iommu_alloc failed, "
@@ -545,7 +552,7 @@ dma_addr_t iommu_map_single(struct iommu
 						tbl, vaddr, npages);
 			}
 		} else
-			dma_handle |= (uaddr & ~PAGE_MASK);
+			dma_handle |= (uaddr & ~IOMMU_PAGE_MASK);
 	}
 
 	return dma_handle;
@@ -554,11 +561,14 @@ dma_addr_t iommu_map_single(struct iommu
 void iommu_unmap_single(struct iommu_table *tbl, dma_addr_t dma_handle,
 		size_t size, enum dma_data_direction direction)
 {
+	unsigned int npages;
+
 	BUG_ON(direction == DMA_NONE);
 
-	if (tbl)
-		iommu_free(tbl, dma_handle, (PAGE_ALIGN(dma_handle + size) -
-					(dma_handle & PAGE_MASK)) >> PAGE_SHIFT);
+	if (tbl) {
+		npages = iommu_num_pages(dma_handle, size);
+		iommu_free(tbl, dma_handle, npages);
+	}
 }
 
 /* Allocates a contiguous real buffer and creates mappings over it.
@@ -570,11 +580,11 @@ void *iommu_alloc_coherent(struct iommu_
 {
 	void *ret = NULL;
 	dma_addr_t mapping;
-	unsigned int npages, order;
+	unsigned int order;
+	unsigned int nio_pages, io_order;
 	struct page *page;
 
 	size = PAGE_ALIGN(size);
-	npages = size >> PAGE_SHIFT;
 	order = get_order(size);
 
  	/*
@@ -598,8 +608,10 @@ void *iommu_alloc_coherent(struct iommu_
 	memset(ret, 0, size);
 
 	/* Set up tces to cover the allocated range */
-	mapping = iommu_alloc(tbl, ret, npages, DMA_BIDIRECTIONAL,
-			      mask >> PAGE_SHIFT, order);
+	nio_pages = size >> IOMMU_PAGE_SHIFT;
+	io_order = get_iommu_order(size);
+	mapping = iommu_alloc(tbl, ret, nio_pages, DMA_BIDIRECTIONAL,
+			      mask >> IOMMU_PAGE_SHIFT, io_order);
 	if (mapping == DMA_ERROR_CODE) {
 		free_pages((unsigned long)ret, order);
 		return NULL;
@@ -611,12 +623,13 @@ void *iommu_alloc_coherent(struct iommu_
 void iommu_free_coherent(struct iommu_table *tbl, size_t size,
 			 void *vaddr, dma_addr_t dma_handle)
 {
-	unsigned int npages;
-
 	if (tbl) {
+		unsigned int nio_pages;
+
+		size = PAGE_ALIGN(size);
+		nio_pages = size >> IOMMU_PAGE_SHIFT;
+		iommu_free(tbl, dma_handle, nio_pages);
 		size = PAGE_ALIGN(size);
-		npages = size >> PAGE_SHIFT;
-		iommu_free(tbl, dma_handle, npages);
 		free_pages((unsigned long)vaddr, get_order(size));
 	}
 }
Index: linux-cell/include/asm-powerpc/iommu.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/iommu.h	2006-10-24 14:44:26.000000000 +1000
+++ linux-cell/include/asm-powerpc/iommu.h	2006-10-24 14:58:45.000000000 +1000
@@ -22,17 +22,35 @@
 #define _ASM_IOMMU_H
 #ifdef __KERNEL__
 
-#include <asm/types.h>
+#include <linux/compiler.h>
 #include <linux/spinlock.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <asm/types.h>
+#include <asm/bitops.h>
+
+#define IOMMU_PAGE_SHIFT      12
+#define IOMMU_PAGE_SIZE       (ASM_CONST(1) << IOMMU_PAGE_SHIFT)
+#define IOMMU_PAGE_MASK       (~((1 << IOMMU_PAGE_SHIFT) - 1))
+#define IOMMU_PAGE_ALIGN(addr) _ALIGN_UP(addr, IOMMU_PAGE_SIZE)
+
+#ifndef __ASSEMBLY__
+
+/* Pure 2^n version of get_order */
+static __inline__ __attribute_const__ int get_iommu_order(unsigned long size)
+{
+	return __ilog2((size - 1) >> IOMMU_PAGE_SHIFT) + 1;
+}
+
+#endif   /* __ASSEMBLY__ */
+
 
 /*
  * IOMAP_MAX_ORDER defines the largest contiguous block
  * of dma space we can get.  IOMAP_MAX_ORDER = 13
  * allows up to 2**12 pages (4096 * 4096) = 16 MB
  */
-#define IOMAP_MAX_ORDER 13
+#define IOMAP_MAX_ORDER		13
 
 struct iommu_table {
 	unsigned long  it_busno;     /* Bus number this table belongs to */
Index: linux-cell/include/asm-powerpc/tce.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/tce.h	2006-10-06 13:45:32.000000000 +1000
+++ linux-cell/include/asm-powerpc/tce.h	2006-10-24 14:59:20.000000000 +1000
@@ -22,6 +22,8 @@
 #define _ASM_POWERPC_TCE_H
 #ifdef __KERNEL__
 
+#include <asm/iommu.h>
+
 /*
  * Tces come in two formats, one for the virtual bus and a different
  * format for PCI
@@ -33,7 +35,6 @@
 
 #define TCE_SHIFT	12
 #define TCE_PAGE_SIZE	(1 << TCE_SHIFT)
-#define TCE_PAGE_FACTOR	(PAGE_SHIFT - TCE_SHIFT)
 
 #define TCE_ENTRY_SIZE		8		/* each TCE is 64 bits */
 
Index: linux-cell/arch/powerpc/kernel/vio.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/vio.c	2006-10-24 14:44:26.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/vio.c	2006-10-24 14:44:55.000000000 +1000
@@ -92,9 +92,9 @@ static struct iommu_table *vio_build_iom
 				&tbl->it_index, &offset, &size);
 
 		/* TCE table size - measured in tce entries */
-		tbl->it_size = size >> PAGE_SHIFT;
+		tbl->it_size = size >> IOMMU_PAGE_SHIFT;
 		/* offset for VIO should always be 0 */
-		tbl->it_offset = offset >> PAGE_SHIFT;
+		tbl->it_offset = offset >> IOMMU_PAGE_SHIFT;
 		tbl->it_busno = 0;
 		tbl->it_type = TCE_VB;
 
Index: linux-cell/arch/powerpc/platforms/pseries/iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/pseries/iommu.c	2006-10-24 14:44:26.000000000 +1000
+++ linux-cell/arch/powerpc/platforms/pseries/iommu.c	2006-10-24 15:00:07.000000000 +1000
@@ -57,9 +57,6 @@ static void tce_build_pSeries(struct iom
 	u64 *tcep;
 	u64 rpn;
 
-	index <<= TCE_PAGE_FACTOR;
-	npages <<= TCE_PAGE_FACTOR;
-
 	proto_tce = TCE_PCI_READ; // Read allowed
 
 	if (direction != DMA_TO_DEVICE)
@@ -82,9 +79,6 @@ static void tce_free_pSeries(struct iomm
 {
 	u64 *tcep;
 
-	npages <<= TCE_PAGE_FACTOR;
-	index <<= TCE_PAGE_FACTOR;
-
 	tcep = ((u64 *)tbl->it_base) + index;
 
 	while (npages--)
@@ -95,7 +89,6 @@ static unsigned long tce_get_pseries(str
 {
 	u64 *tcep;
 
-	index <<= TCE_PAGE_FACTOR;
 	tcep = ((u64 *)tbl->it_base) + index;
 
 	return *tcep;
@@ -109,9 +102,6 @@ static void tce_build_pSeriesLP(struct i
 	u64 proto_tce, tce;
 	u64 rpn;
 
-	tcenum <<= TCE_PAGE_FACTOR;
-	npages <<= TCE_PAGE_FACTOR;
-
 	rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT;
 	proto_tce = TCE_PCI_READ;
 	if (direction != DMA_TO_DEVICE)
@@ -146,7 +136,7 @@ static void tce_buildmulti_pSeriesLP(str
 	u64 rpn;
 	long l, limit;
 
-	if (TCE_PAGE_FACTOR == 0 && npages == 1)
+	if (npages == 1)
 		return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
 					   direction);
 
@@ -164,9 +154,6 @@ static void tce_buildmulti_pSeriesLP(str
 		__get_cpu_var(tce_page) = tcep;
 	}
 
-	tcenum <<= TCE_PAGE_FACTOR;
-	npages <<= TCE_PAGE_FACTOR;
-
 	rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT;
 	proto_tce = TCE_PCI_READ;
 	if (direction != DMA_TO_DEVICE)
@@ -207,9 +194,6 @@ static void tce_free_pSeriesLP(struct io
 {
 	u64 rc;
 
-	tcenum <<= TCE_PAGE_FACTOR;
-	npages <<= TCE_PAGE_FACTOR;
-
 	while (npages--) {
 		rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, 0);
 
@@ -229,9 +213,6 @@ static void tce_freemulti_pSeriesLP(stru
 {
 	u64 rc;
 
-	tcenum <<= TCE_PAGE_FACTOR;
-	npages <<= TCE_PAGE_FACTOR;
-
 	rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages);
 
 	if (rc && printk_ratelimit()) {
@@ -248,7 +229,6 @@ static unsigned long tce_get_pSeriesLP(s
 	u64 rc;
 	unsigned long tce_ret;
 
-	tcenum <<= TCE_PAGE_FACTOR;
 	rc = plpar_tce_get((u64)tbl->it_index, (u64)tcenum << 12, &tce_ret);
 
 	if (rc && printk_ratelimit()) {
@@ -289,7 +269,7 @@ static void iommu_table_setparms(struct 
 	tbl->it_busno = phb->bus->number;
 
 	/* Units of tce entries */
-	tbl->it_offset = phb->dma_window_base_cur >> PAGE_SHIFT;
+	tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT;
 
 	/* Test if we are going over 2GB of DMA space */
 	if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) {
@@ -300,7 +280,7 @@ static void iommu_table_setparms(struct 
 	phb->dma_window_base_cur += phb->dma_window_size;
 
 	/* Set the tce table size - measured in entries */
-	tbl->it_size = phb->dma_window_size >> PAGE_SHIFT;
+	tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT;
 
 	tbl->it_index = 0;
 	tbl->it_blocksize = 16;
@@ -325,8 +305,8 @@ static void iommu_table_setparms_lpar(st
 	tbl->it_base   = 0;
 	tbl->it_blocksize  = 16;
 	tbl->it_type = TCE_PCI;
-	tbl->it_offset = offset >> PAGE_SHIFT;
-	tbl->it_size = size >> PAGE_SHIFT;
+	tbl->it_offset = offset >> IOMMU_PAGE_SHIFT;
+	tbl->it_size = size >> IOMMU_PAGE_SHIFT;
 }
 
 static void iommu_bus_setup_pSeries(struct pci_bus *bus)
@@ -522,8 +502,6 @@ static void iommu_dev_setup_pSeriesLP(st
 	const void *dma_window = NULL;
 	struct pci_dn *pci;
 
-	DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, pci_name(dev));
-
 	/* dev setup for LPAR is a little tricky, since the device tree might
 	 * contain the dma-window properties per-device and not neccesarily
 	 * for the bus. So we need to search upwards in the tree until we
@@ -532,6 +510,9 @@ static void iommu_dev_setup_pSeriesLP(st
 	 */
 	dn = pci_device_to_OF_node(dev);
 
+	DBG("iommu_dev_setup_pSeriesLP, dev %p (%s) %s\n",
+	     dev, pci_name(dev), dn->full_name);
+
 	for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
 	     pdn = pdn->parent) {
 		dma_window = get_property(pdn, "ibm,dma-window", NULL);
Index: linux-cell/arch/powerpc/sysdev/dart.h
===================================================================
--- linux-cell.orig/arch/powerpc/sysdev/dart.h	2006-10-06 13:47:54.000000000 +1000
+++ linux-cell/arch/powerpc/sysdev/dart.h	2006-10-24 15:01:16.000000000 +1000
@@ -72,7 +72,6 @@
 
 #define DART_PAGE_SHIFT		12
 #define DART_PAGE_SIZE		(1 << DART_PAGE_SHIFT)
-#define DART_PAGE_FACTOR	(PAGE_SHIFT - DART_PAGE_SHIFT)
 
 
 #endif /* _POWERPC_SYSDEV_DART_H */
Index: linux-cell/arch/powerpc/sysdev/dart_iommu.c
===================================================================
--- linux-cell.orig/arch/powerpc/sysdev/dart_iommu.c	2006-10-24 14:44:26.000000000 +1000
+++ linux-cell/arch/powerpc/sysdev/dart_iommu.c	2006-10-24 15:01:47.000000000 +1000
@@ -156,9 +156,6 @@ static void dart_build(struct iommu_tabl
 
 	DBG("dart: build at: %lx, %lx, addr: %x\n", index, npages, uaddr);
 
-	index <<= DART_PAGE_FACTOR;
-	npages <<= DART_PAGE_FACTOR;
-
 	dp = ((unsigned int*)tbl->it_base) + index;
 
 	/* On U3, all memory is contigous, so we can move this
@@ -199,9 +196,6 @@ static void dart_free(struct iommu_table
 
 	DBG("dart: free at: %lx, %lx\n", index, npages);
 
-	index <<= DART_PAGE_FACTOR;
-	npages <<= DART_PAGE_FACTOR;
-
 	dp  = ((unsigned int *)tbl->it_base) + index;
 
 	while (npages--)
@@ -281,7 +275,7 @@ static void iommu_table_dart_setup(void)
 	iommu_table_dart.it_busno = 0;
 	iommu_table_dart.it_offset = 0;
 	/* it_size is in number of entries */
-	iommu_table_dart.it_size = (dart_tablesize / sizeof(u32)) >> DART_PAGE_FACTOR;
+	iommu_table_dart.it_size = dart_tablesize / sizeof(u32);
 
 	/* Initialize the common IOMMU code */
 	iommu_table_dart.it_base = (unsigned long)dart_vbase;

^ permalink raw reply

* [PATCH] powerpc: Consolidate feature fixup code
From: Benjamin Herrenschmidt @ 2006-10-24  6:42 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev list


    There are currently two versions of the functions for applying the
    feature fixups, one for CPU features and one for firmware features. In
    addition, they are both in assembly and with separate implementations
    for 32 and 64 bits. identify_cpu() is also implemented in assembly and
    separately for 32 and 64 bits.
    
    This patch replaces them with a pair of C functions. The call sites are
    slightly moved on ppc64 as well to be called from C instead of from
    assembly, though it's a very small change, and thus shouldn't cause any
    problem.
    
    Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
    Acked-by: Olof Johansson <olof@lixom.net>
    Signed-off-by: Paul Mackerras <paulus@samba.org>

---

New version fixes the missing call to setup_cpu on ppc64. Also includes
change from Paulus to fix build on ARCH=ppc

diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index f23aad6..dc7ccca 100644
Index: linux-cell/arch/powerpc/kernel/cputable.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/cputable.c	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/cputable.c	2006-10-24 16:35:51.000000000 +1000
@@ -18,6 +18,7 @@
 
 #include <asm/oprofile_impl.h>
 #include <asm/cputable.h>
+#include <asm/prom.h>		/* for PTRRELOC on ARCH=ppc */
 
 struct cpu_spec* cur_cpu_spec = NULL;
 EXPORT_SYMBOL(cur_cpu_spec);
@@ -73,7 +74,7 @@ extern void __restore_cpu_ppc970(void);
 #define PPC_FEATURE_SPE_COMP	0
 #endif
 
-struct cpu_spec	cpu_specs[] = {
+static struct cpu_spec cpu_specs[] = {
 #ifdef CONFIG_PPC64
 	{	/* Power3 */
 		.pvr_mask		= 0xffff0000,
@@ -1152,3 +1153,72 @@ struct cpu_spec	cpu_specs[] = {
 #endif /* !CLASSIC_PPC */
 #endif /* CONFIG_PPC32 */
 };
+
+struct cpu_spec *identify_cpu(unsigned long offset)
+{
+	struct cpu_spec *s = cpu_specs;
+	struct cpu_spec **cur = &cur_cpu_spec;
+	unsigned int pvr = mfspr(SPRN_PVR);
+	int i;
+
+	s = PTRRELOC(s);
+	cur = PTRRELOC(cur);
+
+	if (*cur != NULL)
+		return PTRRELOC(*cur);
+
+	for (i = 0; i < ARRAY_SIZE(cpu_specs); i++,s++)
+		if ((pvr & s->pvr_mask) == s->pvr_value) {
+			*cur = cpu_specs + i;
+#ifdef CONFIG_PPC64
+			/* ppc64 expects identify_cpu to also call setup_cpu
+			 * for that processor. I will consolidate that at a
+			 * later time, for now, just use our friend #ifdef.
+			 * we also don't need to PTRRELOC the function pointer
+			 * on ppc64 as we are running at 0 in real mode.
+			 */
+			if (s->cpu_setup) {
+				s->cpu_setup(offset, s);
+			}
+#endif /* CONFIG_PPC64 */
+			return s;
+		}
+	BUG();
+	return NULL;
+}
+
+void do_feature_fixups(unsigned long offset, unsigned long value,
+		       void *fixup_start, void *fixup_end)
+{
+	struct fixup_entry {
+		unsigned long	mask;
+		unsigned long	value;
+		unsigned int	*start;
+		unsigned int	*end;
+	} *fcur, *fend;
+
+	fcur = fixup_start;
+	fend = fixup_end;
+
+	for (; fcur < fend; fcur++) {
+		unsigned int *pstart, *pend, *p;
+
+		if ((value & fcur->mask) == fcur->value)
+			continue;
+
+		/* These PTRRELOCs will disappear once the new scheme for
+		 * modules and vdso is implemented
+		 */
+		pstart = PTRRELOC(fcur->start);
+		pend = PTRRELOC(fcur->end);
+
+		for (p = pstart; p < pend; p++) {
+			*p = 0x60000000u;
+			asm volatile ("dcbst 0, %0" : : "r" (p));
+		}
+		asm volatile ("sync" : : : "memory");
+		for (p = pstart; p < pend; p++)
+			asm volatile ("icbi 0,%0" : : "r" (p));
+		asm volatile ("sync; isync" : : : "memory");
+	}
+}
Index: linux-cell/arch/powerpc/kernel/head_64.S
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/head_64.S	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/head_64.S	2006-10-24 16:31:25.000000000 +1000
@@ -1580,11 +1580,6 @@ _STATIC(__start_initialization_iSeries)
 	li	r0,0
 	stdu	r0,-STACK_FRAME_OVERHEAD(r1)
 
-	LOAD_REG_IMMEDIATE(r3,cpu_specs)
-	LOAD_REG_IMMEDIATE(r4,cur_cpu_spec)
-	li	r5,0
-	bl	.identify_cpu
-
 	LOAD_REG_IMMEDIATE(r2,__toc_start)
 	addi	r2,r2,0x4000
 	addi	r2,r2,0x4000
@@ -1964,13 +1959,6 @@ _STATIC(start_here_multiplatform)
 	addi	r2,r2,0x4000
 	add	r2,r2,r26
 
-	LOAD_REG_IMMEDIATE(r3, cpu_specs)
-	add	r3,r3,r26
-	LOAD_REG_IMMEDIATE(r4,cur_cpu_spec)
-	add	r4,r4,r26
-	mr	r5,r26
-	bl	.identify_cpu
-
 	/* Do very early kernel initializations, including initial hash table,
 	 * stab and slb setup before we turn on relocation.	*/
 
@@ -2000,13 +1988,6 @@ _STATIC(start_here_common)
 	li	r0,0
 	stdu	r0,-STACK_FRAME_OVERHEAD(r1)
 
-	/* Apply the CPUs-specific fixups (nop out sections not relevant
-	 * to this CPU
-	 */
-	li	r3,0
-	bl	.do_cpu_ftr_fixups
-	bl	.do_fw_ftr_fixups
-
 	/* ptr to current */
 	LOAD_REG_IMMEDIATE(r4, init_task)
 	std	r4,PACACURRENT(r13)
Index: linux-cell/arch/powerpc/kernel/misc_32.S
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/misc_32.S	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/misc_32.S	2006-10-24 16:31:25.000000000 +1000
@@ -102,80 +102,6 @@ _GLOBAL(reloc_got2)
 	blr
 
 /*
- * identify_cpu,
- * called with r3 = data offset and r4 = CPU number
- * doesn't change r3
- */
-_GLOBAL(identify_cpu)
-	addis	r8,r3,cpu_specs@ha
-	addi	r8,r8,cpu_specs@l
-	mfpvr	r7
-1:
-	lwz	r5,CPU_SPEC_PVR_MASK(r8)
-	and	r5,r5,r7
-	lwz	r6,CPU_SPEC_PVR_VALUE(r8)
-	cmplw	0,r6,r5
-	beq	1f
-	addi	r8,r8,CPU_SPEC_ENTRY_SIZE
-	b	1b
-1:
-	addis	r6,r3,cur_cpu_spec@ha
-	addi	r6,r6,cur_cpu_spec@l
-	sub	r8,r8,r3
-	stw	r8,0(r6)
-	blr
-
-/*
- * do_cpu_ftr_fixups - goes through the list of CPU feature fixups
- * and writes nop's over sections of code that don't apply for this cpu.
- * r3 = data offset (not changed)
- */
-_GLOBAL(do_cpu_ftr_fixups)
-	/* Get CPU 0 features */
-	addis	r6,r3,cur_cpu_spec@ha
-	addi	r6,r6,cur_cpu_spec@l
-	lwz	r4,0(r6)
-	add	r4,r4,r3
-	lwz	r4,CPU_SPEC_FEATURES(r4)
-
-	/* Get the fixup table */
-	addis	r6,r3,__start___ftr_fixup@ha
-	addi	r6,r6,__start___ftr_fixup@l
-	addis	r7,r3,__stop___ftr_fixup@ha
-	addi	r7,r7,__stop___ftr_fixup@l
-
-	/* Do the fixup */
-1:	cmplw	0,r6,r7
-	bgelr
-	addi	r6,r6,16
-	lwz	r8,-16(r6)	/* mask */
-	and	r8,r8,r4
-	lwz	r9,-12(r6)	/* value */
-	cmplw	0,r8,r9
-	beq	1b
-	lwz	r8,-8(r6)	/* section begin */
-	lwz	r9,-4(r6)	/* section end */
-	subf.	r9,r8,r9
-	beq	1b
-	/* write nops over the section of code */
-	/* todo: if large section, add a branch at the start of it */
-	srwi	r9,r9,2
-	mtctr	r9
-	add	r8,r8,r3
-	lis	r0,0x60000000@h	/* nop */
-3:	stw	r0,0(r8)
-	andi.	r10,r4,CPU_FTR_SPLIT_ID_CACHE@l
-	beq	2f
-	dcbst	0,r8		/* suboptimal, but simpler */
-	sync
-	icbi	0,r8
-2:	addi	r8,r8,4
-	bdnz	3b
-	sync			/* additional sync needed on g4 */
-	isync
-	b	1b
-
-/*
  * call_setup_cpu - call the setup_cpu function for this cpu
  * r3 = data offset, r24 = cpu number
  *
Index: linux-cell/arch/powerpc/kernel/misc_64.S
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/misc_64.S	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/misc_64.S	2006-10-24 16:31:25.000000000 +1000
@@ -246,130 +246,6 @@ _GLOBAL(__flush_dcache_icache)
 	isync
 	blr
 
-/*
- * identify_cpu and calls setup_cpu
- * In:	r3 = base of the cpu_specs array
- *	r4 = address of cur_cpu_spec
- *	r5 = relocation offset
- */
-_GLOBAL(identify_cpu)
-	mfpvr	r7
-1:
-	lwz	r8,CPU_SPEC_PVR_MASK(r3)
-	and	r8,r8,r7
-	lwz	r9,CPU_SPEC_PVR_VALUE(r3)
-	cmplw	0,r9,r8
-	beq	1f
-	addi	r3,r3,CPU_SPEC_ENTRY_SIZE
-	b	1b
-1:
-	sub	r0,r3,r5
-	std	r0,0(r4)
-	ld	r4,CPU_SPEC_SETUP(r3)
-	cmpdi	0,r4,0
-	add	r4,r4,r5
-	beqlr
-	ld	r4,0(r4)
-	add	r4,r4,r5
-	mtctr	r4
-	/* Calling convention for cpu setup is r3=offset, r4=cur_cpu_spec */
-	mr	r4,r3
-	mr	r3,r5
-	bctr
-
-/*
- * do_cpu_ftr_fixups - goes through the list of CPU feature fixups
- * and writes nop's over sections of code that don't apply for this cpu.
- * r3 = data offset (not changed)
- */
-_GLOBAL(do_cpu_ftr_fixups)
-	/* Get CPU 0 features */
-	LOAD_REG_IMMEDIATE(r6,cur_cpu_spec)
-	sub	r6,r6,r3
-	ld	r4,0(r6)
-	sub	r4,r4,r3
-	ld	r4,CPU_SPEC_FEATURES(r4)
-	/* Get the fixup table */
-	LOAD_REG_IMMEDIATE(r6,__start___ftr_fixup)
-	sub	r6,r6,r3
-	LOAD_REG_IMMEDIATE(r7,__stop___ftr_fixup)
-	sub	r7,r7,r3
-	/* Do the fixup */
-1:	cmpld	r6,r7
-	bgelr
-	addi	r6,r6,32
-	ld	r8,-32(r6)	/* mask */
-	and	r8,r8,r4
-	ld	r9,-24(r6)	/* value */
-	cmpld	r8,r9
-	beq	1b
-	ld	r8,-16(r6)	/* section begin */
-	ld	r9,-8(r6)	/* section end */
-	subf.	r9,r8,r9
-	beq	1b
-	/* write nops over the section of code */
-	/* todo: if large section, add a branch at the start of it */
-	srwi	r9,r9,2
-	mtctr	r9
-	sub	r8,r8,r3
-	lis	r0,0x60000000@h	/* nop */
-3:	stw	r0,0(r8)
-	andi.	r10,r4,CPU_FTR_SPLIT_ID_CACHE@l
-	beq	2f
-	dcbst	0,r8		/* suboptimal, but simpler */
-	sync
-	icbi	0,r8
-2:	addi	r8,r8,4
-	bdnz	3b
-	sync			/* additional sync needed on g4 */
-	isync
-	b	1b
-
-/*
- * do_fw_ftr_fixups - goes through the list of firmware feature fixups
- * and writes nop's over sections of code that don't apply for this firmware.
- * r3 = data offset (not changed)
- */
-_GLOBAL(do_fw_ftr_fixups)
-	/* Get firmware features */
-	LOAD_REG_IMMEDIATE(r6,powerpc_firmware_features)
-	sub	r6,r6,r3
-	ld	r4,0(r6)
-	/* Get the fixup table */
-	LOAD_REG_IMMEDIATE(r6,__start___fw_ftr_fixup)
-	sub	r6,r6,r3
-	LOAD_REG_IMMEDIATE(r7,__stop___fw_ftr_fixup)
-	sub	r7,r7,r3
-	/* Do the fixup */
-1:	cmpld	r6,r7
-	bgelr
-	addi	r6,r6,32
-	ld	r8,-32(r6)	/* mask */
-	and	r8,r8,r4
-	ld	r9,-24(r6)	/* value */
-	cmpld	r8,r9
-	beq	1b
-	ld	r8,-16(r6)	/* section begin */
-	ld	r9,-8(r6)	/* section end */
-	subf.	r9,r8,r9
-	beq	1b
-	/* write nops over the section of code */
-	/* todo: if large section, add a branch at the start of it */
-	srwi	r9,r9,2
-	mtctr	r9
-	sub	r8,r8,r3
-	lis	r0,0x60000000@h	/* nop */
-3:	stw	r0,0(r8)
-BEGIN_FTR_SECTION
-	dcbst	0,r8		/* suboptimal, but simpler */
-	sync
-	icbi	0,r8
-END_FTR_SECTION_IFSET(CPU_FTR_SPLIT_ID_CACHE)
-	addi	r8,r8,4
-	bdnz	3b
-	sync			/* additional sync needed on g4 */
-	isync
-	b	1b
 
 #if defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE)
 /*
Index: linux-cell/arch/powerpc/kernel/setup_32.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/setup_32.c	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/setup_32.c	2006-10-24 16:31:25.000000000 +1000
@@ -91,6 +91,7 @@ int ucache_bsize;
 unsigned long __init early_init(unsigned long dt_ptr)
 {
 	unsigned long offset = reloc_offset();
+	struct cpu_spec *spec;
 
 	/* First zero the BSS -- use memset_io, some platforms don't have
 	 * caches on yet */
@@ -100,8 +101,11 @@ unsigned long __init early_init(unsigned
 	 * Identify the CPU type and fix up code sections
 	 * that depend on which cpu we have.
 	 */
-	identify_cpu(offset, 0);
-	do_cpu_ftr_fixups(offset);
+	spec = identify_cpu(offset);
+
+	do_feature_fixups(offset, spec->cpu_features,
+			  PTRRELOC(&__start___ftr_fixup),
+			  PTRRELOC(&__stop___ftr_fixup));
 
 	return KERNELBASE + offset;
 }
Index: linux-cell/arch/powerpc/kernel/setup_64.c
===================================================================
--- linux-cell.orig/arch/powerpc/kernel/setup_64.c	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/powerpc/kernel/setup_64.c	2006-10-24 16:31:25.000000000 +1000
@@ -170,6 +170,9 @@ void __init setup_paca(int cpu)
 
 void __init early_setup(unsigned long dt_ptr)
 {
+	/* Identify CPU type */
+	identify_cpu(0);
+
 	/* Assume we're on cpu 0 for now. Don't write to the paca yet! */
 	setup_paca(0);
 
@@ -348,6 +351,14 @@ void __init setup_system(void)
 {
 	DBG(" -> setup_system()\n");
 
+	/* Apply the CPUs-specific and firmware specific fixups to kernel
+	 * text (nop out sections not relevant to this CPU or this firmware)
+	 */
+	do_feature_fixups(0, cur_cpu_spec->cpu_features,
+			  &__start___ftr_fixup, &__stop___ftr_fixup);
+	do_feature_fixups(0, powerpc_firmware_features,
+			  &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup);
+
 	/*
 	 * Unflatten the device-tree passed by prom_init or kexec
 	 */
Index: linux-cell/arch/powerpc/platforms/iseries/setup.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/iseries/setup.c	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/powerpc/platforms/iseries/setup.c	2006-10-24 16:31:25.000000000 +1000
@@ -694,6 +694,11 @@ 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);
+
 	powerpc_firmware_features |= FW_FEATURE_ISERIES;
 	powerpc_firmware_features |= FW_FEATURE_LPAR;
 
Index: linux-cell/arch/ppc/kernel/misc.S
===================================================================
--- linux-cell.orig/arch/ppc/kernel/misc.S	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/ppc/kernel/misc.S	2006-10-24 16:31:25.000000000 +1000
@@ -110,80 +110,6 @@ _GLOBAL(reloc_got2)
 	blr
 
 /*
- * identify_cpu,
- * called with r3 = data offset and r4 = CPU number
- * doesn't change r3
- */
-_GLOBAL(identify_cpu)
-	addis	r8,r3,cpu_specs@ha
-	addi	r8,r8,cpu_specs@l
-	mfpvr	r7
-1:
-	lwz	r5,CPU_SPEC_PVR_MASK(r8)
-	and	r5,r5,r7
-	lwz	r6,CPU_SPEC_PVR_VALUE(r8)
-	cmplw	0,r6,r5
-	beq	1f
-	addi	r8,r8,CPU_SPEC_ENTRY_SIZE
-	b	1b
-1:
-	addis	r6,r3,cur_cpu_spec@ha
-	addi	r6,r6,cur_cpu_spec@l
-	sub	r8,r8,r3
-	stw	r8,0(r6)
-	blr
-
-/*
- * do_cpu_ftr_fixups - goes through the list of CPU feature fixups
- * and writes nop's over sections of code that don't apply for this cpu.
- * r3 = data offset (not changed)
- */
-_GLOBAL(do_cpu_ftr_fixups)
-	/* Get CPU 0 features */
-	addis	r6,r3,cur_cpu_spec@ha
-	addi	r6,r6,cur_cpu_spec@l
-	lwz	r4,0(r6)
-	add	r4,r4,r3
-	lwz	r4,CPU_SPEC_FEATURES(r4)
-
-	/* Get the fixup table */
-	addis	r6,r3,__start___ftr_fixup@ha
-	addi	r6,r6,__start___ftr_fixup@l
-	addis	r7,r3,__stop___ftr_fixup@ha
-	addi	r7,r7,__stop___ftr_fixup@l
-
-	/* Do the fixup */
-1:	cmplw	0,r6,r7
-	bgelr
-	addi	r6,r6,16
-	lwz	r8,-16(r6)	/* mask */
-	and	r8,r8,r4
-	lwz	r9,-12(r6)	/* value */
-	cmplw	0,r8,r9
-	beq	1b
-	lwz	r8,-8(r6)	/* section begin */
-	lwz	r9,-4(r6)	/* section end */
-	subf.	r9,r8,r9
-	beq	1b
-	/* write nops over the section of code */
-	/* todo: if large section, add a branch at the start of it */
-	srwi	r9,r9,2
-	mtctr	r9
-	add	r8,r8,r3
-	lis	r0,0x60000000@h	/* nop */
-3:	stw	r0,0(r8)
-	andi.	r10,r4,CPU_FTR_SPLIT_ID_CACHE@l
-	beq	2f
-	dcbst	0,r8		/* suboptimal, but simpler */
-	sync
-	icbi	0,r8
-2:	addi	r8,r8,4
-	bdnz	3b
-	sync			/* additional sync needed on g4 */
-	isync
-	b	1b
-
-/*
  * call_setup_cpu - call the setup_cpu function for this cpu
  * r3 = data offset, r24 = cpu number
  *
Index: linux-cell/arch/ppc/kernel/setup.c
===================================================================
--- linux-cell.orig/arch/ppc/kernel/setup.c	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/arch/ppc/kernel/setup.c	2006-10-24 16:31:25.000000000 +1000
@@ -38,6 +38,7 @@
 #include <asm/nvram.h>
 #include <asm/xmon.h>
 #include <asm/ocp.h>
+#include <asm/prom.h>
 
 #define USES_PPC_SYS (defined(CONFIG_85xx) || defined(CONFIG_83xx) || \
 		      defined(CONFIG_MPC10X_BRIDGE) || defined(CONFIG_8260) || \
@@ -53,8 +54,6 @@
 
 extern void platform_init(unsigned long r3, unsigned long r4,
 		unsigned long r5, unsigned long r6, unsigned long r7);
-extern void identify_cpu(unsigned long offset, unsigned long cpu);
-extern void do_cpu_ftr_fixups(unsigned long offset);
 extern void reloc_got2(unsigned long offset);
 
 extern void ppc6xx_idle(void);
@@ -301,6 +300,7 @@ early_init(int r3, int r4, int r5)
 {
  	unsigned long phys;
 	unsigned long offset = reloc_offset();
+	struct cpu_spec *spec;
 
  	/* Default */
  	phys = offset + KERNELBASE;
@@ -313,8 +313,10 @@ early_init(int r3, int r4, int r5)
 	 * Identify the CPU type and fix up code sections
 	 * that depend on which cpu we have.
 	 */
-	identify_cpu(offset, 0);
-	do_cpu_ftr_fixups(offset);
+	spec = identify_cpu(offset);
+	do_feature_fixups(offset, spec->cpu_features,
+			  PTRRELOC(&__start___ftr_fixup),
+			  PTRRELOC(&__stop___ftr_fixup));
 
 	return phys;
 }
Index: linux-cell/include/asm-powerpc/cputable.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/cputable.h	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/include/asm-powerpc/cputable.h	2006-10-24 16:31:25.000000000 +1000
@@ -89,8 +89,11 @@ struct cpu_spec {
 
 extern struct cpu_spec		*cur_cpu_spec;
 
-extern void identify_cpu(unsigned long offset, unsigned long cpu);
-extern void do_cpu_ftr_fixups(unsigned long offset);
+extern unsigned int __start___ftr_fixup, __stop___ftr_fixup;
+
+extern struct cpu_spec *identify_cpu(unsigned long offset);
+extern void do_feature_fixups(unsigned long offset, unsigned long value,
+			      void *fixup_start, void *fixup_end);
 
 #endif /* __ASSEMBLY__ */
 
Index: linux-cell/include/asm-powerpc/firmware.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/firmware.h	2006-10-24 16:30:52.000000000 +1000
+++ linux-cell/include/asm-powerpc/firmware.h	2006-10-24 16:31:25.000000000 +1000
@@ -96,6 +96,8 @@ extern void machine_check_fwnmi(void);
 /* This is true if we are using the firmware NMI handler (typically LPAR) */
 extern int fwnmi_active;
 
+extern unsigned int __start___fw_ftr_fixup, __stop___fw_ftr_fixup;
+
 #else /* __ASSEMBLY__ */
 
 #define BEGIN_FW_FTR_SECTION		96:

^ permalink raw reply

* pci_set_power_state() failure and breaking suspend
From: Benjamin Herrenschmidt @ 2006-10-24  6:54 UTC (permalink / raw)
  To: linux1394-devel; +Cc: linuxppc-dev list, Linux Kernel list, Greg KH

So I noticed a small regression that I think might uncover a deeper
issue...

Recently, ohci1394 grew some "proper" error handling in its suspend
function, something that looks like:

        err = pci_set_power_state(pdev, pci_choose_state(pdev, state));
        if (err)
                goto out;

First, it breaks some old PowerBooks where the internal OHCI had PM
feature exposed on PCI (the pmac specific code that follows those lines
is enough on those machines).

That can easily be fixed by removing the if (err) goto out; statement
and having the pmac code set err to 0 in certain conditions, and I'll be
happy to submit a patch for this.

However, this raises the question of do we actually want to prevent
machines to suspend when they have a PCI device that don't have the PCI
PM capability ? I'm asking that because I can easily imagine that sort
of construct growing into more drivers (sounds logical if you don't
think) and I can even imagine somebody thinking it's a good idea to slap
a __must_check on pci_set_power_state() ... 

I think the answer is not simple. For a lot of devices, it will be
harmless, they will be either unaffected by the sleep transition or
powered down and back up, and the driver will cope just fine. I suppose
there can be issues for devices that do not support it, though none
obvious comes to mind.

It's one of those policies that are hard to define. But I think the best
approach for now is to not fail the suspend when pci_set_power_state()
returns an error.

Ben.

^ permalink raw reply

* Re: MPC8xx CPM I2C interface driver
From: Norbert van Bolhuis @ 2006-10-24  6:58 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: linuxppc-embedded
In-Reply-To: <20061023025001.45e821c3@localhost.localdomain>

Hi Vitaly,

>>Is there no MPC8xx CPM I2C interface driver in (latest) linux v2.6 or
>>am I mistaken ?
>>There's no code for CONFIG_I2C_ALGO8XX.
> 
> The answer is - no, there is no stuff in the 2.6 kernel. I used to have the port from 2.4 a while ago (an 
> improvement to the patches posted here btw)  but had no chance to cleanup it and push through lm_sensors.
> 
> 
>>I've a MPC870 based board and I was hoping for a MPC8xx CPM I2C
>>interface driver to be there (allowing user applications to open and
>>read/write /dev/i2c-0)
>>
> 
> You should be able to locate sources in this list's archives. Ping me once more if not, I'll try to dig it out of my HDD then...
> 

Yes indeed. I got i2c-algo-8xx.c from 2005-August. Also in the mail archives there
are some patches to this file and the I2C RPX adapter (which is about the only one
using I2C algo 8xx).
Btw. One of the changes had to do with I2C RPX adapter not requesting the I2C IRQ
since I2C algo is already doing that.

Anyway, it all seems to work now.

Thanks.

N.

-- 
This message has been scanned for viruses and is believed to be clean

^ permalink raw reply

* RE: [PATCH] qe_ic: Do a sync when masking interrupts.
From: Li Yang-r58472 @ 2006-10-24  7:17 UTC (permalink / raw)
  To: Wood Scott-B07421, Segher Boessenkool; +Cc: Paul Mackerras, linuxppc-dev
In-Reply-To: <453D097D.6090603@freescale.com>

> -----Original Message-----
> From: Scott Wood [mailto:scottwood@freescale.com]
> Sent: Tuesday, October 24, 2006 2:27 AM
> To: Segher Boessenkool
> Cc: mrz5149@acm.org; Li Yang-r58472; Paul Mackerras;
linuxppc-dev@ozlabs.org
> Subject: Re: [PATCH] qe_ic: Do a sync when masking interrupts.
>=20
> Segher Boessenkool wrote:
> > If a sync after an MMIO write is enough, then (in almost all
> > cases) so is an eieio.
>=20
> In this case, the spurious interrupts still happen with eieio, but not
> with sync. =20

eieio on e300 core is just a no-op.

- Leo

^ permalink raw reply

* RE: ARCH=powerpc: IRQ numbers change mysteriously
From: Li Yang-r58472 @ 2006-10-24  7:20 UTC (permalink / raw)
  To: Fredrik Roubert, linuxppc-embedded
In-Reply-To: <20061023164116.GC15953@igloo.df.lth.se>

The irq number on powerpc ARCH is now a virtual number.  This change was
introduced by Ben's irq mapping change in early 2.6.18-rc.

- Leo

> -----Original Message-----
> From: linuxppc-embedded-bounces+leoli=3Dfreescale.com@ozlabs.org
> [mailto:linuxppc-embedded-bounces+leoli=3Dfreescale.com@ozlabs.org] On
Behalf Of
> Fredrik Roubert
> Sent: Tuesday, October 24, 2006 12:41 AM
> To: linuxppc-embedded@ozlabs.org
> Subject: ARCH=3Dpowerpc: IRQ numbers change mysteriously
>=20
> Hi!
>=20
> On my MPC8439 based board, running Linux 2.6.18 with ARCH=3Dpowerpc, =
the
> IRQ's for the I2C behave in a for me mysterious way.
>=20
> In my DTS file, I have the following:
>=20
>=20
> i2c@3000 {
> 	device_type =3D "i2c";
> 	compatible =3D "fsl-i2c";
> 	reg =3D <3000 100>;
> 	interrupts =3D <e 8>;
> 	interrupt-parent =3D <&/soc8349@ff400000/pic@700>;
> 	dfsrr;
> };
>=20
> i2c@3100 {
> 	device_type =3D "i2c";
> 	compatible =3D "fsl-i2c";
> 	reg =3D <3000 100>;
> 	interrupts =3D <f 8>;
> 	interrupt-parent =3D <&/soc8349@ff400000/pic@700>;
> 	dfsrr;
> };
>=20
>=20
> But when I boot the system and cat /proc/interrupts, the output says:
>=20
>=20
> 18:         61   IPIC   Level     i2c-mpc
> 19:          0   IPIC   Level     i2c-mpc
>=20
>=20
> This doesn't make sense to me. Why are the IRQ numbers changed? They
are
> still adjacent, so it seems to be some logic to the change. Does
anyone
> know what this is?
>=20
> Cheers // Fredrik Roubert
>=20
> --
> Barco Medical Imaging          |  +32 56 233549
> http://www.barco.com/medical/  |  fredrik.roubert@barco.com
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox