/*
* 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.
*/
/*
* TODO:
* - maybe add timeout to commands ?
* - blocking version of time functions
* - polling version of i2c commands (including timer that works with
* interrutps off)
* - maybe avoid some data copies with i2c by directly using the smu cmd
* buffer and a lower level internal interface
* - understand SMU -> CPU events and implement reception of them via
* the userland interface
*/
#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 <linux/completion.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/sysdev.h>
#include <linux/poll.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>
#include <asm/uaccess.h>
#include <asm/of_device.h>
#define VERSION "0.7"
#define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
#undef DEBUG_SMU
#ifdef DEBUG_SMU
#define DPRINTK(fmt, args...) do { udbg_printf(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
*/
#define SMU_MAX_DATA 254
struct smu_cmd_buf {
u8 cmd;
u8 length;
u8 data[SMU_MAX_DATA];
};
struct smu_device {
spinlock_t lock;
struct device_node *of_node;
struct of_device *of_dev;
int doorbell; /* doorbell gpio */
u32 __iomem *db_buf; /* doorbell buffer */
int db_irq;
int msg;
int msg_irq;
struct smu_cmd_buf *cmd_buf; /* command buffer virtual */
u32 cmd_buf_abs; /* command buffer absolute */
struct list_head cmd_list;
struct smu_cmd *cmd_cur; /* pending command */
struct list_head cmd_i2c_list;
struct smu_i2c_cmd *cmd_i2c_cur; /* pending i2c command */
struct timer_list i2c_timer;
};
/*
* I don't think there will ever be more than one SMU, so
* for now, just hard code that
*/
static struct smu_device *smu;
static DECLARE_MUTEX(smu_part_access);
/*
* SMU driver low level stuff
*/
static void smu_start_cmd(void)
{
unsigned long faddr, fend;
struct smu_cmd *cmd;
if (list_empty(&smu->cmd_list))
return;
/* Fetch first command in queue */
cmd = list_entry(smu->cmd_list.next, struct smu_cmd, link);
smu->cmd_cur = cmd;
list_del(&cmd->link);
DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
cmd->data_len);
DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n",
((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1],
((u8 *)cmd->data_buf)[2], ((