xref: /openbmc/linux/drivers/platform/olpc/olpc-ec.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1ff206db4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2392a325cSAndres Salomon /*
3392a325cSAndres Salomon  * Generic driver for the OLPC Embedded Controller.
4392a325cSAndres Salomon  *
5f48d1496SPaul Gortmaker  * Author: Andres Salomon <dilinger@queued.net>
6f48d1496SPaul Gortmaker  *
7392a325cSAndres Salomon  * Copyright (C) 2011-2012 One Laptop per Child Foundation.
8392a325cSAndres Salomon  */
93d26c20bSAndres Salomon #include <linux/completion.h>
106cca83d4SAndres Salomon #include <linux/debugfs.h>
113d26c20bSAndres Salomon #include <linux/spinlock.h>
123d26c20bSAndres Salomon #include <linux/mutex.h>
13ac250415SAndres Salomon #include <linux/platform_device.h>
14d278b7a2SAndres Salomon #include <linux/slab.h>
153d26c20bSAndres Salomon #include <linux/workqueue.h>
16f48d1496SPaul Gortmaker #include <linux/init.h>
173d26c20bSAndres Salomon #include <linux/list.h>
18231c0c21SLubomir Rintel #include <linux/regulator/driver.h>
193d26c20bSAndres Salomon #include <linux/olpc-ec.h>
20392a325cSAndres Salomon 
213d26c20bSAndres Salomon struct ec_cmd_desc {
223d26c20bSAndres Salomon 	u8 cmd;
233d26c20bSAndres Salomon 	u8 *inbuf, *outbuf;
243d26c20bSAndres Salomon 	size_t inlen, outlen;
253d26c20bSAndres Salomon 
263d26c20bSAndres Salomon 	int err;
273d26c20bSAndres Salomon 	struct completion finished;
283d26c20bSAndres Salomon 	struct list_head node;
293d26c20bSAndres Salomon 
303d26c20bSAndres Salomon 	void *priv;
313d26c20bSAndres Salomon };
323d26c20bSAndres Salomon 
33d278b7a2SAndres Salomon struct olpc_ec_priv {
34d278b7a2SAndres Salomon 	struct olpc_ec_driver *drv;
35ec9964b4SLubomir Rintel 	u8 version;
3699ecb01cSAndres Salomon 	struct work_struct worker;
3799ecb01cSAndres Salomon 	struct mutex cmd_lock;
3899ecb01cSAndres Salomon 
39231c0c21SLubomir Rintel 	/* DCON regulator */
40231c0c21SLubomir Rintel 	bool dcon_enabled;
41231c0c21SLubomir Rintel 
4299ecb01cSAndres Salomon 	/* Pending EC commands */
4399ecb01cSAndres Salomon 	struct list_head cmd_q;
4499ecb01cSAndres Salomon 	spinlock_t cmd_q_lock;
45d278b7a2SAndres Salomon 
466cca83d4SAndres Salomon 	struct dentry *dbgfs_dir;
476cca83d4SAndres Salomon 
48d278b7a2SAndres Salomon 	/*
49ec9964b4SLubomir Rintel 	 * EC event mask to be applied during suspend (defining wakeup
50ec9964b4SLubomir Rintel 	 * sources).
51ec9964b4SLubomir Rintel 	 */
52ec9964b4SLubomir Rintel 	u16 ec_wakeup_mask;
53ec9964b4SLubomir Rintel 
54ec9964b4SLubomir Rintel 	/*
55d278b7a2SAndres Salomon 	 * Running an EC command while suspending means we don't always finish
56d278b7a2SAndres Salomon 	 * the command before the machine suspends.  This means that the EC
57d278b7a2SAndres Salomon 	 * is expecting the command protocol to finish, but we after a period
58d278b7a2SAndres Salomon 	 * of time (while the OS is asleep) the EC times out and restarts its
59d278b7a2SAndres Salomon 	 * idle loop.  Meanwhile, the OS wakes up, thinks it's still in the
60d278b7a2SAndres Salomon 	 * middle of the command protocol, starts throwing random things at
61d278b7a2SAndres Salomon 	 * the EC... and everyone's uphappy.
62d278b7a2SAndres Salomon 	 */
63d278b7a2SAndres Salomon 	bool suspended;
64d278b7a2SAndres Salomon };
65d278b7a2SAndres Salomon 
663d26c20bSAndres Salomon static struct olpc_ec_driver *ec_driver;
67d278b7a2SAndres Salomon static struct olpc_ec_priv *ec_priv;
683d26c20bSAndres Salomon static void *ec_cb_arg;
693d26c20bSAndres Salomon 
olpc_ec_driver_register(struct olpc_ec_driver * drv,void * arg)703d26c20bSAndres Salomon void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg)
713d26c20bSAndres Salomon {
723d26c20bSAndres Salomon 	ec_driver = drv;
733d26c20bSAndres Salomon 	ec_cb_arg = arg;
743d26c20bSAndres Salomon }
753d26c20bSAndres Salomon EXPORT_SYMBOL_GPL(olpc_ec_driver_register);
763d26c20bSAndres Salomon 
olpc_ec_worker(struct work_struct * w)773d26c20bSAndres Salomon static void olpc_ec_worker(struct work_struct *w)
783d26c20bSAndres Salomon {
7999ecb01cSAndres Salomon 	struct olpc_ec_priv *ec = container_of(w, struct olpc_ec_priv, worker);
803d26c20bSAndres Salomon 	struct ec_cmd_desc *desc = NULL;
813d26c20bSAndres Salomon 	unsigned long flags;
823d26c20bSAndres Salomon 
833d26c20bSAndres Salomon 	/* Grab the first pending command from the queue */
8499ecb01cSAndres Salomon 	spin_lock_irqsave(&ec->cmd_q_lock, flags);
8599ecb01cSAndres Salomon 	if (!list_empty(&ec->cmd_q)) {
8699ecb01cSAndres Salomon 		desc = list_first_entry(&ec->cmd_q, struct ec_cmd_desc, node);
873d26c20bSAndres Salomon 		list_del(&desc->node);
883d26c20bSAndres Salomon 	}
8999ecb01cSAndres Salomon 	spin_unlock_irqrestore(&ec->cmd_q_lock, flags);
903d26c20bSAndres Salomon 
913d26c20bSAndres Salomon 	/* Do we actually have anything to do? */
923d26c20bSAndres Salomon 	if (!desc)
933d26c20bSAndres Salomon 		return;
943d26c20bSAndres Salomon 
953d26c20bSAndres Salomon 	/* Protect the EC hw with a mutex; only run one cmd at a time */
9699ecb01cSAndres Salomon 	mutex_lock(&ec->cmd_lock);
973d26c20bSAndres Salomon 	desc->err = ec_driver->ec_cmd(desc->cmd, desc->inbuf, desc->inlen,
983d26c20bSAndres Salomon 			desc->outbuf, desc->outlen, ec_cb_arg);
9999ecb01cSAndres Salomon 	mutex_unlock(&ec->cmd_lock);
1003d26c20bSAndres Salomon 
1013d26c20bSAndres Salomon 	/* Finished, wake up olpc_ec_cmd() */
1023d26c20bSAndres Salomon 	complete(&desc->finished);
1033d26c20bSAndres Salomon 
1043d26c20bSAndres Salomon 	/* Run the worker thread again in case there are more cmds pending */
10599ecb01cSAndres Salomon 	schedule_work(&ec->worker);
1063d26c20bSAndres Salomon }
1073d26c20bSAndres Salomon 
1083d26c20bSAndres Salomon /*
1093d26c20bSAndres Salomon  * Throw a cmd descripter onto the list.  We now have SMP OLPC machines, so
1103d26c20bSAndres Salomon  * locking is pretty critical.
1113d26c20bSAndres Salomon  */
queue_ec_descriptor(struct ec_cmd_desc * desc,struct olpc_ec_priv * ec)11299ecb01cSAndres Salomon static void queue_ec_descriptor(struct ec_cmd_desc *desc,
11399ecb01cSAndres Salomon 		struct olpc_ec_priv *ec)
1143d26c20bSAndres Salomon {
1153d26c20bSAndres Salomon 	unsigned long flags;
1163d26c20bSAndres Salomon 
1173d26c20bSAndres Salomon 	INIT_LIST_HEAD(&desc->node);
1183d26c20bSAndres Salomon 
11999ecb01cSAndres Salomon 	spin_lock_irqsave(&ec->cmd_q_lock, flags);
12099ecb01cSAndres Salomon 	list_add_tail(&desc->node, &ec->cmd_q);
12199ecb01cSAndres Salomon 	spin_unlock_irqrestore(&ec->cmd_q_lock, flags);
1223d26c20bSAndres Salomon 
12399ecb01cSAndres Salomon 	schedule_work(&ec->worker);
1243d26c20bSAndres Salomon }
1253d26c20bSAndres Salomon 
olpc_ec_cmd(u8 cmd,u8 * inbuf,size_t inlen,u8 * outbuf,size_t outlen)126392a325cSAndres Salomon int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
127392a325cSAndres Salomon {
128d278b7a2SAndres Salomon 	struct olpc_ec_priv *ec = ec_priv;
1293d26c20bSAndres Salomon 	struct ec_cmd_desc desc;
1303d26c20bSAndres Salomon 
131560331eaSLubomir Rintel 	/* Driver not yet registered. */
132560331eaSLubomir Rintel 	if (!ec_driver)
133560331eaSLubomir Rintel 		return -EPROBE_DEFER;
134560331eaSLubomir Rintel 
135560331eaSLubomir Rintel 	if (WARN_ON(!ec_driver->ec_cmd))
1363d26c20bSAndres Salomon 		return -ENODEV;
1373d26c20bSAndres Salomon 
138d278b7a2SAndres Salomon 	if (!ec)
139d278b7a2SAndres Salomon 		return -ENOMEM;
140d278b7a2SAndres Salomon 
141d278b7a2SAndres Salomon 	/* Suspending in the middle of a command hoses things really badly */
142d278b7a2SAndres Salomon 	if (WARN_ON(ec->suspended))
143d278b7a2SAndres Salomon 		return -EBUSY;
144d278b7a2SAndres Salomon 
1453d26c20bSAndres Salomon 	might_sleep();
1463d26c20bSAndres Salomon 
1473d26c20bSAndres Salomon 	desc.cmd = cmd;
1483d26c20bSAndres Salomon 	desc.inbuf = inbuf;
1493d26c20bSAndres Salomon 	desc.outbuf = outbuf;
1503d26c20bSAndres Salomon 	desc.inlen = inlen;
1513d26c20bSAndres Salomon 	desc.outlen = outlen;
1523d26c20bSAndres Salomon 	desc.err = 0;
1533d26c20bSAndres Salomon 	init_completion(&desc.finished);
1543d26c20bSAndres Salomon 
15599ecb01cSAndres Salomon 	queue_ec_descriptor(&desc, ec);
1563d26c20bSAndres Salomon 
1573d26c20bSAndres Salomon 	/* Timeouts must be handled in the platform-specific EC hook */
1583d26c20bSAndres Salomon 	wait_for_completion(&desc.finished);
1593d26c20bSAndres Salomon 
1603d26c20bSAndres Salomon 	/* The worker thread dequeues the cmd; no need to do anything here */
1613d26c20bSAndres Salomon 	return desc.err;
162392a325cSAndres Salomon }
163392a325cSAndres Salomon EXPORT_SYMBOL_GPL(olpc_ec_cmd);
164ac250415SAndres Salomon 
olpc_ec_wakeup_set(u16 value)165ec9964b4SLubomir Rintel void olpc_ec_wakeup_set(u16 value)
166ec9964b4SLubomir Rintel {
167ec9964b4SLubomir Rintel 	struct olpc_ec_priv *ec = ec_priv;
168ec9964b4SLubomir Rintel 
169ec9964b4SLubomir Rintel 	if (WARN_ON(!ec))
170ec9964b4SLubomir Rintel 		return;
171ec9964b4SLubomir Rintel 
172ec9964b4SLubomir Rintel 	ec->ec_wakeup_mask |= value;
173ec9964b4SLubomir Rintel }
174ec9964b4SLubomir Rintel EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
175ec9964b4SLubomir Rintel 
olpc_ec_wakeup_clear(u16 value)176ec9964b4SLubomir Rintel void olpc_ec_wakeup_clear(u16 value)
177ec9964b4SLubomir Rintel {
178ec9964b4SLubomir Rintel 	struct olpc_ec_priv *ec = ec_priv;
179ec9964b4SLubomir Rintel 
180ec9964b4SLubomir Rintel 	if (WARN_ON(!ec))
181ec9964b4SLubomir Rintel 		return;
182ec9964b4SLubomir Rintel 
183ec9964b4SLubomir Rintel 	ec->ec_wakeup_mask &= ~value;
184ec9964b4SLubomir Rintel }
185ec9964b4SLubomir Rintel EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
186ec9964b4SLubomir Rintel 
olpc_ec_mask_write(u16 bits)187ec9964b4SLubomir Rintel int olpc_ec_mask_write(u16 bits)
188ec9964b4SLubomir Rintel {
189ec9964b4SLubomir Rintel 	struct olpc_ec_priv *ec = ec_priv;
190ec9964b4SLubomir Rintel 
191ec9964b4SLubomir Rintel 	if (WARN_ON(!ec))
192ec9964b4SLubomir Rintel 		return -ENODEV;
193ec9964b4SLubomir Rintel 
194ec9964b4SLubomir Rintel 	/* EC version 0x5f adds support for wide SCI mask */
195ec9964b4SLubomir Rintel 	if (ec->version >= 0x5f) {
196ec9964b4SLubomir Rintel 		__be16 ec_word = cpu_to_be16(bits);
197ec9964b4SLubomir Rintel 
198ec9964b4SLubomir Rintel 		return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *)&ec_word, 2, NULL, 0);
199ec9964b4SLubomir Rintel 	} else {
200ec9964b4SLubomir Rintel 		u8 ec_byte = bits & 0xff;
201ec9964b4SLubomir Rintel 
202ec9964b4SLubomir Rintel 		return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
203ec9964b4SLubomir Rintel 	}
204ec9964b4SLubomir Rintel }
205ec9964b4SLubomir Rintel EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
206ec9964b4SLubomir Rintel 
207ec9964b4SLubomir Rintel /*
208ec9964b4SLubomir Rintel  * Returns true if the compile and runtime configurations allow for EC events
209ec9964b4SLubomir Rintel  * to wake the system.
210ec9964b4SLubomir Rintel  */
olpc_ec_wakeup_available(void)211ec9964b4SLubomir Rintel bool olpc_ec_wakeup_available(void)
212ec9964b4SLubomir Rintel {
213ec9964b4SLubomir Rintel 	if (WARN_ON(!ec_driver))
214ec9964b4SLubomir Rintel 		return false;
215ec9964b4SLubomir Rintel 
216ec9964b4SLubomir Rintel 	return ec_driver->wakeup_available;
217ec9964b4SLubomir Rintel }
218ec9964b4SLubomir Rintel EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
219ec9964b4SLubomir Rintel 
olpc_ec_sci_query(u16 * sci_value)220ec9964b4SLubomir Rintel int olpc_ec_sci_query(u16 *sci_value)
221ec9964b4SLubomir Rintel {
222ec9964b4SLubomir Rintel 	struct olpc_ec_priv *ec = ec_priv;
223ec9964b4SLubomir Rintel 	int ret;
224ec9964b4SLubomir Rintel 
225ec9964b4SLubomir Rintel 	if (WARN_ON(!ec))
226ec9964b4SLubomir Rintel 		return -ENODEV;
227ec9964b4SLubomir Rintel 
228ec9964b4SLubomir Rintel 	/* EC version 0x5f adds support for wide SCI mask */
229ec9964b4SLubomir Rintel 	if (ec->version >= 0x5f) {
230ec9964b4SLubomir Rintel 		__be16 ec_word;
231ec9964b4SLubomir Rintel 
232ec9964b4SLubomir Rintel 		ret = olpc_ec_cmd(EC_EXT_SCI_QUERY, NULL, 0, (void *)&ec_word, 2);
233ec9964b4SLubomir Rintel 		if (ret == 0)
234ec9964b4SLubomir Rintel 			*sci_value = be16_to_cpu(ec_word);
235ec9964b4SLubomir Rintel 	} else {
236ec9964b4SLubomir Rintel 		u8 ec_byte;
237ec9964b4SLubomir Rintel 
238ec9964b4SLubomir Rintel 		ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
239ec9964b4SLubomir Rintel 		if (ret == 0)
240ec9964b4SLubomir Rintel 			*sci_value = ec_byte;
241ec9964b4SLubomir Rintel 	}
242ec9964b4SLubomir Rintel 
243ec9964b4SLubomir Rintel 	return ret;
244ec9964b4SLubomir Rintel }
245ec9964b4SLubomir Rintel EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
246ec9964b4SLubomir Rintel 
2476cca83d4SAndres Salomon #ifdef CONFIG_DEBUG_FS
2486cca83d4SAndres Salomon 
2496cca83d4SAndres Salomon /*
2506cca83d4SAndres Salomon  * debugfs support for "generic commands", to allow sending
2516cca83d4SAndres Salomon  * arbitrary EC commands from userspace.
2526cca83d4SAndres Salomon  */
2536cca83d4SAndres Salomon 
2546cca83d4SAndres Salomon #define EC_MAX_CMD_ARGS (5 + 1)		/* cmd byte + 5 args */
2556cca83d4SAndres Salomon #define EC_MAX_CMD_REPLY (8)
2566cca83d4SAndres Salomon 
2576cca83d4SAndres Salomon static DEFINE_MUTEX(ec_dbgfs_lock);
2586cca83d4SAndres Salomon static unsigned char ec_dbgfs_resp[EC_MAX_CMD_REPLY];
2596cca83d4SAndres Salomon static unsigned int ec_dbgfs_resp_bytes;
2606cca83d4SAndres Salomon 
ec_dbgfs_cmd_write(struct file * file,const char __user * buf,size_t size,loff_t * ppos)2616cca83d4SAndres Salomon static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf,
2626cca83d4SAndres Salomon 		size_t size, loff_t *ppos)
2636cca83d4SAndres Salomon {
2646cca83d4SAndres Salomon 	int i, m;
2656cca83d4SAndres Salomon 	unsigned char ec_cmd[EC_MAX_CMD_ARGS];
2666cca83d4SAndres Salomon 	unsigned int ec_cmd_int[EC_MAX_CMD_ARGS];
267*40ec787eSDan Carpenter 	char cmdbuf[64] = "";
2686cca83d4SAndres Salomon 	int ec_cmd_bytes;
2696cca83d4SAndres Salomon 
2706cca83d4SAndres Salomon 	mutex_lock(&ec_dbgfs_lock);
2716cca83d4SAndres Salomon 
2726cca83d4SAndres Salomon 	size = simple_write_to_buffer(cmdbuf, sizeof(cmdbuf), ppos, buf, size);
2736cca83d4SAndres Salomon 
2746cca83d4SAndres Salomon 	m = sscanf(cmdbuf, "%x:%u %x %x %x %x %x", &ec_cmd_int[0],
2756cca83d4SAndres Salomon 			&ec_dbgfs_resp_bytes, &ec_cmd_int[1], &ec_cmd_int[2],
2766cca83d4SAndres Salomon 			&ec_cmd_int[3], &ec_cmd_int[4], &ec_cmd_int[5]);
2776cca83d4SAndres Salomon 	if (m < 2 || ec_dbgfs_resp_bytes > EC_MAX_CMD_REPLY) {
2786cca83d4SAndres Salomon 		/* reset to prevent overflow on read */
2796cca83d4SAndres Salomon 		ec_dbgfs_resp_bytes = 0;
2806cca83d4SAndres Salomon 
2816cca83d4SAndres Salomon 		pr_debug("olpc-ec: bad ec cmd:  cmd:response-count [arg1 [arg2 ...]]\n");
2826cca83d4SAndres Salomon 		size = -EINVAL;
2836cca83d4SAndres Salomon 		goto out;
2846cca83d4SAndres Salomon 	}
2856cca83d4SAndres Salomon 
2866cca83d4SAndres Salomon 	/* convert scanf'd ints to char */
2876cca83d4SAndres Salomon 	ec_cmd_bytes = m - 2;
2886cca83d4SAndres Salomon 	for (i = 0; i <= ec_cmd_bytes; i++)
2896cca83d4SAndres Salomon 		ec_cmd[i] = ec_cmd_int[i];
2906cca83d4SAndres Salomon 
2917d3777d1SAndy Shevchenko 	pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %5ph, want %d returns\n",
2927d3777d1SAndy Shevchenko 			ec_cmd[0], ec_cmd_bytes, ec_cmd + 1,
2937d3777d1SAndy Shevchenko 			ec_dbgfs_resp_bytes);
2946cca83d4SAndres Salomon 
2956cca83d4SAndres Salomon 	olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1],
2966cca83d4SAndres Salomon 			ec_cmd_bytes, ec_dbgfs_resp, ec_dbgfs_resp_bytes);
2976cca83d4SAndres Salomon 
2987d3777d1SAndy Shevchenko 	pr_debug("olpc-ec: response %8ph (%d bytes expected)\n",
2997d3777d1SAndy Shevchenko 			ec_dbgfs_resp, ec_dbgfs_resp_bytes);
3006cca83d4SAndres Salomon 
3016cca83d4SAndres Salomon out:
3026cca83d4SAndres Salomon 	mutex_unlock(&ec_dbgfs_lock);
3036cca83d4SAndres Salomon 	return size;
3046cca83d4SAndres Salomon }
3056cca83d4SAndres Salomon 
ec_dbgfs_cmd_read(struct file * file,char __user * buf,size_t size,loff_t * ppos)3066cca83d4SAndres Salomon static ssize_t ec_dbgfs_cmd_read(struct file *file, char __user *buf,
3076cca83d4SAndres Salomon 		size_t size, loff_t *ppos)
3086cca83d4SAndres Salomon {
3096cca83d4SAndres Salomon 	unsigned int i, r;
3106cca83d4SAndres Salomon 	char *rp;
3116cca83d4SAndres Salomon 	char respbuf[64];
3126cca83d4SAndres Salomon 
3136cca83d4SAndres Salomon 	mutex_lock(&ec_dbgfs_lock);
3146cca83d4SAndres Salomon 	rp = respbuf;
3156cca83d4SAndres Salomon 	rp += sprintf(rp, "%02x", ec_dbgfs_resp[0]);
3166cca83d4SAndres Salomon 	for (i = 1; i < ec_dbgfs_resp_bytes; i++)
3176cca83d4SAndres Salomon 		rp += sprintf(rp, ", %02x", ec_dbgfs_resp[i]);
3186cca83d4SAndres Salomon 	mutex_unlock(&ec_dbgfs_lock);
3196cca83d4SAndres Salomon 	rp += sprintf(rp, "\n");
3206cca83d4SAndres Salomon 
3216cca83d4SAndres Salomon 	r = rp - respbuf;
3226cca83d4SAndres Salomon 	return simple_read_from_buffer(buf, size, ppos, respbuf, r);
3236cca83d4SAndres Salomon }
3246cca83d4SAndres Salomon 
3256cca83d4SAndres Salomon static const struct file_operations ec_dbgfs_ops = {
3266cca83d4SAndres Salomon 	.write = ec_dbgfs_cmd_write,
3276cca83d4SAndres Salomon 	.read = ec_dbgfs_cmd_read,
3286cca83d4SAndres Salomon };
3296cca83d4SAndres Salomon 
olpc_ec_setup_debugfs(void)3306cca83d4SAndres Salomon static struct dentry *olpc_ec_setup_debugfs(void)
3316cca83d4SAndres Salomon {
3326cca83d4SAndres Salomon 	struct dentry *dbgfs_dir;
3336cca83d4SAndres Salomon 
3346cca83d4SAndres Salomon 	dbgfs_dir = debugfs_create_dir("olpc-ec", NULL);
3356cca83d4SAndres Salomon 	if (IS_ERR_OR_NULL(dbgfs_dir))
3366cca83d4SAndres Salomon 		return NULL;
3376cca83d4SAndres Salomon 
3386cca83d4SAndres Salomon 	debugfs_create_file("cmd", 0600, dbgfs_dir, NULL, &ec_dbgfs_ops);
3396cca83d4SAndres Salomon 
3406cca83d4SAndres Salomon 	return dbgfs_dir;
3416cca83d4SAndres Salomon }
3426cca83d4SAndres Salomon 
3436cca83d4SAndres Salomon #else
3446cca83d4SAndres Salomon 
olpc_ec_setup_debugfs(void)3456cca83d4SAndres Salomon static struct dentry *olpc_ec_setup_debugfs(void)
3466cca83d4SAndres Salomon {
3476cca83d4SAndres Salomon 	return NULL;
3486cca83d4SAndres Salomon }
3496cca83d4SAndres Salomon 
3506cca83d4SAndres Salomon #endif /* CONFIG_DEBUG_FS */
3516cca83d4SAndres Salomon 
olpc_ec_set_dcon_power(struct olpc_ec_priv * ec,bool state)352231c0c21SLubomir Rintel static int olpc_ec_set_dcon_power(struct olpc_ec_priv *ec, bool state)
353231c0c21SLubomir Rintel {
354231c0c21SLubomir Rintel 	unsigned char ec_byte = state;
355231c0c21SLubomir Rintel 	int ret;
356231c0c21SLubomir Rintel 
357231c0c21SLubomir Rintel 	if (ec->dcon_enabled == state)
358231c0c21SLubomir Rintel 		return 0;
359231c0c21SLubomir Rintel 
360231c0c21SLubomir Rintel 	ret = olpc_ec_cmd(EC_DCON_POWER_MODE, &ec_byte, 1, NULL, 0);
361231c0c21SLubomir Rintel 	if (ret)
362231c0c21SLubomir Rintel 		return ret;
363231c0c21SLubomir Rintel 
364231c0c21SLubomir Rintel 	ec->dcon_enabled = state;
365231c0c21SLubomir Rintel 	return 0;
366231c0c21SLubomir Rintel }
367231c0c21SLubomir Rintel 
dcon_regulator_enable(struct regulator_dev * rdev)368231c0c21SLubomir Rintel static int dcon_regulator_enable(struct regulator_dev *rdev)
369231c0c21SLubomir Rintel {
370231c0c21SLubomir Rintel 	struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
371231c0c21SLubomir Rintel 
372231c0c21SLubomir Rintel 	return olpc_ec_set_dcon_power(ec, true);
373231c0c21SLubomir Rintel }
374231c0c21SLubomir Rintel 
dcon_regulator_disable(struct regulator_dev * rdev)375231c0c21SLubomir Rintel static int dcon_regulator_disable(struct regulator_dev *rdev)
376231c0c21SLubomir Rintel {
377231c0c21SLubomir Rintel 	struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
378231c0c21SLubomir Rintel 
379231c0c21SLubomir Rintel 	return olpc_ec_set_dcon_power(ec, false);
380231c0c21SLubomir Rintel }
381231c0c21SLubomir Rintel 
dcon_regulator_is_enabled(struct regulator_dev * rdev)382231c0c21SLubomir Rintel static int dcon_regulator_is_enabled(struct regulator_dev *rdev)
383231c0c21SLubomir Rintel {
384231c0c21SLubomir Rintel 	struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
385231c0c21SLubomir Rintel 
386231c0c21SLubomir Rintel 	return ec->dcon_enabled ? 1 : 0;
387231c0c21SLubomir Rintel }
388231c0c21SLubomir Rintel 
3890f1f7f22SRikard Falkeborn static const struct regulator_ops dcon_regulator_ops = {
390231c0c21SLubomir Rintel 	.enable		= dcon_regulator_enable,
391231c0c21SLubomir Rintel 	.disable	= dcon_regulator_disable,
392231c0c21SLubomir Rintel 	.is_enabled	= dcon_regulator_is_enabled,
393231c0c21SLubomir Rintel };
394231c0c21SLubomir Rintel 
395231c0c21SLubomir Rintel static const struct regulator_desc dcon_desc = {
396231c0c21SLubomir Rintel 	.name		= "dcon",
397231c0c21SLubomir Rintel 	.id		= 0,
398231c0c21SLubomir Rintel 	.ops		= &dcon_regulator_ops,
399231c0c21SLubomir Rintel 	.type		= REGULATOR_VOLTAGE,
400231c0c21SLubomir Rintel 	.owner		= THIS_MODULE,
401fa707a58SLubomir Rintel 	.enable_time	= 25000,
402231c0c21SLubomir Rintel };
403231c0c21SLubomir Rintel 
olpc_ec_probe(struct platform_device * pdev)404ac250415SAndres Salomon static int olpc_ec_probe(struct platform_device *pdev)
405ac250415SAndres Salomon {
406d278b7a2SAndres Salomon 	struct olpc_ec_priv *ec;
407231c0c21SLubomir Rintel 	struct regulator_config config = { };
40823f8b0a1SLubomir Rintel 	struct regulator_dev *regulator;
409ac250415SAndres Salomon 	int err;
410ac250415SAndres Salomon 
411ac250415SAndres Salomon 	if (!ec_driver)
412ac250415SAndres Salomon 		return -ENODEV;
413ac250415SAndres Salomon 
414d278b7a2SAndres Salomon 	ec = kzalloc(sizeof(*ec), GFP_KERNEL);
415d278b7a2SAndres Salomon 	if (!ec)
416d278b7a2SAndres Salomon 		return -ENOMEM;
41799ecb01cSAndres Salomon 
418d278b7a2SAndres Salomon 	ec->drv = ec_driver;
41999ecb01cSAndres Salomon 	INIT_WORK(&ec->worker, olpc_ec_worker);
42099ecb01cSAndres Salomon 	mutex_init(&ec->cmd_lock);
42199ecb01cSAndres Salomon 
42299ecb01cSAndres Salomon 	INIT_LIST_HEAD(&ec->cmd_q);
42399ecb01cSAndres Salomon 	spin_lock_init(&ec->cmd_q_lock);
42499ecb01cSAndres Salomon 
425d278b7a2SAndres Salomon 	ec_priv = ec;
426d278b7a2SAndres Salomon 	platform_set_drvdata(pdev, ec);
427d278b7a2SAndres Salomon 
428ec9964b4SLubomir Rintel 	/* get the EC revision */
429ec9964b4SLubomir Rintel 	err = olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0, &ec->version, 1);
430cec551eaSLubomir Rintel 	if (err)
431cec551eaSLubomir Rintel 		goto error;
432ac250415SAndres Salomon 
433231c0c21SLubomir Rintel 	config.dev = pdev->dev.parent;
434231c0c21SLubomir Rintel 	config.driver_data = ec;
435231c0c21SLubomir Rintel 	ec->dcon_enabled = true;
43623f8b0a1SLubomir Rintel 	regulator = devm_regulator_register(&pdev->dev, &dcon_desc, &config);
43723f8b0a1SLubomir Rintel 	if (IS_ERR(regulator)) {
438231c0c21SLubomir Rintel 		dev_err(&pdev->dev, "failed to register DCON regulator\n");
43923f8b0a1SLubomir Rintel 		err = PTR_ERR(regulator);
440cec551eaSLubomir Rintel 		goto error;
441231c0c21SLubomir Rintel 	}
442231c0c21SLubomir Rintel 
443ec9964b4SLubomir Rintel 	ec->dbgfs_dir = olpc_ec_setup_debugfs();
444ec9964b4SLubomir Rintel 
445cec551eaSLubomir Rintel 	return 0;
446cec551eaSLubomir Rintel 
447cec551eaSLubomir Rintel error:
448cec551eaSLubomir Rintel 	ec_priv = NULL;
449cec551eaSLubomir Rintel 	kfree(ec);
450ac250415SAndres Salomon 	return err;
451ac250415SAndres Salomon }
452ac250415SAndres Salomon 
olpc_ec_suspend(struct device * dev)453ac250415SAndres Salomon static int olpc_ec_suspend(struct device *dev)
454ac250415SAndres Salomon {
455ac250415SAndres Salomon 	struct platform_device *pdev = to_platform_device(dev);
456d278b7a2SAndres Salomon 	struct olpc_ec_priv *ec = platform_get_drvdata(pdev);
457d278b7a2SAndres Salomon 	int err = 0;
458d278b7a2SAndres Salomon 
459ec9964b4SLubomir Rintel 	olpc_ec_mask_write(ec->ec_wakeup_mask);
460ec9964b4SLubomir Rintel 
461d278b7a2SAndres Salomon 	if (ec_driver->suspend)
462d278b7a2SAndres Salomon 		err = ec_driver->suspend(pdev);
463d278b7a2SAndres Salomon 	if (!err)
464d278b7a2SAndres Salomon 		ec->suspended = true;
465d278b7a2SAndres Salomon 
466d278b7a2SAndres Salomon 	return err;
467ac250415SAndres Salomon }
468ac250415SAndres Salomon 
olpc_ec_resume(struct device * dev)469ac250415SAndres Salomon static int olpc_ec_resume(struct device *dev)
470ac250415SAndres Salomon {
471ac250415SAndres Salomon 	struct platform_device *pdev = to_platform_device(dev);
472d278b7a2SAndres Salomon 	struct olpc_ec_priv *ec = platform_get_drvdata(pdev);
473d278b7a2SAndres Salomon 
474d278b7a2SAndres Salomon 	ec->suspended = false;
475ac250415SAndres Salomon 	return ec_driver->resume ? ec_driver->resume(pdev) : 0;
476ac250415SAndres Salomon }
477ac250415SAndres Salomon 
478ac250415SAndres Salomon static const struct dev_pm_ops olpc_ec_pm_ops = {
479ac250415SAndres Salomon 	.suspend_late = olpc_ec_suspend,
480ac250415SAndres Salomon 	.resume_early = olpc_ec_resume,
481ac250415SAndres Salomon };
482ac250415SAndres Salomon 
483ac250415SAndres Salomon static struct platform_driver olpc_ec_plat_driver = {
484ac250415SAndres Salomon 	.probe = olpc_ec_probe,
485ac250415SAndres Salomon 	.driver = {
486ac250415SAndres Salomon 		.name = "olpc-ec",
487ac250415SAndres Salomon 		.pm = &olpc_ec_pm_ops,
488ac250415SAndres Salomon 	},
489ac250415SAndres Salomon };
490ac250415SAndres Salomon 
olpc_ec_init_module(void)491ac250415SAndres Salomon static int __init olpc_ec_init_module(void)
492ac250415SAndres Salomon {
493ac250415SAndres Salomon 	return platform_driver_register(&olpc_ec_plat_driver);
494ac250415SAndres Salomon }
49593dbc1b3SDaniel Drake arch_initcall(olpc_ec_init_module);
496