From: Andres Salomon <hidden> Date: 2012-07-19 01:21:44
The OLPC EC (Embedded Controller) code that is currently upstream is
x86-only, originally written for the XO-1. Since then, we've had the
XO-1.5 (also x86), and XO-1.75 (arm-based) enter mass production. The
1.75 uses a vastly different EC protocol, and future hardware revisions
are likely to change it even further.
However, the drivers do share quite a bit of code, so it makes sense to
have a platform-agnostic driver that calls into platform-specific hooks
for each XO's EC driver. This is the first stab and creating such a
beast (with further patches pending). Aside from the lack of code
duplication, this is helpful for fixing bugs in one place (for example,
we fixed an EC suspend/resume bug in 1.75 that I've just seen happen on
1.5 without these patches. With these patches, the problem goes away).
These patches are against Linus's current HEAD; let me know if they
don't apply somewhere, and I'll happily redo them against the -next
tree. I'm assuming that these changes (which touch places like x86,
wireless, and staging) should go through either the x86 tree, or
through akpm's tree.
Alternatively, if the reviews are positive and I can get SOBs from the
relevant maintainers, I can set up a platform-olpc tree somewhere and
request a pull from Linus.
From: Andres Salomon <hidden> Date: 2012-07-19 01:25:30
The OLPC EC driver has outgrown arch/x86/platform/. It's time to both
share common code amongst different architectures, as well as move it out
of arch/x86/. The XO-1.75 is ARM-based, and the EC driver shares a lot of
code with the x86 code.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/include/asm/olpc.h | 19 +++----------------
arch/x86/platform/olpc/olpc.c | 4 ++--
drivers/platform/Makefile | 1 +
drivers/platform/olpc/olpc-ec.c | 16 ++++++++++++++++
include/linux/olpc-ec.h | 29 +++++++++++++++++++++++++++++
5 files changed, 51 insertions(+), 18 deletions(-)
create mode 100644 drivers/platform/olpc/olpc-ec.c
create mode 100644 include/linux/olpc-ec.h
@@ -125,7 +125,7 @@ static int __wait_on_obf(unsigned int line, unsigned int port, int desired)*<http://wiki.laptop.org/go/Ec_specification>. Unfortunately, while*OpenFirmware'ssourceisavailable,theEC'sisnot.*/-intolpc_ec_cmd(unsignedcharcmd,unsignedchar*inbuf,size_tinlen,+intolpc_ec_cmd_x86(unsignedcharcmd,unsignedchar*inbuf,size_tinlen,unsignedchar*outbuf,size_toutlen){unsignedlongflags;
@@ -0,0 +1,16 @@+/*+*GenericdriverfortheOLPCEmbeddedController.+*+*Copyright(C)2011-2012OneLaptopperChildFoundation.+*+*LicensedundertheGPLv2orlater.+*/+#include<linux/module.h>+#include<asm/olpc.h>++intolpc_ec_cmd(u8cmd,u8*inbuf,size_tinlen,u8*outbuf,size_toutlen)+{+/* Currently a stub; this will be expanded upon later. */+returnolpc_ec_cmd_x86(cmd,inbuf,inlen,outbuf,outlen);+}+EXPORT_SYMBOL_GPL(olpc_ec_cmd);
From: Andres Salomon <hidden> Date: 2012-07-19 01:26:40
This provides a new API allows different OLPC architectures to override the
EC driver. x86 and ARM OLPC machines use completely different EC backends.
The olpc_ec_cmd is synchronous, and waits for the workqueue to send the
command to the EC. Multiple callers can run olpc_ec_cmd() at once, and
they will by serialized and sleep while only one executes on the EC at a time.
We don't provide an unregister function, as that doesn't make sense within
the context of OLPC machines - there's only ever 1 EC, it's critical to
functionality, and it certainly not hotpluggable.
Signed-off-by: Andres Salomon <redacted>
---
drivers/platform/olpc/olpc-ec.c | 112 ++++++++++++++++++++++++++++++++++++++-
include/linux/olpc-ec.h | 6 ++
2 files changed, 116 insertions(+), 2 deletions(-)
@@ -5,12 +5,120 @@**LicensedundertheGPLv2orlater.*/+#include<linux/completion.h>+#include<linux/spinlock.h>+#include<linux/mutex.h>+#include<linux/workqueue.h>#include<linux/module.h>+#include<linux/list.h>+#include<linux/olpc-ec.h>#include<asm/olpc.h>+structec_cmd_desc{+u8cmd;+u8*inbuf,*outbuf;+size_tinlen,outlen;++interr;+structcompletionfinished;+structlist_headnode;++void*priv;+};++staticvoidolpc_ec_worker(structwork_struct*w);++staticDECLARE_WORK(ec_worker,olpc_ec_worker);+staticLIST_HEAD(ec_cmd_q);+staticDEFINE_SPINLOCK(ec_cmd_q_lock);++staticstructolpc_ec_driver*ec_driver;+staticvoid*ec_cb_arg;+staticDEFINE_MUTEX(ec_cb_lock);++voidolpc_ec_driver_register(structolpc_ec_driver*drv,void*arg)+{+ec_driver=drv;+ec_cb_arg=arg;+}+EXPORT_SYMBOL_GPL(olpc_ec_driver_register);++staticvoidolpc_ec_worker(structwork_struct*w)+{+structec_cmd_desc*desc=NULL;+unsignedlongflags;++/* Grab the first pending command from the queue */+spin_lock_irqsave(&ec_cmd_q_lock,flags);+if(!list_empty(&ec_cmd_q)){+desc=list_first_entry(&ec_cmd_q,structec_cmd_desc,node);+list_del(&desc->node);+}+spin_unlock_irqrestore(&ec_cmd_q_lock,flags);++/* Do we actually have anything to do? */+if(!desc)+return;++/* Protect the EC hw with a mutex; only run one cmd at a time */+mutex_lock(&ec_cb_lock);+desc->err=ec_driver->ec_cmd(desc->cmd,desc->inbuf,desc->inlen,+desc->outbuf,desc->outlen,ec_cb_arg);+mutex_unlock(&ec_cb_lock);++/* Finished, wake up olpc_ec_cmd() */+complete(&desc->finished);++/* Run the worker thread again in case there are more cmds pending */+schedule_work(&ec_worker);+}++/*+*Throwacmddescripterontothelist.WenowhaveSMPOLPCmachines,so+*lockingisprettycritical.+*/+staticvoidqueue_ec_descriptor(structec_cmd_desc*desc)+{+unsignedlongflags;++INIT_LIST_HEAD(&desc->node);++spin_lock_irqsave(&ec_cmd_q_lock,flags);+list_add_tail(&desc->node,&ec_cmd_q);+spin_unlock_irqrestore(&ec_cmd_q_lock,flags);++schedule_work(&ec_worker);+}+intolpc_ec_cmd(u8cmd,u8*inbuf,size_tinlen,u8*outbuf,size_toutlen){-/* Currently a stub; this will be expanded upon later. */-returnolpc_ec_cmd_x86(cmd,inbuf,inlen,outbuf,outlen);+structec_cmd_descdesc;++/* XXX: this will be removed in later patches */+/* Are we using old-style callers? */+if(!ec_driver||!ec_driver->ec_cmd)+returnolpc_ec_cmd_x86(cmd,inbuf,inlen,outbuf,outlen);++/* Ensure a driver and ec hook have been registered */+if(WARN_ON(!ec_driver||!ec_driver->ec_cmd))+return-ENODEV;++might_sleep();++desc.cmd=cmd;+desc.inbuf=inbuf;+desc.outbuf=outbuf;+desc.inlen=inlen;+desc.outlen=outlen;+desc.err=0;+init_completion(&desc.finished);++queue_ec_descriptor(&desc);++/* Timeouts must be handled in the platform-specific EC hook */+wait_for_completion(&desc.finished);++/* The worker thread dequeues the cmd; no need to do anything here */+returndesc.err;}EXPORT_SYMBOL_GPL(olpc_ec_cmd);
From: Andres Salomon <hidden> Date: 2012-07-19 01:27:54
The 1.75-based OLPC EC driver already does this; let's do it for all EC
drivers. This gives us nice suspend/resume hooks, amongst other things.
We want to run the EC's suspend hooks later than other drivers (which may
be setting wakeup masks or be running EC commands). We also want to run
the EC's resume hooks earlier than other drivers (which may want to run EC
commands).
Signed-off-by: Andres Salomon <redacted>
---
drivers/platform/olpc/olpc-ec.c | 48 +++++++++++++++++++++++++++++++++++++++
include/linux/olpc-ec.h | 6 +++++
2 files changed, 54 insertions(+), 0 deletions(-)
From: Andres Salomon <hidden> Date: 2012-07-19 01:29:16
A problem we've noticed on XO-1.75 is when we suspend in the middle of
an EC command. Don't allow that.
In the process, create a private object for the generic EC driver to use;
we have a framework for passing around a struct, use that rather than a
proliferation of global variables.
Signed-off-by: Andres Salomon <redacted>
---
drivers/platform/olpc/olpc-ec.c | 46 ++++++++++++++++++++++++++++++++++++++-
1 files changed, 45 insertions(+), 1 deletions(-)
@@ -93,6 +110,7 @@ static void queue_ec_descriptor(struct ec_cmd_desc *desc)intolpc_ec_cmd(u8cmd,u8*inbuf,size_tinlen,u8*outbuf,size_toutlen){+structolpc_ec_priv*ec=ec_priv;structec_cmd_descdesc;/* XXX: this will be removed in later patches */
@@ -104,6 +122,13 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)if(WARN_ON(!ec_driver||!ec_driver->ec_cmd))return-ENODEV;+if(!ec)+return-ENOMEM;++/* Suspending in the middle of a command hoses things really badly */+if(WARN_ON(ec->suspended))+return-EBUSY;+might_sleep();desc.cmd=cmd;
From: Andres Salomon <hidden> Date: 2012-07-19 01:31:06
This uses the new EC driver framework in drivers/platform/olpc. The
XO-1 and XO-1.5-specific code is still in arch/x86, but the generic stuff
(including a new workqueue; no more running EC commands with IRQs disabled!)
can be shared with other architectures.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/include/asm/olpc.h | 5 ---
arch/x86/platform/olpc/olpc.c | 53 ++++++++++++++++++++-------------------
drivers/platform/olpc/olpc-ec.c | 5 ---
3 files changed, 27 insertions(+), 36 deletions(-)
@@ -126,16 +123,13 @@ static int __wait_on_obf(unsigned int line, unsigned int port, int desired)*<http://wiki.laptop.org/go/Ec_specification>. Unfortunately, while*OpenFirmware'ssourceisavailable,theEC'sisnot.*/-intolpc_ec_cmd_x86(unsignedcharcmd,unsignedchar*inbuf,size_tinlen,-unsignedchar*outbuf,size_toutlen)+staticintolpc_xo1_ec_cmd(u8cmd,u8*inbuf,size_tinlen,u8*outbuf,+size_toutlen,void*arg){-unsignedlongflags;intret=-EIO;inti;intrestarts=0;-spin_lock_irqsave(&ec_lock,flags);-/* Clear OBF */for(i=0;i<10&&(obf_status(0x6c)==1);i++)inb(0x68);
@@ -425,8 +417,28 @@ static int __init add_xo1_platform_devices(void)return0;}-staticstructsyscore_opsolpc_syscore_ops={+staticintolpc_xo1_ec_probe(structplatform_device*pdev)+{+/* get the EC revision */+olpc_ec_cmd(EC_FIRMWARE_REV,NULL,0,+(unsignedchar*)&olpc_platform_info.ecver,1);++/* EC version 0x5f adds support for wide SCI mask */+if(olpc_platform_info.ecver>=0x5f)+olpc_platform_info.flags|=OLPC_F_EC_WIDE_SCI;++pr_info("OLPC board revision %s%X (EC=%x)\n",+((olpc_platform_info.boardrev&0xf)<8)?"pre":"",+olpc_platform_info.boardrev>>4,+olpc_platform_info.ecver);++return0;+}++staticstructolpc_ec_driverec_xo1_driver={.suspend=olpc_ec_suspend,+.probe=olpc_xo1_ec_probe,+.ec_cmd=olpc_xo1_ec_cmd,};staticint__initolpc_init(void)
@@ -436,16 +448,14 @@ static int __init olpc_init(void)if(!olpc_ofw_present()||!platform_detect())return0;-spin_lock_init(&ec_lock);+/* register the XO-1 and 1.5-specific EC handler */+olpc_ec_driver_register(&ec_xo1_driver,NULL);+platform_device_register_simple("olpc-ec",-1,NULL,0);/* assume B1 and above models always have a DCON */if(olpc_board_at_least(olpc_board(0xb1)))olpc_platform_info.flags|=OLPC_F_DCON;-/* get the EC revision */-olpc_ec_cmd(EC_FIRMWARE_REV,NULL,0,-(unsignedchar*)&olpc_platform_info.ecver,1);-#ifdef CONFIG_PCI_OLPC/* If the VSA exists let it emulate PCI, if not emulate in kernel.*XO-1only.*/
@@ -453,14 +463,6 @@ static int __init olpc_init(void)!cs5535_has_vsa2())x86_init.pci.arch_init=pci_olpc_init;#endif-/* EC version 0x5f adds support for wide SCI mask */-if(olpc_platform_info.ecver>=0x5f)-olpc_platform_info.flags|=OLPC_F_EC_WIDE_SCI;--printk(KERN_INFO"OLPC board revision %s%X (EC=%x)\n",-((olpc_platform_info.boardrev&0xf)<8)?"pre":"",-olpc_platform_info.boardrev>>4,-olpc_platform_info.ecver);if(olpc_platform_info.boardrev<olpc_board_pre(0xd0)){/* XO-1 */r=add_xo1_platform_devices();
@@ -468,7 +470,6 @@ static int __init olpc_init(void)returnr;}-register_syscore_ops(&olpc_syscore_ops);setup_debugfs();return0;
@@ -113,11 +113,6 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)structolpc_ec_priv*ec=ec_priv;structec_cmd_descdesc;-/* XXX: this will be removed in later patches */-/* Are we using old-style callers? */-if(!ec_driver||!ec_driver->ec_cmd)-returnolpc_ec_cmd_x86(cmd,inbuf,inlen,outbuf,outlen);-/* Ensure a driver and ec hook have been registered */if(WARN_ON(!ec_driver||!ec_driver->ec_cmd))return-ENODEV;
From: Andres Salomon <hidden> Date: 2012-07-19 01:31:38
There's nothing about the debugfs interface for the EC driver that is
architecture-specific, so move it into the arch-independent driver.
The code is mostly unchanged with the exception of renamed variables, coding
style changes, and API updates.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/platform/olpc/olpc.c | 97 --------------------------------
drivers/platform/olpc/olpc-ec.c | 117 +++++++++++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+), 97 deletions(-)
@@ -66,49 +65,51 @@ EXPORT_SYMBOL_GPL(olpc_ec_driver_register);staticvoidolpc_ec_worker(structwork_struct*w){+structolpc_ec_priv*ec=container_of(w,structolpc_ec_priv,worker);structec_cmd_desc*desc=NULL;unsignedlongflags;/* Grab the first pending command from the queue */-spin_lock_irqsave(&ec_cmd_q_lock,flags);-if(!list_empty(&ec_cmd_q)){-desc=list_first_entry(&ec_cmd_q,structec_cmd_desc,node);+spin_lock_irqsave(&ec->cmd_q_lock,flags);+if(!list_empty(&ec->cmd_q)){+desc=list_first_entry(&ec->cmd_q,structec_cmd_desc,node);list_del(&desc->node);}-spin_unlock_irqrestore(&ec_cmd_q_lock,flags);+spin_unlock_irqrestore(&ec->cmd_q_lock,flags);/* Do we actually have anything to do? */if(!desc)return;/* Protect the EC hw with a mutex; only run one cmd at a time */-mutex_lock(&ec_cb_lock);+mutex_lock(&ec->cmd_lock);desc->err=ec_driver->ec_cmd(desc->cmd,desc->inbuf,desc->inlen,desc->outbuf,desc->outlen,ec_cb_arg);-mutex_unlock(&ec_cb_lock);+mutex_unlock(&ec->cmd_lock);/* Finished, wake up olpc_ec_cmd() */complete(&desc->finished);/* Run the worker thread again in case there are more cmds pending */-schedule_work(&ec_worker);+schedule_work(&ec->worker);}/**Throwacmddescripterontothelist.WenowhaveSMPOLPCmachines,so*lockingisprettycritical.*/-staticvoidqueue_ec_descriptor(structec_cmd_desc*desc)+staticvoidqueue_ec_descriptor(structec_cmd_desc*desc,+structolpc_ec_priv*ec){unsignedlongflags;INIT_LIST_HEAD(&desc->node);-spin_lock_irqsave(&ec_cmd_q_lock,flags);-list_add_tail(&desc->node,&ec_cmd_q);-spin_unlock_irqrestore(&ec_cmd_q_lock,flags);+spin_lock_irqsave(&ec->cmd_q_lock,flags);+list_add_tail(&desc->node,&ec->cmd_q);+spin_unlock_irqrestore(&ec->cmd_q_lock,flags);-schedule_work(&ec_worker);+schedule_work(&ec->worker);}intolpc_ec_cmd(u8cmd,u8*inbuf,size_tinlen,u8*outbuf,size_toutlen)
@@ -137,7 +138,7 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)desc.err=0;init_completion(&desc.finished);-queue_ec_descriptor(&desc);+queue_ec_descriptor(&desc,ec);/* Timeouts must be handled in the platform-specific EC hook */wait_for_completion(&desc.finished);
@@ -266,7 +267,14 @@ static int olpc_ec_probe(struct platform_device *pdev)ec=kzalloc(sizeof(*ec),GFP_KERNEL);if(!ec)return-ENOMEM;+ec->drv=ec_driver;+INIT_WORK(&ec->worker,olpc_ec_worker);+mutex_init(&ec->cmd_lock);++INIT_LIST_HEAD(&ec->cmd_q);+spin_lock_init(&ec->cmd_q_lock);+ec_priv=ec;platform_set_drvdata(pdev,ec);
From: Andres Salomon <hidden> Date: 2012-07-19 01:32:43
The new EC driver calls platform-specific suspend and resume hooks; run
XO-1-specific EC commands from there, rather than deep in s/r code. If we
attempt to run EC commands after the new EC driver has suspended, it is
refused by the ec->suspended checks.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/platform/olpc/olpc-xo1-pm.c | 15 ------------
arch/x86/platform/olpc/olpc.c | 43 ++++++++++++++++++++++++++++-----
2 files changed, 36 insertions(+), 22 deletions(-)
@@ -52,16 +52,11 @@ EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);staticintxo1_power_state_enter(suspend_state_tpm_state){unsignedlongsaved_sci_mask;-intr;/* Only STR is supported */if(pm_state!=PM_SUSPEND_MEM)return-EINVAL;-r=olpc_ec_cmd(EC_SET_SCI_INHIBIT,NULL,0,NULL,0);-if(r)-returnr;-/**SaveSCImask(thisgetslostsincePM1_ENisusedasamaskfor*wakeupevents,whichisnotnecessarilythesameeventset)
@@ -77,16 +72,6 @@ static int xo1_power_state_enter(suspend_state_t pm_state)/* Restore SCI mask (using dword access to CS5536_PM1_EN) */outl(saved_sci_mask,acpi_base+CS5536_PM1_STS);-/* Tell the EC to stop inhibiting SCIs */-olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE,NULL,0,NULL,0);--/*-*TellthewirelessmoduletorestartUSBcommunication.-*Mustbedonetwice.-*/-olpc_ec_cmd(EC_WAKE_UP_WLAN,NULL,0,NULL,0);-olpc_ec_cmd(EC_WAKE_UP_WLAN,NULL,0,NULL,0);-return0;}
@@ -263,11 +263,6 @@ int olpc_ec_sci_query(u16 *sci_value)}EXPORT_SYMBOL_GPL(olpc_ec_sci_query);-staticintolpc_ec_suspend(structplatform_device*pdev)-{-returnolpc_ec_mask_write(ec_wakeup_mask);-}-staticbool__initcheck_ofw_architecture(structdevice_node*root){constchar*olpc_arch;
@@ -339,9 +334,40 @@ static int olpc_xo1_ec_probe(struct platform_device *pdev)return0;}+staticintolpc_xo1_ec_suspend(structplatform_device*pdev)+{+olpc_ec_mask_write(ec_wakeup_mask);++/*+*SquelchSCIswhilesuspended.Thisisafixfor+*<http://dev.laptop.org/ticket/1835>.+*/+returnolpc_ec_cmd(EC_SET_SCI_INHIBIT,NULL,0,NULL,0);+}++staticintolpc_xo1_ec_resume(structplatform_device*pdev)+{+/* Tell the EC to stop inhibiting SCIs */+olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE,NULL,0,NULL,0);++/*+*TellthewirelessmoduletorestartUSBcommunication.+*Mustbedonetwice.+*/+olpc_ec_cmd(EC_WAKE_UP_WLAN,NULL,0,NULL,0);+olpc_ec_cmd(EC_WAKE_UP_WLAN,NULL,0,NULL,0);++return0;+}staticstructolpc_ec_driverec_xo1_driver={-.suspend=olpc_ec_suspend,+.probe=olpc_xo1_ec_probe,+.suspend=olpc_xo1_ec_suspend,+.resume=olpc_xo1_ec_resume,+.ec_cmd=olpc_xo1_ec_cmd,+};++staticstructolpc_ec_driverec_xo1_5_driver={.probe=olpc_xo1_ec_probe,.ec_cmd=olpc_xo1_ec_cmd,};
@@ -354,7 +380,10 @@ static int __init olpc_init(void)return0;/* register the XO-1 and 1.5-specific EC handler */-olpc_ec_driver_register(&ec_xo1_driver,NULL);+if(olpc_platform_info.boardrev<olpc_board_pre(0xd0))/* XO-1 */+olpc_ec_driver_register(&ec_xo1_driver,NULL);+else+olpc_ec_driver_register(&ec_xo1_5_driver,NULL);platform_device_register_simple("olpc-ec",-1,NULL,0);/* assume B1 and above models always have a DCON */
From: Andres Salomon <hidden> Date: 2012-07-19 04:27:04
The OLPC EC driver has outgrown arch/x86/platform/. It's time to both
share common code amongst different architectures, as well as move it out
of arch/x86/. The XO-1.75 is ARM-based, and the EC driver shares a lot of
code with the x86 code.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/include/asm/olpc.h | 19 +++----------------
arch/x86/platform/olpc/olpc.c | 4 ++--
drivers/platform/Makefile | 1 +
drivers/platform/olpc/olpc-ec.c | 16 ++++++++++++++++
include/linux/olpc-ec.h | 29 +++++++++++++++++++++++++++++
5 files changed, 51 insertions(+), 18 deletions(-)
create mode 100644 drivers/platform/olpc/olpc-ec.c
create mode 100644 include/linux/olpc-ec.h
@@ -125,7 +125,7 @@ static int __wait_on_obf(unsigned int line, unsigned int port, int desired)*<http://wiki.laptop.org/go/Ec_specification>. Unfortunately, while*OpenFirmware'ssourceisavailable,theEC'sisnot.*/-intolpc_ec_cmd(unsignedcharcmd,unsignedchar*inbuf,size_tinlen,+intolpc_ec_cmd_x86(unsignedcharcmd,unsignedchar*inbuf,size_tinlen,unsignedchar*outbuf,size_toutlen){unsignedlongflags;
@@ -0,0 +1,16 @@+/*+*GenericdriverfortheOLPCEmbeddedController.+*+*Copyright(C)2011-2012OneLaptopperChildFoundation.+*+*LicensedundertheGPLv2orlater.+*/+#include<linux/module.h>+#include<asm/olpc.h>++intolpc_ec_cmd(u8cmd,u8*inbuf,size_tinlen,u8*outbuf,size_toutlen)+{+/* Currently a stub; this will be expanded upon later. */+returnolpc_ec_cmd_x86(cmd,inbuf,inlen,outbuf,outlen);+}+EXPORT_SYMBOL_GPL(olpc_ec_cmd);
From: Andres Salomon <hidden> Date: 2012-07-19 04:28:41
This provides a new API allows different OLPC architectures to override the
EC driver. x86 and ARM OLPC machines use completely different EC backends.
The olpc_ec_cmd is synchronous, and waits for the workqueue to send the
command to the EC. Multiple callers can run olpc_ec_cmd() at once, and
they will by serialized and sleep while only one executes on the EC at a time.
We don't provide an unregister function, as that doesn't make sense within
the context of OLPC machines - there's only ever 1 EC, it's critical to
functionality, and it certainly not hotpluggable.
Signed-off-by: Andres Salomon <redacted>
---
drivers/platform/olpc/olpc-ec.c | 112 ++++++++++++++++++++++++++++++++++++++-
include/linux/olpc-ec.h | 6 ++
2 files changed, 116 insertions(+), 2 deletions(-)
@@ -5,12 +5,120 @@**LicensedundertheGPLv2orlater.*/+#include<linux/completion.h>+#include<linux/spinlock.h>+#include<linux/mutex.h>+#include<linux/workqueue.h>#include<linux/module.h>+#include<linux/list.h>+#include<linux/olpc-ec.h>#include<asm/olpc.h>+structec_cmd_desc{+u8cmd;+u8*inbuf,*outbuf;+size_tinlen,outlen;++interr;+structcompletionfinished;+structlist_headnode;++void*priv;+};++staticvoidolpc_ec_worker(structwork_struct*w);++staticDECLARE_WORK(ec_worker,olpc_ec_worker);+staticLIST_HEAD(ec_cmd_q);+staticDEFINE_SPINLOCK(ec_cmd_q_lock);++staticstructolpc_ec_driver*ec_driver;+staticvoid*ec_cb_arg;+staticDEFINE_MUTEX(ec_cb_lock);++voidolpc_ec_driver_register(structolpc_ec_driver*drv,void*arg)+{+ec_driver=drv;+ec_cb_arg=arg;+}+EXPORT_SYMBOL_GPL(olpc_ec_driver_register);++staticvoidolpc_ec_worker(structwork_struct*w)+{+structec_cmd_desc*desc=NULL;+unsignedlongflags;++/* Grab the first pending command from the queue */+spin_lock_irqsave(&ec_cmd_q_lock,flags);+if(!list_empty(&ec_cmd_q)){+desc=list_first_entry(&ec_cmd_q,structec_cmd_desc,node);+list_del(&desc->node);+}+spin_unlock_irqrestore(&ec_cmd_q_lock,flags);++/* Do we actually have anything to do? */+if(!desc)+return;++/* Protect the EC hw with a mutex; only run one cmd at a time */+mutex_lock(&ec_cb_lock);+desc->err=ec_driver->ec_cmd(desc->cmd,desc->inbuf,desc->inlen,+desc->outbuf,desc->outlen,ec_cb_arg);+mutex_unlock(&ec_cb_lock);++/* Finished, wake up olpc_ec_cmd() */+complete(&desc->finished);++/* Run the worker thread again in case there are more cmds pending */+schedule_work(&ec_worker);+}++/*+*Throwacmddescripterontothelist.WenowhaveSMPOLPCmachines,so+*lockingisprettycritical.+*/+staticvoidqueue_ec_descriptor(structec_cmd_desc*desc)+{+unsignedlongflags;++INIT_LIST_HEAD(&desc->node);++spin_lock_irqsave(&ec_cmd_q_lock,flags);+list_add_tail(&desc->node,&ec_cmd_q);+spin_unlock_irqrestore(&ec_cmd_q_lock,flags);++schedule_work(&ec_worker);+}+intolpc_ec_cmd(u8cmd,u8*inbuf,size_tinlen,u8*outbuf,size_toutlen){-/* Currently a stub; this will be expanded upon later. */-returnolpc_ec_cmd_x86(cmd,inbuf,inlen,outbuf,outlen);+structec_cmd_descdesc;++/* XXX: this will be removed in later patches */+/* Are we using old-style callers? */+if(!ec_driver||!ec_driver->ec_cmd)+returnolpc_ec_cmd_x86(cmd,inbuf,inlen,outbuf,outlen);++/* Ensure a driver and ec hook have been registered */+if(WARN_ON(!ec_driver||!ec_driver->ec_cmd))+return-ENODEV;++might_sleep();++desc.cmd=cmd;+desc.inbuf=inbuf;+desc.outbuf=outbuf;+desc.inlen=inlen;+desc.outlen=outlen;+desc.err=0;+init_completion(&desc.finished);++queue_ec_descriptor(&desc);++/* Timeouts must be handled in the platform-specific EC hook */+wait_for_completion(&desc.finished);++/* The worker thread dequeues the cmd; no need to do anything here */+returndesc.err;}EXPORT_SYMBOL_GPL(olpc_ec_cmd);
From: Andres Salomon <hidden> Date: 2012-07-19 04:28:47
A problem we've noticed on XO-1.75 is when we suspend in the middle of
an EC command. Don't allow that.
In the process, create a private object for the generic EC driver to
use; we have a framework for passing around a struct, use that rather
than a proliferation of global variables.
Signed-off-by: Andres Salomon <redacted>
---
drivers/platform/olpc/olpc-ec.c | 46
++++++++++++++++++++++++++++++++++++++- 1 files changed, 45
insertions(+), 1 deletions(-)
diff --git a/drivers/platform/olpc/olpc-ec.c
b/drivers/platform/olpc/olpc-ec.c index d00523c..cfba41f 100644
always finish
+ * the command before the machine suspends. This means that
the EC
+ * is expecting the command protocol to finish, but we after a
period
+ * of time (while the OS is asleep) the EC times out and
restarts its
+ * idle loop. Meanwhile, the OS wakes up, thinks it's still
in the
+ * middle of the command protocol, starts throwing random
things at
+ * the EC... and everyone's uphappy.
+ */
+ bool suspended;
+};
+
static void olpc_ec_worker(struct work_struct *w);
static DECLARE_WORK(ec_worker, olpc_ec_worker);
From: Andres Salomon <hidden> Date: 2012-07-19 04:28:55
This uses the new EC driver framework in drivers/platform/olpc. The
XO-1 and XO-1.5-specific code is still in arch/x86, but the generic
stuff (including a new workqueue; no more running EC commands with IRQs
disabled!) can be shared with other architectures.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/include/asm/olpc.h | 5 ---
arch/x86/platform/olpc/olpc.c | 53
++++++++++++++++++++------------------- drivers/platform/olpc/olpc-ec.c
| 5 --- 3 files changed, 27 insertions(+), 36 deletions(-)
u8 *outbuf, size_t outlen) struct olpc_ec_priv *ec = ec_priv;
struct ec_cmd_desc desc;
- /* XXX: this will be removed in later patches */
- /* Are we using old-style callers? */
- if (!ec_driver || !ec_driver->ec_cmd)
- return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf,
outlen); -
/* Ensure a driver and ec hook have been registered */
if (WARN_ON(!ec_driver || !ec_driver->ec_cmd))
return -ENODEV;
--
1.7.2.5
From: Andres Salomon <hidden> Date: 2012-07-19 04:29:10
There's nothing about the debugfs interface for the EC driver that is
architecture-specific, so move it into the arch-independent driver.
The code is mostly unchanged with the exception of renamed variables,
coding style changes, and API updates.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/platform/olpc/olpc.c | 97 --------------------------------
drivers/platform/olpc/olpc-ec.c | 117
+++++++++++++++++++++++++++++++++++++++ 2 files changed, 117
insertions(+), 97 deletions(-)
diff --git a/arch/x86/platform/olpc/olpc.c
b/arch/x86/platform/olpc/olpc.c index 4590096..ed41b43 100644
From: Andres Salomon <hidden> Date: 2012-07-19 04:29:45
The new EC driver calls platform-specific suspend and resume hooks; run
XO-1-specific EC commands from there, rather than deep in s/r code. If
we attempt to run EC commands after the new EC driver has suspended, it
is refused by the ec->suspended checks.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/platform/olpc/olpc-xo1-pm.c | 15 ------------
arch/x86/platform/olpc/olpc.c | 43
++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 22
deletions(-)
diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c
b/arch/x86/platform/olpc/olpc-xo1-pm.c index 8054b64..d75582d 100644
@@ -52,16 +52,11 @@ EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);staticintxo1_power_state_enter(suspend_state_tpm_state){unsignedlongsaved_sci_mask;-intr;/* Only STR is supported */if(pm_state!=PM_SUSPEND_MEM)return-EINVAL;-r=olpc_ec_cmd(EC_SET_SCI_INHIBIT,NULL,0,NULL,0);-if(r)-returnr;-/**SaveSCImask(thisgetslostsincePM1_ENisusedasa
mask for
* wakeup events, which is not necessarily the same event set)
@@ -77,16 +72,6 @@ static int xo1_power_state_enter(suspend_state_t
pm_state) /* Restore SCI mask (using dword access to CS5536_PM1_EN) */
outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
- /* Tell the EC to stop inhibiting SCIs */
- olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
-
- /*
- * Tell the wireless module to restart USB communication.
- * Must be done twice.
- */
- olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
- olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
-
return 0;
}
diff --git a/arch/x86/platform/olpc/olpc.c
b/arch/x86/platform/olpc/olpc.c index ed41b43..2737608 100644
From: Andres Salomon <hidden> Date: 2012-07-19 04:30:43
The 1.75-based OLPC EC driver already does this; let's do it for all EC
drivers. This gives us nice suspend/resume hooks, amongst other things.
We want to run the EC's suspend hooks later than other drivers (which may
be setting wakeup masks or be running EC commands). We also want to run
the EC's resume hooks earlier than other drivers (which may want to run EC
commands).
Signed-off-by: Andres Salomon <redacted>
---
drivers/platform/olpc/olpc-ec.c | 48 +++++++++++++++++++++++++++++++++++++++
include/linux/olpc-ec.h | 6 +++++
2 files changed, 54 insertions(+), 0 deletions(-)
From: Andres Salomon <hidden> Date: 2012-07-19 04:36:19
Damnit, my mailer mangled the resend. Okay, 3rd time's a charm..
(sorry for the noise!)
On Wed, 18 Jul 2012 21:28:27 -0700
Andres Salomon [off-list ref] wrote:
quoted hunk
This uses the new EC driver framework in drivers/platform/olpc. The
XO-1 and XO-1.5-specific code is still in arch/x86, but the generic
stuff (including a new workqueue; no more running EC commands with
IRQs disabled!) can be shared with other architectures.
Signed-off-by: Andres Salomon <redacted>
---
arch/x86/include/asm/olpc.h | 5 ---
arch/x86/platform/olpc/olpc.c | 53
++++++++++++++++++++-------------------
drivers/platform/olpc/olpc-ec.c | 5 --- 3 files changed, 27
insertions(+), 36 deletions(-)
u8 *outbuf, size_t outlen) struct olpc_ec_priv *ec = ec_priv;
struct ec_cmd_desc desc;
- /* XXX: this will be removed in later patches */
- /* Are we using old-style callers? */
- if (!ec_driver || !ec_driver->ec_cmd)
- return olpc_ec_cmd_x86(cmd, inbuf, inlen, outbuf,
outlen); -
/* Ensure a driver and ec hook have been registered */
if (WARN_ON(!ec_driver || !ec_driver->ec_cmd))
return -ENODEV;