Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
new file mode 100644
index 0000000..fb53573
--- /dev/null
+++ b/drivers/macintosh/smu.c
@@ -0,0 +1,364 @@
+/*
+ * PowerMac G5 SMU driver
+ *
+ * Copyright 2004 J. Mayer <l_indien@magic.fr>
+ * Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+/*
+ * For now, this driver includes:
+ * - RTC get & set
+ * - reboot & shutdown commands
+ * all synchronous with IRQ disabled (ugh)
+ *
+ * TODO:
+ *   rework in a way the PMU driver works, that is asynchronous
+ *   with a queue of commands. I'll do that as soon as I have an
+ *   SMU based machine at hand. Some more cleanup is needed too,
+ *   like maybe fitting it into a platform device, etc...
+ *   Also check what's up with cache coherency, and if we really
+ *   can't do better than flushing the cache, maybe build a table
+ *   of command len/reply len like the PMU driver to only flush
+ *   what is actually necessary.
+ *   --BenH.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/bootmem.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/smu.h>
+#include <asm/sections.h>
+#include <asm/abs_addr.h>
+
+#define DEBUG_SMU 1
+
+#ifdef DEBUG_SMU
+#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
+#else
+#define DPRINTK(fmt, args...) do { } while (0)
+#endif
+
+/*
+ * This is the command buffer passed to the SMU hardware
+ */
+struct smu_cmd_buf {
+	u8 cmd;
+	u8 length;
+	u8 data[0x0FFE];
+};
+
+struct smu_device {
+	spinlock_t		lock;
+	struct device_node	*of_node;
+	int			db_ack;		/* doorbell ack GPIO */
+	int			db_req;		/* doorbell req GPIO */
+	u32 __iomem		*db_buf;	/* doorbell buffer */
+	struct smu_cmd_buf	*cmd_buf;	/* command buffer virtual */
+	u32			cmd_buf_abs;	/* command buffer absolute */
+};
+
+/*
+ * I don't think there will ever be more than one SMU, so
+ * for now, just hard code that
+ */
+static struct smu_device	*smu;
+
+/*
+ * SMU low level communication stuff
+ */
+static inline int smu_cmd_stat(struct smu_cmd_buf *cmd_buf, u8 cmd_ack)
+{
+	rmb();
+	return cmd_buf->cmd == cmd_ack && cmd_buf->length != 0;
+}
+
+static inline u8 smu_save_ack_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	return (~cmd_buf->cmd) & 0xff;
+}
+
+static void smu_send_cmd(struct smu_device *dev)
+{
+	/* SMU command buf is currently cacheable, we need a physical
+	 * address. This isn't exactly a DMA mapping here, I suspect
+	 * the SMU is actually communicating with us via i2c to the
+	 * northbridge or the CPU to access RAM.
+	 */
+	writel(dev->cmd_buf_abs, dev->db_buf);
+
+	/* Ring the SMU doorbell */
+	pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, dev->db_req, 4);
+	pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, dev->db_req, 4);
+}
+
+static int smu_cmd_done(struct smu_device *dev)
+{
+	unsigned long wait = 0;
+	int gpio;
+
+	/* Check the SMU doorbell */
+	do  {
+		gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO,
+					    NULL, dev->db_ack);
+		if ((gpio & 7) == 7)
+			return 0;
+		udelay(100);
+	} while(++wait < 10000);
+
+	printk(KERN_ERR "SMU timeout !\n");
+	return -ENXIO;
+}
+
+static int smu_do_cmd(struct smu_device *dev)
+{
+	int rc;
+	u8 cmd_ack;
+
+	DPRINTK("SMU do_cmd %02x len=%d %02x\n",
+		dev->cmd_buf->cmd, dev->cmd_buf->length,
+		dev->cmd_buf->data[0]);
+
+	cmd_ack = smu_save_ack_cmd(dev->cmd_buf);
+
+	/* Clear cmd_buf cache lines */
+	flush_inval_dcache_range((unsigned long)dev->cmd_buf,
+				 ((unsigned long)dev->cmd_buf) +
+				 sizeof(struct smu_cmd_buf));
+	smu_send_cmd(dev);
+	rc = smu_cmd_done(dev);
+	if (rc == 0)
+		rc = smu_cmd_stat(dev->cmd_buf, cmd_ack) ? 0 : -1;
+
+	DPRINTK("SMU do_cmd %02x len=%d %02x => %d (%02x)\n",
+		dev->cmd_buf->cmd, dev->cmd_buf->length,
+		dev->cmd_buf->data[0], rc, cmd_ack);
+
+	return rc;
+}
+
+/* RTC low level commands */
+static inline int bcd2hex (int n)
+{
+	return (((n & 0xf0) >> 4) * 10) + (n & 0xf);
+}
+
+static inline int hex2bcd (int n)
+{
+	return ((n / 10) << 4) + (n % 10);
+}
+
+#if 0
+static inline void smu_fill_set_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 8;
+	cmd_buf->data[0] = 0x00;
+	memset(cmd_buf->data + 1, 0, 7);
+}
+
+static inline void smu_fill_get_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 1;
+	cmd_buf->data[0] = 0x01;
+}
+
+static inline void smu_fill_dis_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 1;
+	cmd_buf->data[0] = 0x02;
+}
+#endif
+
+static inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf,
+					struct rtc_time *time)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 8;
+	cmd_buf->data[0] = 0x80;
+	cmd_buf->data[1] = hex2bcd(time->tm_sec);
+	cmd_buf->data[2] = hex2bcd(time->tm_min);
+	cmd_buf->data[3] = hex2bcd(time->tm_hour);
+	cmd_buf->data[4] = time->tm_wday;
+	cmd_buf->data[5] = hex2bcd(time->tm_mday);
+	cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1;
+	cmd_buf->data[7] = hex2bcd(time->tm_year - 100);
+}
+
+static inline void smu_fill_get_rtc_cmd(struct smu_cmd_buf *cmd_buf)
+{
+	cmd_buf->cmd = 0x8e;
+	cmd_buf->length = 1;
+	cmd_buf->data[0] = 0x81;
+}
+
+static void smu_parse_get_rtc_reply(struct smu_cmd_buf *cmd_buf,
+				    struct rtc_time *time)
+{
+	time->tm_sec = bcd2hex(cmd_buf->data[0]);
+	time->tm_min = bcd2hex(cmd_buf->data[1]);
+	time->tm_hour = bcd2hex(cmd_buf->data[2]);
+	time->tm_wday = bcd2hex(cmd_buf->data[3]);
+	time->tm_mday = bcd2hex(cmd_buf->data[4]);
+	time->tm_mon = bcd2hex(cmd_buf->data[5]) - 1;
+	time->tm_year = bcd2hex(cmd_buf->data[6]) + 100;
+}
+
+int smu_get_rtc_time(struct rtc_time *time)
+{
+	unsigned long flags;
+	int rc;
+
+	if (smu == NULL)
+		return -ENODEV;
+
+	memset(time, 0, sizeof(struct rtc_time));
+	spin_lock_irqsave(&smu->lock, flags);
+	smu_fill_get_rtc_cmd(smu->cmd_buf);
+	rc = smu_do_cmd(smu);
+	if (rc == 0)
+		smu_parse_get_rtc_reply(smu->cmd_buf, time);
+	spin_unlock_irqrestore(&smu->lock, flags);
+
+	return rc;
+}
+
+int smu_set_rtc_time(struct rtc_time *time)
+{
+	unsigned long flags;
+	int rc;
+
+	if (smu == NULL)
+		return -ENODEV;
+
+	spin_lock_irqsave(&smu->lock, flags);
+	smu_fill_set_rtc_cmd(smu->cmd_buf, time);
+	rc = smu_do_cmd(smu);
+	spin_unlock_irqrestore(&smu->lock, flags);
+
+	return rc;
+}
+
+void smu_shutdown(void)
+{
+	const unsigned char *command = "SHUTDOWN";
+	unsigned long flags;
+
+	if (smu == NULL)
+		return;
+
+	spin_lock_irqsave(&smu->lock, flags);
+	smu->cmd_buf->cmd = 0xaa;
+	smu->cmd_buf->length = strlen(command);
+	strcpy(smu->cmd_buf->data, command);
+	smu_do_cmd(smu);
+	for (;;)
+		;
+	spin_unlock_irqrestore(&smu->lock, flags);
+}
+
+void smu_restart(void)
+{
+	const unsigned char *command = "RESTART";
+	unsigned long flags;
+
+	if (smu == NULL)
+		return;
+
+	spin_lock_irqsave(&smu->lock, flags);
+	smu->cmd_buf->cmd = 0xaa;
+	smu->cmd_buf->length = strlen(command);
+	strcpy(smu->cmd_buf->data, command);
+	smu_do_cmd(smu);
+	for (;;)
+		;
+	spin_unlock_irqrestore(&smu->lock, flags);
+}
+
+int smu_present(void)
+{
+	return smu != NULL;
+}
+
+
+int smu_init (void)
+{
+	struct device_node *np;
+	u32 *data;
+
+        np = of_find_node_by_type(NULL, "smu");
+        if (np == NULL)
+		return -ENODEV;
+
+	if (smu_cmdbuf_abs == 0) {
+		printk(KERN_ERR "SMU: Command buffer not allocated !\n");
+		return -EINVAL;
+	}
+
+	smu = alloc_bootmem(sizeof(struct smu_device));
+	if (smu == NULL)
+		return -ENOMEM;
+	memset(smu, 0, sizeof(*smu));
+
+	spin_lock_init(&smu->lock);
+	smu->of_node = np;
+	/* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a
+	 * 32 bits value safely
+	 */
+	smu->cmd_buf_abs = (u32)smu_cmdbuf_abs;
+	smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs);
+
+	np = of_find_node_by_name(NULL, "smu-doorbell");
+	if (np == NULL) {
+		printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n");
+		goto fail;
+	}
+	data = (u32 *)get_property(np, "reg", NULL);
+	of_node_put(np);
+	if (data == NULL) {
+		printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n");
+		goto fail;
+	}
+
+	/* Current setup has one doorbell GPIO that does both doorbell
+	 * and ack. GPIOs are at 0x50, best would be to find that out
+	 * in the device-tree though.
+	 */
+	smu->db_req = 0x50 + *data;
+	smu->db_ack = 0x50 + *data;
+
+	/* Doorbell buffer is currently hard-coded, I didn't find a proper
+	 * device-tree entry giving the address. Best would probably to use
+	 * an offset for K2 base though, but let's do it that way for now.
+	 */
+	smu->db_buf = ioremap(0x8000860c, 0x1000);
+	if (smu->db_buf == NULL) {
+		printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n");
+		goto fail;
+	}
+
+	sys_ctrler = SYS_CTRLER_SMU;
+	return 0;
+
+ fail:
+	smu = NULL;
+	return -ENXIO;
+
+}