VisionFive2 Linux kernel

StarFive Tech Linux Kernel for VisionFive (JH7110) boards (mirror)

More than 9999 Commits   32 Branches   54 Tags
2c162f9b41722 (Thomas Gleixner        2019-06-03 07:45:02 +0200    1) // SPDX-License-Identifier: GPL-2.0-only
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    2) /*
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    3)  * PowerMac G5 SMU driver
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    4)  *
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    5)  * Copyright 2004 J. Mayer <l_indien@magic.fr>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    6)  * Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    7)  */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    8) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700    9) /*
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   10)  * TODO:
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   11)  *  - maybe add timeout to commands ?
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   12)  *  - blocking version of time functions
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   13)  *  - polling version of i2c commands (including timer that works with
f18816ba20655 (Joe Perches            2008-02-03 17:18:02 +0200   14)  *    interrupts off)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   15)  *  - maybe avoid some data copies with i2c by directly using the smu cmd
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   16)  *    buffer and a lower level internal interface
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   17)  *  - understand SMU -> CPU events and implement reception of them via
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   18)  *    the userland interface
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   19)  */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   20) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   21) #include <linux/types.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   22) #include <linux/kernel.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   23) #include <linux/device.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   24) #include <linux/dmapool.h>
57c8a661d95df (Mike Rapoport          2018-10-30 15:09:49 -0700   25) #include <linux/memblock.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   26) #include <linux/vmalloc.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   27) #include <linux/highmem.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   28) #include <linux/jiffies.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   29) #include <linux/interrupt.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   30) #include <linux/rtc.h>
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   31) #include <linux/completion.h>
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   32) #include <linux/miscdevice.h>
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   33) #include <linux/delay.h>
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   34) #include <linux/poll.h>
14cc3e2b633bb (Ingo Molnar            2006-03-26 01:37:14 -0800   35) #include <linux/mutex.h>
ad9e05aef7c86 (Stephen Rothwell       2008-05-23 16:27:02 +1000   36) #include <linux/of_device.h>
5af5073004071 (Rob Herring            2013-09-17 14:28:33 -0500   37) #include <linux/of_irq.h>
ad9e05aef7c86 (Stephen Rothwell       2008-05-23 16:27:02 +1000   38) #include <linux/of_platform.h>
5a0e3ad6af866 (Tejun Heo              2010-03-24 17:04:11 +0900   39) #include <linux/slab.h>
174cd4b1e5fbd (Ingo Molnar            2017-02-02 19:15:33 +0100   40) #include <linux/sched/signal.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   41) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   42) #include <asm/byteorder.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   43) #include <asm/io.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   44) #include <asm/prom.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   45) #include <asm/machdep.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   46) #include <asm/pmac_feature.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   47) #include <asm/smu.h>
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   48) #include <asm/sections.h>
7c0f6ba682b9c (Linus Torvalds         2016-12-24 11:46:01 -0800   49) #include <linux/uaccess.h>
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   50) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100   51) #define VERSION "0.7"
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   52) #define AUTHOR  "(c) 2005 Benjamin Herrenschmidt, IBM Corp."
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   53) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   54) #undef DEBUG_SMU
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   55) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   56) #ifdef DEBUG_SMU
1beb6a7d6cbed (Benjamin Herrenschmidt 2005-12-14 13:10:10 +1100   57) #define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   58) #else
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   59) #define DPRINTK(fmt, args...) do { } while (0)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   60) #endif
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   61) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   62) /*
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   63)  * This is the command buffer passed to the SMU hardware
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   64)  */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   65) #define SMU_MAX_DATA	254
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   66) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   67) struct smu_cmd_buf {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   68) 	u8 cmd;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   69) 	u8 length;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   70) 	u8 data[SMU_MAX_DATA];
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   71) };
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   72) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   73) struct smu_device {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   74) 	spinlock_t		lock;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   75) 	struct device_node	*of_node;
2dc1158137682 (Grant Likely           2010-08-06 09:25:50 -0600   76) 	struct platform_device	*of_dev;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   77) 	int			doorbell;	/* doorbell gpio */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   78) 	u32 __iomem		*db_buf;	/* doorbell buffer */
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700   79) 	struct device_node	*db_node;
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700   80) 	unsigned int		db_irq;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   81) 	int			msg;
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700   82) 	struct device_node	*msg_node;
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700   83) 	unsigned int		msg_irq;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   84) 	struct smu_cmd_buf	*cmd_buf;	/* command buffer virtual */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   85) 	u32			cmd_buf_abs;	/* command buffer absolute */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   86) 	struct list_head	cmd_list;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   87) 	struct smu_cmd		*cmd_cur;	/* pending command */
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100   88) 	int			broken_nap;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   89) 	struct list_head	cmd_i2c_list;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   90) 	struct smu_i2c_cmd	*cmd_i2c_cur;	/* pending i2c command */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700   91) 	struct timer_list	i2c_timer;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   92) };
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   93) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   94) /*
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   95)  * I don't think there will ever be more than one SMU, so
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   96)  * for now, just hard code that
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   97)  */
d851b6e04ee97 (Arnd Bergmann          2010-06-02 14:28:52 +0200   98) static DEFINE_MUTEX(smu_mutex);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700   99) static struct smu_device	*smu;
14cc3e2b633bb (Ingo Molnar            2006-03-26 01:37:14 -0800  100) static DEFINE_MUTEX(smu_part_access);
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  101) static int smu_irq_inited;
91b6fad5cf16c (Benjamin Herrenschmidt 2016-07-05 15:03:50 +1000  102) static unsigned long smu_cmdbuf_abs;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  103) 
0788f28575801 (Kees Cook              2017-10-21 13:43:53 -0700  104) static void smu_i2c_retry(struct timer_list *t);
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  105) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  106) /*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  107)  * SMU driver low level stuff
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  108)  */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  109) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  110) static void smu_start_cmd(void)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  111) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  112) 	unsigned long faddr, fend;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  113) 	struct smu_cmd *cmd;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  114) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  115) 	if (list_empty(&smu->cmd_list))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  116) 		return;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  117) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  118) 	/* Fetch first command in queue */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  119) 	cmd = list_entry(smu->cmd_list.next, struct smu_cmd, link);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  120) 	smu->cmd_cur = cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  121) 	list_del(&cmd->link);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  122) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  123) 	DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  124) 		cmd->data_len);
ebd004e4edd56 (Andy Shevchenko        2013-04-22 03:02:19 +0000  125) 	DPRINTK("SMU: data buffer: %8ph\n", cmd->data_buf);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  126) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  127) 	/* Fill the SMU command buffer */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  128) 	smu->cmd_buf->cmd = cmd->cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  129) 	smu->cmd_buf->length = cmd->data_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  130) 	memcpy(smu->cmd_buf->data, cmd->data_buf, cmd->data_len);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  131) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  132) 	/* Flush command and data to RAM */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  133) 	faddr = (unsigned long)smu->cmd_buf;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  134) 	fend = faddr + smu->cmd_buf->length + 2;
1cfb725fb1899 (Christophe Leroy       2019-05-14 09:05:13 +0000  135) 	flush_dcache_range(faddr, fend);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  136) 
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  137) 
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  138) 	/* We also disable NAP mode for the duration of the command
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  139) 	 * on U3 based machines.
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  140) 	 * This is slightly racy as it can be written back to 1 by a sysctl
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  141) 	 * but that never happens in practice. There seem to be an issue with
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  142) 	 * U3 based machines such as the iMac G5 where napping for the
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  143) 	 * whole duration of the command prevents the SMU from fetching it
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  144) 	 * from memory. This might be related to the strange i2c based
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  145) 	 * mechanism the SMU uses to access memory.
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  146) 	 */
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  147) 	if (smu->broken_nap)
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  148) 		powersave_nap = 0;
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  149) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  150) 	/* This isn't exactly a DMA mapping here, I suspect
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  151) 	 * the SMU is actually communicating with us via i2c to the
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  152) 	 * northbridge or the CPU to access RAM.
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  153) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  154) 	writel(smu->cmd_buf_abs, smu->db_buf);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  155) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  156) 	/* Ring the SMU doorbell */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  157) 	pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, smu->doorbell, 4);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  158) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  159) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  160) 
7d12e780e003f (David Howells          2006-10-05 14:55:46 +0100  161) static irqreturn_t smu_db_intr(int irq, void *arg)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  162) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  163) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  164) 	struct smu_cmd *cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  165) 	void (*done)(struct smu_cmd *cmd, void *misc) = NULL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  166) 	void *misc = NULL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  167) 	u8 gpio;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  168) 	int rc = 0;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  169) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  170) 	/* SMU completed the command, well, we hope, let's make sure
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  171) 	 * of it
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  172) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  173) 	spin_lock_irqsave(&smu->lock, flags);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  174) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  175) 	gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell);
a44fe13eab664 (Benjamin Herrenschmidt 2005-09-30 08:25:17 +1000  176) 	if ((gpio & 7) != 7) {
a44fe13eab664 (Benjamin Herrenschmidt 2005-09-30 08:25:17 +1000  177) 		spin_unlock_irqrestore(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  178) 		return IRQ_HANDLED;
a44fe13eab664 (Benjamin Herrenschmidt 2005-09-30 08:25:17 +1000  179) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  180) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  181) 	cmd = smu->cmd_cur;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  182) 	smu->cmd_cur = NULL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  183) 	if (cmd == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  184) 		goto bail;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  185) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  186) 	if (rc == 0) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  187) 		unsigned long faddr;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  188) 		int reply_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  189) 		u8 ack;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  190) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  191) 		/* CPU might have brought back the cache line, so we need
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  192) 		 * to flush again before peeking at the SMU response. We
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  193) 		 * flush the entire buffer for now as we haven't read the
efad798b9f013 (Paulius Zaleckas       2008-02-03 15:42:53 +0200  194) 		 * reply length (it's only 2 cache lines anyway)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  195) 		 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  196) 		faddr = (unsigned long)smu->cmd_buf;
1cfb725fb1899 (Christophe Leroy       2019-05-14 09:05:13 +0000  197) 		flush_dcache_range(faddr, faddr + 256);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  198) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  199) 		/* Now check ack */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  200) 		ack = (~cmd->cmd) & 0xff;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  201) 		if (ack != smu->cmd_buf->cmd) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  202) 			DPRINTK("SMU: incorrect ack, want %x got %x\n",
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  203) 				ack, smu->cmd_buf->cmd);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  204) 			rc = -EIO;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  205) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  206) 		reply_len = rc == 0 ? smu->cmd_buf->length : 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  207) 		DPRINTK("SMU: reply len: %d\n", reply_len);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  208) 		if (reply_len > cmd->reply_len) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  209) 			printk(KERN_WARNING "SMU: reply buffer too small,"
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  210) 			       "got %d bytes for a %d bytes buffer\n",
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  211) 			       reply_len, cmd->reply_len);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  212) 			reply_len = cmd->reply_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  213) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  214) 		cmd->reply_len = reply_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  215) 		if (cmd->reply_buf && reply_len)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  216) 			memcpy(cmd->reply_buf, smu->cmd_buf->data, reply_len);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  217) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  218) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  219) 	/* Now complete the command. Write status last in order as we lost
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  220) 	 * ownership of the command structure as soon as it's no longer -1
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  221) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  222) 	done = cmd->done;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  223) 	misc = cmd->misc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  224) 	mb();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  225) 	cmd->status = rc;
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  226) 
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  227) 	/* Re-enable NAP mode */
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  228) 	if (smu->broken_nap)
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  229) 		powersave_nap = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  230)  bail:
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  231) 	/* Start next command if any */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  232) 	smu_start_cmd();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  233) 	spin_unlock_irqrestore(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  234) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  235) 	/* Call command completion handler if any */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  236) 	if (done)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  237) 		done(cmd, misc);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  238) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  239) 	/* It's an edge interrupt, nothing to do */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  240) 	return IRQ_HANDLED;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  241) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  242) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  243) 
7d12e780e003f (David Howells          2006-10-05 14:55:46 +0100  244) static irqreturn_t smu_msg_intr(int irq, void *arg)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  245) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  246) 	/* I don't quite know what to do with this one, we seem to never
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  247) 	 * receive it, so I suspect we have to arm it someway in the SMU
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  248) 	 * to start getting events that way.
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  249) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  250) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  251) 	printk(KERN_INFO "SMU: message interrupt !\n");
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  252) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  253) 	/* It's an edge interrupt, nothing to do */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  254) 	return IRQ_HANDLED;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  255) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  256) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  257) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  258) /*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  259)  * Queued command management.
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  260)  *
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  261)  */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  262) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  263) int smu_queue_cmd(struct smu_cmd *cmd)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  264) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  265) 	unsigned long flags;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  266) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  267) 	if (smu == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  268) 		return -ENODEV;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  269) 	if (cmd->data_len > SMU_MAX_DATA ||
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  270) 	    cmd->reply_len > SMU_MAX_DATA)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  271) 		return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  272) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  273) 	cmd->status = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  274) 	spin_lock_irqsave(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  275) 	list_add_tail(&cmd->link, &smu->cmd_list);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  276) 	if (smu->cmd_cur == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  277) 		smu_start_cmd();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  278) 	spin_unlock_irqrestore(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  279) 
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  280) 	/* Workaround for early calls when irq isn't available */
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  281) 	if (!smu_irq_inited || !smu->db_irq)
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  282) 		smu_spinwait_cmd(cmd);
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  283) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  284) 	return 0;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  285) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  286) EXPORT_SYMBOL(smu_queue_cmd);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  287) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  288) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  289) int smu_queue_simple(struct smu_simple_cmd *scmd, u8 command,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  290) 		     unsigned int data_len,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  291) 		     void (*done)(struct smu_cmd *cmd, void *misc),
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  292) 		     void *misc, ...)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  293) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  294) 	struct smu_cmd *cmd = &scmd->cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  295) 	va_list list;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  296) 	int i;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  297) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  298) 	if (data_len > sizeof(scmd->buffer))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  299) 		return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  300) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  301) 	memset(scmd, 0, sizeof(*scmd));
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  302) 	cmd->cmd = command;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  303) 	cmd->data_len = data_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  304) 	cmd->data_buf = scmd->buffer;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  305) 	cmd->reply_len = sizeof(scmd->buffer);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  306) 	cmd->reply_buf = scmd->buffer;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  307) 	cmd->done = done;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  308) 	cmd->misc = misc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  309) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  310) 	va_start(list, misc);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  311) 	for (i = 0; i < data_len; ++i)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  312) 		scmd->buffer[i] = (u8)va_arg(list, int);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  313) 	va_end(list);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  314) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  315) 	return smu_queue_cmd(cmd);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  316) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  317) EXPORT_SYMBOL(smu_queue_simple);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  318) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  319) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  320) void smu_poll(void)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  321) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  322) 	u8 gpio;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  323) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  324) 	if (smu == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  325) 		return;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  326) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  327) 	gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, smu->doorbell);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  328) 	if ((gpio & 7) == 7)
7d12e780e003f (David Howells          2006-10-05 14:55:46 +0100  329) 		smu_db_intr(smu->db_irq, smu);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  330) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  331) EXPORT_SYMBOL(smu_poll);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  332) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  333) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  334) void smu_done_complete(struct smu_cmd *cmd, void *misc)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  335) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  336) 	struct completion *comp = misc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  337) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  338) 	complete(comp);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  339) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  340) EXPORT_SYMBOL(smu_done_complete);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  341) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  342) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  343) void smu_spinwait_cmd(struct smu_cmd *cmd)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  344) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  345) 	while(cmd->status == 1)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  346) 		smu_poll();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  347) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  348) EXPORT_SYMBOL(smu_spinwait_cmd);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  349) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  350) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  351) /* RTC low level commands */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  352) static inline int bcd2hex (int n)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  353) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  354) 	return (((n & 0xf0) >> 4) * 10) + (n & 0xf);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  355) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  356) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  357) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  358) static inline int hex2bcd (int n)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  359) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  360) 	return ((n / 10) << 4) + (n % 10);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  361) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  362) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  363) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  364) static inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf,
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  365) 					struct rtc_time *time)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  366) {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  367) 	cmd_buf->cmd = 0x8e;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  368) 	cmd_buf->length = 8;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  369) 	cmd_buf->data[0] = 0x80;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  370) 	cmd_buf->data[1] = hex2bcd(time->tm_sec);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  371) 	cmd_buf->data[2] = hex2bcd(time->tm_min);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  372) 	cmd_buf->data[3] = hex2bcd(time->tm_hour);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  373) 	cmd_buf->data[4] = time->tm_wday;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  374) 	cmd_buf->data[5] = hex2bcd(time->tm_mday);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  375) 	cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  376) 	cmd_buf->data[7] = hex2bcd(time->tm_year - 100);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  377) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  378) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  379) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  380) int smu_get_rtc_time(struct rtc_time *time, int spinwait)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  381) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  382) 	struct smu_simple_cmd cmd;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  383) 	int rc;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  384) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  385) 	if (smu == NULL)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  386) 		return -ENODEV;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  387) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  388) 	memset(time, 0, sizeof(struct rtc_time));
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  389) 	rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 1, NULL, NULL,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  390) 			      SMU_CMD_RTC_GET_DATETIME);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  391) 	if (rc)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  392) 		return rc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  393) 	smu_spinwait_simple(&cmd);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  394) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  395) 	time->tm_sec = bcd2hex(cmd.buffer[0]);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  396) 	time->tm_min = bcd2hex(cmd.buffer[1]);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  397) 	time->tm_hour = bcd2hex(cmd.buffer[2]);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  398) 	time->tm_wday = bcd2hex(cmd.buffer[3]);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  399) 	time->tm_mday = bcd2hex(cmd.buffer[4]);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  400) 	time->tm_mon = bcd2hex(cmd.buffer[5]) - 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  401) 	time->tm_year = bcd2hex(cmd.buffer[6]) + 100;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  402) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  403) 	return 0;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  404) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  405) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  406) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  407) int smu_set_rtc_time(struct rtc_time *time, int spinwait)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  408) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  409) 	struct smu_simple_cmd cmd;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  410) 	int rc;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  411) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  412) 	if (smu == NULL)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  413) 		return -ENODEV;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  414) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  415) 	rc = smu_queue_simple(&cmd, SMU_CMD_RTC_COMMAND, 8, NULL, NULL,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  416) 			      SMU_CMD_RTC_SET_DATETIME,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  417) 			      hex2bcd(time->tm_sec),
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  418) 			      hex2bcd(time->tm_min),
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  419) 			      hex2bcd(time->tm_hour),
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  420) 			      time->tm_wday,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  421) 			      hex2bcd(time->tm_mday),
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  422) 			      hex2bcd(time->tm_mon) + 1,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  423) 			      hex2bcd(time->tm_year - 100));
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  424) 	if (rc)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  425) 		return rc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  426) 	smu_spinwait_simple(&cmd);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  427) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  428) 	return 0;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  429) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  430) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  431) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  432) void smu_shutdown(void)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  433) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  434) 	struct smu_simple_cmd cmd;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  435) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  436) 	if (smu == NULL)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  437) 		return;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  438) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  439) 	if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 9, NULL, NULL,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  440) 			     'S', 'H', 'U', 'T', 'D', 'O', 'W', 'N', 0))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  441) 		return;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  442) 	smu_spinwait_simple(&cmd);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  443) 	for (;;)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  444) 		;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  445) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  446) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  447) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  448) void smu_restart(void)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  449) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  450) 	struct smu_simple_cmd cmd;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  451) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  452) 	if (smu == NULL)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  453) 		return;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  454) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  455) 	if (smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, NULL, NULL,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  456) 			     'R', 'E', 'S', 'T', 'A', 'R', 'T', 0))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  457) 		return;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  458) 	smu_spinwait_simple(&cmd);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  459) 	for (;;)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  460) 		;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  461) }
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  462) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  463) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  464) int smu_present(void)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  465) {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  466) 	return smu != NULL;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  467) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  468) EXPORT_SYMBOL(smu_present);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  469) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  470) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  471) int __init smu_init (void)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  472) {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  473) 	struct device_node *np;
018a3d1db7cdb (Jeremy Kerr            2006-07-12 15:40:29 +1000  474) 	const u32 *data;
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  475) 	int ret = 0;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  476) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  477)         np = of_find_node_by_type(NULL, "smu");
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  478)         if (np == NULL)
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  479) 		return -ENODEV;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  480) 
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  481) 	printk(KERN_INFO "SMU: Driver %s %s\n", VERSION, AUTHOR);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  482) 
91b6fad5cf16c (Benjamin Herrenschmidt 2016-07-05 15:03:50 +1000  483) 	/*
91b6fad5cf16c (Benjamin Herrenschmidt 2016-07-05 15:03:50 +1000  484) 	 * SMU based G5s need some memory below 2Gb. Thankfully this is
91b6fad5cf16c (Benjamin Herrenschmidt 2016-07-05 15:03:50 +1000  485) 	 * called at a time where memblock is still available.
91b6fad5cf16c (Benjamin Herrenschmidt 2016-07-05 15:03:50 +1000  486) 	 */
0ba9e6edd4c2e (Mike Rapoport          2019-03-11 23:29:35 -0700  487) 	smu_cmdbuf_abs = memblock_phys_alloc_range(4096, 4096, 0, 0x80000000UL);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  488) 	if (smu_cmdbuf_abs == 0) {
91b6fad5cf16c (Benjamin Herrenschmidt 2016-07-05 15:03:50 +1000  489) 		printk(KERN_ERR "SMU: Command buffer allocation failed !\n");
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  490) 		ret = -EINVAL;
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  491) 		goto fail_np;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  492) 	}
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  493) 
7e1c4e27928e5 (Mike Rapoport          2018-10-30 15:09:57 -0700  494) 	smu = memblock_alloc(sizeof(struct smu_device), SMP_CACHE_BYTES);
8a7f97b902f4f (Mike Rapoport          2019-03-11 23:30:31 -0700  495) 	if (!smu)
8a7f97b902f4f (Mike Rapoport          2019-03-11 23:30:31 -0700  496) 		panic("%s: Failed to allocate %zu bytes\n", __func__,
8a7f97b902f4f (Mike Rapoport          2019-03-11 23:30:31 -0700  497) 		      sizeof(struct smu_device));
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  498) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  499) 	spin_lock_init(&smu->lock);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  500) 	INIT_LIST_HEAD(&smu->cmd_list);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  501) 	INIT_LIST_HEAD(&smu->cmd_i2c_list);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  502) 	smu->of_node = np;
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  503) 	smu->db_irq = 0;
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  504) 	smu->msg_irq = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  505) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  506) 	/* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  507) 	 * 32 bits value safely
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  508) 	 */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  509) 	smu->cmd_buf_abs = (u32)smu_cmdbuf_abs;
48817c58066fe (Michael Ellerman       2012-07-25 21:19:54 +0000  510) 	smu->cmd_buf = __va(smu_cmdbuf_abs);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  511) 
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  512) 	smu->db_node = of_find_node_by_name(NULL, "smu-doorbell");
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  513) 	if (smu->db_node == NULL) {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  514) 		printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n");
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  515) 		ret = -ENXIO;
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  516) 		goto fail_bootmem;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  517) 	}
01b2726dd11ef (Stephen Rothwell       2007-04-27 13:41:15 +1000  518) 	data = of_get_property(smu->db_node, "reg", NULL);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  519) 	if (data == NULL) {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  520) 		printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n");
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  521) 		ret = -ENXIO;
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  522) 		goto fail_db_node;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  523) 	}
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  524) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  525) 	/* Current setup has one doorbell GPIO that does both doorbell
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  526) 	 * and ack. GPIOs are at 0x50, best would be to find that out
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  527) 	 * in the device-tree though.
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  528) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  529) 	smu->doorbell = *data;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  530) 	if (smu->doorbell < 0x50)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  531) 		smu->doorbell += 0x50;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  532) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  533) 	/* Now look for the smu-interrupt GPIO */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  534) 	do {
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  535) 		smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt");
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  536) 		if (smu->msg_node == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  537) 			break;
01b2726dd11ef (Stephen Rothwell       2007-04-27 13:41:15 +1000  538) 		data = of_get_property(smu->msg_node, "reg", NULL);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  539) 		if (data == NULL) {
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  540) 			of_node_put(smu->msg_node);
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  541) 			smu->msg_node = NULL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  542) 			break;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  543) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  544) 		smu->msg = *data;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  545) 		if (smu->msg < 0x50)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  546) 			smu->msg += 0x50;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  547) 	} while(0);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  548) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  549) 	/* Doorbell buffer is currently hard-coded, I didn't find a proper
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  550) 	 * device-tree entry giving the address. Best would probably to use
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  551) 	 * an offset for K2 base though, but let's do it that way for now.
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  552) 	 */
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  553) 	smu->db_buf = ioremap(0x8000860c, 0x1000);
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  554) 	if (smu->db_buf == NULL) {
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  555) 		printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n");
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  556) 		ret = -ENXIO;
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  557) 		goto fail_msg_node;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  558) 	}
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  559) 
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  560) 	/* U3 has an issue with NAP mode when issuing SMU commands */
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  561) 	smu->broken_nap = pmac_get_uninorth_variant() < 4;
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  562) 	if (smu->broken_nap)
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  563) 		printk(KERN_INFO "SMU: using NAP mode workaround\n");
592a607bbc053 (Benjamin Herrenschmidt 2008-02-07 14:29:43 +1100  564) 
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  565) 	sys_ctrler = SYS_CTRLER_SMU;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  566) 	return 0;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  567) 
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  568) fail_msg_node:
4b7d8358819da (Markus Elfring         2015-02-04 21:32:27 +0100  569) 	of_node_put(smu->msg_node);
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  570) fail_db_node:
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  571) 	of_node_put(smu->db_node);
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  572) fail_bootmem:
2013288f72388 (Mike Rapoport          2018-10-30 15:09:21 -0700  573) 	memblock_free(__pa(smu), sizeof(struct smu_device));
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  574) 	smu = NULL;
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  575) fail_np:
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  576) 	of_node_put(np);
73f38fe1b563a (Julia Lawall           2008-06-24 04:14:52 +1000  577) 	return ret;
^1da177e4c3f4 (Linus Torvalds         2005-04-16 15:20:36 -0700  578) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  579) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  580) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  581) static int smu_late_init(void)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  582) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  583) 	if (!smu)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  584) 		return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  585) 
0788f28575801 (Kees Cook              2017-10-21 13:43:53 -0700  586) 	timer_setup(&smu->i2c_timer, smu_i2c_retry, 0);
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  587) 
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  588) 	if (smu->db_node) {
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  589) 		smu->db_irq = irq_of_parse_and_map(smu->db_node, 0);
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  590) 		if (!smu->db_irq)
b6a945ae03fd3 (Rob Herring            2017-07-18 16:43:12 -0500  591) 			printk(KERN_ERR "smu: failed to map irq for node %pOF\n",
b6a945ae03fd3 (Rob Herring            2017-07-18 16:43:12 -0500  592) 			       smu->db_node);
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  593) 	}
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  594) 	if (smu->msg_node) {
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  595) 		smu->msg_irq = irq_of_parse_and_map(smu->msg_node, 0);
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  596) 		if (!smu->msg_irq)
b6a945ae03fd3 (Rob Herring            2017-07-18 16:43:12 -0500  597) 			printk(KERN_ERR "smu: failed to map irq for node %pOF\n",
b6a945ae03fd3 (Rob Herring            2017-07-18 16:43:12 -0500  598) 			       smu->msg_node);
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  599) 	}
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  600) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  601) 	/*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  602) 	 * Try to request the interrupts
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  603) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  604) 
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  605) 	if (smu->db_irq) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  606) 		if (request_irq(smu->db_irq, smu_db_intr,
dace145374b8e (Thomas Gleixner        2006-07-01 19:29:38 -0700  607) 				IRQF_SHARED, "SMU doorbell", smu) < 0) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  608) 			printk(KERN_WARNING "SMU: can't "
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  609) 			       "request interrupt %d\n",
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  610) 			       smu->db_irq);
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  611) 			smu->db_irq = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  612) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  613) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  614) 
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  615) 	if (smu->msg_irq) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  616) 		if (request_irq(smu->msg_irq, smu_msg_intr,
dace145374b8e (Thomas Gleixner        2006-07-01 19:29:38 -0700  617) 				IRQF_SHARED, "SMU message", smu) < 0) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  618) 			printk(KERN_WARNING "SMU: can't "
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  619) 			       "request interrupt %d\n",
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  620) 			       smu->msg_irq);
ef24ba7091517 (Michael Ellerman       2016-09-06 21:53:24 +1000  621) 			smu->msg_irq = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  622) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  623) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  624) 
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  625) 	smu_irq_inited = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  626) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  627) }
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  628) /* This has to be before arch_initcall as the low i2c stuff relies on the
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  629)  * above having been done before we reach arch_initcalls
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  630)  */
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  631) core_initcall(smu_late_init);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  632) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  633) /*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  634)  * sysfs visibility
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  635)  */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  636) 
c4028958b6eca (David Howells          2006-11-22 14:57:56 +0000  637) static void smu_expose_childs(struct work_struct *unused)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  638) {
a28d3af2a26c8 (Benjamin Herrenschmidt 2006-01-07 11:35:26 +1100  639) 	struct device_node *np;
a28d3af2a26c8 (Benjamin Herrenschmidt 2006-01-07 11:35:26 +1100  640) 
9c826d31a7381 (Qinglang Miao          2020-09-16 14:21:22 +0800  641) 	for_each_child_of_node(smu->of_node, np)
55b61fec22caa (Stephen Rothwell       2007-05-03 17:26:52 +1000  642) 		if (of_device_is_compatible(np, "smu-sensors"))
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  643) 			of_platform_device_create(np, "smu-sensors",
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  644) 						  &smu->of_dev->dev);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  645) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  646) 
c4028958b6eca (David Howells          2006-11-22 14:57:56 +0000  647) static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  648) 
000061245a679 (Grant Likely           2011-02-22 19:59:54 -0700  649) static int smu_platform_probe(struct platform_device* dev)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  650) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  651) 	if (!smu)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  652) 		return -ENODEV;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  653) 	smu->of_dev = dev;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  654) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  655) 	/*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  656) 	 * Ok, we are matched, now expose all i2c busses. We have to defer
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  657) 	 * that unfortunately or it would deadlock inside the device model
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  658) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  659) 	schedule_work(&smu_expose_childs_work);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  660) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  661) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  662) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  663) 
46759a7c13264 (Márton Németh          2010-01-10 01:43:14 +0000  664) static const struct of_device_id smu_platform_match[] =
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  665) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  666) 	{
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  667) 		.type		= "smu",
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  668) 	},
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  669) 	{},
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  670) };
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  671) 
000061245a679 (Grant Likely           2011-02-22 19:59:54 -0700  672) static struct platform_driver smu_of_platform_driver =
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  673) {
4018294b53d1d (Grant Likely           2010-04-13 16:13:02 -0700  674) 	.driver = {
4018294b53d1d (Grant Likely           2010-04-13 16:13:02 -0700  675) 		.name = "smu",
4018294b53d1d (Grant Likely           2010-04-13 16:13:02 -0700  676) 		.of_match_table = smu_platform_match,
4018294b53d1d (Grant Likely           2010-04-13 16:13:02 -0700  677) 	},
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  678) 	.probe		= smu_platform_probe,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  679) };
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  680) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  681) static int __init smu_init_sysfs(void)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  682) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  683) 	/*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  684) 	 * For now, we don't power manage machines with an SMU chip,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  685) 	 * I'm a bit too far from figuring out how that works with those
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  686) 	 * new chipsets, but that will come back and bite us
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  687) 	 */
000061245a679 (Grant Likely           2011-02-22 19:59:54 -0700  688) 	platform_driver_register(&smu_of_platform_driver);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  689) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  690) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  691) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  692) device_initcall(smu_init_sysfs);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  693) 
2dc1158137682 (Grant Likely           2010-08-06 09:25:50 -0600  694) struct platform_device *smu_get_ofdev(void)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  695) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  696) 	if (!smu)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  697) 		return NULL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  698) 	return smu->of_dev;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  699) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  700) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  701) EXPORT_SYMBOL_GPL(smu_get_ofdev);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  702) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  703) /*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  704)  * i2c interface
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  705)  */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  706) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  707) static void smu_i2c_complete_command(struct smu_i2c_cmd *cmd, int fail)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  708) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  709) 	void (*done)(struct smu_i2c_cmd *cmd, void *misc) = cmd->done;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  710) 	void *misc = cmd->misc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  711) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  712) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  713) 	/* Check for read case */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  714) 	if (!fail && cmd->read) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  715) 		if (cmd->pdata[0] < 1)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  716) 			fail = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  717) 		else
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  718) 			memcpy(cmd->info.data, &cmd->pdata[1],
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  719) 			       cmd->info.datalen);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  720) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  721) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  722) 	DPRINTK("SMU: completing, success: %d\n", !fail);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  723) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  724) 	/* Update status and mark no pending i2c command with lock
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  725) 	 * held so nobody comes in while we dequeue an eventual
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  726) 	 * pending next i2c command
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  727) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  728) 	spin_lock_irqsave(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  729) 	smu->cmd_i2c_cur = NULL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  730) 	wmb();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  731) 	cmd->status = fail ? -EIO : 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  732) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  733) 	/* Is there another i2c command waiting ? */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  734) 	if (!list_empty(&smu->cmd_i2c_list)) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  735) 		struct smu_i2c_cmd *newcmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  736) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  737) 		/* Fetch it, new current, remove from list */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  738) 		newcmd = list_entry(smu->cmd_i2c_list.next,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  739) 				    struct smu_i2c_cmd, link);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  740) 		smu->cmd_i2c_cur = newcmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  741) 		list_del(&cmd->link);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  742) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  743) 		/* Queue with low level smu */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  744) 		list_add_tail(&cmd->scmd.link, &smu->cmd_list);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  745) 		if (smu->cmd_cur == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  746) 			smu_start_cmd();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  747) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  748) 	spin_unlock_irqrestore(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  749) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  750) 	/* Call command completion handler if any */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  751) 	if (done)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  752) 		done(cmd, misc);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  753) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  754) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  755) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  756) 
0788f28575801 (Kees Cook              2017-10-21 13:43:53 -0700  757) static void smu_i2c_retry(struct timer_list *unused)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  758) {
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  759) 	struct smu_i2c_cmd	*cmd = smu->cmd_i2c_cur;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  760) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  761) 	DPRINTK("SMU: i2c failure, requeuing...\n");
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  762) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  763) 	/* requeue command simply by resetting reply_len */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  764) 	cmd->pdata[0] = 0xff;
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  765) 	cmd->scmd.reply_len = sizeof(cmd->pdata);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  766) 	smu_queue_cmd(&cmd->scmd);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  767) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  768) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  769) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  770) static void smu_i2c_low_completion(struct smu_cmd *scmd, void *misc)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  771) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  772) 	struct smu_i2c_cmd	*cmd = misc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  773) 	int			fail = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  774) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  775) 	DPRINTK("SMU: i2c compl. stage=%d status=%x pdata[0]=%x rlen: %x\n",
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  776) 		cmd->stage, scmd->status, cmd->pdata[0], scmd->reply_len);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  777) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  778) 	/* Check for possible status */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  779) 	if (scmd->status < 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  780) 		fail = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  781) 	else if (cmd->read) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  782) 		if (cmd->stage == 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  783) 			fail = cmd->pdata[0] != 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  784) 		else
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  785) 			fail = cmd->pdata[0] >= 0x80;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  786) 	} else {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  787) 		fail = cmd->pdata[0] != 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  788) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  789) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  790) 	/* Handle failures by requeuing command, after 5ms interval
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  791) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  792) 	if (fail && --cmd->retries > 0) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  793) 		DPRINTK("SMU: i2c failure, starting timer...\n");
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  794) 		BUG_ON(cmd != smu->cmd_i2c_cur);
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  795) 		if (!smu_irq_inited) {
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  796) 			mdelay(5);
0788f28575801 (Kees Cook              2017-10-21 13:43:53 -0700  797) 			smu_i2c_retry(NULL);
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  798) 			return;
f620753b95845 (Benjamin Herrenschmidt 2006-07-10 04:44:44 -0700  799) 		}
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  800) 		mod_timer(&smu->i2c_timer, jiffies + msecs_to_jiffies(5));
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  801) 		return;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  802) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  803) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  804) 	/* If failure or stage 1, command is complete */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  805) 	if (fail || cmd->stage != 0) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  806) 		smu_i2c_complete_command(cmd, fail);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  807) 		return;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  808) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  809) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  810) 	DPRINTK("SMU: going to stage 1\n");
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  811) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  812) 	/* Ok, initial command complete, now poll status */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  813) 	scmd->reply_buf = cmd->pdata;
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  814) 	scmd->reply_len = sizeof(cmd->pdata);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  815) 	scmd->data_buf = cmd->pdata;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  816) 	scmd->data_len = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  817) 	cmd->pdata[0] = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  818) 	cmd->stage = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  819) 	cmd->retries = 20;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  820) 	smu_queue_cmd(scmd);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  821) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  822) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  823) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  824) int smu_queue_i2c(struct smu_i2c_cmd *cmd)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  825) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  826) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  827) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  828) 	if (smu == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  829) 		return -ENODEV;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  830) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  831) 	/* Fill most fields of scmd */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  832) 	cmd->scmd.cmd = SMU_CMD_I2C_COMMAND;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  833) 	cmd->scmd.done = smu_i2c_low_completion;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  834) 	cmd->scmd.misc = cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  835) 	cmd->scmd.reply_buf = cmd->pdata;
730745a5c4509 (Benjamin Herrenschmidt 2006-01-07 11:30:44 +1100  836) 	cmd->scmd.reply_len = sizeof(cmd->pdata);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  837) 	cmd->scmd.data_buf = (u8 *)(char *)&cmd->info;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  838) 	cmd->scmd.status = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  839) 	cmd->stage = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  840) 	cmd->pdata[0] = 0xff;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  841) 	cmd->retries = 20;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  842) 	cmd->status = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  843) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  844) 	/* Check transfer type, sanitize some "info" fields
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  845) 	 * based on transfer type and do more checking
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  846) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  847) 	cmd->info.caddr = cmd->info.devaddr;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  848) 	cmd->read = cmd->info.devaddr & 0x01;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  849) 	switch(cmd->info.type) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  850) 	case SMU_I2C_TRANSFER_SIMPLE:
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  851) 		memset(&cmd->info.sublen, 0, 4);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  852) 		break;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  853) 	case SMU_I2C_TRANSFER_COMBINED:
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  854) 		cmd->info.devaddr &= 0xfe;
df561f6688fef (Gustavo A. R. Silva    2020-08-23 17:36:59 -0500  855) 		fallthrough;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  856) 	case SMU_I2C_TRANSFER_STDSUB:
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  857) 		if (cmd->info.sublen > 3)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  858) 			return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  859) 		break;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  860) 	default:
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  861) 		return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  862) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  863) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  864) 	/* Finish setting up command based on transfer direction
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  865) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  866) 	if (cmd->read) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  867) 		if (cmd->info.datalen > SMU_I2C_READ_MAX)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  868) 			return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  869) 		memset(cmd->info.data, 0xff, cmd->info.datalen);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  870) 		cmd->scmd.data_len = 9;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  871) 	} else {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  872) 		if (cmd->info.datalen > SMU_I2C_WRITE_MAX)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  873) 			return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  874) 		cmd->scmd.data_len = 9 + cmd->info.datalen;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  875) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  876) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  877) 	DPRINTK("SMU: i2c enqueuing command\n");
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  878) 	DPRINTK("SMU:   %s, len=%d bus=%x addr=%x sub0=%x type=%x\n",
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  879) 		cmd->read ? "read" : "write", cmd->info.datalen,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  880) 		cmd->info.bus, cmd->info.caddr,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  881) 		cmd->info.subaddr[0], cmd->info.type);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  882) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  883) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  884) 	/* Enqueue command in i2c list, and if empty, enqueue also in
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  885) 	 * main command list
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  886) 	 */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  887) 	spin_lock_irqsave(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  888) 	if (smu->cmd_i2c_cur == NULL) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  889) 		smu->cmd_i2c_cur = cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  890) 		list_add_tail(&cmd->scmd.link, &smu->cmd_list);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  891) 		if (smu->cmd_cur == NULL)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  892) 			smu_start_cmd();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  893) 	} else
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  894) 		list_add_tail(&cmd->link, &smu->cmd_i2c_list);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  895) 	spin_unlock_irqrestore(&smu->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  896) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  897) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  898) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700  899) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  900) /*
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  901)  * Handling of "partitions"
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  902)  */
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  903) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  904) static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  905) {
6e9a4738c9fad (Peter Zijlstra         2006-09-30 23:28:10 -0700  906) 	DECLARE_COMPLETION_ONSTACK(comp);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  907) 	unsigned int chunk;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  908) 	struct smu_cmd cmd;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  909) 	int rc;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  910) 	u8 params[8];
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  911) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  912) 	/* We currently use a chunk size of 0xe. We could check the
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  913) 	 * SMU firmware version and use bigger sizes though
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  914) 	 */
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  915) 	chunk = 0xe;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  916) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  917) 	while (len) {
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  918) 		unsigned int clen = min(len, chunk);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  919) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  920) 		cmd.cmd = SMU_CMD_MISC_ee_COMMAND;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  921) 		cmd.data_len = 7;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  922) 		cmd.data_buf = params;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  923) 		cmd.reply_len = chunk;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  924) 		cmd.reply_buf = dest;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  925) 		cmd.done = smu_done_complete;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  926) 		cmd.misc = &comp;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  927) 		params[0] = SMU_CMD_MISC_ee_GET_DATABLOCK_REC;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  928) 		params[1] = 0x4;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  929) 		*((u32 *)&params[2]) = addr;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  930) 		params[6] = clen;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  931) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  932) 		rc = smu_queue_cmd(&cmd);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  933) 		if (rc)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  934) 			return rc;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  935) 		wait_for_completion(&comp);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  936) 		if (cmd.status != 0)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  937) 			return rc;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  938) 		if (cmd.reply_len != clen) {
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  939) 			printk(KERN_DEBUG "SMU: short read in "
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  940) 			       "smu_read_datablock, got: %d, want: %d\n",
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  941) 			       cmd.reply_len, clen);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  942) 			return -EIO;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  943) 		}
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  944) 		len -= clen;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  945) 		addr += clen;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  946) 		dest += clen;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  947) 	}
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  948) 	return 0;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  949) }
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  950) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  951) static struct smu_sdbp_header *smu_create_sdb_partition(int id)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  952) {
6e9a4738c9fad (Peter Zijlstra         2006-09-30 23:28:10 -0700  953) 	DECLARE_COMPLETION_ONSTACK(comp);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  954) 	struct smu_simple_cmd cmd;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  955) 	unsigned int addr, len, tlen;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  956) 	struct smu_sdbp_header *hdr;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  957) 	struct property *prop;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  958) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  959) 	/* First query the partition info */
1beb6a7d6cbed (Benjamin Herrenschmidt 2005-12-14 13:10:10 +1100  960) 	DPRINTK("SMU: Query partition infos ... (irq=%d)\n", smu->db_irq);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  961) 	smu_queue_simple(&cmd, SMU_CMD_PARTITION_COMMAND, 2,
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  962) 			 smu_done_complete, &comp,
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  963) 			 SMU_CMD_PARTITION_LATEST, id);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  964) 	wait_for_completion(&comp);
1beb6a7d6cbed (Benjamin Herrenschmidt 2005-12-14 13:10:10 +1100  965) 	DPRINTK("SMU: done, status: %d, reply_len: %d\n",
1beb6a7d6cbed (Benjamin Herrenschmidt 2005-12-14 13:10:10 +1100  966) 		cmd.cmd.status, cmd.cmd.reply_len);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  967) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  968) 	/* Partition doesn't exist (or other error) */
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  969) 	if (cmd.cmd.status != 0 || cmd.cmd.reply_len != 6)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  970) 		return NULL;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  971) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  972) 	/* Fetch address and length from reply */
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  973) 	addr = *((u16 *)cmd.buffer);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  974) 	len = cmd.buffer[3] << 2;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  975) 	/* Calucluate total length to allocate, including the 17 bytes
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  976) 	 * for "sdb-partition-XX" that we append at the end of the buffer
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  977) 	 */
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  978) 	tlen = sizeof(struct property) + len + 18;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  979) 
cd86128088554 (Robert P. J. Day       2006-12-13 00:34:52 -0800  980) 	prop = kzalloc(tlen, GFP_KERNEL);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  981) 	if (prop == NULL)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  982) 		return NULL;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  983) 	hdr = (struct smu_sdbp_header *)(prop + 1);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  984) 	prop->name = ((char *)prop) + tlen - 18;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  985) 	sprintf(prop->name, "sdb-partition-%02x", id);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  986) 	prop->length = len;
1a38147ed0737 (Stephen Rothwell       2007-04-03 10:58:52 +1000  987) 	prop->value = hdr;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  988) 	prop->next = NULL;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  989) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  990) 	/* Read the datablock */
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  991) 	if (smu_read_datablock((u8 *)hdr, addr, len)) {
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  992) 		printk(KERN_DEBUG "SMU: datablock read failed while reading "
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  993) 		       "partition %02x !\n", id);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  994) 		goto failure;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  995) 	}
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  996) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  997) 	/* Got it, check a few things and create the property */
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  998) 	if (hdr->id != id) {
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100  999) 		printk(KERN_DEBUG "SMU: Reading partition %02x and got "
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1000) 		       "%02x !\n", id, hdr->id);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1001) 		goto failure;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1002) 	}
79d1c712958f9 (Nathan Fontenot        2012-10-02 16:58:46 +0000 1003) 	if (of_add_property(smu->of_node, prop)) {
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1004) 		printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x "
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1005) 		       "property !\n", id);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1006) 		goto failure;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1007) 	}
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1008) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1009) 	return hdr;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1010)  failure:
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1011) 	kfree(prop);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1012) 	return NULL;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1013) }
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1014) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1015) /* Note: Only allowed to return error code in pointers (using ERR_PTR)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1016)  * when interruptible is 1
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1017)  */
3db8715ec9dc1 (Wang Wensheng          2020-09-14 12:26:15 +0000 1018) static const struct smu_sdbp_header *__smu_get_sdb_partition(int id,
018a3d1db7cdb (Jeremy Kerr            2006-07-12 15:40:29 +1000 1019) 		unsigned int *size, int interruptible)
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1020) {
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1021) 	char pname[32];
018a3d1db7cdb (Jeremy Kerr            2006-07-12 15:40:29 +1000 1022) 	const struct smu_sdbp_header *part;
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1023) 
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1024) 	if (!smu)
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1025) 		return NULL;
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1026) 
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1027) 	sprintf(pname, "sdb-partition-%02x", id);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1028) 
1beb6a7d6cbed (Benjamin Herrenschmidt 2005-12-14 13:10:10 +1100 1029) 	DPRINTK("smu_get_sdb_partition(%02x)\n", id);
1beb6a7d6cbed (Benjamin Herrenschmidt 2005-12-14 13:10:10 +1100 1030) 
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1031) 	if (interruptible) {
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1032) 		int rc;
14cc3e2b633bb (Ingo Molnar            2006-03-26 01:37:14 -0800 1033) 		rc = mutex_lock_interruptible(&smu_part_access);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1034) 		if (rc)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1035) 			return ERR_PTR(rc);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1036) 	} else
14cc3e2b633bb (Ingo Molnar            2006-03-26 01:37:14 -0800 1037) 		mutex_lock(&smu_part_access);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1038) 
01b2726dd11ef (Stephen Rothwell       2007-04-27 13:41:15 +1000 1039) 	part = of_get_property(smu->of_node, pname, size);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1040) 	if (part == NULL) {
1beb6a7d6cbed (Benjamin Herrenschmidt 2005-12-14 13:10:10 +1100 1041) 		DPRINTK("trying to extract from SMU ...\n");
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1042) 		part = smu_create_sdb_partition(id);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1043) 		if (part != NULL && size)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1044) 			*size = part->len << 2;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1045) 	}
14cc3e2b633bb (Ingo Molnar            2006-03-26 01:37:14 -0800 1046) 	mutex_unlock(&smu_part_access);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1047) 	return part;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1048) }
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1049) 
018a3d1db7cdb (Jeremy Kerr            2006-07-12 15:40:29 +1000 1050) const struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1051) {
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1052) 	return __smu_get_sdb_partition(id, size, 0);
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1053) }
4350147a816b9 (Benjamin Herrenschmidt 2005-11-07 14:27:33 +1100 1054) EXPORT_SYMBOL(smu_get_sdb_partition);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1055) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1056) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1057) /*
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1058)  * Userland driver interface
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1059)  */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1060) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1061) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1062) static LIST_HEAD(smu_clist);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1063) static DEFINE_SPINLOCK(smu_clist_lock);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1064) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1065) enum smu_file_mode {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1066) 	smu_file_commands,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1067) 	smu_file_events,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1068) 	smu_file_closing
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1069) };
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1070) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1071) struct smu_private
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1072) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1073) 	struct list_head	list;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1074) 	enum smu_file_mode	mode;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1075) 	int			busy;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1076) 	struct smu_cmd		cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1077) 	spinlock_t		lock;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1078) 	wait_queue_head_t	wait;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1079) 	u8			buffer[SMU_MAX_DATA];
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1080) };
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1081) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1082) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1083) static int smu_open(struct inode *inode, struct file *file)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1084) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1085) 	struct smu_private *pp;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1086) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1087) 
dd00cc486ab1c (Yoann Padioleau        2007-07-19 01:49:03 -0700 1088) 	pp = kzalloc(sizeof(struct smu_private), GFP_KERNEL);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1089) 	if (pp == 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1090) 		return -ENOMEM;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1091) 	spin_lock_init(&pp->lock);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1092) 	pp->mode = smu_file_commands;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1093) 	init_waitqueue_head(&pp->wait);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1094) 
d851b6e04ee97 (Arnd Bergmann          2010-06-02 14:28:52 +0200 1095) 	mutex_lock(&smu_mutex);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1096) 	spin_lock_irqsave(&smu_clist_lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1097) 	list_add(&pp->list, &smu_clist);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1098) 	spin_unlock_irqrestore(&smu_clist_lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1099) 	file->private_data = pp;
d851b6e04ee97 (Arnd Bergmann          2010-06-02 14:28:52 +0200 1100) 	mutex_unlock(&smu_mutex);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1101) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1102) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1103) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1104) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1105) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1106) static void smu_user_cmd_done(struct smu_cmd *cmd, void *misc)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1107) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1108) 	struct smu_private *pp = misc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1109) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1110) 	wake_up_all(&pp->wait);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1111) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1112) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1113) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1114) static ssize_t smu_write(struct file *file, const char __user *buf,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1115) 			 size_t count, loff_t *ppos)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1116) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1117) 	struct smu_private *pp = file->private_data;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1118) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1119) 	struct smu_user_cmd_hdr hdr;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1120) 	int rc = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1121) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1122) 	if (pp->busy)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1123) 		return -EBUSY;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1124) 	else if (copy_from_user(&hdr, buf, sizeof(hdr)))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1125) 		return -EFAULT;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1126) 	else if (hdr.cmdtype == SMU_CMDTYPE_WANTS_EVENTS) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1127) 		pp->mode = smu_file_events;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1128) 		return 0;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1129) 	} else if (hdr.cmdtype == SMU_CMDTYPE_GET_PARTITION) {
018a3d1db7cdb (Jeremy Kerr            2006-07-12 15:40:29 +1000 1130) 		const struct smu_sdbp_header *part;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1131) 		part = __smu_get_sdb_partition(hdr.cmd, NULL, 1);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1132) 		if (part == NULL)
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1133) 			return -EINVAL;
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1134) 		else if (IS_ERR(part))
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1135) 			return PTR_ERR(part);
183d020258dfd (Benjamin Herrenschmidt 2005-11-07 14:29:02 +1100 1136) 		return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1137) 	} else if (hdr.cmdtype != SMU_CMDTYPE_SMU)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1138) 		return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1139) 	else if (pp->mode != smu_file_commands)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1140) 		return -EBADFD;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1141) 	else if (hdr.data_len > SMU_MAX_DATA)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1142) 		return -EINVAL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1143) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1144) 	spin_lock_irqsave(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1145) 	if (pp->busy) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1146) 		spin_unlock_irqrestore(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1147) 		return -EBUSY;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1148) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1149) 	pp->busy = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1150) 	pp->cmd.status = 1;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1151) 	spin_unlock_irqrestore(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1152) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1153) 	if (copy_from_user(pp->buffer, buf + sizeof(hdr), hdr.data_len)) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1154) 		pp->busy = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1155) 		return -EFAULT;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1156) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1157) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1158) 	pp->cmd.cmd = hdr.cmd;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1159) 	pp->cmd.data_len = hdr.data_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1160) 	pp->cmd.reply_len = SMU_MAX_DATA;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1161) 	pp->cmd.data_buf = pp->buffer;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1162) 	pp->cmd.reply_buf = pp->buffer;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1163) 	pp->cmd.done = smu_user_cmd_done;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1164) 	pp->cmd.misc = pp;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1165) 	rc = smu_queue_cmd(&pp->cmd);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1166) 	if (rc < 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1167) 		return rc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1168) 	return count;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1169) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1170) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1171) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1172) static ssize_t smu_read_command(struct file *file, struct smu_private *pp,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1173) 				char __user *buf, size_t count)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1174) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1175) 	DECLARE_WAITQUEUE(wait, current);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1176) 	struct smu_user_reply_hdr hdr;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1177) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1178) 	int size, rc = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1179) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1180) 	if (!pp->busy)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1181) 		return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1182) 	if (count < sizeof(struct smu_user_reply_hdr))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1183) 		return -EOVERFLOW;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1184) 	spin_lock_irqsave(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1185) 	if (pp->cmd.status == 1) {
86e4754ac8fde (Julia Lawall           2010-03-29 05:34:46 +0000 1186) 		if (file->f_flags & O_NONBLOCK) {
86e4754ac8fde (Julia Lawall           2010-03-29 05:34:46 +0000 1187) 			spin_unlock_irqrestore(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1188) 			return -EAGAIN;
86e4754ac8fde (Julia Lawall           2010-03-29 05:34:46 +0000 1189) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1190) 		add_wait_queue(&pp->wait, &wait);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1191) 		for (;;) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1192) 			set_current_state(TASK_INTERRUPTIBLE);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1193) 			rc = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1194) 			if (pp->cmd.status != 1)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1195) 				break;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1196) 			rc = -ERESTARTSYS;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1197) 			if (signal_pending(current))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1198) 				break;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1199) 			spin_unlock_irqrestore(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1200) 			schedule();
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1201) 			spin_lock_irqsave(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1202) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1203) 		set_current_state(TASK_RUNNING);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1204) 		remove_wait_queue(&pp->wait, &wait);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1205) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1206) 	spin_unlock_irqrestore(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1207) 	if (rc)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1208) 		return rc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1209) 	if (pp->cmd.status != 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1210) 		pp->cmd.reply_len = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1211) 	size = sizeof(hdr) + pp->cmd.reply_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1212) 	if (count < size)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1213) 		size = count;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1214) 	rc = size;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1215) 	hdr.status = pp->cmd.status;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1216) 	hdr.reply_len = pp->cmd.reply_len;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1217) 	if (copy_to_user(buf, &hdr, sizeof(hdr)))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1218) 		return -EFAULT;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1219) 	size -= sizeof(hdr);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1220) 	if (size && copy_to_user(buf + sizeof(hdr), pp->buffer, size))
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1221) 		return -EFAULT;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1222) 	pp->busy = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1223) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1224) 	return rc;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1225) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1226) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1227) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1228) static ssize_t smu_read_events(struct file *file, struct smu_private *pp,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1229) 			       char __user *buf, size_t count)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1230) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1231) 	/* Not implemented */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1232) 	msleep_interruptible(1000);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1233) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1234) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1235) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1236) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1237) static ssize_t smu_read(struct file *file, char __user *buf,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1238) 			size_t count, loff_t *ppos)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1239) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1240) 	struct smu_private *pp = file->private_data;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1241) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1242) 	if (pp->mode == smu_file_commands)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1243) 		return smu_read_command(file, pp, buf, count);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1244) 	if (pp->mode == smu_file_events)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1245) 		return smu_read_events(file, pp, buf, count);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1246) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1247) 	return -EBADFD;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1248) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1249) 
afc9a42b7464f (Al Viro                2017-07-03 06:39:46 -0400 1250) static __poll_t smu_fpoll(struct file *file, poll_table *wait)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1251) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1252) 	struct smu_private *pp = file->private_data;
afc9a42b7464f (Al Viro                2017-07-03 06:39:46 -0400 1253) 	__poll_t mask = 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1254) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1255) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1256) 	if (pp == 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1257) 		return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1258) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1259) 	if (pp->mode == smu_file_commands) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1260) 		poll_wait(file, &pp->wait, wait);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1261) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1262) 		spin_lock_irqsave(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1263) 		if (pp->busy && pp->cmd.status != 1)
a9a08845e9acb (Linus Torvalds         2018-02-11 14:34:03 -0800 1264) 			mask |= EPOLLIN;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1265) 		spin_unlock_irqrestore(&pp->lock, flags);
2055fb41ea6bf (Rasmus Villemoes       2014-06-20 21:44:27 +0200 1266) 	}
2055fb41ea6bf (Rasmus Villemoes       2014-06-20 21:44:27 +0200 1267) 	if (pp->mode == smu_file_events) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1268) 		/* Not yet implemented */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1269) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1270) 	return mask;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1271) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1272) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1273) static int smu_release(struct inode *inode, struct file *file)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1274) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1275) 	struct smu_private *pp = file->private_data;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1276) 	unsigned long flags;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1277) 	unsigned int busy;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1278) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1279) 	if (pp == 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1280) 		return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1281) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1282) 	file->private_data = NULL;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1283) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1284) 	/* Mark file as closing to avoid races with new request */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1285) 	spin_lock_irqsave(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1286) 	pp->mode = smu_file_closing;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1287) 	busy = pp->busy;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1288) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1289) 	/* Wait for any pending request to complete */
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1290) 	if (busy && pp->cmd.status == 1) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1291) 		DECLARE_WAITQUEUE(wait, current);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1292) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1293) 		add_wait_queue(&pp->wait, &wait);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1294) 		for (;;) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1295) 			set_current_state(TASK_UNINTERRUPTIBLE);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1296) 			if (pp->cmd.status != 1)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1297) 				break;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1298) 			spin_unlock_irqrestore(&pp->lock, flags);
94256dd680f83 (Andrew Morton          2007-04-16 22:53:25 -0700 1299) 			schedule();
94256dd680f83 (Andrew Morton          2007-04-16 22:53:25 -0700 1300) 			spin_lock_irqsave(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1301) 		}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1302) 		set_current_state(TASK_RUNNING);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1303) 		remove_wait_queue(&pp->wait, &wait);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1304) 	}
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1305) 	spin_unlock_irqrestore(&pp->lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1306) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1307) 	spin_lock_irqsave(&smu_clist_lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1308) 	list_del(&pp->list);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1309) 	spin_unlock_irqrestore(&smu_clist_lock, flags);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1310) 	kfree(pp);
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1311) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1312) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1313) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1314) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1315) 
fa027c2a0a0d6 (Arjan van de Ven       2007-02-12 00:55:33 -0800 1316) static const struct file_operations smu_device_fops = {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1317) 	.llseek		= no_llseek,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1318) 	.read		= smu_read,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1319) 	.write		= smu_write,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1320) 	.poll		= smu_fpoll,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1321) 	.open		= smu_open,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1322) 	.release	= smu_release,
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1323) };
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1324) 
6b67f62cf655c (Stephen Rothwell       2005-09-27 14:09:39 +1000 1325) static struct miscdevice pmu_device = {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1326) 	MISC_DYNAMIC_MINOR, "smu", &smu_device_fops
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1327) };
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1328) 
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1329) static int smu_device_init(void)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1330) {
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1331) 	if (!smu)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1332) 		return -ENODEV;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1333) 	if (misc_register(&pmu_device) < 0)
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1334) 		printk(KERN_ERR "via-pmu: cannot register misc device.\n");
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1335) 	return 0;
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1336) }
0365ba7fb1fa9 (Benjamin Herrenschmidt 2005-09-22 21:44:06 -0700 1337) device_initcall(smu_device_init);