2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-04-29 00:59:53 -07:00
/*
* Support for the OLPC DCON and OLPC EC access
*
* Copyright © 2006 Advanced Micro Devices , Inc .
* Copyright © 2007 - 2008 Andres Salomon < dilinger @ debian . org >
*/
# include <linux/kernel.h>
# include <linux/init.h>
2016-07-13 20:18:58 -04:00
# include <linux/export.h>
2008-04-29 00:59:53 -07:00
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/string.h>
2010-10-13 19:10:42 +01:00
# include <linux/platform_device.h>
2011-03-13 15:10:17 +00:00
# include <linux/of.h>
2011-06-25 17:34:13 +01:00
# include <linux/syscore_ops.h>
2012-03-27 16:07:40 +01:00
# include <linux/mutex.h>
2012-07-11 01:16:29 -07:00
# include <linux/olpc-ec.h>
2010-02-22 05:42:04 -08:00
2008-04-29 00:59:53 -07:00
# include <asm/geode.h>
2010-02-22 05:42:04 -08:00
# include <asm/setup.h>
2008-04-29 00:59:53 -07:00
# include <asm/olpc.h>
2010-06-18 17:46:53 -04:00
# include <asm/olpc_ofw.h>
2008-04-29 00:59:53 -07:00
struct olpc_platform_t olpc_platform_info ;
EXPORT_SYMBOL_GPL ( olpc_platform_info ) ;
/* what the timeout *should* be (in ms) */
# define EC_BASE_TIMEOUT 20
/* the timeout that bugs in the EC might force us to actually use */
static int ec_timeout = EC_BASE_TIMEOUT ;
static int __init olpc_ec_timeout_set ( char * str )
{
if ( get_option ( & str , & ec_timeout ) ! = 1 ) {
ec_timeout = EC_BASE_TIMEOUT ;
printk ( KERN_ERR " olpc-ec: invalid argument to "
" 'olpc_ec_timeout=', ignoring! \n " ) ;
}
printk ( KERN_DEBUG " olpc-ec: using %d ms delay for EC commands. \n " ,
ec_timeout ) ;
return 1 ;
}
__setup ( " olpc_ec_timeout= " , olpc_ec_timeout_set ) ;
/*
* These { i , o } bf_status functions return whether the buffers are full or not .
*/
static inline unsigned int ibf_status ( unsigned int port )
{
return ! ! ( inb ( port ) & 0x02 ) ;
}
static inline unsigned int obf_status ( unsigned int port )
{
return inb ( port ) & 0x01 ;
}
# define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
static int __wait_on_ibf ( unsigned int line , unsigned int port , int desired )
{
unsigned int timeo ;
int state = ibf_status ( port ) ;
for ( timeo = ec_timeout ; state ! = desired & & timeo ; timeo - - ) {
mdelay ( 1 ) ;
state = ibf_status ( port ) ;
}
if ( ( state = = desired ) & & ( ec_timeout > EC_BASE_TIMEOUT ) & &
timeo < ( ec_timeout - EC_BASE_TIMEOUT ) ) {
printk ( KERN_WARNING " olpc-ec: %d: waited %u ms for IBF! \n " ,
line , ec_timeout - timeo ) ;
}
return ! ( state = = desired ) ;
}
# define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
static int __wait_on_obf ( unsigned int line , unsigned int port , int desired )
{
unsigned int timeo ;
int state = obf_status ( port ) ;
for ( timeo = ec_timeout ; state ! = desired & & timeo ; timeo - - ) {
mdelay ( 1 ) ;
state = obf_status ( port ) ;
}
if ( ( state = = desired ) & & ( ec_timeout > EC_BASE_TIMEOUT ) & &
timeo < ( ec_timeout - EC_BASE_TIMEOUT ) ) {
printk ( KERN_WARNING " olpc-ec: %d: waited %u ms for OBF! \n " ,
line , ec_timeout - timeo ) ;
}
return ! ( state = = desired ) ;
}
/*
* This allows the kernel to run Embedded Controller commands . The EC is
* documented at < http : //wiki.laptop.org/go/Embedded_controller>, and the
* available EC commands are here :
* < http : //wiki.laptop.org/go/Ec_specification>. Unfortunately, while
* OpenFirmware ' s source is available , the EC ' s is not .
*/
2012-07-12 17:57:28 -07:00
static int olpc_xo1_ec_cmd ( u8 cmd , u8 * inbuf , size_t inlen , u8 * outbuf ,
size_t outlen , void * arg )
2008-04-29 00:59:53 -07:00
{
int ret = - EIO ;
int i ;
2010-10-01 18:17:19 +01:00
int restarts = 0 ;
2008-04-29 00:59:53 -07:00
/* Clear OBF */
for ( i = 0 ; i < 10 & & ( obf_status ( 0x6c ) = = 1 ) ; i + + )
inb ( 0x68 ) ;
if ( i = = 10 ) {
printk ( KERN_ERR " olpc-ec: timeout while attempting to "
" clear OBF flag! \n " ) ;
goto err ;
}
if ( wait_on_ibf ( 0x6c , 0 ) ) {
printk ( KERN_ERR " olpc-ec: timeout waiting for EC to "
" quiesce! \n " ) ;
goto err ;
}
restart :
/*
* Note that if we time out during any IBF checks , that ' s a failure ;
* we have to return . There ' s no way for the kernel to clear that .
*
* If we time out during an OBF check , we can restart the command ;
* reissuing it will clear the OBF flag , and we should be alright .
* The OBF flag will sometimes misbehave due to what we believe
* is a hardware quirk . .
*/
2010-06-16 23:19:28 -04:00
pr_devel ( " olpc-ec: running cmd 0x%x \n " , cmd ) ;
2008-04-29 00:59:53 -07:00
outb ( cmd , 0x6c ) ;
if ( wait_on_ibf ( 0x6c , 0 ) ) {
printk ( KERN_ERR " olpc-ec: timeout waiting for EC to read "
" command! \n " ) ;
goto err ;
}
if ( inbuf & & inlen ) {
/* write data to EC */
for ( i = 0 ; i < inlen ; i + + ) {
2011-07-26 16:42:26 +01:00
pr_devel ( " olpc-ec: sending cmd arg 0x%x \n " , inbuf [ i ] ) ;
outb ( inbuf [ i ] , 0x68 ) ;
2008-04-29 00:59:53 -07:00
if ( wait_on_ibf ( 0x6c , 0 ) ) {
printk ( KERN_ERR " olpc-ec: timeout waiting for "
" EC accept data! \n " ) ;
goto err ;
}
}
}
if ( outbuf & & outlen ) {
/* read data from EC */
for ( i = 0 ; i < outlen ; i + + ) {
if ( wait_on_obf ( 0x6c , 1 ) ) {
printk ( KERN_ERR " olpc-ec: timeout waiting for "
" EC to provide data! \n " ) ;
2010-10-01 18:17:19 +01:00
if ( restarts + + < 10 )
goto restart ;
goto err ;
2008-04-29 00:59:53 -07:00
}
outbuf [ i ] = inb ( 0x68 ) ;
2010-06-16 23:19:28 -04:00
pr_devel ( " olpc-ec: received 0x%x \n " , outbuf [ i ] ) ;
2008-04-29 00:59:53 -07:00
}
}
ret = 0 ;
err :
return ret ;
}
2011-03-13 15:10:17 +00:00
static bool __init check_ofw_architecture ( struct device_node * root )
2010-09-23 17:28:46 +01:00
{
2011-03-13 15:10:17 +00:00
const char * olpc_arch ;
int propsize ;
2010-09-23 17:28:46 +01:00
2011-03-13 15:10:17 +00:00
olpc_arch = of_get_property ( root , " architecture " , & propsize ) ;
2010-09-23 17:28:46 +01:00
return propsize = = 5 & & strncmp ( " OLPC " , olpc_arch , 5 ) = = 0 ;
}
2011-03-13 15:10:17 +00:00
static u32 __init get_board_revision ( struct device_node * root )
2008-04-29 00:59:53 -07:00
{
2011-03-13 15:10:17 +00:00
int propsize ;
const __be32 * rev ;
rev = of_get_property ( root , " board-revision-int " , & propsize ) ;
if ( propsize ! = 4 )
return 0 ;
return be32_to_cpu ( * rev ) ;
2008-04-29 00:59:53 -07:00
}
2010-09-23 17:28:46 +01:00
static bool __init platform_detect ( void )
2008-04-29 00:59:53 -07:00
{
2011-03-13 15:10:17 +00:00
struct device_node * root = of_find_node_by_path ( " / " ) ;
bool success ;
if ( ! root )
2010-09-23 17:28:46 +01:00
return false ;
2011-03-13 15:10:17 +00:00
success = check_ofw_architecture ( root ) ;
if ( success ) {
olpc_platform_info . boardrev = get_board_revision ( root ) ;
olpc_platform_info . flags | = OLPC_F_PRESENT ;
2019-05-13 09:56:34 +02:00
pr_info ( " OLPC board revision %s%X \n " ,
( ( olpc_platform_info . boardrev & 0xf ) < 8 ) ? " pre " : " " ,
olpc_platform_info . boardrev > > 4 ) ;
2011-03-13 15:10:17 +00:00
}
of_node_put ( root ) ;
return success ;
2008-04-29 00:59:53 -07:00
}
2010-10-13 19:10:42 +01:00
static int __init add_xo1_platform_devices ( void )
{
struct platform_device * pdev ;
pdev = platform_device_register_simple ( " xo1-rfkill " , - 1 , NULL , 0 ) ;
if ( IS_ERR ( pdev ) )
return PTR_ERR ( pdev ) ;
pdev = platform_device_register_simple ( " olpc-xo1 " , - 1 , NULL , 0 ) ;
2018-08-01 00:08:06 +08:00
return PTR_ERR_OR_ZERO ( pdev ) ;
2010-10-13 19:10:42 +01:00
}
2012-07-17 01:26:10 -07:00
static int olpc_xo1_ec_suspend ( struct platform_device * pdev )
{
/*
* Squelch SCIs while suspended . This is a fix for
* < http : //dev.laptop.org/ticket/1835>.
*/
return olpc_ec_cmd ( EC_SET_SCI_INHIBIT , NULL , 0 , NULL , 0 ) ;
}
static int olpc_xo1_ec_resume ( struct platform_device * pdev )
{
/* 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 ;
}
2012-07-12 17:57:28 -07:00
static struct olpc_ec_driver ec_xo1_driver = {
2012-07-17 01:26:10 -07:00
. suspend = olpc_xo1_ec_suspend ,
. resume = olpc_xo1_ec_resume ,
. ec_cmd = olpc_xo1_ec_cmd ,
2019-05-13 09:56:34 +02:00
# ifdef CONFIG_OLPC_XO1_SCI
/*
* XO - 1 EC wakeups are available when olpc - xo1 - sci driver is
* compiled in
*/
. wakeup_available = true ,
# endif
2012-07-17 01:26:10 -07:00
} ;
static struct olpc_ec_driver ec_xo1_5_driver = {
2012-07-12 17:57:28 -07:00
. ec_cmd = olpc_xo1_ec_cmd ,
2021-08-03 13:35:24 +02:00
# ifdef CONFIG_OLPC_XO15_SCI
2019-05-13 09:56:34 +02:00
/*
* XO - 1.5 EC wakeups are available when olpc - xo15 - sci driver is
* compiled in
*/
. wakeup_available = true ,
# endif
2011-06-25 17:34:13 +01:00
} ;
2008-04-29 00:59:53 -07:00
static int __init olpc_init ( void )
{
2010-10-13 19:10:42 +01:00
int r = 0 ;
2010-09-23 17:28:46 +01:00
if ( ! olpc_ofw_present ( ) | | ! platform_detect ( ) )
2008-04-29 00:59:53 -07:00
return 0 ;
2012-07-12 17:57:28 -07:00
/* register the XO-1 and 1.5-specific EC handler */
2012-07-17 01:26:10 -07:00
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 ) ;
2012-07-12 17:57:28 -07:00
platform_device_register_simple ( " olpc-ec " , - 1 , NULL , 0 ) ;
2008-04-29 00:59:53 -07:00
/* assume B1 and above models always have a DCON */
if ( olpc_board_at_least ( olpc_board ( 0xb1 ) ) )
olpc_platform_info . flags | = OLPC_F_DCON ;
2010-02-22 05:42:04 -08:00
# ifdef CONFIG_PCI_OLPC
2010-09-23 17:28:04 +01:00
/* If the VSA exists let it emulate PCI, if not emulate in kernel.
* XO - 1 only . */
if ( olpc_platform_info . boardrev < olpc_board_pre ( 0xd0 ) & &
! cs5535_has_vsa2 ( ) )
2010-02-22 05:42:04 -08:00
x86_init . pci . arch_init = pci_olpc_init ;
# endif
2008-04-29 00:59:53 -07:00
2010-10-13 19:10:42 +01:00
if ( olpc_platform_info . boardrev < olpc_board_pre ( 0xd0 ) ) { /* XO-1 */
r = add_xo1_platform_devices ( ) ;
if ( r )
return r ;
}
2008-04-29 00:59:53 -07:00
return 0 ;
}
postcore_initcall ( olpc_init ) ;