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